用JS类封装粒子特效(四)
本讲,主要讨论两个问题:① 控制粒子的行进与暂停;② 改变粒子的外在形态;一、控制粒子的运动
我们继续使用上一讲 用JS类封装粒子特效(三) 里的 Lizi 类,但要控制粒子的运行与暂停,需要给粒子的运动函数 moving() 加一个条件,这个条件在 class{} 类的外部声明,是一个全局变量,类可以使用它:
//声明粒子运动的布尔变量 :true - 可以移动;false - 不可以移动
let canMove = true;
//创建一个 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'); //粒子标签
}
//创建粒子
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() {
if(canMove) {
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));
}
}
可以看到,移动粒子的函数 moving() 里,加入了一个 if 条件语句,if(canMove) {...} 中,canMove 变量是我们前面声明的全局布尔变量,if(canMove) 等于 if(canMove === true),意为如果 canMove 为真;若 canMove 为真,则执行花括号里的代码块,即令粒子移动。if 语句在这里不管 requestAnimationFrame 定时器,这意味着只要窗口处于活动中, moving这个函数总在执行,只是当 canMove === false 时,moving() 函数除了被递归调用什么都不做,对资源的消耗并没有加大。
这样,如果 Lizi 类被实例化应用,它就是可控的,不过我们要设计一个控制机制以控制什么情况 canMove = true/false,例如单击粒子的父元素:
mydiv.onclick = () => canMove = !canMove;
上一行代码放在 class {} 体之外,它不属于类里的代码;mydiv.conclick 是 id="mydiv" 的元素被单击的事件,= () => 是箭头函数,函数体的内容本应放在 {} 之内,因为只有一行,花括号可以省略。canMove = !canMove 是布尔变量取反,当前为真时变为假,反之变为真。
通过单击宿主元素只是为了演示,可以考虑将 canMove 变量的赋值变化交给其他机制,例如与音频的播放/暂停同步。
二、改变粒子的形态
粒子可以是各色各样的,改变其形态方法和渠道也很多,这里通过加工 li-zi 的 CSS 选择器把它弄成所需的外观,并给它加一个关键帧动画。以下是 li-zi 选择器和 keyframes 关键帧动画的设计:
li-zi {
position: absolute;
border-radius: 50%;
clip-path: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 100% 50%, 95% 50%, 27.5% 88.97%, 27.5% 11.03%, 95% 50%, 100% 50%, 100% 0%);
animation: rot 6s infinite linear;
}
@keyframes rot { to { transform: rotate(360deg); } }
以上,使用 clip-path 属性对 li-zi 元素内切一个三角形。方法不止用 clip-path,最简单的,或许是加个背景图片。效果和代码请看下楼。
<style>
#mydiv {
margin: 20px auto;
width: 740px;
height: 400px;
border: 1px solid purple;
background: linear-gradient(to right top,antiquewhite,purple);
position: relative;
}
li-zi {
position: absolute;
border-radius: 50%;
clip-path: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 100% 50%, 95% 50%, 27.5% 88.97%, 27.5% 11.03%, 95% 50%, 100% 50%, 100% 0%);
animation: rot 6s infinite linear;
}
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="mydiv"></div>
<script>
//声明粒子运动的布尔变量 :true - 可以移动;false - 不可以移动
let canMove = true;
//创建一个 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'); //粒子标签
}
//创建粒子
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() {
if(canMove) {
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 = 10 + Math.ceil(Math.random() * 20);
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();
});
mydiv.onclick = () => canMove = !canMove;
</script>
二楼代码
<style>
#mydiv {
margin: 20px auto;
width: 740px;
height: 400px;
border: 1px solid purple;
background: linear-gradient(to right top,antiquewhite,purple);
position: relative;
}
li-zi {
position: absolute;
border-radius: 50%;
clip-path: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 100% 50%, 95% 50%, 27.5% 88.97%, 27.5% 11.03%, 95% 50%, 100% 50%, 100% 0%);
animation: rot 6s infinite linear;
}
@keyframes rot { to { transform: rotate(360deg); } }
</style>
<div id="mydiv"></div>
<script>
//声明粒子运动的布尔变量 :true - 可以移动;false - 不可以移动
let canMove = true;
//创建一个 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'); //粒子标签
}
//创建粒子
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() {
if(canMove) {
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 = ; //步幅数组
//实例化 Lizi
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 = 10 + Math.ceil(Math.random() * 20);
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();
});
mydiv.onclick = () => canMove = !canMove; // 宿主元素单击事件 :粒子运动/暂停
</script>
老师讲的知识点(一)已在时空贴里使用。。
(二)更好了,粒子形态可以千变万化了。。。{:4_178:} 南无月 发表于 2023-7-9 09:07
老师讲的知识点(一)已在时空贴里使用。。
(二)更好了,粒子形态可以千变万化了。。。
{:4_199:} 漂亮!老师厉害!{:5_116:} 焱鑫磊 发表于 2023-7-9 09:22
漂亮!老师厉害!
{:4_190:} 马黑黑 发表于 2023-7-9 09:08
抄物转星移的时候顺手抄的{:4_173:} 南无月 发表于 2023-7-9 10:56
抄物转星移的时候顺手抄的
{:4_199:} 马黑黑 发表于 2023-7-9 10:57
{:4_205:}嘚瑟一下 南无月 发表于 2023-7-9 11:02
嘚瑟一下
应该的 这个类真好,还有这个控制移动的代码呢。学习了{:4_187:} 红影 发表于 2023-7-9 12:58
这个类真好,还有这个控制移动的代码呢。学习了
前面做的帖子用到过的吧 马黑黑 发表于 2023-7-9 12:59
前面做的帖子用到过的吧
是的,黑黑单独就这个讲解一下,更清楚了{:4_187:} 红影 发表于 2023-7-9 15:14
是的,黑黑单独就这个讲解一下,更清楚了
保姆级讲义,多啰嗦一下下 马黑黑 发表于 2023-7-9 17:24
保姆级讲义,多啰嗦一下下
非常细致透彻{:4_199:} 红影 发表于 2023-7-9 23:35
非常细致透彻
还行 马黑黑 发表于 2023-7-9 23:42
还行
这个效果也好看。 红影 发表于 2023-7-10 16:56
这个效果也好看。
不太差