持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情
在开发过程中,常常会遇到进度条的需求,本次就是给进度条添加一下好看的效果,比如说在进度条的运动过程中,有粒子不断的漂浮而出。
效果:
考虑到粒子会非常多,那么就不能使用简单的DOM元素去生成一粒粒的粒子
所以我使用了Canvas
画布去进行粒子的绘制,至于进度条虽然可以绘制在canvas
画布中,但是考虑到方便对已有的进度条添加粒子效果,本次的进度条先使用div
元素绘制了。
进度条
那么先创建一个进度条吧
我使用是vue3
,先定义html
<template>
<div class="process">
<!-- 进度条 -->
<div class="process-bar">
<div class="current" id="create-particles"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
let processdata = ref({
d: 45 + '%'
})
onMounted(()=>{ })
</script>
css
使用了预编译的less
<style scoped lang="less">
// 宽高
.wd(@w, @h) {
width: @w;
height: @h;
}
.process {
.wd(100%, 100%);
display: flex;
align-items: center;
justify-content: center;
background-color: #1E1E1E;
.process-bar {
@width: 300px;
@height: 20px;
.wd(@width, @height);
background-color: #000;
border-radius: calc(@height / 2);
.current {
width: v-bind('processdata.d');
height: @height;
background-color: #A543DC;
border-radius: calc(@height / 2);
}
}
}
</style>
这样一来,就能出现一个进度在45%的进度条了
粒子方法
那么接下来就是重点的粒子方法,粒子是用canvas画布绘制的,所以需要使用document.createElement('canvas')
去创建一个canvas
元素
// 粒子画布初始化方法
const initparticles = (width: number, height: number, color: string, nums: number, startend: boolean)=>{
let canvasParticles = <HTMLCanvasElement>document.getElementById('myCanvasParticles');
let fatherDom = document.getElementById("create-particles");
if (!fatherDom) return ;
// 如果没有canvas
if (!canvasParticles) {
canvasParticles = document.createElement('canvas');
canvasParticles.id = "myCanvasParticles";
fatherDom.appendChild(canvasParticles);
}
canvasParticles.width = width;
canvasParticles.height = height;
canvasParticles.style.top = `calc(50% - ${height / 2}px)`;
......
}
初始化粒子画布方法传入4个参数:
参数 | 作用 |
---|---|
width | 画布的宽度 |
height | 画布的高度 |
color | 粒子的颜色 |
nums | 粒子的数量 |
startend | 粒子是否开始渲染/结束渲染 |
这样就可以在#create-particles
元素中创建画布了,然后在less
对画布元素修改为绝对定位:
#create-particles {
position: relative;
#myCanvasParticles {
position: absolute ;
right: 0px ;
}
}
这样画布的位置就会在进度条靠右侧的位置,并且上下居中
在画布中,有许许多多的粒子,并且每个粒子的运动方向和颜色都是不同的,所以可以创建一个粒子的类
// 画布
let ctx = canvasParticles.getContext("2d") as CanvasRenderingContext2D;
// 定义粒子类
class Partical {
// 粒子坐标
x: number = 0;
y: number = 0;
initx: number;
inity: number;
// 粒子速度
speed: number = 0;
// 粒子运动方向
directionAngle: number = 0;
// 粒子大小
radius: number = 2;
// 粒子在两方向上运动速度
vector: { x: number, y: number };
// 画布大小
canvas: { w: number, h: number };
constructor(...data: [x: number, y: number, speed: number, directionAngle: number, canvas: { w: number, h: number }]) {
[this.x, this.y, this.speed, this.directionAngle, this.canvas] = data;
[ this.initx, this.inity] = [this.x, this.y];
this.vector = {
x: -Math.abs(this.speed * Math.cos(this.directionAngle * Math.PI / 180)), //粒子在x轴的速度
y: this.speed * Math.sin(this.directionAngle * Math.PI / 180), //粒子在y轴的速度
}
};
//绘制粒子的函数
draw(color: string){
...
}
}
这样先new
一个粒子的实例出来看看
let one = new Partical(20, 40, 1, 120 + Math.floor(Math.random() * (140 - 120)), { w: width, h: height });
one.draw('#ff0');
目前粒子是不会运动的,所以需要在class
类中添加运动方法
// 粒子更新方法
update(color: string) {
this.x += this.vector.x;
this.y += this.vector.y;
this.draw(color);
}
重新调整一下粒子的初始位置到进度条的右侧,并且使用requestAnimationFrame
设置定时运动方法,就可以看到以下效果:生成的小黄点粒子已经开始在运动了
既然一个粒子已经生成好啦,那么接下来就是生成nums
的数量的粒子
但是此时当粒子从画布中超出后就一次性结束了,所以需要重新让粒子回到原起始位置,然粒子不断循环起来
// 飞出画布外
[this.x, this.y] = [ this.initx, this.inity];
但是此时又出现了一个新问题,那就是粒子最初是同步出现的,这样虽然后面会因为每个粒子的方向不同,显现出不同。但是最初的时候还是一下子就会全部发散出去。
所以可以在requestAnimationFrame
中再生成粒子实例,每十次requestAnimationFrame
循环生成一个粒子