基本原理
Cavnas的确能实现很多很酷炫狂拽屌炸天的效果,但是交互性要差很多,简单的鼠标单击选中某个图形,都要花费一番周折。
Canvas只是一个dom节点,所有监听的事件都只能绑定在这个节点上,但是我们可能需要对Canvas上的某个元素进行操作。基本的原理就是事件还是绑定在Canvas上,通过判断点击发生的位置是否在Canvas中某个图形的路径内(这里注意,我没有说是某个图形的区域内),从而进一步确定是在哪个图形上戳来戳去。
isPointInPath
为了让我们判断点击点到底是否在路径内,Canvas提供了context.isPointInPath(x,y)
方法,参数自然是某个点的横竖坐标。
来个例子,先随便画三个矩形:
var rectList = [ [50,50,50,50], [120,120,80,60], [30,150,60,80] ];var drawRect = function(x,y,w,h){ ctx.beginPath(); ctx.rect(x,y,w,h); }
// init
ctx.clearRect(0, 0,canvas.width(), canvas.height());
$.each(rectList, function(n,v){
drawRect(rectList[n][0],rectList[n][1],rectList[n][2],rectList[n][3]);
ctx.stroke();
});
另外再写个getCanvasPoint
方法,主要把点击坐标换算成Canvas里面的坐标:
var getCanvasPoint = function(x,y) { var canvasOffset = canvas.offset(); return { x: x - canvasOffset.left, y: y - canvasOffset.top } }
是时候该isPointInPath
方法登场,实现点中某个矩形的时候改变矩形边框的颜色
canvas.on("mousedown", function(e){ moving = true; var point = getCanvasPoint(e.pageX, e.pageY); $.each(rectList, function(n, v) { drawRect(rectList[n][0],rectList[n][1],rectList[n][2],rectList[n][3]); if(ctx.isPointInPath(point.x, point.y)){ moveItem = n; startPos.x = point.x; startPos.y = point.y; ctx.strokeStyle = "#ff4444"; } else { ctx.strokeStyle = "#000"; } ctx.stroke(); }) });
这里我们从预定义的rectList
中依次拿出每个长方形参数,分表是左上角的x坐标,y坐标,长,宽。然后先画出长方形的路径,画完一个用isPointInPath
方法判断一下点击点是否在当前路径中,如果在,就给当前的改颜色,如果不在就画出来然后继续画下一个长方形继续判断路径。
是不是很麻烦,isPointInPath
只对当前路径有效,也就说我们不能把三个长方形都画完了再判断某个点是否在第一个长方形路径中,而必须是每次画完一个长方形就要判断一次,而且Canvas中路径是不能保存的,要改变某一条路径,就必须把整个Canvas重新画一遍。
所以这里我们在循环中不单单是绘制了点中的长方形,还要绘制没点的长方形。
拖动元素
基本原理就是上面那样,看到这里,你已经洞悉了Canvas实现点击等事件的小秘密。那我们就继续实现拖动其中的元素吧。
canvas.on("mousemove", function(e){ if( !moving && moveItem == null ) { return; } ctx.clearRect(0, 0,canvas.width(), canvas.height()); var point = getCanvasPoint(e.pageX, e.pageY); $.each(rectList, function(n, v) { var moveX = 0, moveY = 0; if(n !== moveItem ){ ctx.strokeStyle = "#000"; } else { moveX = point.x - startPos.x; moveY = point.y - startPos.y; ctx.strokeStyle = "#ff4444"; } drawRect(rectList[n][0] + moveX,rectList[n][1] + moveY,rectList[n][2],rectList[n][3]); ctx.stroke(); }) });
依然是熟悉的味道,依然是熟悉的配方。我们还是依赖mousemove方法,移动端请用touchmove方法。
因为之前在点击的时候我们已经把是哪个长方形被选中给保存在变量moveItem中了,所以这里就直接判断就好了。是要拖动的元素就把长方形的x,y对应拖动的距离进行更新。同样是要把整个Canvas都绘制一遍。
另外在mouseup事件中保存下状态,方便下次绘画:
canvas.on("mouseup", function(e){ moving = false; $.each(rectList, function(n){ if (n == moveItem){ var point = getCanvasPoint(e.pageX, e.pageY); rectList[n][0] += point.x - startPos.x; rectList[n][1] += point.y - startPos.y; } }); startPos = {} moveItem = null });
记住:在Canvas中不管你搞什么幺蛾子,都要按照之前的规则把所有路径绘制一遍。
完整代码:
https://github.com/bob-chen/canvas-demo/blob/master/basic/movebymouse.html
参考
http://jo2.org/html5-canvas-ispointinpath/