起因

基于一个复杂的图片预览的弹窗之上新增一个裁剪功用,直接用新的插件改掉本来的功用又很复杂而且操作办法与需求不符。因而需求自定义一个简略的裁剪功用。以canvas画布元素与图片标签的方位联系和大小进行裁剪。

canvas计划

流程

  • 首要创建canvas对像
  • 指定canvas的高度宽度(非style的宽高)
  • 核算原图需求剪切的相关参数
  • 经过drawImage办法将需求的部分画上去
  • 经过toDataURL办法获取图片base64的值
  • 完结,后续可对base64值进行其他操作。
    //本地或线上图片地址
    const SRC = "./aa.jpg"
    const canvas = document.getElementById('canvas-cut')
    const cut = canvas.getContext('2d')
    const img = new Image()
    img.src = SRC
    //处理toDataURL遇跨域资源导致的报错
    img.crossOrigin = 'Anonymous'
    img.onload = function() {
        cut.drawImage(img,295,40,100,100,0 ,0,100,100)
        var imgbase64 = canvas.toDataURL("image/png")
        //base64资源
        console.log(imgbase64)
    };

drawImage参数

drawImage 办法允许在 canvas 中插入其他图画(img和canvas元素)

以左上角为原点x,y轴的间隔

    //参数有三种传值办法
    drawImage(image, dx, dy)  
    drawImage(image, dx, dy, dw, dh)
    //dx,dy 在画布上裁剪后图画放置的 x,y坐标方位
    //sw和sh为在原图中需求截取的宽高,最终会以缩放的办法以dw,dh的大小展示在canvas中
    drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)

手写裁剪图片的方案以及遇到问题的解决历程

适配原图的缩放旋转

当原图存在缩放和旋转的情况,裁剪也需求进行缩放旋转

旋转

canvas的旋转也是以左上角有为原点为旋转,旋转90度之后,则内容为空白了,因而需求translate特点移动一个间隔

cut.rotate(Math.PI / 2)
cut.translate(0, -1 * canvas.height)

手写裁剪图片的方案以及遇到问题的解决历程

//顺时针与逆时针旋转-270~270之间
// rationNum = (旋转角度/90)%4
cut.rotate(Math.PI * rationNum / 2)
switch (rationNum) {
    case -1:
    case 3:
      cut.translate(-1 * canvas.width, 0)
      break
    case 1:
    case -3:
      cut.translate(0, -1 * canvas.height)
      break
    case -2:
    case 2:
      cut.translate(-1 * canvas.width, -1 * canvas.height)
      break
  }
}

手写裁剪图片的方案以及遇到问题的解决历程

依照上图旋转之后,画布与参照图片的联系,在实践裁剪的图的裁剪方位将会有变化(原点变化了) 这儿需求根据canvas与展示的图片的方位联系,推算出裁剪原图和canvas的方位联系即sx,sy的值

手写裁剪图片的方案以及遇到问题的解决历程

//drawLocal为裁剪的方位sx,sy最终还需求乘以缩放份额
//留意:我这儿的canvas是正方形的所以width和height值一样,此处都用canvas.width
//drawLocal在原图上面裁剪的起始位sx,sy的值
switch (rationNum) {
  case -1:
  case 3:
    drawLocal = {
      x: img.width - (canvasXY.y - imgXY.y) - canvas.width,
      y: canvasXY.x - imgXY.x
    }
    break
  case 1:
  case -3:
    drawLocal = {
      x: canvasXY.y - imgXY.y,
      y: img.height - (canvasXY.x - imgXY.x) - canvas.width
    }
    break
  case -2:
  case 2:
    drawLocal = {
      x: img.width - (canvasXY.x - imgXY.x) - canvas.width,
      y: img.height - (canvasXY.y - imgXY.y) - canvas.width
    }
    break
}

手写裁剪图片的方案以及遇到问题的解决历程

缩放

实践中,看到的是一张图片,裁剪的是以new Image()重生的图片,因而一切的间隔有必要以画布与展示的图片核算坐标数据,然后再以缩放份额进行核算

const imgScale = img.style.transform ? img.style.transform.match(/scale\((\S*)\)/)[1] : 1
const scaleNum = originImg.width / (img.width * imgScale)

图片有两张:一张能够缩放旋转的图,一张是实践截取内容的图片

//最终大致如下
//留意:这儿canvas为一个正方形
cut.drawImage(
    originImg,
    drawLocal.x * ration,
    drawLocal.y * ration,
    canvas.width * ration,
    canvas.width * ration,
    0,
    0,
    canvas.width,
    canvas.width
)

内部图片不支持跨域与canvas crossOrigin特点的抵触

所谓的跨域是因为浏览器的同源策略而引起的。(协议,域名,端口号)

可是img和link标签只能实现单向通信,即只能从客户端向服务器传递数据。img获取图片进行加载,link是获取样式表加载,而script回来的是javascript代码执行。因而,script,img,link标签不受跨域影响。 可是canvas需求对裁剪的图片进行可跨域的设置

originImg.setAttribute('crossOrigin', 'anonymous') 因而会呈现,页面分明有图片,可是设置该特点就回401(设置为允许跨域之后会图片的恳求会不带上cookie) 解决计划便是运用ajax发起恳求获取图片资源然后再设置给img(因为img标签不受跨域的影响,当用ajax恳求是很可能呈现跨域的现象,这儿需求后端设置一下

//data 拿到资源然后转位base64格式
const reader = new FileReader()
reader.readAsDataURL(data)
reader.onload = ({ target }) => {
  callback((target as FileReader).result as string)
}

重置canvas画布内容

  • 运用:context.clearRect(0, 0, canvas.width, canvas.height)

    这是铲除整个canvas的最快和最具描绘性的办法。

  • 不要运用:canvas.width = canvas.width

    重置canvas.width重置一切canvas状态(例如,转化,lineWidth,strokeStyle等),它十分缓慢,不适用于一切浏览器,并且不描绘你实践测验的内容去做(自己赋值自己???重点是typescript也无法解析)

关于git

因为屡次的测验以及本地有署理,之前跨域恳求在打包发布至测验环境才会呈现跨域的问题,产生很多git log,一把辛酸泪。所以这儿需求git兼并多个commit,否则不好看

手写裁剪图片的方案以及遇到问题的解决历程
//找到最开始的commit id
git log
//软重置
git reset --soft 1111111111
//生成一次新的commit
git commit -m'XXX功用'
git push -f

最终

总结一下,canvas裁剪会遇到图片资源跨域的问题,适配图片的旋转缩放,不同的预览图片的插件,旋转缩放的办法也不同,但最终是需求核算出份额即可。。。。。

希望遇到的每一个坑都是一次进步~