vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能
程序员文章站
2023-08-02 22:13:34
前端工作中,经常需要图片裁剪的场景,cropper.js是一款优秀的前端插件,api十分丰富。
本文是在vue-cli项目下封装图片裁剪插件,效果图如下:
话不多说...
前端工作中,经常需要图片裁剪的场景,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>
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>
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实现图片裁剪组件功能
-
基于cropper.js封装vue实现在线图片裁剪组件功能
-
vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能
-
使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能
-
基于cropper.js封装vue实现在线图片裁剪组件功能
-
基于cropper.js封装vue实现在线图片裁剪组件的功能
-
利用cropper.js封装vue如何实现在线图片裁剪组件功能(详细教程)
-
使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能
-
利用cropper.js封装vue如何实现在线图片裁剪组件功能(详细教程)
-
基于cropper.js封装vue实现在线图片裁剪组件的功能