canvas——粒子系統(1)
阿新 • • 發佈:2017-06-14
str 存儲 upd title 們的 this 位置 ctx 鼠標移動
這個動畫在很早之前就見過,當時就沒迷住了。最近在學canavs動畫,動手實現了一下。代碼在這裏。展示效果在這裏。
這屬於粒子系統的一種,粒子系統就是需要管理一堆粒子嘛,動畫實現的關鍵在於,遍歷這些粒子,並更新它們的位置。
粒子
每個粒子都需要包含自己的橫縱坐標想x、y,半徑r,各分量上的加速度ax、ay,速度vx、vy,還有所屬的場景owner,這裏的粒子加速度均為0。
// 父類
class Sprite { constructor(args={}) { this.x = args.x || 0; this.y = args.y || 0; this.vx = args.vx || 0; this.vy = args.vy || 0; this.ax = args.ax || 0; this.ay = args.ay || 0; } moveTo(x, y) { this.x = x; this.y = y; } update() { this.vx += this.ax; this.vy += this.ay; this.x += this.vx; this.y += this.vy; } render() { return true; } }
// 粒子 class Particle extends Sprite{ constructor(args) { super(args); this.owner = args.owner; this.r = args.r || 10; this.color = args.color || ‘black‘; this.adjust = this.adjust.bind(this); } update() { super.update(); if(this.x < this.r || this.x + this.r > this.owner.w) { this.vx *= -1; this.x = this.adjust(0, this.owner.w, this.x); } if(this.y < this.r || this.y + this.r > this.owner.h) { this.vy *= -1; this.y = this.adjust(0, this.owner.h, this.y); } } render(ctx) { ctx.beginPath(); ctx.fillStyle = this.color; ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2, false); ctx.closePath(); ctx.fill(); } adjust(min, max, v) { return v > max ? max : (v < min ? min : v); } }
父類的update()中用於改變對象的坐標,particle類的update()在調用了父類的update方法之後,進行邊界檢測。
邊界檢測
邊界檢測屬於碰撞檢測的一種。在改變粒子位置之後,對粒子進行邊界檢測,防止粒子逃出canvas哦。本例中的粒子是圓形的,可以通過粒子中心點與邊界之間的距離進行判斷,若小於粒子自身半徑,則對粒子坐標進行修正,確保粒子始終位於canvas中。
/* * this.x 粒子橫坐標 * this.y 粒子縱坐標 * this.r 粒子半徑 * this.owner.w 粒子所在場景(canvas)寬度 * this.owner.h 粒子所在場景(canvas)高度 */ if(this.x < this.r || this.x + this.r > this.owner.w) { this.vx *= -1; this.x = this.adjust(0, this.owner.w, this.x); } if(this.y < this.r || this.y + this.r > this.owner.h) { this.vy *= -1; this.y = this.adjust(0, this.owner.h, this.y); }
當粒子坐標超出邊界時,使用adjust()重置粒子坐標,確保粒子在canvas內。
adjust(min, max, v) { return v > max ? max : (v < min ? min : v); }
粒子系統
粒子系統就是對粒子進行管理的。
this.nodes = []; // 保存粒子 this.edges = []; // 粒子成對保存,用於連線
存儲edges時,使用雙層循環,內循環n的從i + 1開始,避免重復保存。
for(let i = 0, len = this.nodes.length; i < len; ++i) { for(let n = i + 1; n < len; ++n) { this.edges.push({ from: this.nodes[i], to: this.nodes[n] }) } }
計算兩個粒子之間的距離。
lengthOfEdge(edge) { let w = Math.abs(edge.from.x - edge.to.x), h = Math.abs(edge.from.y - edge.to.y); return Math.sqrt(w * w + h * h); }
粒子間距離越短,連線越粗、越深。
this.ctx.lineWidth = (1 - l / this.threshold) * 2.5; this.ctx.globalAlpha = 1 - l / this.threshold;
超出一定距離就不連線。
let l = this.lengthOfEdge(edge); if(l > this.threshold) { return; }
鼠標事件
這裏為了與用戶有互動,加入了鼠標事件。當鼠標在canvas內移動時,第一個粒子nodes[0]的跟隨鼠標移動。當鼠標靜止或者在canvas外時,則按照原來的速度進行移動。
mouserEnter(e) { this.mouse = this.nodes[0]; } mouseMove(e) { this.mouse.x = e.offsetX; this.mouse.y = e.offsetY; } mouseLeave() { this.mouse = null; }
至於動畫的更新,建議使用requestAnimationFrame()。
canvas——粒子系統(1)