vue-cli项目结合Element-ui基于cropper.js封装vue图片裁剪组件

vue

前端工作中,经常需要图片裁剪的场景,cropper.js是一款优秀的前端插件,api十分丰富。

本文是在vue-cli项目下封装图片裁剪插件,效果图如下:

话不多说,看步骤吧。

第一步:准备开发环境

cropper.js是基于jquery的,所以要先安装jquery

执行命令:

  npm  install --save-dev jquery cropper

 为webpack配置添加jquery的映射

修改webpack.base.conf.js配置,添加标红的一行

第二步:新建图片裁剪组件 

index.vue内容:

由于用了element-ui,其中布局就引用了element-ui的组件

 template:

   

<template>

<div class="modal-dialog modal-lg" :id="id">

<div class="modal-content">

<form class="avatar-form" enctype="multipart/form-data" method="post">

<div class="modal-header">

</div>

<div class="modal-body">

<div class="avatar-body">

<!-- Upload image and data -->

<div class="avatar-upload">

<input type="hidden" class="avatar-src" name="avatar_src">

<input type="hidden" class="avatar-data" name="ci">

<label for="avatarInput" class="el-button el-button--primary">选择图片</label>

<input type="file" class="avatar-input " style="visibility: hidden" id="avatarInput" name="file">

</div>

<!-- Crop and preview -->

<el-row>

<el-col :span="18">

<div class="avatar-wrapper"></div>

</el-col>

<el-col :span="6" style="overflow: hidden;">

<div style="padding-left: 10px">

<div class="avatar-preview preview-lg" ></div>

<div class="avatar-preview avatar-preview-round preview-md"></div>

<!--<div class="avatar-preview preview-sm"></div>-->

</div>

</el-col>

</el-row>

<el-row class="avatar-btns">

<el-col :span="18">

<el-button-group>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-180" title="Rotate -180 degrees">-180deg</button>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-90" title="Rotate -90 degrees">-90deg</button>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-45" title="Rotate -45 degrees">-45deg</button>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="45" title="Rotate 45 degrees">45deg</button>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="90" title="Rotate 90 degrees">90deg</button>

<button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="180" title="Rotate 180 degrees">180deg</button>

</el-button-group>

</el-col>

<el-col :span="6"></el-col>

</el-row>

<el-row>

<!--<button type="submit" class="btn btn-primary btn-block avatar-save">裁取</button>-->

</el-row>

</div>

</div>

</form>

</div>

</div>

</template>

View Code

style:

<style rel="stylesheet/scss" lang=\'scss\' scoped>

/*@import "cropper/dist/cropper.css";*/

/*!

* Cropper v3.1.3

* https://github.com/fengyuanchen/cropper

*

* Copyright (c) 2014-2017 Chen Fengyuan

* Released under the MIT license

*

* Date: 2017-10-21T10:03:37.133Z

*/

.avatar-wrapper{

width: 100%;

height: 100%;

overflow: hidden;

}

.cropper-container {

direction: ltr;

font-size: 0;

line-height: 0;

position: relative;

-ms-touch-action: none;

touch-action: none;

-webkit-user-select: none;

-moz-user-select: none;

-ms-user-select: none;

user-select: none;

}

.cropper-container img {/*Avoid margin top issue (Occur only when margin-top <= -height)

*/

display: block;

height: 100%;

image-orientation: 0deg;

max-height: none !important;

max-width: none !important;

min-height: 0 !important;

min-width: 0 !important;

width: 100%;

}

.cropper-wrap-box,

.cropper-canvas,

.cropper-drag-box,

.cropper-crop-box,

.cropper-modal {

bottom: 0;

left: 0;

position: absolute;

right: 0;

top: 0;

}

.cropper-wrap-box,

.cropper-canvas {

overflow: hidden;

}

.cropper-drag-box {

background-color: #fff;

opacity: 0;

}

.cropper-modal {

background-color: #000;

opacity: .5;

}

.cropper-view-box {

display: block;

height: 100%;

outline-color: rgba(51, 153, 255, 0.75);

outline: 1px solid #39f;

overflow: hidden;

width: 100%;

}

.cropper-dashed {

border: 0 dashed #eee;

display: block;

opacity: .5;

position: absolute;

}

