马黑黑 发表于 2023-7-9 09:01

用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,最简单的,或许是加个背景图片。效果和代码请看下楼。

马黑黑 发表于 2023-7-9 09:01

<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>

马黑黑 发表于 2023-7-9 09:02

二楼代码
<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>

南无月 发表于 2023-7-9 09:07

老师讲的知识点(一)已在时空贴里使用。。
(二)更好了,粒子形态可以千变万化了。。。{:4_178:}

马黑黑 发表于 2023-7-9 09:08

南无月 发表于 2023-7-9 09:07
老师讲的知识点(一)已在时空贴里使用。。
(二)更好了,粒子形态可以千变万化了。。。

{:4_199:}

焱鑫磊 发表于 2023-7-9 09:22

漂亮!老师厉害!{:5_116:}

马黑黑 发表于 2023-7-9 10:28

焱鑫磊 发表于 2023-7-9 09:22
漂亮!老师厉害!

{:4_190:}

南无月 发表于 2023-7-9 10:56

马黑黑 发表于 2023-7-9 09:08


抄物转星移的时候顺手抄的{:4_173:}

马黑黑 发表于 2023-7-9 10:57

南无月 发表于 2023-7-9 10:56
抄物转星移的时候顺手抄的

{:4_199:}

南无月 发表于 2023-7-9 11:02

马黑黑 发表于 2023-7-9 10:57


{:4_205:}嘚瑟一下

马黑黑 发表于 2023-7-9 11:05

南无月 发表于 2023-7-9 11:02
嘚瑟一下

应该的

红影 发表于 2023-7-9 12:58

这个类真好,还有这个控制移动的代码呢。学习了{:4_187:}

马黑黑 发表于 2023-7-9 12:59

红影 发表于 2023-7-9 12:58
这个类真好,还有这个控制移动的代码呢。学习了

前面做的帖子用到过的吧

红影 发表于 2023-7-9 15:14

马黑黑 发表于 2023-7-9 12:59
前面做的帖子用到过的吧

是的,黑黑单独就这个讲解一下,更清楚了{:4_187:}

马黑黑 发表于 2023-7-9 17:24

红影 发表于 2023-7-9 15:14
是的,黑黑单独就这个讲解一下,更清楚了

保姆级讲义,多啰嗦一下下

醉美水芙蓉 发表于 2023-7-9 17:44

红影 发表于 2023-7-9 23:35

马黑黑 发表于 2023-7-9 17:24
保姆级讲义,多啰嗦一下下

非常细致透彻{:4_199:}

马黑黑 发表于 2023-7-9 23:42

红影 发表于 2023-7-9 23:35
非常细致透彻

还行

红影 发表于 2023-7-10 16:56

马黑黑 发表于 2023-7-9 23:42
还行

这个效果也好看。

马黑黑 发表于 2023-7-10 19:45

红影 发表于 2023-7-10 16:56
这个效果也好看。

不太差
页: [1] 2 3 4 5
查看完整版本: 用JS类封装粒子特效(四)