前段时间做微信公众号Vue项目的时候,要用到图片裁剪功能,但是上网找的好几个vue插件在ios端都会出现问题, 但是这个完美兼容ios与安卓,可以放心使用。

是由 https://www.jianshu.com/p/82dbf309f9b1?from=groupmessage 这个修改而来的,已经修改成了在单独的组件可以用了,详情请看前面的链接。

修改后的组件完整代码如下:

<!-- 这个图片剪裁插件,兼容ios与安卓 --><template><div class="vue-box"><img :src="imgUrl" alt="" id="img"><input type="file" class="file" accept="image/png,image/jpg,image/jpeg" @change="change($event)"></div>
</template>
<script>
import Cropper from 'cropperjs'
import Exif from 'exif-js'
export default {props: {// 定义的宽高比widthRate: {type: Number,default: 1,},// 定义的宽高比heightRate: {type: Number,default: 1},imgUrl: {type: String,default: function() {return ''}}},data() {return {}},methods: {change(event) {let image = document.getElementById('img'); //预览对象this.clip(event, {resultObj: image,aspectWithRatio: Number(this.widthRate),aspectHeightRatio: Number(this.heightRate)})},//初始化方法initilize(opt) {let self = this;this.options = opt;//创建domthis.createElement();this.resultObj = opt.resultObj;//初始化裁剪对象this.cropper = new Cropper(this.preview, {aspectRatio: opt.aspectWithRatio / opt.aspectHeightRatio,// aspectRatio: 1/1,autoCropArea: opt.autoCropArea || 0.8,viewMode: 2,guides: true,cropBoxResizable: true, //是否通过拖动来调整剪裁框的大小cropBoxMovable: true, //是否通过拖拽来移动剪裁框。dragCrop: false,dragMode: "move",//‘crop’: 可以产生一个新的裁剪框3 ‘move’: 只可以移动3 ‘none’: 什么也不处理center: true,zoomable: true, //是否允许放大图像。zoomOnTouch: true,//是否可以通过拖动触摸来放大图像。scalable: true,// minCropBoxHeight: 750,// minCropBoxWidth: 750,background: false,checkOrientation: true,checkCrossOrigin: true,zoomable: false,zoomOnWheel: false,center: false,toggleDragModeOnDblclick: false,ready: function() {// console.log(self.cropper.rotate(90))if (opt.aspectRatio == 'Free') {let cropBox = self.cropper.cropBox;cropBox.querySelector('span.cropper-view-box').style.outline = 'none';self.cropper.disable();}}});},//创建一些必要的DOM,用于图片裁剪createElement() {//初始化图片为空对象this.preview = null;// <img src="../../assets/app/loading.gif">let str = '<div><img id="clip_image" src="originUrl"></div><button type="button" id="cancel_clip">取消</button><button type="button" id="clip_button">确定</button>';str += '<div class="crop_loading"><div class="crop_content"><div class="crop_text">图片修剪中...</div></div></div>';str += '<div class="crop_success"><div class="crop_success_text">上传成功</div></div></div>';let body = document.getElementsByTagName('body')[0];this.reagion = document.createElement('div');this.reagion.id = 'clip_container';this.reagion.className = 'container';this.reagion.innerHTML = str;//添加创建好的DOM元素body.appendChild(this.reagion);this.preview = document.getElementById('clip_image');//绑定一些方法this.initFunction();},//初始化一些函数绑定initFunction() {let self = this;this.clickBtn = document.getElementById('clip_button');this.cancelBtn = document.getElementById('cancel_clip');//确定事件this.addEvent(this.clickBtn, 'click', function() {self.crop();})//取消事件this.addEvent(this.cancelBtn, 'click', function() {self.destoried();})//清空input的值this.addEvent(this.fileObj, 'click', function() {this.value = '';})},//外部接口,用于input['file']对象change时的调用clip(e, opt) {let self = this;this.fileObj = e.srcElement;let files = e.target.files || e.dataTransfer.files;if (!files.length) return false; //不是图片直接返回//调用初始化方法this.initilize(opt);//获取图片文件资源this.picValue = files[0];//去获取拍照时的信息,解决拍出来的照片旋转问题// Exif.getData( files[0] , function(){//   self.Orientation = Exif.getTag( files[0], 'Orientation');//   console.log(self.Orientation)// });//调用方法转成url格式this.originUrl = this.getObjectURL(this.picValue);//每次替换图片要重新得到新的urlif (this.cropper) {this.cropper.replace(this.originUrl);}},//图片转码方法getObjectURL(file) {let url = null;if (window.createObjectURL != undefined) { // basicurl = window.createObjectURL(file);} else if (window.URL != undefined) { // mozilla(firefox)url = window.URL.createObjectURL(file);} else if (window.webkitURL != undefined) { // webkit or chromeurl = window.webkitURL.createObjectURL(file);}return url;},//点击确定进行裁剪crop() {let self = this;let image = new Image();let croppedCanvas;let roundedCanvas;// Cropdocument.querySelector('.crop_loading').style.display = 'block';setTimeout(function() {croppedCanvas = self.cropper.getCroppedCanvas();// RoundroundedCanvas = self.getRoundedCanvas(croppedCanvas);let imgData = roundedCanvas.toDataURL();image.src = imgData;//判断图片是否大于100k,不大于直接上传,反之压缩if (imgData.length < (100 * 1024)) {self.resultObj.src = imgData;//图片上传self.postImg(imgData);} else {image.onload = function() {//压缩处理let data = self.compress(image, self.Orientation);self.resultObj.src = data;//图片上传self.postImg(data);}}}, 20)},//获取裁剪图片资源getRoundedCanvas(sourceCanvas) {let canvas = document.createElement('canvas');let context = canvas.getContext('2d');let width = sourceCanvas.width;let height = sourceCanvas.height;canvas.width = width;canvas.height = height;context.imageSmoothingEnabled = true;context.drawImage(sourceCanvas, 0, 0, width, height);context.globalCompositeOperation = 'destination-in';context.beginPath();context.rect(0, 0, width, height);context.fill();return canvas;},//销毁原来的对象destoried() {let self = this;//移除事件this.removeEvent(this.clickBtn, 'click', null);this.removeEvent(this.cancelBtn, 'click', null);this.removeEvent(this.fileObj, 'click', null);//移除裁剪框this.reagion.parentNode.removeChild(this.reagion);//销毁裁剪对象this.cropper.destroy();this.cropper = null;},//图片上传postImg(imageData) {// console.log(imageData)this.$emit('callback', imageData)//这边写图片的上传let self = this;self.destoried();// window.setTimeout( function () {//   document.querySelector('.crop_loading').style.display = 'none';//   document.querySelector('.crop_success').style.display = 'block';//   //裁剪完后摧毁对象//     self.destoried();// },3000)},//图片旋转rotateImg(img, direction, canvas) {//最小与最大旋转方向,图片旋转4次后回到原方向const min_step = 0;const max_step = 3;if (img == null) return;//img的高度和宽度不能在img元素隐藏后获取,否则会出错let height = img.height;let width = img.width;let step = 2;if (step == null) {step = min_step;}if (direction == 'right') {step++;//旋转到原位置,即超过最大值step > max_step && (step = min_step);} else {step--;step < min_step && (step = max_step);}//旋转角度以弧度值为参数let degree = step * 90 * Math.PI / 180;let ctx = canvas.getContext('2d');switch (step) {case 0:canvas.width = width;canvas.height = height;ctx.drawImage(img, 0, 0);break;case 1:canvas.width = height;canvas.height = width;ctx.rotate(degree);ctx.drawImage(img, 0, -height);break;case 2:canvas.width = width;canvas.height = height;ctx.rotate(degree);ctx.drawImage(img, -width, -height);break;case 3:canvas.width = height;canvas.height = width;ctx.rotate(degree);ctx.drawImage(img, -width, 0);break;}},//图片压缩compress(img, Orientation) {let canvas = document.createElement("canvas");let ctx = canvas.getContext('2d');//瓦片canvaslet tCanvas = document.createElement("canvas");let tctx = tCanvas.getContext("2d");let initSize = img.src.length;let width = img.width;let height = img.height;//如果图片大于四百万像素,计算压缩比并将大小压至400万以下let ratio;if ((ratio = width * height / 4000000) > 1) {console.log("大于400万像素")ratio = Math.sqrt(ratio);width /= ratio;height /= ratio;} else {ratio = 1;}canvas.width = width;canvas.height = height;//        铺底色ctx.fillStyle = "#fff";ctx.fillRect(0, 0, canvas.width, canvas.height);//如果图片像素大于100万则使用瓦片绘制let count;if ((count = width * height / 1000000) > 1) {count = ~~(Math.sqrt(count) + 1); //计算要分成多少块瓦片//            计算每块瓦片的宽和高let nw = ~~(width / count);let nh = ~~(height / count);tCanvas.width = nw;tCanvas.height = nh;for (let i = 0; i < count; i++) {for (let j = 0; j < count; j++) {tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);}}} else {ctx.drawImage(img, 0, 0, width, height);}//修复ios上传图片的时候 被旋转的问题if (Orientation != "" && Orientation != 1) {switch (Orientation) {case 6: //需要顺时针(向左)90度旋转this.rotateImg(img, 'left', canvas);break;case 8: //需要逆时针(向右)90度旋转this.rotateImg(img, 'right', canvas);break;case 3: //需要180度旋转this.rotateImg(img, 'right', canvas); //转两次this.rotateImg(img, 'right', canvas);break;}}//进行最小压缩// let ndata = canvas.toDataURL( 'image/jpeg' , 0.1);let ndata = canvas.toDataURL('image/png', 0.1);console.log('压缩前:' + initSize);console.log('压缩后:' + ndata.length);console.log('压缩率:' + ~~(100 * (initSize - ndata.length) / initSize) + "%");tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;return ndata;},//添加事件addEvent(obj, type, fn) {if (obj.addEventListener) {obj.addEventListener(type, fn, false);} else {obj.attachEvent('on' + type, fn);}},//移除事件removeEvent(obj, type, fn) {if (obj.removeEventListener) {obj.removeEventListener(type, fn, false);} else {obj.detachEvent('on' + type, fn);}},}
}
</script>
<style scoped>
.vue-box {position: relative;width: 100%;height: 100%;min-height: 0.4rem;
}.vue-box .file {position: absolute;width: 100%;height: 100%;opacity: 0;top: 0;left: 0;z-index: 99;cursor: pointer;
}img {display: none;width: 100%;height: 100%;
}h3 {text-align: center;
}h1,
h2 {font-weight: normal;
}ul {list-style-type: none;padding: 0;
}li {display: inline-block;margin: 0 10px;
}a {color: #42b983;
}
</style>
<style>
#app {font-family: 'Avenir', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}* {margin: 0;padding: 0;
}#app {font-family: 'Avenir', Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;box-sizing: border-box;
}img {/*display: block;*//*margin: 0 auto;*/
}input[type='file'] {outline: none;/*margin-top: 20px;*/
}* {margin: 0;padding: 0;
}#clip_button {position: absolute;right: 10%;bottom: 20px;width: 80px;height: 40px;border: none;border-radius: 2px;background: #1AAD19;color: #fff;
}#cancel_clip {position: absolute;left: 10%;bottom: 20px;width: 80px;height: 40px;border: none;border-radius: 2px;color: #fff;background: #E64340;
}#clip_container.container {z-index: 99999;position: fixed;padding-top: 60px;left: 0;top: 0;right: 0;bottom: 0;background: rgba(0, 0, 0, 1);
}#clip_container.container>div {position: absolute;width: 100%;height: 100%;top: 50%;left: 50%;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);
}#clip_image {max-width: 100%;
}.cropper-container {font-size: 0;line-height: 0;position: relative;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;direction: ltr;-ms-touch-action: none;touch-action: none
}.crop_loading,
.crop_success {display: none;position: fixed;top: 0;left: 0;width: 100%;height: 100%;z-index: 9;
}.crop_loading .crop_content {position: absolute;top: 50%;left: 50%;text-align: center;background: #000;opacity: 0.9;height: 66px;width: 140px;vertical-align: middle;color: #fff;padding-top: 20px;font-size: 16px;-webkit-border-radius: 3px;border-radius: 3px;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);
}.crop_loading .crop_content img {margin-top: 15px;margin-bottom: 10px;
}.crop_success .crop_success_text {position: absolute;top: 50%;left: 50%;text-align: center;background: #000;opacity: 0.9;width: 120px;height: 30px;color: #fff;line-height: 30px;font-size: 16px;-webkit-border-radius: 3px;border-radius: 3px;-webkit-transform: translate(-50%, -50%);transform: translate(-50%, -50%);
}.cropper-container img {/* Avoid margin top issue (Occur only when margin-top <= -height) */display: block;min-width: 0 !important;max-width: none !important;min-height: 0 !important;max-height: none !important;width: 100%;height: 100%;image-orientation: 0deg
}.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {position: absolute;top: 0;right: 0;bottom: 0;left: 0;
}.cropper-wrap-box {overflow: hidden;
}.cropper-drag-box {opacity: 0;background-color: #fff;
}.cropper-modal {opacity: .5;background-color: #000;
}.cropper-view-box {display: block;overflow: hidden;width: 100%;height: 100%;outline: 1px solid #39f;outline-color: rgba(51, 153, 255, 0.75);
}.cropper-dashed {position: absolute;display: block;opacity: .5;border: 0 dashed #eee
}.cropper-dashed.dashed-h {top: 33.33333%;left: 0;width: 100%;height: 33.33333%;border-top-width: 1px;border-bottom-width: 1px
}.cropper-dashed.dashed-v {top: 0;left: 33.33333%;width: 33.33333%;height: 100%;border-right-width: 1px;border-left-width: 1px
}.cropper-center {position: absolute;top: 50%;left: 50%;display: block;width: 0;height: 0;opacity: .75
}.cropper-center:before,
.cropper-center:after {position: absolute;display: block;content: ' ';background-color: #eee
}.cropper-center:before {top: 0;left: -3px;width: 7px;height: 1px
}.cropper-center:after {top: -3px;left: 0;width: 1px;height: 7px
}.cropper-face,
.cropper-line,
.cropper-point {position: absolute;display: block;width: 100%;height: 100%;opacity: .1;
}.cropper-face {top: 0;left: 0;background-color: #fff;
}.cropper-line {background-color: #39f
}.cropper-line.line-e {top: 0;right: -3px;width: 5px;cursor: e-resize
}.cropper-line.line-n {top: -3px;left: 0;height: 5px;cursor: n-resize
}.cropper-line.line-w {top: 0;left: -3px;width: 5px;cursor: w-resize
}.cropper-line.line-s {bottom: -3px;left: 0;height: 5px;cursor: s-resize
}.cropper-point {width: 5px;height: 5px;opacity: .75;background-color: #39f
}.cropper-point.point-e {top: 50%;right: -3px;margin-top: -3px;cursor: e-resize
}.cropper-point.point-n {top: -3px;left: 50%;margin-left: -3px;cursor: n-resize
}.cropper-point.point-w {top: 50%;left: -3px;margin-top: -3px;cursor: w-resize
}.cropper-point.point-s {bottom: -3px;left: 50%;margin-left: -3px;cursor: s-resize
}.cropper-point.point-ne {top: -3px;right: -3px;cursor: ne-resize
}.cropper-point.point-nw {top: -3px;left: -3px;cursor: nw-resize
}.cropper-point.point-sw {bottom: -3px;left: -3px;cursor: sw-resize
}.cropper-point.point-se {right: -3px;bottom: -3px;width: 20px;height: 20px;cursor: se-resize;opacity: 1
}@media (min-width: 768px) {.cropper-point.point-se {width: 15px;height: 15px}
}@media (min-width: 992px) {.cropper-point.point-se {width: 10px;height: 10px}
}@media (min-width: 1200px) {.cropper-point.point-se {width: 5px;height: 5px;opacity: .75}
}.cropper-point.point-se:before {position: absolute;right: -50%;bottom: -50%;display: block;width: 200%;height: 200%;content: ' ';opacity: 0;background-color: #39f
}.cropper-invisible {opacity: 0;
}.cropper-bg {background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMzTjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}.cropper-hide {position: absolute;display: block;width: 0;height: 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;
}
</style>