.cropper-dashed.dashed-h {

border-bottom-width: 1px;

border-top-width: 1px;

height: 33.33333%;

left: 0;

top: 33.33333%;

width: 100%;

}

.cropper-dashed.dashed-v {

border-left-width: 1px;

border-right-width: 1px;

height: 100%;

left: 33.33333%;

top: 0;

width: 33.33333%;

}

.cropper-center {

display: block;

height: 0;

left: 50%;

opacity: .75;

position: absolute;

top: 50%;

width: 0;

}

.cropper-center:before,

.cropper-center:after {

background-color: #eee;

content: \' \';

display: block;

position: absolute;

}

.cropper-center:before {

height: 1px;

left: -3px;

top: 0;

width: 7px;

}

.cropper-center:after {

height: 7px;

left: 0;

top: -3px;

width: 1px;

}

.cropper-face,

.cropper-line,

.cropper-point {

display: block;

height: 100%;

opacity: .1;

position: absolute;

width: 100%;

}

.cropper-face {

background-color: #fff;

left: 0;

top: 0;

}

.cropper-line {

background-color: #39f;

}

.cropper-line.line-e {

cursor: e-resize;

right: -3px;

top: 0;

width: 5px;

}

.cropper-line.line-n {

cursor: n-resize;

height: 5px;

left: 0;

top: -3px;

}

.cropper-line.line-w {

cursor: w-resize;

left: -3px;

top: 0;

width: 5px;

}

.cropper-line.line-s {

bottom: -3px;

cursor: s-resize;

height: 5px;

left: 0;

}

.cropper-point {

background-color: #39f;

height: 5px;

opacity: .75;

width: 5px;

}

.cropper-point.point-e {

cursor: e-resize;

margin-top: -3px;

right: -3px;

top: 50%;

}

.cropper-point.point-n {

cursor: n-resize;

left: 50%;

margin-left: -3px;

top: -3px;

}

.cropper-point.point-w {

cursor: w-resize;

left: -3px;

margin-top: -3px;

top: 50%;

}

.cropper-point.point-s {

bottom: -3px;

cursor: s-resize;

left: 50%;

margin-left: -3px;

}

.cropper-point.point-ne {

cursor: ne-resize;

right: -3px;

top: -3px;

}

.cropper-point.point-nw {

cursor: nw-resize;

left: -3px;

top: -3px;

}

.cropper-point.point-sw {

bottom: -3px;

cursor: sw-resize;

left: -3px;

}

.cropper-point.point-se {

bottom: -3px;

cursor: se-resize;

height: 20px;

opacity: 1;

right: -3px;

width: 20px;

}

@media (min-width: 768px) {

.cropper-point.point-se {

height: 15px;

width: 15px;

}

}

@media (min-width: 992px) {

.cropper-point.point-se {

height: 10px;

width: 10px;

}

}

@media (min-width: 1200px) {

.cropper-point.point-se {

height: 5px;

opacity: .75;

width: 5px;

}

}

.cropper-point.point-se:before {

background-color: #39f;

bottom: -50%;

content: \' \';

display: block;

height: 200%;

opacity: 0;

position: absolute;

right: -50%;

width: 200%;

}

.cropper-invisible {

opacity: 0;

}

.cropper-bg {

background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC\');

}

.cropper-hide {

display: block;

height: 0;

position: absolute;

width: 0;

}

.cropper-hidden {

display: none !important;

}

.cropper-move {

cursor: move;

}

.cropper-crop {

cursor: crosshair;

}

.cropper-disabled .cropper-drag-box,

.cropper-disabled .cropper-face,

.cropper-disabled .cropper-line,

.cropper-disabled .cropper-point {

cursor: not-allowed;

}

.avatar-view {

display: block;

margin: 15% auto 5%;

height: 220px;

width: 220px;

border: 3px solid #fff;

border-radius: 5px;

box-shadow: 0 0 5px rgba(0,0,0,.15);

cursor: pointer;

overflow: hidden;

}

.avatar-view img {

width: 100%;

}

.avatar-body {

padding-right: 15px;

padding-left: 15px;

}

.avatar-upload {

overflow: hidden;

}

.avatar-upload label {

display: block;

float: left;

clear: left;

width: 100px;

}

.avatar-upload input {

display: block;

margin-left: 110px;

}

.avatar-alert {

margin-top: 10px;

margin-bottom: 10px;

}

