用JS类封装粒子特效(三)
本帖最后由 马黑黑 于 2023-7-8 13:01 编辑上一讲 用JS类封装粒子特效(二)的 5楼 代码能驱使紫色正方形在父元素内上下左右来回移动。我们先来复习一下 Lizi 类完整代码,然后通过实例化配置实现真正的粒子特效:
class Lizi {
//构造函数 : 属性设计、设置 pa - 粒子宿主元素,size - 粒子大小
constructor(pa,size = 20) {
this.pa = pa;
this.size = size;
this.bg = 'purple';
this.left = 10;
this.top = 10;
this.xstep = 1; //水平移动步幅
this.ystep = 1; //垂直移动步幅
this.ele = document.createElement('li-zi'); //粒子元素 HTML标签名为 li-zi
}
//创建粒子
creating() {
this.ele.style.cssText = `
width: ${this.size}px;
height: ${this.size}px;
left: ${this.left}px;
top: ${this.top}px;
background: ${this.bg};
`;
this.pa.appendChild(this.ele);
this.moving();
}
//移动粒子
moving() {
this.left += this.xstep;
this.top += this.ystep;
if(this.left <= 0 || this.left >= this.pa.offsetWidth - this.size) this.xstep = -this.xstep;
if(this.top <= 0 || this.top >= this.pa.offsetHeight - this.size) this.ystep = -this.ystep;
this.ele.style.left = this.left + 'px';
this.ele.style.top = this.top + 'px';
requestAnimationFrame(this.moving.bind(this));
}
}
重点在:
① 构造函数里设计了 xstep 和 ystep 两个属性用以储存 x 和 y 方向的行进步幅;
② creating 函数调用了 moving 函数,粒子一旦创建立马动起来;
③ moving 函数驱使粒子运动,它通过改变粒子对象的 left 和 top 两个值,并对应去更改粒子元素属性 ele 的 CSS 的 left(style.left)和 top(style.top)属性达成运动目的,其中有边界判断、根据判断改变xy行进步幅的正负值,最后通过 requestAnimationFrame 定时器做永恒动力。
这样,实例化调用这个 Lizi 类可以是非常简单的:
let lz = new Lizi(mydiv,30); //实例化粒子
lz.creating(); //创建一个粒子
本讲,我们要创建更多的粒子,这些粒子是圆形的(在CSS中设置),它们的尺寸、位置、颜色以及移动步幅都不尽相同,需要一一配置。让我们在实例化类对象中完成这一操作:
//设置一组步幅数组 :以便令粒子的运动速度有差异性
let stepAr = ;
//创建30个粒子
Array.from({length: 30}).forEach((element) => {
//随机获取步幅索引
let xIdx = Math.floor(Math.random() * stepAr.length),
yIdx = Math.floor(Math.random() * stepAr.length);
element = new Lizi(mydiv); //实例化Lizi类
//尺寸区间 :4 - 14
element.size = 4 + Math.ceil(Math.random() * 10);
//左边值
element.left = Math.floor(Math.random() * (mydiv.offsetWidth - element.size));
//上边值
element.top = Math.floor(Math.random() * (mydiv.offsetHeight - element.size));
//x步幅
element.xstep = stepAr;
//y步幅
element.ystep = stepAr;
//随机背景色 :十六进制颜色
element.bg = '#' + Math.random().toString(16).substr(-6);
//创建粒子
element.creating();
});
粒子的形状我们可以在CSS的 li-zi 选择器中设置,border-radius: 50%; ,当然也可以在JS实例化的 creating() 函数执行后加入一句:element.ele.style.borderRadius = '50'; 。
这里,实例化代码看上去好复杂,实则逻辑清晰,都是对应 Lizi 类的属性进行相关属性配置、调用类提供的方法。在配置类实例化属性时,我们用上了 JS 的内置对象 Math,它提供丰富的数学操作,比如,我们用 Math.random() 生成随机数并用到各种算法中,用 Math.floor 和 Math.ceil 向下、向上获取整数,等等,还有其他的 JS 内置的东东,相关知识请参阅 JS:随机生成十六进制颜色值 - 马黑黑教程专版 - 花潮论坛 - Powered by Discuz! (huachaowang.com) (2楼)。
以下是本讲示例的完整代码:
<style>
#mydiv {
margin: 20px auto;
width: 600px;
height: 300px;
border: 1px solid purple;
position: relative;
}
li-zi {
position: absolute;
border-radius: 50%;
}
</style>
<div id="mydiv"></div>
<script>
class Lizi {
constructor(pa,size = 20) {
this.pa = pa;
this.size = size;
this.bg = 'purple';
this.left = 10;
this.top = 10;
this.xstep = 1;
this.ystep = 1;
this.ele = document.createElement('li-zi');
}
creating() {
this.ele.style.cssText = `
width: ${this.size}px;
height: ${this.size}px;
left: ${this.left}px;
top: ${this.top}px;
background: ${this.bg};
`;
this.pa.appendChild(this.ele);
this.moving();
}
moving() {
this.left += this.xstep;
this.top += this.ystep;
if(this.left <= 0 || this.left >= this.pa.offsetWidth - this.size) this.xstep = -this.xstep;
if(this.top <= 0 || this.top >= this.pa.offsetHeight - this.size) this.ystep = -this.ystep;
this.ele.style.left = this.left + 'px';
this.ele.style.top = this.top + 'px';
requestAnimationFrame(this.moving.bind(this));
}
}
let stepAr = ; //步幅数组
Array.from({length: 30}).forEach((element) => {
let xIdx = Math.floor(Math.random() * stepAr.length),
yIdx = Math.floor(Math.random() * stepAr.length);
element = new Lizi(mydiv);
element.size = 4 + Math.ceil(Math.random() * 10);
element.left = Math.floor(Math.random() * (mydiv.offsetWidth - element.size));
element.top = Math.floor(Math.random() * (mydiv.offsetHeight - element.size));
element.xstep = stepAr;
element.ystep = stepAr;
element.bg = '#' + Math.random().toString(16).substr(-6);
element.creating();
});
</script>运行效果请看下一楼。
<style>
#mydiv {
margin: 20px auto;
width: 600px;
height: 300px;
border: 1px solid purple;
position: relative;
}
li-zi {
position: absolute;
border-radius: 50%;
}
</style>
<div id="mydiv"></div>
<script>
class Lizi {
constructor(pa,size = 20) {
this.pa = pa;
this.size = size;
this.bg = 'purple';
this.left = 10;
this.top = 10;
this.xstep = 1;
this.ystep = 1;
this.ele = document.createElement('li-zi');
}
creating() {
this.ele.style.cssText = `
width: ${this.size}px;
height: ${this.size}px;
left: ${this.left}px;
top: ${this.top}px;
background: ${this.bg};
`;
this.pa.appendChild(this.ele);
this.moving();
}
moving() {
this.left += this.xstep;
this.top += this.ystep;
if(this.left <= 0 || this.left >= this.pa.offsetWidth - this.size) this.xstep = -this.xstep;
if(this.top <= 0 || this.top >= this.pa.offsetHeight - this.size) this.ystep = -this.ystep;
this.ele.style.left = this.left + 'px';
this.ele.style.top = this.top + 'px';
requestAnimationFrame(this.moving.bind(this));
}
}
let stepAr = ; //步幅数组
Array.from({length: 30}).forEach((element) => {
let xIdx = Math.floor(Math.random() * stepAr.length),
yIdx = Math.floor(Math.random() * stepAr.length);
element = new Lizi(mydiv);
element.size = 4 + Math.ceil(Math.random() * 10);
element.left = Math.floor(Math.random() * (mydiv.offsetWidth - element.size));
element.top = Math.floor(Math.random() * (mydiv.offsetHeight - element.size));
element.xstep = stepAr;
element.ystep = stepAr;
element.bg = '#' + Math.random().toString(16).substr(-6);
element.creating();
});
</script> 一粒到多粒,到颜色、位置、步幅随机变,老师逐字逐句细细讲解,极明白的教程。。。
{:4_190:}上杯茶给老师喝。。。 这个单独列出来挺好,可以单独用到其他播放器效果的帖子里了{:4_187:} 步幅取了这么多,取少点也可以吧。
let xIdx = Math.floor(Math.random() * stepAr.length),这句具体的含义没看懂{:5_102:}stepAr.length好像不是里面的取值,而是数组长度?
这些粒子是圆形的(在CSS中设置)
这个能在css中设置,其他的好像不能?我去掉圆形加上转动,结果它不转呢。 才发现这个li-zi { }前面没有点啊#什么的,这样也可以啊{:4_173:} 学习!{:4_187:} 焱鑫磊 发表于 2023-7-8 16:13
学习!
{:4_190:} 南无月 发表于 2023-7-8 13:43
一粒到多粒,到颜色、位置、步幅随机变,老师逐字逐句细细讲解,极明白的教程。。。
上杯茶给老 ...
谢茶 红影 发表于 2023-7-8 15:32
才发现这个li-zi { }前面没有点啊#什么的,这样也可以啊
你好像翘过课?我在最近做的时钟中,那个完美版,用的就是自定义HTML标签,它的命名有个规范,用字母命名,分两部分,中间有连接符 - 连结起来。在 CSS 里,选择器这么写:
li-zi { ... }
css-doodle { ... }
在 HTML 里,标签这么写:
<li-zi> ... </li-zi>
<css-doodle> ... </css-doodle>
li-zi 也好,css-doodle 也好,都是自定义标签,浏览器都支持。一般来说,自定义标签自然拥有 div 的一切属性。 本帖最后由 马黑黑 于 2023-7-8 16:52 编辑
红影 发表于 2023-7-8 15:18
步幅取了这么多,取少点也可以吧。
let xIdx = Math.floor(Math.random() * stepAr.length),这句具体的含 ...
取多取少取决于你对粒子运行速度的差异性的要求。一般来说,至少要有两个,一正一负,以保证粒子运行方向有上下左右这四个:
let stepAr = ;
然后,每一个粒子的 xstep 和 ystep 随机取这两个值,正正、正负、正负、负正、负负四个组合都可能存在。
至于 xIdx 和 yIdx,它们通过 Math 获得随机索引,分析如下:
stepAr.length 是数组 stepAr 的长度,即,有多少个数组元素;
Math.random() 是获得 0 - 0.999... 之间的随机数(浮点数);
Math.random() * stepAr.length 得到的数字也是浮点数,我们需要对之取整:
Math.floor(number) 是向下取整,如果 number 为 0.999...,则向下取整后是 0,如果 number 是 1.999... ,向下取整后得到的是 1;
现在,把 Math.random() * stepAr.length 替换 number,就是对 Math.random() * stepAr.length 向下取整,假设数组元素为 10 个,则可能得到的最小值是 0 和 0.n 之间,向下取整后是 0 、最大值是 9.999...,向下取整后是 9,正好对应最后一个数组元素的索引(数组索引从 0 开始)。 红影 发表于 2023-7-8 15:26
这些粒子是圆形的(在CSS中设置)
这个能在css中设置,其他的好像不能?我去掉圆形加上转动,结果它不转呢 ...
那应该是你的设计有问题。试下这么设置CSS:
<style>
#mydiv {
margin: 20px auto;
width: 600px;
height: 300px;
border: 1px solid purple;
position: relative;
}
li-zi {
position: absolute;
animation: rot 6s infinite linear;
}
@keyframes rot { to { transform: rotate(360deg); } }
</style> 也只能看一眼,这研究太高深了 樵歌 发表于 2023-7-8 19:33
也只能看一眼,这研究太高深了
高度不到一米 马黑黑 发表于 2023-7-8 16:27
谢茶
不用客气一下应该的 南无月 发表于 2023-7-8 19:45
不用客气一下应该的
{:4_189:} 马黑黑 发表于 2023-7-8 16:32
你好像翘过课?我在最近做的时钟中,那个完美版,用的就是自定义HTML标签,它的命名有个规范,用字母命名 ...
哦,自定义标签是不用管这个的。学过呀,忘了呗。{:4_173:} 红影 发表于 2023-7-8 20:24
哦,自定义标签是不用管这个的。学过呀,忘了呗。
脸没混熟