<!-- 这个图片剪裁插件,兼容ios与安卓 --><template><div class="vue-box"><img style="display:block;" :src="imgUrl" alt id="img" /><inputtype="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: 2},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,movable: true, // 启动移动图像cropBoxResizable: true, //是否通过拖动来调整剪裁框的大小cropBoxMovable: true, //是否通过拖拽来移动剪裁框。dragCrop: false,dragMode: "move",//‘crop’: 可以产生一个新的裁剪框3 ‘move’: 只可以移动3 ‘none’: 什么也不处理center: true,zoomable: true, //是否允许放大图像。zoomOnTouch: true,//是否可以通过拖动触摸来放大图像。scalable: true,rotatable: 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;// <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="xuanzhuan_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.xuanzhuanBtn = document.getElementById('xuanzhuan_clip');//确定事件this.addEvent(this.clickBtn, 'click', function () {self.crop();})//旋转事件this.addEvent(this.xuanzhuanBtn, 'click', function () {self.cropper.rotate(90)})//取消事件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;
}
#xuanzhuan_clip {position: absolute;left: 40%;bottom: 20px;width: 80px;height: 40px;border: none;border-radius: 2px;color: #fff;background: #e64340;
}
#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: 0.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: 0.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: 0.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: 0.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: 0.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: 0.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 移动端剪裁相关推荐

  1. 解决vue移动端适配问题

    解决vue移动端适配问题 参考文章: (1)解决vue移动端适配问题 (2)https://www.cnblogs.com/both-eyes/p/10106021.html 备忘一下.

  2. 手把手教你 Vue 服务端渲染

    写在前面 在写这篇文章之前,我有写一篇 Vue 预渲染的教程 以及 在线示例,有需要的可以看一下~ [下面开始 Vue 服务端渲染] 服务端渲染 = SSR = Server-Side Renderi ...

  3. vue移动端项目缓存问题实践

    最近在做一个vue移动端项目,被缓存问题搞得头都大了,积累了一些经验,特此记录总结下,权当是最近项目问题的一个回顾吧! 先描述下问题场景:A页面->B页面->C页面.假设A页面是列表页面, ...

  4. [vue] 移动端ui你用的是哪个ui库?有遇到过什么问题吗?

    [vue] 移动端ui你用的是哪个ui库?有遇到过什么问题吗? vant,mint等等吧,各有各的坑,不过大部分都是可以查到解决方案的 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, ...

  5. vue移动端html5页面根据屏幕适配的四种解决方法

    最近做了两个关于h5页面对接公众号的项目,不得不提打开微信浏览器内置地图导航的功能确实有点恶心.下次想起来了的话,进行总结分享一下如何处理.在vue移动端h5页面当中,其中适配是经常会遇到的问题,这块 ...

  6. Vue服务端配置示例

    Vue服务端配置示例 后端配置示例 #Apache <IfModule mod_rewrite.c>RewriteEngine OnRewriteBase /RewriteRule ^in ...

  7. Vue 服务端渲染(SSR)、Nuxt.js - 从入门到实践

    前言 10月初有幸接到公司官网改版需求,要求采用服务端渲染模式对原网站进行seo优化. 由于团队一直使用的vue技术栈,所以我第一时间想到的就是采用vue 服务端渲染(SSR)来实现该需求,即能减少团 ...

  8. vue移动端网页微信分享

    vue移动端网页微信分享 1. 开篇说明 2. 过程 3. 先上代码 4. 使用说明 兼容ios 5. 注意 1. 为什么请求后端接口时,url需要编码---encodeURIComponent 2. ...

  9. 关于vue移动端下载图片

    最近在做一个vue移动端的项目,设计技术为vue2+vue相关+vant-ui+less,搭配浙里办Bridge插件.也算是几年来第一次做移动端相关的项目了,在做的过程中,记录下几个问题,一起分享下我 ...

  10. vue移动端pdf在线预览,并实现手势缩放、移动

    通常我们会遇到ios手机不能下载文件的问题,那是因为苹果手机的拦截机制,这时我们只能通过别的方法来解决问题,我这里的解决方法是直接实现在线预览 pdf预览跟图片预览不同,pdf可能会有多张,而图片只有 ...

最新文章

  1. 编译安装pdo mysql_centos 6.5下编译安装php5.6,pdo_mysql问题。
  2. hdu 1174:爆头(计算几何,三维叉积求点到线的距离)
  3. [Jmeter系列]Jmeter源码编译步骤(转)
  4. sql 赋值 null_巩固SQL - 窗口函数amp;变量amp;数据透视图
  5. 关于计算机网络拓扑说法正确,计算机网络试题库 - 谢希仁(第六版)(答案解析)...
  6. python字典数据类型笔记_python笔记2-数据类型:元组、字典常用操作
  7. MaxCompute/DataWorks权限问题排查建议
  8. PTA-航空公司VIP客户查询
  9. swot分析模板_学生个人生涯规划报告模板
  10. unity编辑器拓展九——删除场景中丢失的脚本
  11. 如何在Jetson NANO上安装无线WIFI模块
  12. matlab continue语句,continue语句
  13. windows10系统-12-WPS文档编辑排版
  14. 漫画 | Java语言是如何诞生的?
  15. 上线项目 Docker部署项目到服务器总结
  16. 特惠|好物推荐iPhone充电线两条装6.9元
  17. 【企业架构】企业架构师的战略角色
  18. [财务自由之路].(德)博多·舍费尔.mobi kindle支持
  19. PostgreSQL 数据库中 DISTINCT 关键字的 4 种用法
  20. 通过C语言程序修改控制台的背景和前景颜色

热门文章

  1. 信息系统项目管理师考点之上午理论知识点总结
  2. 第11章 枚举与泛型总结
  3. 打开和设置IDEA欢迎界面
  4. Qt5 QFileDialog中文英文交界处乱码问题
  5. python手机壁纸_【python日常学习】爬取各种图片(电脑、手机壁纸,美女、明星图片,头像等等)...
  6. Codeforce Gym 100015I Identity Checker 暴力
  7. Tegra3--NVIDIA--黄仁勋
  8. Android热更新方案Robust——美团热更新(热修复)使用介绍
  9. 【亲测有效】鼠标滚轮在下滑的时候总是上下乱窜解决办法
  10. 如何学好高中数学 提高高中数学成绩秒杀技巧(这几点很重要)