本文参考youtube上一位大佬的分享:www.youtube.com/watch?v=D6E…
实现效果:
- gif图片画质只能这样了,具体效果可以参考:web.coinpx.io/#/canvas/fl…
思路:
- 多个的色块(这里用圆)随机运动,并且大小也随着运动变化
- 每个色块半径放大后,看起来就有了流体效果
- 更改色块之间的混合模式,实现不同颜色的混合效果
实现demo代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
* {
outline: 0;
margin: 0;
padding: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
overflow: hidden;
background-color: #fff;
}
canvas {
width: 100%;
height: 100%;
}
</style>
<body>
<script>
// 颜色集合
const COLORS = [
{ r: 45, g: 74, b: 227 },
{ r: 250, g: 255, b: 89 },
{ r: 255, g: 104, b: 248 },
{ r: 44, g: 209, b: 252 },
{ r: 54, g: 233, b: 84 },
]
class App {
constructor() {
// 创建画布
this.canvas = document.createElement("canvas")
document.body.appendChild(this.canvas)
this.ctx = this.canvas.getContext("2d")
this.pixelRatio = window.devicePixelRatio > 1 ? 2 : 1
// 色块球的数量
this.totalParticles = 15
this.particles = []
// 最大半径与最小半径,这个半径越大流体效果看起来更好看(个人喜好)
this.maxRadius = 900
this.minRadius = 400
window.addEventListener("resize", this.resize.bind(this), false)
this.resize()
this.animate()
}
// 重置画布大小
resize() {
this.stageWidth = document.body.clientWidth
this.stageHeight = document.body.clientHeight
this.canvas.width = this.stageWidth * this.pixelRatio
this.canvas.height = this.stageHeight * this.pixelRatio
this.ctx.scale(this.pixelRatio, this.pixelRatio)
// 色块之间的混合模式
// lighten overlay saturation color luminosity 等,所有可选值请查看官方文档
this.ctx.globalCompositeOperation = "saturation"
this.createParticles()
}
// 创建色块球
createParticles() {
let curColor = 0
this.particles = []
for (let i = 0; i < this.totalParticles; i++) {
this.particles.push(
new GlowParticle(
Math.random() * this.stageWidth,
Math.random() * this.stageHeight,
Math.random() * (this.maxRadius - this.minRadius) + this.minRadius,
COLORS[curColor % COLORS.length]
)
)
curColor++
}
}
animate() {
window.requestAnimationFrame(this.animate.bind(this))
// 清除画布内容
this.ctx.clearRect(0, 0, this.stageWidth, this.stageHeight)
for (let i = 0; i < this.totalParticles; i++) {
const item = this.particles[i]
item.animate(this.ctx, this.stageWidth, this.stageHeight)
}
}
}
window.onload = () => {
new App()
}
const PI2 = Math.PI * 2
// 运动速度
const speed = 2
// 色块对象
class GlowParticle {
constructor(x, y, radius, rgb) {
this.x = x
this.y = y
this.radius = radius
this.rgb = rgb
// 随机运动速度
this.vx = Math.random() * speed
this.vy = Math.random() * speed
this.sinValue = Math.random()
}
animate(ctx, stageWidth, stageHeight) {
this.sinValue += 0.01
// Math.sin(this.sinValue) 值-1~1之间,让圆变大后又缩小
this.radius += Math.sin(this.sinValue)
this.x += this.vx
this.y += this.vy
// 超出边界
if (this.x < -10) {
this.vx *= -1
} else if (this.x > stageWidth + 10) {
this.vx *= -1
}
if (this.y < -10) {
this.vy *= -1
} else if (this.y > stageHeight + 10) {
this.vy *= -1
}
// 绘图
ctx.beginPath()
// 径向渐变
const g = ctx.createRadialGradient(this.x, this.y, this.radius * 0.01, this.x, this.y, this.radius)
// 渐变颜色过渡
g.addColorStop(0, `rgba(${this.rgb.r},${this.rgb.g},${this.rgb.b},1)`)
g.addColorStop(1, `rgba(${this.rgb.r},${this.rgb.g},${this.rgb.b},0)`)
ctx.fillStyle = g
ctx.arc(this.x, this.y, this.radius, 0, PI2, false)
ctx.fill()
}
}
</script>
</body>
</html>