关于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;//创建domthis.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);//每次替换图片要重新得到新的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();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");//瓦片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/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]);
var array = [];
array.push(bytes.charCodeAt(j));
var blob = new Blob([new Uint8Array(array)], {type: ‘image/jpeg’});
form.append(“file”, blob, Date.now() + ‘.jpg’);

图片的上传
暂无

最终效果:


图片裁剪参考:https://blog.csdn.net/ch834301/article/details/79963152

ps: 需要源码的请联系我(请尊重一下别人的劳动成果,源码需要付费20元哦。):

关于vue项目中移动端实现用户选择照片、照片裁剪、一次上传多张图片功能。相关推荐

  1. Vue项目中最简单的使用集成UEditor方式,含图片上传

    Vue 3 项目参考这里 前言 封面是UEditor的 百度指数 折线图.虽然今天已经是 2018 年,且优秀的富文本编辑器层出不穷(包括移动端),但从图中可以看出UEditor仍然维持着较高的搜索热 ...

  2. vue实现星级评价及上传多张图片等功能(类似淘宝商品评价页面)

    最近在写一个关于vue的商城项目,然后集成在移动端中,开发需求中有一界面,类似淘宝商城评价界面!实现效果图如下所示: 评价页 点击看大图,且可左右滑动 功能需求分析 默认为5颗星,为非常满意,4颗满意 ...

  3. Vue项目中实现用户登录及token验证

    一.什么是token token的意思是"令牌",是服务端生成的一串字符串,作为客户端进行请求的一个标识.当用户第一次登录后,服务器生成一个token并将此token返回给客户端, ...

  4. 如何在Vue项目中使用vw实现移动端适配(转)

    有关于移动端的适配布局一直以来都是众说纷纭,对应的解决方案也是有很多种.在<使用Flexible实现手淘H5页面的终端适配>提出了Flexible的布局方案,随着viewport单位越来越 ...

  5. 如何在vue项目中实现前端埋点?埋点用户操作之Vue实现

    埋点又称为事件追踪,目的是收集用户行为数据,例如用户点击了什么按钮,浏览了哪些页面,浏览了多长时间.从哪个页面进入的当前页(转化率)等. 刨除node等实现方式,对于前端来说, 保存这些操作或者说记录 ...

  6. 【vue】如何在Vue项目中使用vw实现移动端适配(转)

    有关于移动端的适配布局一直以来都是众说纷纭,对应的解决方案也是有很多种.在<使用Flexible实现手淘H5页面的终端适配>提出了Flexible的布局方案,随着viewport单位越来越 ...

  7. 转:如何在Vue项目中使用vw实现移动端适配

    https://www.w3cplus.com/mobile/vw-layout-in-vue.html 有关于移动端的适配布局一直以来都是众说纷纭,对应的解决方案也是有很多种.在<使用Flex ...

  8. canvas java 上传截图_在Vue项目中使用html2canvas生成页面截图并上传

    使用方法 项目中引入 npm install html2canvas html代码 //html代码 js代码 // 引入html2canvas import html2canvas from 'ht ...

  9. 去除vue项目中的#及其ie9兼容性

    一.如何去除vue项目中访问地址的# vue2中在路由配置中添加mode(vue-cli创建的项目在src/router/index.js) 1 export default new Router({ ...

  10. vue项目中通过WebSocket实现实时消息提示及遇到的问题

    vue项目中通过WebSocket实现实时消息提示(前端代码) 需求说明 后台有新增消息通知,并实时推送给用户端,用websocket可以很方便的解决这个问题,但是websocket有个弊端不兼容IE ...

最新文章

  1. Blender从头到尾创建低多边形角色学习教程 Low Poly Characters – Blender Bitesize Course
  2. 【剑指offer-Java版】38数字在排序数组中出现的次数
  3. python中等于号可以用is代替_python中is与双等于号“==”的区别示例详解
  4. python 装饰器是啥?
  5. promise is promose
  6. 错误The request sent by the client was syntactically incorrect ()的解决
  7. nginx: [emerg] getpwnam(nginx) failed in /usr/local/nginx/conf/nginx.conf:2
  8. chkdsk 和sfc.exe修复命令
  9. java集合中retainAll方法
  10. 哈工大期末考试java_哈尔滨工业大学2019算法设计期末试题
  11. vue项目接入高拍仪
  12. DLL注入技术之消息钩子注入(HOOK简单的实现)
  13. 假如我来架构12306网站(二) - 浅谈系统需求调研
  14. php微信支付扫码源码下载,微信支付:扫码支付+APP支付
  15. 已知T(n)=2T(n/2)+n,求O(n)?
  16. CodeSys轴控指令使用方法
  17. 第16集丨阳明心学量子力学
  18. 【置顶】图灵近期出版和即将出版的新书
  19. Sql 查询学生成绩表中每个科目的最高分及对应科目和学生
  20. 谐波小波matlab,基于MATLAB小波变换在电网谐波检测的仿真与研究

热门文章

  1. testcenter自动化
  2. Linux c 地址空间 堆栈 数据段 代码段 变量存储位置
  3. Acme CAD Converter 命令行模式
  4. 【第三方互联】12、支付宝(Alipay)授权第三方登录
  5. jquery为dom元素追加样式,使用addClass不生效
  6. MC(移动立方体)算法
  7. 论文笔记|Unsupervised Keyphrase Extraction by Jointly Modeling Local and Global Context
  8. 关于微信投票刷票的js代码
  9. 人民币对美元汇率中间价报6.7472元 上调469个基点
  10. word中快捷键整理