详解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 相关文章推荐
使用AOP改善javascript代码
May 01 Javascript
全面了解JS中的匿名函数
Jun 29 Javascript
AngularJs中 ng-repeat指令中实现含有自定义指令的动态html的方法
Jan 19 Javascript
Angular+Bootstrap+Spring Boot实现分页功能实例代码
Jul 21 Javascript
React Native之ListView实现九宫格效果的示例
Aug 02 Javascript
浅谈vue的踩坑路
Aug 31 Javascript
AngularJS集合数据遍历显示的实例
Dec 27 Javascript
解决vue 更改计算属性后select选中值不更改的问题
Mar 02 Javascript
Webpack中publicPath路径问题详解
May 03 Javascript
在vue中使用vuex,修改state的值示例
Nov 08 Javascript
Vue 组件复用多次自定义参数操作
Jul 27 Javascript
uniapp开发打包多端应用完整方法指南
Dec 24 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
Apache2 httpd.conf 中文版
2006/12/06 PHP
浅析php中三个等号(===)和两个等号(==)的区别
2013/08/06 PHP
Laravel 5框架学习之Eloquent (laravel 的ORM)
2015/04/08 PHP
Ext javascript建立超链接,进行事件处理的实现方法
2009/03/22 Javascript
javascript 触发HTML元素绑定的函数
2010/09/11 Javascript
jquery分页插件AmSetPager(自写)
2013/04/15 Javascript
JavaScript的事件绑定(方便不支持js的时候)
2013/10/01 Javascript
用jquery.sortElements实现table排序
2014/05/04 Javascript
Javascript 多物体运动的实现
2014/12/24 Javascript
js实现精确到秒的倒计时效果
2016/05/29 Javascript
用AngularJS来实现监察表单按钮的禁用效果
2016/11/02 Javascript
AngularJS中filter的使用实例详解
2017/08/25 Javascript
前端常见跨域解决方案(全)
2017/09/19 Javascript
详解ES6中的Map与Set集合
2019/03/22 Javascript
[14:19]2018年度COSER大赛-完美盛典
2018/12/16 DOTA
Python实现的双色球生成功能示例
2017/12/18 Python
在matplotlib的图中设置中文标签的方法
2018/12/13 Python
Python3数字求和的实例
2019/02/19 Python
win10下安装Anaconda的教程(python环境+jupyter_notebook)
2019/10/23 Python
python中常见错误及解决方法
2020/06/21 Python
通过实例了解python__slots__使用方法
2020/09/14 Python
python的setattr函数实例用法
2020/12/16 Python
python 基于opencv实现高斯平滑
2020/12/18 Python
传统HTML页面实现模块化加载的方法
2018/10/15 HTML / CSS
Fresh馥蕾诗英国官网:法国LVMH集团旗下高端天然护肤品牌
2018/11/01 全球购物
P D PAOLA意大利官网:西班牙著名的珠宝首饰品牌
2019/09/24 全球购物
STRATHBERRY苏贝瑞包包官网:西班牙高级工匠手工打造
2020/11/10 全球购物
自荐书封面下载
2013/11/29 职场文书
生物专业个人自荐信范文
2013/11/29 职场文书
家佳咖啡店创业计划书
2013/12/27 职场文书
文明青少年标兵事迹材料
2014/01/28 职场文书
大学自主招生自荐信范文
2014/02/26 职场文书
2015年秋季开学典礼校长致辞
2015/07/16 职场文书
JavaScript中时间格式化新思路toLocaleString()
2021/11/07 Javascript
mysql聚集索引、辅助索引、覆盖索引、联合索引的使用
2022/02/12 MySQL
字节飞书面试promise.all实现示例
2022/06/16 Javascript