前言
最近广州疫情爆发,而且还老是下雨! 但让人感动的是,很多很多人,不分昼夜,冒着大雨奔赴一线,风雨无阻的守护我们!
想自己动手给网页来场雨的洗礼,记住这些可爱的人,同时希望疫情早些过去
分析需求
给整个页面下场雨,一场雨是由无数的雨滴构成,所以我们是不是可以用div
或者canvas
来实现雨滴,另外需要一个专门生成雨滴构造函数,并且每滴雨它出现的位置、大小、下落速度和方向都是不一的
下雨
使用div模拟雨滴
body .rain {
display: block;
width: 2px;
height: 50px;
background-image: radial-gradient(#fff 0%, rgba(255, 255, 255, 0) 60%);
margin: 0 auto;
}
显示一滴雨如图
但是我们需要很多雨滴,那我们就不能这样写了
使用js动态生成N滴雨
1.首先先得有个构造函数
通过构造函数生成雨滴,但需要注意每滴雨的位置、速度和方向不一都可能不一样的
class Rain {
constructor(opt = {}) {
// 每个el即是一个雨滴
this.el = null;
// 雨点的出现的位置
this.x = 0;
this.y = 0;
//雨的颗粒度大小
this.width = opt.width;
this.maxWidth = opt.maxWidth || 3;
this.minWidth = opt.minWidth || 1;
// 雨点的长度
this.height = 0;
this.maxHeight = opt.maxHeight || 30;
this.minHeight = opt.minHeight || 20;
// 雨的下落速度
this.speed_x = 0
this.speed_y = 0
this.maxSpeed = opt.maxSpeed || 100;
this.minSpeed = opt.minSpeed || 10;
// 初始化数据
this.init();
}
}
如上,通过new Rain
产生的对象身上会有我们提前预设的属性,比如雨滴的大小width
和height
,x
和y
轴下落速率speed_x
和speed_y
等等
其中我们还需要一个init
函数,来初始化对象
2.初始化对象基本信息
init() {
// 初始化指定范围的随机雨点大小
this.width = Math.floor(Math.random() * this.maxWidth + this.minWidth)
this.height = Math.floor(Math.random() * this.maxHeight + this.minHeight)
// 初始化指定范围的随机雨点位置
this.x = Math.floor(Math.random() * (window.innerWidth - this.width))
this.y = Math.floor(Math.random() * (window.innerHeight - this.height))
// 初始化指定范围的随机雨点速度
this.speed_y = Math.random() * this.maxSpeed + this.minSpeed
this.speed_x = Math.random() * this.maxSpeed
}
如上代码,初始化雨滴包括三组值width
和height
值,x
轴和y
轴的位置,以及下雨的速度speed_y
和speed_x
,设置两个速度的原因是让雨的水平方向和垂直方向的速度不一样。需要注意,这里可以控制雨的大小,所以以后可以在这里调整雨
初始化雨滴的信息后,需要将雨滴的样式添加上
3.添加设置样式函数
setStyle() {
this.el.style.cssText = `
position:fixed;
left:0;
top:0;
display:block;
width:${this.width}px;
height:${this.height}px;
background-image: radial-gradient(#fff 0%, rgba(255, 255, 255, 0) 60%);
z-index: 999999999;
opacity:.6;
pointer-events: none;
transform: translate(${this.x}px, ${this.y}px);
`
}
通过setStyle
函数设置好雨滴的基本样式后,接着需要一个创建真实元素的函数,将雨滴渲染出来
4.渲染雨滴
render() {
this.el = document.createElement('div')
this.setStyle()
document.body.appendChild(this.el)
}
创建render
函数,将对象的el赋值为新创建的div dom
元素,然后通过setStyle
设置样式,最后通过appendChild
添加到页面中
5.造1000雨滴试试
let rainList = []
for (let i = 0; i <= 1000; i++) {
let rain = new Rain();
rain.render();
rainList.push(rain);
}
通过for
循环创建我们想要的雨点数,并渲染到页面中,通过rainList
将创建的雨滴存起来。
创建雨滴结果如下
但是雨还不能动,得想办法让它动起来
6.让雨下起来
创建一个move
方法,改变雨滴的位置即可实现动,但是需要注意判断边界,当超出边界时,重新初始化让它跑到其他可视区去,并重新设置随机样式,这样才有连续的雨滴下落
move() {
// this.x += this.speed_x
this.y += this.speed_y
// 判断边界
if (this.x < -this.width || this.x > window.innerWidth || this.y > window.innerHeight) {
this.init()
this.setStyle()
}
this.el.style.transform = `translate(${this.x}px, ${this.y}px)`
}
上述代码调用一次只会动一次,所以需要用到定时器,让它持续动。或者使用window.requestAnimationFrame
,这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数。requestAnimationFrame
比起 setTimeout、setInterval
的优势主要有两点:
requestAnimationFrame
会把每一帧中的所有DOM
操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60
帧;- 在隐藏或不可见的元素中,
requestAnimationFrame
将不会进行重绘或回流,这当然就意味着更少的的cpu
和内存使用量;
function moveRain() {
window.requestAnimationFrame(() => {
rainList.forEach((item) => {
item.move()
})
moveRain()
})
}
最后执行moveRain
moveRain()
7.效果展示
结果如图
可以看到大致的结果已经出来了,雨滴样式以及大小和快慢可以自己调,会出现不同的样式雨:如大一些的雨
let rain = new Rain({
maxWidth:5,
speemaxSpeed:150
});
结果如下
另外下午不可能每次只有向下飘,有时候刮风,上下飘时还左右斜飘。这可以调整水平偏移位置来调整 如:
move() {
this.x += 5;
...
}
结果如图
小结
通过这个案例,复习了原生api
的基本使用。但是仍然存在很多问题,还有一些需要完善的地方。比如:
- 代码的复用性;能不能用于各个想引用的元素上?
- 那么多的
div
移动带来的性能问题 - 顶部出现的雨总是比较少,其实主要原因是当第一次超出可视区域时;
- 除了这种方式,是不是我们可以使用其他方式来实现呢。比如
canvas
,性能会更佳 等等...
备注:
- 灵感来源:juejin.cn/post/691085…
- 上述代码已上传github:github.com/jCodeLife/r…
- 新手小白,难免有跑偏。有不正确的,希望批评指出,我会及时更正,感谢!!