OpenHarmony - 基于ArkUI(JS)實現(xiàn)移動粒子效果背景
前言
在web端博客逛多了,偶然間發(fā)現(xiàn)了一種網(wǎng)頁背景,線條能自發(fā)的運動,并且可以讓這些線條向鼠標聚集,就覺得挺有意思的,讓我們來試著用鴻蒙JS來實現(xiàn)這個炫酷的背景吧!
效果演示
實現(xiàn)步驟
1、創(chuàng)建canvas標簽
設置畫布的大小,背景顏色,以及觸摸事件。
<div class="container">
<canvas ref="canvas1" style="width: 100%; height: 100%; background-color: rgb(80, 80, 80);" @touchmove="handleMove"></canvas>
</div>
2、創(chuàng)建粒子
中學我們就知道,兩點成線。頁面中的這些線條其實都是點與點之間的連線,粒子運動,就造成了線條的運動,所以我們第一步先用數(shù)組來存儲頁面中的這些粒子。x和y代表粒子的坐標位置,xa和ya分別代表粒子水平方向和垂直方向運動的速度,max代表粒子成線的最小距離條件。
export default {
data: {
dots: [], // 存儲粒子對象的數(shù)組
},
// 創(chuàng)建粒子,并存儲到數(shù)組中
drawBackground() {
// 創(chuàng)建粒子,并存儲到數(shù)組中
for (let i=0; i<180; i++) {
// 粒子的位置
let x = Math.random() * 360
let y = Math.random() * 780
// 水平移動的速度,垂直移動的速度
let xa = Math.random() * 0.5
let ya = Math.random() * 0.5
this.dots.push({ x, y, xa, ya, max: 3600 })
}
},
}
3、手指觸摸事件
web端的效果是跟隨鼠標的移動,移動端沒有鼠標,那我們就讓粒子向手指移動的地方靠近。用手指觸摸事件來模擬鼠標移動事件。
x代表手指觸摸屏幕的橫坐標,y代表手指觸摸屏幕的縱坐標,max代表粒子向手指觸摸屏幕位置靠近的最小距離條件。
handleMove事件的作用,手指觸摸屏幕時更新手指的坐標位置。
export default {
data: {
mouse: { // 手指位置
x: null,
y: null,
max: 3200
}
},
handleMove(e) {
this.mouse.x = e.touches[0].localX
this.mouse.y = e.touches[0].localY
}
}
4、粒子的運動
前面我們已經準備好的粒子對象數(shù)組dots和模擬鼠標的手指對象,現(xiàn)在正戲要開始了。我們設定一個函數(shù),接收canvas上下文對象,用來規(guī)定粒子的運動,并且畫出粒子之間的線條。
我們創(chuàng)建一個新的數(shù)組nDots,用來存儲手指對象和所有的粒子對象,接著遍歷所有的粒子,規(guī)定粒子的運動。
當粒子運動到畫布的邊界時,我們要做邊界處理,讓粒子向反方向運動。
用上下文對象繪制粒子,為了讓線條連線處不突兀,把粒子顏色也設置為背景顏色。
接著開始遍歷nDots數(shù)組,若遍歷到的是同一個粒子,則直接進入下一次循環(huán)。用勾股定理算出粒子之間的距離dDistance,當dDistance小于粒子間連線的最小距離時,繪制粒子間的線條;如果是手指對象,當dDistance小于向手指位置靠近的最小距離時,粒子向手指觸摸位置靠近。
最后,我們刪除比較過的粒子對象。
draw(ctx) {
const self = this;
// 清空整個畫布
ctx.clearRect(0, 0, 360, 780);
// 粒子和鼠標的結合,把鼠標數(shù)組添加到粒子數(shù)組中
const nDots = [this.mouse, this.dots];
// 粒子運動
this.dots.forEach(function (dot) {
dot.x += dot.xa;
dot.y += dot.ya;
// 粒子運動的邊界(畫布),反彈
dot.xa *= (dot.x > 360 || dot.x < 0) ? -1 : 1;
dot.ya *= (dot.y > 780 || dot.y < 0) ? -1 : 1;
// 繪制粒子
ctx.fillRect(dot.x, dot.y, 1, 1);
ctx.fillStyle = "#282828";
// 連線
for (let i=0; i<nDots.length; i++) {
let d = nDots[i];
if(d == dot) {
continue;
}
// 計算粒子的距離
let dx = dot.x - d.x;
let dy = dot.y - d.y;
let dDistance = Math.pow(dx, 2) + Math.pow(dy, 2);
// 連線操作
if (dDistance < d.max) {
// 處理觸摸事件
if (d == self.mouse && dDistance > d.max / 2) {
dot.x -= dx * 0.03;
dot.y -= dy * 0.03;
}
// 繪制線條
ctx.beginPath();
ctx.lineWidth = 0.7;
ctx.strokeStyle = 'rgba(80, 130, 189, 0.9)';
// 起始位置
ctx.moveTo(dot.x, dot.y);
// 結束位置
ctx.lineTo(d.x, d.y);
ctx.stroke();
ctx.closePath();
}
}
// 刪除比較過的粒子
nDots.splice(nDots.indexOf(dot), 1);
})
},
5、展示背景
最后,我們看看有沒有生效,在onShow方法中調用drawBackground方法,點亮背景。
export default {
onShow() {
this.drawBackground();
},
drawBackground() {
// 創(chuàng)建粒子,并存儲到數(shù)組中
for (let i=0; i<180; i++) {
// 粒子的位置
let x = Math.random() * 360;
let y = Math.random() * 780;
// 水平移動的速度,垂直移動的速度
let xa = Math.random() * 0.5;
let ya = Math.random() * 0.5;
this.dots.push({ x, y, xa, ya, max: 3600 }); // 兩個粒子相吸的最小距離
}
const can = this.$refs.canvas1;
const ctx = can.getContext('2d');
setInterval(() => {
this.draw(ctx);
}, 1000 / 60);
}
}
總結
- 處理粒子向手指對象運動時,為了讓速度不會太快,增加條件dDistance > d.max / 2。
- 當粒子運動邊界時,需要給粒子一個反方向的速度。
- 在onInit生命周期中無法拿到canvas的dom,需要在onShow方法中獲取。
- 粒子運動的速度看起來不太柔和,需要調試下參數(shù)。