.avatar-wrapper {

height: 364px;

width: 100%;

margin-top: 15px;

box-shadow: inset 0 0 5px rgba(0,0,0,.25);

background-color: #fcfcfc;

overflow: hidden;

}

.avatar-wrapper img {

display: block;

height: auto;

max-width: 100%;

}

.avatar-preview {

float: left;

margin-top: 15px;

margin-right: 15px;

border: 1px solid #eee;

border-radius: 4px;

background-color: #fff;

overflow: hidden;

}

.avatar-preview:hover {

border-color: #ccf;

box-shadow: 0 0 5px rgba(0,0,0,.15);

}

.avatar-preview img {

width: 100%;

}

.avatar-preview-round{

border-radius: 50%;

}

.preview-lg {

height: 184px;

width: 184px;

margin-top: 15px;

}

.preview-md {

height: 100px;

width: 100px;

}

.preview-sm {

height: 50px;

width: 50px;

}

@media (min-width: 992px) {

.avatar-preview {

float: none;

}

}

.avatar-btns {

margin-top: 30px;

margin-bottom: 15px;

}

.avatar-btns .btn-group {

margin-right: 5px;

}

</style>

View Code

script:

<script>

import $ from \'jquery\'

import \'cropper/dist/cropper.js\'

export default {

props:{

id:String

},

data(){

return {

$container:null,

$avatarView:null,

$avatarModal : null,

$loading : null,

$avatarForm : null,

$avatarUpload : null,

$avatarSrc : null,

$avatarData : null,

$avatarInput : null,

$avatarSave: null,

$avatarBtns : null,

$avatarWrapper : null,

$avatarPreview: null,

support: {

fileList: !!$(\'<input type="file">\').prop(\'files\'),

blobURLs: !!window.URL && URL.createObjectURL,

formData: !!window.FormData

}

}

},

created(){},

mounted(){

this.$container = $(\'#\'+this.id);

this.$avatarForm = this.$container.find(\'.avatar-form\');

this.$avatarUpload = this.$avatarForm.find(\'.avatar-upload\');

this.$avatarSrc = this.$avatarForm.find(\'.avatar-src\');

this.$avatarData = this.$avatarForm.find(\'.avatar-data\');

this.$avatarInput = this.$avatarForm.find(\'.avatar-input\');

this.$avatarSave = this.$avatarForm.find(\'.avatar-save\');

this.$avatarWrapper = this.$container.find(\'.avatar-wrapper\');

this.$avatarPreview = this.$container.find(\'.avatar-preview\');

this.$avatarBtns = this.$container.find(\'.avatar-btns\');

this.$nextTick(function () {

this.init();

})

},

methods:{

init: function () {

this.support.datauri = this.support.fileList && this.support.blobURLs;

this.addListener();

// this.startCropper();

},

addListener: function () {

this.$avatarInput.on(\'change\', $.proxy(this.change, this));

this.$avatarForm.on(\'submit\', $.proxy(this.submit, this));

this.$avatarBtns.on(\'click\', $.proxy(this.rotate, this));

},

initPreview: function () {

var url = this.$avatar.attr(\'src\');

this.$avatarPreview.html(\'<img src="\' + url + \'">\');

},

initIframe: function () {

var target = \'upload-iframe-\' + (new Date()).getTime();

var $iframe = $(\'<iframe>\').attr({

name: target,

src: \'\'

});

var _this = this;

// Ready ifrmae

$iframe.one(\'load\', function () {

// respond response

$iframe.on(\'load\', function () {

var data;

try {

data = $(this).contents().find(\'body\').text();

} catch (e) {

console.log(e.message);

}

if (data) {

try {

data = $.parseJSON(data);

} catch (e) {

console.log(e.message);

}

_this.submitDone(data);

} else {

}

_this.submitEnd();

});

});

this.$iframe = $iframe;

this.$avatarForm.attr(\'target\', target).after($iframe.hide());

},

click:function () {

this.initPreview();

},

change: function () {

var files;

var file;

if (this.support.datauri) {

files = this.$avatarInput.prop(\'files\');

if (files.length > 0) {

file = files[0];

if (this.isImageFile(file)) {

if (this.url) {

URL.revokeObjectURL(this.url); // Revoke the old one

}

this.url = URL.createObjectURL(file);

this.startCropper();

}

}

} else {

file = this.$avatarInput.val();

if (this.isImageFile(file)) {

this.syncUpload();

}

}

},

//裁剪提交

submit: function () {

if (!this.$avatarSrc.val() && !this.$avatarInput.val()) {

return false;

}

if (this.support.formData) {

this.ajaxUpload();

return false;

}

},
//旋转事件

rotate: function (e) {

var data;

if (this.active) {

data = $(e.target).data();

if (data.method) {

this.$img.cropper(data.method, data.option);

}

}

},

isImageFile: function (file) {

if (file.type) {

return /^image\/\w+$/.test(file.type);

} else {

return /\.(jpg|jpeg|png|gif)$/.test(file);

}

},

startCropper: function () {

var _this = this;

if (this.active) {

this.$img.cropper(\'replace\', this.url);

} else {

this.$img = $(\'<img src="\' + this.url + \'">\');

this.$avatarWrapper.empty().html(this.$img);

this.$img.cropper({

viewMode:1,

aspectRatio: 1,

preview: this.$avatarPreview,

restore:false,

crop: function (e) {

var json = [

\'{"x":\' + e.x,

\'"y":\' + e.y,

\'"height":\' + e.height,

\'"width":\' + e.width,

\'"rotate":\' + e.rotate + \'}\'

].join();

//裁图参数存起来

_this.$avatarData.val(json);

}

});

this.active = true;

}

},

stopCropper: function () {

if (this.active) {

this.$img.cropper(\'destroy\');

this.$img.remove();

this.active = false;

}

},

ajaxUpload: function () {

var url = \'/oss/file/cropping\';

var data = new FormData(this.$avatarForm[0]);

var _this = this;

$.ajax(url, {

type: \'post\',

data: data,

dataType: \'json\',

processData: false,

contentType: false,

success: function (data,textStatus) {

_this.submitDone(data);

if(data.success){

//将返回的数据传给父组件

_this.$emit(\'cropper-success\',data.data);

_this.cropDone();

}

},

});

},

syncUpload: function () {

this.$avatarSave.click();

},

submitDone: function (data) {

if ($.isPlainObject(data) && data.state === 200) {

if (data.result) {

this.url = data.result;

if (this.support.datauri || this.uploaded) {

this.uploaded = false;

this.cropDone();

} else {

this.uploaded = true;

this.$avatarSrc.val(this.url);

this.startCropper();

}

this.$avatarInput.val(\'\');

} else if (data.message) {

}

} else {

}

},

cropDone: function () {

// this.$avatarForm.get(0).reset();

// this.$avatarSrc.prop(\'src\', this.url);

this.stopCropper();

// this.$container.hide();

}

}

}

</script>

 第三步:父组件引用子组件

用了element-ui中的 el-dialog组件,此时el-dialog组件为父组件

在父组件中引入子组件

import cropper from \'@/components/Cropper/index\'

template:

<template>

<div class="app-main-content" >

<el-dialog :visible.sync="showCropper" title="封面裁图" width="70%">

<cropper id="avatarCrop" ref="cropper" @cropper-success="cropperSuccessHandle"></cropper>

<span slot="footer" class="dialog-footer">

<el-button @click="cancelCropper">取 消</el-button>

<el-button type="primary" @click="toCropper">确 定</el-button>

</span>

</el-dialog>

</div>

script:

import cropper from \'@/components/Cropper/index\'

export default {

name: \'addNews\',

components:{

cropper

},

data(){

return {

avatarUrl2: null,

showCropper:false

}

},

methods:{

//隐藏裁剪框

cancelCropper(){

this.showCropper = false

this.$refs.cropper.cropDone();

},

//父组件调用子组件裁剪方法

toCropper(){

this.$refs.cropper.submit();

},

//子组件裁剪方法成功执行后与父组件通信

cropperSuccessHandle(data){

//返回data

this.showCropper = false

this.avatarUrl2 = data.url

}

}

}

本文结合element-ui,vue-cli,jquery,cropper.js,实现裁图组件的封装,先写到这啦,如果对你有帮助,还请点个赞噢!

以上是 vue-cli项目结合Element-ui基于cropper.js封装vue图片裁剪组件 的全部内容, 来源链接: utcz.com/z/375255.html

回到顶部