vue-cli项目结合Element-ui基于cropper.js封装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