vue项目中实现图片裁剪功能

原文地址https://github.com/MY729/pict... 在vue项目中实现图片裁剪功能演示地址https://my729.github.io/picture-crop-demo/dist/#/ 前言vue版本:3.6.3 https://cli.vuejs.org/zh/cropperjs: 1.5.1 https://github.com/fengyuanchen/cropperjselementUI https://element.eleme.io/#/zh-CN使用 cropperjs插件 和 原生canvas 两种方式实现图片裁剪功能使用cropperjs插件安装cropperjsyarn install cropperjs初始化一个canvas元素,并在上面绘制图片<canvas :id="data.src" ref="canvas"></canvas>// 在canvas上绘制图片drawImg () { this.$nextTick(() => { // 获取canvas节点 let canvas = document.getElementById(this.data.src) if (canvas) { // 设置canvas的宽为canvas的父元素宽度,宽高比3:2 let parentEle = canvas.parentElement canvas.width = parentEle.offsetWidth canvas.height = 2 * parentEle.offsetWidth / 3 let ctx = canvas.getContext('2d') ctx.clearRect(0, 0, canvas.width, canvas.height) let img = new Image() img.crossOrigin = 'Anonymous' img.src = this.data.src img.onload = function () { ctx.drawImage(img, 0, 0, canvas.width, canvas.height) } } })}如果遇到canvas跨域绘制图片报错,设置图片img.crossOrigin = 'Anonymous',并且服务器响应头设置Access-Control-Allow-Origin:*创建cropperjs// 引入import Cropper from 'cropperjs'// 显示裁剪框initCropper () { let cropper = new Cropper(this.$refs.canvas, { checkCrossOrigin: true, viewMode: 2, aspectRatio: 3 / 2 })}更多方法和属性,参考官网: https://github.com/fengyuanchen/cropperjs具体实现,可以查看源码的cropper.vue 或 cropper.one.vue组件: ...

June 6, 2019 · 3 min · jiezi

关于vue项目中移动端实现用户选择照片照片裁剪一次上传多张图片仿微信发朋友圈功能

关于vue项目中移动端实现用户选择照片、照片裁剪、一次上传多张图片功能(仿微信发朋友圈)。最终要实现的效果如下图所示: 涉及的功能有1、图片的选择2、图片从手机相机选择、拍照3、图片选择后的裁剪4、图片在页面的显示效果5、图片的删除6、base64图片转化为file类型的文件7、图片的上传 首先安装cropperjs 和exif-js 裁剪依赖这两个包cnpm install --save cropperjs exif-js 图片的上传相关代码 图片从手机相机选择、拍照图片选择后的裁剪 initilize(opt) { let self = this; this.options = opt; //创建dom this.createElement(); this.resultObj = opt.resultObj; //初始化裁剪对象 this.cropper = new Cropper(this.preview, { aspectRatio: opt.aspectWithRatio / opt.aspectHeightRatio, 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() { 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; 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; 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]; //调用方法转成url格式 this.originUrl = this.getObjectURL(this.picValue); //每次替换图片要重新得到新的url if (this.cropper) { this.cropper.replace(this.originUrl); } }, //图片转码方法 getObjectURL(file) { let url = null; if (window.createObjectURL != undefined) { // basic url = window.createObjectURL(file); } else if (window.URL != undefined) { // mozilla(firefox) url = window.URL.createObjectURL(file); } else if (window.webkitURL != undefined) { // webkit or chrome url = window.webkitURL.createObjectURL(file); } return url; }, //点击确定进行裁剪 crop() { let self = this; let image = new Image(); let croppedCanvas; let roundedCanvas; // Crop document.querySelector(".crop_loading").style.display = "block"; setTimeout(function() { croppedCanvas = self.cropper.getCroppedCanvas(); // Round roundedCanvas = 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(); this.imgList.push(imageData); }, //图片旋转 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"); //瓦片canvas let 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/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); } }图片在页面的显示效果暂无图片的删除base64图片转化为file类型的文件 bytes = window.atob(this.imgList[i].split(',')[1]); ...

April 25, 2019 · 5 min · jiezi

图片裁剪功能学习小结

图片裁剪功能学习小结近期有需要使用图片裁剪的功能,在使用插件和自己写裁剪组件之间犹豫了很久,后来根据需求经过反复的考虑,还是自己封装吧,毕竟自己动手,丰衣足食,对吧?嗯,????????????????????????是的!最后生成裁剪后的图片使用了 html2canvas插件,实现了图片的裁剪功能。但是,发现需求之中竟然有裁剪GIF的需求,然而我的裁剪组件并不能满足这个需求,还存在图片清晰度的问题。为了实现这个需求,在后端使用 node 插件进行截图,但是并没有找个一个满足需求的插件,最后决定使用七牛云的图片高级处理服务。在实现该功能的过程中,对一些插件进行了总结,方便记忆,有什么不正确的地方,希望大家多多指教!一、插件展示二、前端裁剪插件1. html2canvashtml2canvas 能够直接在浏览器端将整个页面或者部分页面生成截图,将DOM结构渲染成canvas画布。使用方法:http://html2canvas.hertzen.co…部分代码:遇到的问题:原因:图片跨域问题,尽管不通过 CORS 就可以在画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的 toBlob(), toDataURL() 或 getImageData() 方法,调用它们会抛出安全错误。解决办法:html2canvas的配置项中配置 allowTaint:true 或 useCORS:true(二者不可共同使用);使用反向代理或者服务器添加响应头 header(“Access-Control-Allow-Origin: *")2. jQuery Jcrop 图像裁剪Jcrop 是一个功能强大的 jQuery 图像裁剪插件,结合后端程序可以快速的实现图片裁剪的功能。(没有使用过…)使用方法:http://code.ciaoca.com/jquery…3. vue-cropper 基于vue的裁剪组件使用方法:http://xyxiao.cn/vue-cropper/…三、node 裁剪插件1. gm第一次首先要安装 GraphicsMagick或者ImageMagick,Mac OS X可以使用brew安装。使用方法: 安装: brew install imagemagick brew install graphicsmagick npm install –save gm 引入: var fs = require(‘fs’) var gm = require(‘gm’).subClass({imageMagick: true})部分代码 gm(./upload/${req.body.name}) .resize(‘300’, ‘468’, ‘^’) .gravity(‘Center’) .crop(‘200’, ‘400’) .write(./upload/${cropName}, function (e) {// 输出的图片路径 if(e) { console.log(e.message) }else { res.send({ stat: 1, data: { mesg: ‘OK’, url: http://${req.headers.host}/upload/${cropName} } }) console.log(‘done’) }优缺点可以裁剪GIF图,但是使用rise函数缩放之后,GIF图会失真。裁剪的图片和原图的大小一样,选中的区域被裁剪,但是其他地方是透明的。效果图左图为缩放之后裁剪的图片,右图为原图3. sharp使用方法 安装 npm install –save sharp 引入: var sharp = require(‘sharp’)部分代码 sharp(‘图片路径’) .extract(top, left, width, height) .resize(150, 150) .sharpen() .quality(100) .toFile(‘裁剪之后保存的路径’, function (err) { if (err) throw err; console.log(‘done!’) res.send({ stat: 1, data: { mesg: ‘OK’, url: http://${req.headers.host}/upload/${cropName} } }) });优缺点sharp只可以裁剪GIF的第一帧,但是使用比较方便参考文献http://shieldax.github.io/201… sharp裁剪 ...

January 8, 2019 · 1 min · jiezi