Canvas做动画与普通JS控制的动画有所不同。本篇着重介绍其中的特殊之处,并实现中国象棋的棋子拖动及撤回动画。

普通JS动画一般只需要控制一些页面元素,改变它们的位置或透明度、色彩等属性,其余的浏览器会帮我们完成。但到了Canvas里面,所有绘制出来的图像元素,并不是各自独立的DOM节点。它们共同做为一个Canvas节点存在。而我们的动画则要求Canvas里的图像元素发生运动。

摆在面前的障碍就是,我们无法像控制DOM节点那样直接控制图像元素的位置等属性。

那么Canvas怎么做动画呢?答案只有一个:不停地重绘。你无法用style.left之类的属性控制图像元素,但你可以设定自己的变量,并利用这些变量绘制图形。这样,只要变量在变,不断重绘的结果就形成了我们所需的动画。

不要对重绘感到恐惧。重绘普遍存在于显示技术中。即使你用JS控制DOM节点进行移动,实际上浏览器也在悄悄的用重绘来实现。

让我们结合上一篇的例子,略做改进,给棋子加上鼠标事件(加在Canvas节点上,通过坐标判断来确定点击等事件是否发生在棋子上),实现棋子的拖动效果。

onmousedown与onmouseup是拖拽的开始与结束事件,我们在这两个事件里所需做的主要工作是进行拖拽状态的标记。onmousemove是拖拽的具体实现,也就是在这里进行重绘。来看下代码:

if (chess.isDragging != true)
return;
var pos = fixMousePoint(e, canvasNode);
chess.offsetRect.moveTo(pos.x - layout.cell / 2, pos.y - layout.cell / 2);
drawBoard(ctx);
chess.drawChess(ctx);

这里首先判断是否处于拖拽状态,如果不是则不做处理。对拖拽的处理也很简单,首先取出鼠标位置;然后更新棋子的offsetRect对象,使之指向当前鼠标位置;最后进行重绘,先绘棋盘再绘棋子(这里为演示方便只使用了一个棋子)。

棋子的移动

棋子的移动(截图没能截到棋子上的鼠标光标)

怎么样,是不是棋子已经能跟随鼠标移动了?是不是挺简单的?当然很简单,本系列的深度内容都在后面,这篇及前篇只是开路先锋。

对于非鼠标拖拽的运动效果,只需要将onmousemove换成setInterval就可以了,道理就是这么简单。不过,让我们把效果玩得更好一些吧。

你一定见过很多动感十足的运动效果,有没有研究过它们的规律呢?比如我们想实现拖拽结束后,让棋子潇洒地飞回原地,这个动作怎么样实现呢?细心的人可能已经发现,多数很酷的滑动效果都是用缓来实现的。缓动也就是,速度从慢到快变化。当然也可以反过来,更可以组合。我们将计划实现一个速度从慢到快再到慢的棋子返回原地的动画。

如何达到这种平滑的速度变化呢?你可以自定义规则来实现,但推荐你在脑海里搜索一下各种数学函数,这些函数真的很优美,也很简单,像我这样把数学还给老师的人也能看懂。最常用的动画函数是幂函数,常用的几种如下图所示:

常用幂函数图像

常用幂函数图像

观察一下x, y都大于零的时候,四个函数的特点。如果把x看作时间,把y看作位移,则y=x时是匀速运动,其它指数为正整数的情况下,速度都是先慢后快,并且指数越高,速度变化幅度越大。

如果我们要实现慢速启动和缓慢停止,则可以将一正一反两个函数拼接起来。有了这些基础概念,剩下的就是将这个函数映射到实际问题中了,只需要“放大”或“缩小”,使选定的图像片段调整到和我们需要的运动距离及时间相对应,就可以实现我们的目的了。

让我们在onmouseup里添加处理,实现缓动画效果:

canvasNode.onmouseup = function(e) {
	if (chess.isDragging) {
		chess.isDragging = false;
		moveChessBack();
	}
}
function moveChessBack() {
	var left = layout.padding + layout.cell * chess.pos.x–layout.cell / 2,
	top = layout.padding + layout.cell * chess.pos.y–layout.cell / 2;
	var dx = left–chess.offsetRect.left,
	dy = top–chess.offsetRect.top;
	var t = 0,
	c = 20;
	var timer = setInterval(function() {
		if (++t > c) {
			clearInterval(timer);
			chess.offsetRect.moveTo(left, top);
			return;
		}
		var ratio = 0;
		if (t <= c / 2) {
			ratio = 2 * t / c;
			ratio = 1–0.5 * Math.pow(ratio, 4);
		} else {
			ratio = 2–2 * t / c;
			ratio = 0.5 * Math.pow(ratio, 4);
		}
		chess.offsetRect.moveTo(left–dx * ratio, top–dy * ratio);
		drawBoard(ctx);
		chess.drawChess(ctx);
	},
	40);
}

看看效果吧,只可惜截图是死的,做视频太烦,随后我把代码附上来供大家参考。本篇解决了基本的动画处理问题,基于这些知识,大家可以发散思维,添枝加叶,让动画更酷些。笔者的示例中改变了正在拖动的棋子的阴影位置,以示区分。我相信大家会有更多更好的主意,欢迎分享出来。

到这里,Canvas动画的制作原理和棋子的运动处理基本解决了,Canvas技术上制作中国象棋游戏的障碍已经扫清,大道前方,你是否看到了光明?哦,不一定,在“花明”之前,总会有些“柳暗”,不知你是否感觉得到。不过没关系,无论世界是黑是白,我们心中要有一个信念,就像尤达大师说的,原力的光明面远比黑暗面强大。对错不重要,把它当做一种信仰就好。

Canvas中国象棋棋子动画演示

本文作者:admin 转载请注明来自:携程设计委员会