背景
- 最近在研究canvas相關的項目,有個需求就是在繪制的貝塞爾曲線上有一個彗星尾巴的效果,網上找了一些案例,如:
- markline.js——輕量級canvas繪制標記線的庫
- canvas拋物線運動軌跡
- 動畫—圓形擴散、運動軌跡
- 但都沒能滿足要求,不過還是感謝這些博文提供的一些思路,思前想后,終於實現相關的demo:
知識點
- canvas基本知識
實現思路
-
思路其實不難,主要有如下步驟
- 1 繪制貝塞爾曲線,起始點、控制點、終點自定;
- 2 定義小球數組,該小球數組記錄貝塞爾曲線上每一份對應的x, y坐標以及小球坐標點的透明度、半徑(小球的透明度由0->1降低,半徑逐漸增大),當小球數組長度超過指定數組長度,清除最先進入的一個小球對應的坐標;
- 根據貝塞爾曲線軌跡繪制當前的小球數組。 -
ok,看一下效果圖吧:

- 順附demo源碼:
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style type="text/css">
html,
body {
padding: 0;
margin: 0;
}
canvas {
border: 1px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" width="500" height="500"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx= canvas.getContext('2d');
var width = canvas.width;
var height = canvas.height;
//畫貝塞爾曲線參數 起始 兩個控制點,終點以及當前和總的比例數
let sx = 10,
sy = 10,
cx1 = 50,
cy1 = 10,
cx2 = 150,
cy2 = 500,
ex = 500,
ey = 500,
t = 0,
n = 200;
//小球運動軌跡信息數組
let bubbles = [{a:1, x:1, y: 1, r:4, s: 1}];
let bubbleNum = 0;
function test() {
t++;
bubbleNum++;
ctx.clearRect(0, 0, 5000, 5000);
ctx.lineWidth = 3;
ctx.strokeStyle = 'green'
ctx.beginPath();
ctx.moveTo(sx, sy);
ctx.bezierCurveTo(cx1, cy1, cx2, cy2, ex, ey);
ctx.stroke();
//計算當前的貝塞爾曲線坐標
let x = sx * Math.pow((1 - t / n), 3) + 3 * cx1 * (t / n) * Math.pow((1 - t / n), 2) + 3 * cx2 * Math.pow((t / n), 2) * (1 - t / n) + ex * Math.pow((t / n), 3);
let y = sy * Math.pow((1 - t / n), 3) + 3 * cy1 * (t / n) * Math.pow((1 - t / n), 2) + 3 * cy2 * Math.pow((t / n), 2) * (1 - t / n) + ey * Math.pow((t / n), 3);
//移動操作
if (bubbleNum > 50) {
bubbles.shift()
}
for(var i=0;i<bubbles.length;i++){
bubbles[i].a = (i+1)*0.02;
bubbles[i].s = (i+1)*0.02;
}
let b = {a: 1, s: 1, x: x, y: y};
bubbles.push(b);
//渲染bubble數組
for (let j = 0; j < bubbles.length; j++) {
let b = bubbles[j];
ctx.save();
ctx.beginPath();
ctx.globalAlpha = b.a; // 值是0-1,0代表完全透明,1代表完全不透明
ctx.fillStyle = 'greenyellow';
ctx.arc(b.x, b.y, b.s * 4, 0, 2 * Math.PI, false);
ctx.fill();
ctx.restore();
}
if (x == 500) {
t = 0;
}
console.log(t);
requestAnimationFrame(test);
}
test();
</script>
</body>
</html>
- 效果是實現了,但是每次需要清除整個canvas畫布,如果需要重繪的內容較多,性能上估計需要進一步優化了。
