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


Posted in Javascript onJune 07, 2019

演示地址

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/cropperjs
  • elementUI https://element.eleme.io/#/zh-CN

使用 cropperjs插件 和 原生canvas 两种方式实现图片裁剪功能

使用cropperjs插件

安装cropperjs

yarn 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组件:

cropper.vue组件:https://github.com/MY729/picture-crop-demo/blob/master/src/components/cropper.vue
cropper.one.vue组件:https://github.com/MY729/picture-crop-demo/blob/master/src/components/cropper.one.vue

使用canvas实现图片裁剪

支持鼠标绘制裁剪框,并移动裁剪框

思路:

  • 在canvas上绘制图片为背景
  • 监听鼠标点击、移动、松开事件

canvas的isPointInPath()方法: 如果给定的点的坐标位于路径之内的话(包括路径的边),否则返回 false

具体实现可查看源码cropper.canvas.vue组件: https://github.com/MY729/picture-crop-demo/blob/master/src/components/cropper.canvas.vue

cropImg () {
 let canvas = document.getElementById(this.data.img_url)
 let ctx = canvas.getContext('2d')
 let img = new Image()
 img.onload = function () {
 ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
 }
 img.src = this.data.src
 let drag = false // 是否拖动矩形
 let flag = false // 是否绘制矩形
 let rectWidth = 0 // 绘制矩形的宽
 let rectHeight = 0 // 绘制矩形的高
 let clickX = 0 // 矩形开始绘制X坐标
 let clickY = 0 // 矩形开始绘制Y坐标
 let dragX = 0 // 当要拖动矩形点击时X坐标
 let dragY = 0 // 当要拖动矩形点击时Y坐标
 let newRectX = 0 // 拖动变化后矩形开始绘制的X坐标
 let newRectY = 0 // 拖动变化后矩形开始绘制的Y坐标
 // 鼠标按下
 canvas.onmousedown = e => {
 // 每次点击前如果有绘制好的矩形框,通过路径绘制出来,用于下面的判断
 ctx.beginPath()
 ctx.setLineDash([6, 6])
 ctx.moveTo(newRectX, newRectY)
 ctx.lineTo(newRectX + rectWidth, newRectY)
 ctx.lineTo(newRectX + rectWidth, newRectY + rectHeight)
 ctx.lineTo(newRectX, newRectY + rectHeight)
 ctx.lineTo(newRectX, newRectY)
 ctx.strokeStyle = 'green'
 ctx.stroke()
 // 每次点击,通过判断鼠标点击的点在矩形框内还是外,来决定重新绘制还是移动矩形框
 if (ctx.isPointInPath(e.offsetX, e.offsetY)) {
 drag = true
 dragX = e.offsetX
 dragY = e.offsetY
 clickX = newRectX
 clickY = newRectY
 } else {
 ctx.clearRect(0, 0, canvas.width, canvas.height)
 ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
 flag = true
 clickX = e.offsetX
 clickY = e.offsetY
 newRectX = e.offsetX
 newRectY = e.offsetY
 }
 }
 // 鼠标抬起
 canvas.onmouseup = () => {
 if (flag) {
 flag = false
 this.sureCrop(clickX, clickY, rectWidth, rectHeight)
 }
 if (drag) {
 drag = false
 this.sureCrop(newRectX, newRectY, rectWidth, rectHeight)
 }
 }
 // 鼠标移动
 canvas.onmousemove = (e) => {
 if (flag) {
 ctx.clearRect(0, 0, canvas.width, canvas.height)
 ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
 rectWidth = e.offsetX - clickX
 rectHeight = e.offsetY - clickY

 ctx.beginPath()
 ctx.strokeStyle = '#FF0000'
 ctx.strokeRect(clickX, clickY, rectWidth, rectHeight)
 ctx.closePath()
 }
 if (drag) {
 ctx.clearRect(0, 0, canvas.width, canvas.height)
 ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
 ctx.beginPath()
 newRectX = clickX + e.offsetX - dragX
 newRectY = clickY + e.offsetY - dragY
 ctx.strokeStyle = 'yellow'
 ctx.strokeRect(newRectX, newRectY, rectWidth, rectHeight)
 ctx.closePath()
 }
 }
},
// 拿到裁剪后的参数,可自行处理图片
sureCrop (x, y, width, height) {
 let canvas = document.getElementById(this.data.img_url + 'after')
 // 设置canvas的宽为canvas的父元素宽度,宽高比3:2
 let parentEle = canvas.parentElement
 canvas.width = parentEle.offsetWidth
 canvas.height = 2 * parentEle.offsetWidth / 3
 let ctx = canvas.getContext('2d')
 let img = new Image()
 img.src = this.data.src
 img.onload = function () {
 ctx.beginPath()
 ctx.moveTo(x, y)
 ctx.lineTo(x + width, y)
 ctx.lineTo(x + width, y + height)
 ctx.lineTo(x, y + height)
 ctx.clip()
 ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
 }
 ctx.stroke()
}

