←首页

最近开始学习Canvas,因为之前没有接触过,所以想找点东西练下手,写写动画。
突然想起之前看过 TQ大神 写的一篇拖拽粘性小红球Canvas实现,重新翻出来看看。顺便用自己的语言重新书写了一遍。写篇文章记录下今天的学习过程。

先贴上效果预览二维码,或者点击查看
扫码看效果

Canvas

Canvas不是什么新鲜的技术了,然而开始写相关代码就在昨天。
Canvas其实就是块画布,如果你学过 MFC的话,你会发现其实很多内容都是相同的。

1
2
3
4
5
6
7
8
9
// 获取画布
var canvas = document.getElementById('canvas');
// 2d画布
var ctx = canvas.getContext('2d');
// 填充矩形(x, y, width, height)
ctx.fillRect(x, y, width, height);
ctx.clearRect(x, y, width, height);
ctx.strokeRect(x, y, width, height);

绘制多边形可以使用路径

1
2
3
4
5
6
7
8
9
10
11
12
...
ctx.beginPath();
ctx.closePath();
// 移动画笔
ctx.moveTo(x, y);
// 画线
ctx.lineTo(x, y);
// 填充,描边
ctx.stroke();
ctx.fill();

绘制弧形,曲线

1
2
3
4
5
6
7
// 绘制规则弧形
ctx.arc(x, y, radius, startAngle, endAngle, clockwise)
// 二阶贝塞尔
ctx.quadraticCurveTo(cp1x, cp1y, x, y)
// 三阶贝塞尔
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

绘制图形常用的就这些了,还有就是注意Canvas坐标轴原点在左上角,角度使用的是弧度(希望你跟我一样忘了什么是弧度)。

三角含树

上次谈起这个沉重的话题应该是高等数学期末考。
但是不要慌! 记住几个常见的公式定理,其他的靠猜就行了。

  1. A + B + C = 180.
  2. 正弦定理 a / sin A = b / sin B = c / sin C
  3. 余弦定理 c^2 = a^2 + b^2 - 2abcos C

维基百科

开始敲代码

为了让动画能够流畅,这里使用了 requestAnimationFrame
一步一步来。

1
2
3
4
5
6
function init() {
// 监听事件
addEventListener();
// 启动 requestAnimationFrame
requestAnimationFrame(loop);
}

监听三个事件,并只在事件监听中改变状态值,绘制图形放在loop中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function touchstart(event) {
event.preventDefault();
isDragging = true;
mousePos.x = event.touches[0].pageX;
mousePos.y = event.touches[0].pageY;
...
}
function touchmove(event) {
...
}
function touchend() {
...
}
function addEventListener() {
window.addEventListener('touchstart', touchstart);
window.addEventListener('touchmove', touchmove);
window.addEventListener('touchend', touchend);
}
function loop() {
// 清除画板,重新绘制
ctx.clearRect(0, 0, width, height);
calPos();
changeSize();
drawBall();
drawDragBall();
// 贝塞尔曲线
drawBezier();
requestAnimationFrame(loop);
}

贝塞尔曲线

我们需要获取先获取两个圆的四个切点,因为都是直角,难度不大。
可以结合图看代码。
变量标注
局部图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
angle = Math.atan((centerPos.x - mousePos.x)/(centerPos.y - mousePos.y));
// 小三角形的两条边
var y = drawRadius * Math.sin(angle);
var x = Math.sqrt(Math.pow(drawRadius, 2) - Math.pow(y, 2));
y1 = centerPos.y - y;
x1 = centerPos.x + x;
y2 = centerPos.y + y;
x2 = centerPos.x - x;
y3 = mousePos.y - y;
x3 = mousePos.x + x;
y4 = mousePos.y + y;
x4 = mousePos.x - x;

再看图确保你知道贝塞尔曲线如何工作。
二阶贝塞尔
三阶贝塞尔
这里以两个圆心的中点为贝塞尔曲线的控制点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 考虑到 控制点可能变动,所以这里算了两次
cx1 = (x1 + x4) / 2;
cy1 = (y1 + y4) / 2;
cx2 = (x2 + x3) / 2;
cy2 = (y2 + y3) / 2;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.quadraticCurveTo(cx1, cy1, x3,y3);
ctx.lineTo(x4,y4);
ctx.quadraticCurveTo(cx2, cy2, x2,y2);
ctx.lineTo(x1,y1);
ctx.fillStyle = '#ea6d9e';
ctx.fill();

收尾动画

为了不让小球回弹的时候略显尴尬,这里用了 TWEEN.js做收尾动画,在手指移开屏幕的时候触发。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function touchend() {
isDragging = false;
// 选择缓动函数
var bounce_animate_type = TWEEN.Easing.Elastic.Out;
// 调用Tween.js,声明开始和结束位置。
tween = new TWEEN.Tween(mousePos)
.to(centerPos, bounceTime)
.easing(bounce_animate_type)
.onUpdate(function() {
calDistance();
})
.onComplete(function() {
dragEnd = true;
cancelAnimationFrame(bounceTimer);
})
.start();
bounce();
}
function bounce(time) {
// drawRadius = ballRadius;
bounceTimer = requestAnimationFrame(bounce);
TWEEN.update(time);
}

主要代码就这些了,最终代码 - github

再次感谢 TQ 的教程。

相关链接

如需转载,请注明出处: http://w3ctrain.com/2016/07/22/canvas-spring-ball/ ,欢迎加入前端Q群(467969149)