基于Vue的移动端图片裁剪组件 vue-clip(完美兼容ios与安卓)相关推荐

  1. 【Vue】移动端图片裁剪组件--vue-imgcut

    移动端图片裁剪组件–vue-imgcut 安装 npm install vue-imgcut --save GitHub地址 使用说明 template模板中 callback :回调函数 img 是 ...

  2. vue 移动端头像裁剪_基于vue的移动端图片裁剪压缩处理

    前端时间做Vue项目的时候,要用到图片压缩处理,网上查阅了资料后,发现并不能满足实际的业务需求:于是,自己结合网上的资料写了一个,有兴趣的小伙伴不妨试试~ 废话不多说,先上效果图,无图无真相嘛~ 效果 ...

  3. VUE图片裁剪组件,基于VueCropper

    VUE图片裁剪组件,基于VueCropper 搬砖的同志麻烦点个赞支持下 效果图 第一步安装vue-cropper插件 npm install vue-cropper 第二步创建组件,把下面的代码复制 ...

  4. vue图片裁剪组件_使用Vue-Rx的Vue.js图像裁剪组件

    vue图片裁剪组件 Vuejs夹 (vuejs-clipper) Vue.js image clipping components using Vue-Rx. 使用Vue-Rx的Vue.js图像裁剪组 ...

  5. vue图片裁剪组件_Vue.js图像裁剪组件

    vue图片裁剪组件 Vuejs夹 (vuejs-clipper) Vue.js image clipping components using Vue-Rx. 使用Vue-Rx的Vue.js图像裁剪组 ...

  6. Vue性能优化:图片与组件,如何实现按需加载?

    Vue性能优化:图片与组件,如何实现按需加载? 前沿:按需加载是性能优化其中的一个环节,可以是图片的按需加载,也就是lazyload来实现按需加载的场景,也可以是组件库的引入,只需部分组件的使用而无需 ...

  7. HarmonyOS第三方组件——鸿蒙图片裁剪组件ohos-Image-Cropper

    前言 基于安卓的平台的图片裁剪组件(https://github.com/ArthurHub/Android-Image-Cropper),实现了鸿蒙平台的迁移和重构,代码已经开源到(https:// ...

  8. html上传图片裁剪,基于HTML5+JS实现本地图片裁剪并上传功能

    基于HTML5+JS实现本地图片裁剪并上传功能 2019-01-07 编程之家 https://www.jb51.cc 编程之家收集整理的这篇文章主要介绍了基于HTML5+JS实现本地图片裁剪并上传功 ...

  9. vue实现移动端图片预览

    vue实现移动端图片预览 1.安装插件 2.main引入 3.使用 注意 扩展 通过使用vue-photo-preview插件,实现移动端图片的预览,全屏等功能. 1.安装插件 npm install ...

  10. vue使用cropperjs实现移动端图片裁剪上传组件

    本组件基于vuejs框架, 使用ES6基本语法, css预编译采用的scss, 图片裁剪模块基于cropperjs,拍照时的图片信息获取使用exif, 图片上传使用XMLHttpRequest 该组件 ...

最新文章

  1. 2019嵌入式智能国际大会圆满落幕,7大专场精彩瞬间释出!
  2. ML基石_LinearModelsForClassfication
  3. 旋转角度_办公娱乐新神器!这款稳固的创意支架,360°旋转随便换角度
  4. Linux操作系统ssh默认22端口修改方法
  5. 检查表单元素的值是否为空
  6. Kafka数据存储详解
  7. 程序员成熟的标志《程序员成长路线图:从入门到优秀》
  8. centos7.0查看IP
  9. 使C#代码现代化——第三部分:值
  10. .NET 工具生成引擎概述
  11. mysql 插入慢_Mongodb 与 MySQL对比
  12. 如何写一份交互说明文档
  13. 我说我水平高,因为我CSDN博客排名前100
  14. Rust : *mut c_void 转型 ‘a mut dyn T
  15. 论文画图软件(转载)
  16. 纠错码 - 海明码/汉明码
  17. mysql 枚举索引_MySQL 索引总结
  18. http://blog.csdn.net/myy629464/article/list/1
  19. 稳定的围棋服务器,中国古典围棋服务器对奕说明
  20. 单相桥式半控整流电路

热门文章

  1. 编程语言选型之技法融合,与领域融合的那些套路
  2. 央行数字货币DCEP首个应用场景落地,新的支付风口领域已现!
  3. 计算机程序设计流程图循环,流程图无限循环怎么画流程图循环结构怎么画
  4. 外设驱动库开发笔记41:ADS1256 ADC驱动
  5. git报错the remote end hung up unexpectedly
  6. python中eval和ast.literal_eval的区别
  7. bootstrap treeview 多级联动check/uncheck
  8. 单片机IO口科普:推挽输出、开漏输出详解
  9. XTU1340Wave
  10. NAACL 2022 | TAMT:通过下游任务无关掩码训练搜索可迁移的BERT子网络