源码地址

https://github.com/MY729/picture-crop-demo

可以直接clone项目,本地运行查看代码和效果

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
FireFox JavaScript全局Event对象
Jun 14 Javascript
JQuery UI DatePicker中z-index默认为1的解决办法
Sep 28 Javascript
GridView中获取被点击行中的DropDownList和TextBox中的值
Jul 18 Javascript
详解JavaScript中shift()方法的使用
Jun 09 Javascript
javascript实现禁止鼠标滚轮事件
Jul 24 Javascript
开启BootStrap学习之旅
May 04 Javascript
基于Marquee.js插件实现的跑马灯效果示例
Jan 25 Javascript
xmlplus组件设计系列之图标(ICON)(1)
May 05 Javascript
vue和webpack项目构建过程常用的npm命令详解
Jun 15 Javascript
解决v-for中使用v-if或者v-bind:class失效的问题
Sep 25 Javascript
vue页面更新patch的实现示例
Mar 25 Javascript
nuxt 每个页面head标签内容设置方式
Nov 05 Javascript
sortable+element 实现表格行拖拽的方法示例
Jun 07 #Javascript
利用Vue-draggable组件实现Vue项目中表格内容的拖拽排序
Jun 07 #Javascript
Vue中实现权限控制的方法示例
Jun 07 #Javascript
vue 父组件中调用子组件函数的方法
Jun 06 #Javascript
通过seajs实现JavaScript的模块开发及按模块加载
Jun 06 #Javascript
小试小程序云开发(小结)
Jun 06 #Javascript
怎么使用javascript深度拷贝一个数组
Jun 06 #Javascript
You might like
PHP 创建标签云函数代码
2010/05/26 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
PHP var关键字相关原理及使用实例解析
2020/07/11 PHP
Flash+XML滚动新闻代码 无图片 附源码下载
2007/11/22 Javascript
jqPlot jquery的页面图表绘制工具
2009/07/25 Javascript
Jquery中对数组的操作代码
2011/08/12 Javascript
nodejs的require模块(文件模块/核心模块)及路径介绍
2013/01/14 NodeJs
JSP中使用JavaScript动态插入删除输入框实现代码
2014/06/13 Javascript
JavaScript如何实现跨域请求
2016/08/05 Javascript
ionic实现可滑动的tab选项卡切换效果
2020/04/15 Javascript
node.js中 stream使用教程
2016/08/28 Javascript
jQuery实现拖动剪裁图片作为头像
2016/12/28 Javascript
Vue源码解析之Template转化为AST的实现方法
2018/12/14 Javascript
Vuex持久化插件(vuex-persistedstate)解决刷新数据消失的问题
2019/04/16 Javascript
react quill中图片上传由默认转成base64改成上传到服务器的方法
2019/10/30 Javascript
vue 子组件和父组件传值的示例
2020/09/11 Javascript
[48:12]Secret vs Optic Supermajor 胜者组 BO3 第三场 6.4
2018/06/05 DOTA
详解Python的单元测试
2015/04/28 Python
Python数据处理numpy.median的实例讲解
2018/04/02 Python
python http基本验证方法
2018/12/26 Python
基于django channel实现websocket的聊天室的方法示例
2019/04/11 Python
python使用MQTT给硬件传输图片的实现方法
2019/05/05 Python
pycharm访问mysql数据库的方法步骤
2019/06/18 Python
简单的Python人脸识别系统
2020/07/14 Python
python中if嵌套命令实例讲解
2021/02/25 Python
HTML5的Geolocation地理位置定位API使用教程
2016/05/12 HTML / CSS
StubHub意大利:购买和出售全球演唱会和体育赛事门票
2017/11/21 全球购物
应届毕业生求职信范文
2013/12/18 职场文书
保密承诺书范文
2014/03/27 职场文书
诚信承诺书
2015/01/19 职场文书
学生检讨书怎么写
2015/05/07 职场文书
归途列车观后感
2015/06/17 职场文书
浅析CSS在DevTools 中架构演变
2021/10/05 HTML / CSS
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS
CentOS7设置ssh服务以及端口修改方式
2022/12/24 Servers
DQL数据查询语句使用示例
2022/12/24 MySQL