为了追踪所画内容,诸如画图应用程序、计算机辅助设计系统(computer-aided design system 简称CAD系统)以及游戏等许多应用程序,都会维护一份包含当前显示对象的列表。一般来讲,这些应用程序都容许用户对当前显示在屏幕上的物体进行操做。比方说,在CAD应用程序中,咱们能够对设计中的元素进行选择、移动、缩放等操做
——《HTML5 Canvas核心技术》canvas
在Canvas中实现拖拽
也一样如此,Canvas提供了一个名为isPointInPath(x, y)
的API,判断点(x, y)
是否在路径之中。若是在路径之中,则返回true。因而咱们能够有以下思路:数组
维护一个能够描述各个路径的
数组
,经过ispointInPath(x, y)
判断点击位置是否在某一个路径之中,若是在此路径之中,选中此路径,进行操做(移动、缩放等),再绘制图形bash
此文我以多边形拖拽为例进行说明
,Demo以下(后面的印子是录屏软件的缘由👹):ide
Demo中的多边形如何绘制以前作过总结,再也不赘述👻:Canvas多边形绘制函数
如下为本文目录post
下图给了大体的说明及伪代码👇,思路并不难,但有部分细节须要处理学习
此处列举代码结构及标注其思路,更详细的代码注释已在CodePen之中
由于本文重点在拖拽(drag),因此对绘图部分描述会较少ui
//绘制多边形路径函数
function drawPolygonPath
//多边形类定义
class Polygon{
...
}
//根据点击事件返回在canvas中的位置
function positoinInCanvas
//获取两点间直线距离
function getDistance
//开始阶段,记录拖拽对象
canvas.onmousedown
//拖拽阶段,画路径,描边
canvas.onmousemove
//结束阶段,更新拖拽对象位置
canvas.onmouseup
复制代码
接下来开始代码中的关键部分及细节处理spa
在程序初始化时,咱们定义一个polygonArray数组设计
polygonArray = []
复制代码
在每次画一个新的多边形以后,都会new一个多边形对象推入数组中进行维护
const polygon = new Polygon(mouseStart.get('x'), mouseStart.get('y'), sideNum, radius);
polygonArray.push(polygon);//记录路径对象
复制代码
在后续点击操做时,须要根据对应信息肯定点击位置是否在路径之中
首先获取点击时在canvas中
的对应位置,个人代码用mouseStart
记录x
及y
接着遍历polygonArray
中的polygon
,遍历中调用polygon.createPath()
,经过isPointInPath()
判断点击位置是否有路径,有的话draggingPolygon = polygon
结束函数
const pos = positionInCanvas(e, canvasLeft, canvasTop);//获取在canvas中的像素位置
//记录鼠标起始点s
mouseStart.set('x', pos.x);
mouseStart.set('y', pos.y);
...
for (let polygon of polygonArray) {
polygon.createPath();
if (ctx.isPointInPath(mouseStart.get('x'), mouseStart.get('y'))) {
draggingPolygon = polygon;
return;
}
}
复制代码
这部分要彻底理解推荐你们根据Demo中两个console.log(draggingPolygon)
及代码进行调试,由于咱们是在mousemove
阶段,这个阶段触发函数很是频繁
我尽可能用语言表达清楚🤖
首先计算move
时与mouseStart
的距离,记为diff,有x轴上的offsetX
,也有y轴上的offsetY
const
pos = positionInCanvas(e, canvasLeft, canvasTop),
diff = new Map([
['offsetX', pos.x - mouseStart.get('x')],
['offsetY', pos.y - mouseStart.get('y')]
]);
复制代码
接着记录当前拖拽对象的centerX
及centerY
,记为temp
let
tempCenterX = draggingPolygon.centerX,
tempCenterY = draggingPolygon.centerY;
复制代码
这里就是难理解的点,为何要记录?继续往下看,后面会使用到。
根据diff
中的offset,设置draggingPolygon新的中心位置
draggingPolygon.centerX += diff.get('offsetX');
draggingPolygon.centerY += diff.get('offsetY');
复制代码
接着清空画布进行绘制新的路径和描边
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let polygon of polygonArray) {
drawPolygonPath(polygon.sideNum,
polygon.radius,
polygon.centerX,
polygon.centerY, ctx);
ctx.stroke();
}
复制代码
最后使用到上文中的tempCenterX
与tempCenterY
:
draggingPolygon.centerX = tempCenterX;
draggingPolygon.centerY = tempCenterY;
复制代码
为何须要这么作呢?
由于咱们的拖拽是基于多边形的原位置
,而mousemove
阶段并不能肯定函数的最终位置
,若是这时没有复原的话,会出现"漂移"
,我把这两行代码注释掉,效果以下:
拖拽完成后是处于mouseup
阶段,此时咱们已经肯定dragginPolygon的最终位置,进行更新便可,最后置为null,排除在没有拖拽多边形状况下,鼠标在画布上移动触发对应代码
const
pos = positionInCanvas(e, canvasLeft, canvasTop),
offsetMap = new Map([
['offsetX', pos.x - mouseStart.get('x')],
['offsetY', pos.y - mouseStart.get('y')]
]);
draggingPolygon.centerX += offsetMap.get('offsetX');
draggingPolygon.centerY += offsetMap.get('offsetY');
draggingPolygon = null;
复制代码
其实这个功能实现并不难,关键是了解一个概念:经过维护当前显示对象的列表及isPointInPath进行判断来实现追踪
最后欢迎你们交流学习🥳
《HTML5 Canvas核心技术》