功能
将一个图片裁剪成需要的大小,例如在上传头像时需要头像图片是一个指定比例或者长度宽度的图片.
实现
事件
在canvas上绑定鼠标按下(mousedown)和移动事件(mousemove),移动端对应事件是touchstart,touchmove
拖动行为
经过一些尝试,发现图片坐标计算不是想象中的这种:鼠标坐标点(x,y)就是图片的目的坐标点的情况,这个认知错误.关键点在于没有认清拖动这个行为.
观察拖动一个图片:在拖动之前,图片有个起始坐标,然后是鼠标在拖动,鼠标拖动开始时(鼠标按下)有个起点坐标,最后停下来(鼠标放开)有个终点坐标.这样是完成了一次拖动.
图片的终点坐标是起始坐标加上鼠标移动的距离.图片的终点坐标,自然会成为下一次拖动的起始坐标.
那么这个拖动过程里,有4个变量: 图片起点坐标,鼠标起点坐标,鼠标终点坐标,图片终点坐标.
坐标计算
根据这个观察,图片在开始进行拖动前,有一个起始坐标imgBegin(x,y),初始设为(0,0).这个起始坐标,是指图片在canvas上绘制时的坐标.
鼠标起点坐标通过mousedown或者touchstart事件获得,因为开始拖动时,要先按住这个图片,会触发鼠标按下事件.
此时得到了鼠标按下时的坐标mStart(x,y),这个坐标是鼠标的起始坐标.
// 1. 图片起始坐标,初始为0,0.(就是绘制时在canvas上的坐标) imgBegin = {x : 0, y : 0}; // 2. 图片终点坐标,初始为0,0 imgTo = { x: 0, y: 0 }; // 3. 鼠标按下时,设置鼠标坐标 mouseDown = (x, y) => { mStart.x = x; mStart.y = y; // 图片起点坐标为上次拖动结束时的坐标 imgBegin.x = imgTo.x; imgBegin.y = imgTo.y; }
鼠标按下后拖动,发生mousemove事件,鼠标坐标在变化,得到新坐标(mMoveX,mMoveY).在拖动进行中,坐标是时时变化的.
这个坐标减去起点坐标,就是鼠标移动的距离,把这个距离加到图片起始坐标,就是图片这次移动后的新坐标位置.imgTo(x, y)
在鼠标移动事件里计算鼠标移动距离和图片新坐标
mousemove = (x, y) => { // 4. 鼠标移动的距离 let dX = x - mStart.x; let dY = y - mStart.y; // 图片终点坐标 imgTo.x = imgBegin.x + dX; imgTo.y = imgBegin.y + dY; // 绘图 ReDrawCanvas(imgTo.x, imgTo.y); }
缩放
缩放是指保持图片比例缩放.缩放比例最大为100%,就是原始图片大小.最小为canvas画布大小,比例计算是: scale = cavWidth / imgWidth
放大缩小按钮在这个范围内缩放图片,每次缩放幅度为5%.
缩放的思路推导: (假定绘制点0,0)
canvas上绘制的坐标和大小参数说明:
// imgTo.x, imgTo.y是图片在canvas的绘制点坐标.(图片终点坐标) // imgScale是缩放比例,canvas的宽度和图片宽度的比值 cavWidth / imgWidth // imgW * imgScale 和 imgH * imgScale 是图片在缩放比例下的宽度和高度 // 也就是图片绘制在canvas上的目标大小 ctx.drawImage(imgele, imgTo.x, imgTo.y, imgW * imgScale, imgH * imgScale);
程序逻辑过程
测试
用鼠标在canvas上拖动,手机端是触摸拖动.调整图片位置.
调节缩放条,调整图片大小