需求
-
实现礼花飘落效果
-
定义几种礼花类型(圆形、方形、矩形、三角形),随机出现
-
降落速度、礼花大小、数量等可以通过参数传入
实现
- 新建
util.js
文件,定义一些常用方法
export const random = () => Math.random()
// 不包含max
export const randomMax = max => Math.floor(Math.random() * max)
// 包含max
export const randomMaxCeil = max => Math.ceil(Math.random() * max)
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = (
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame ||
function (callback) {
return setTimeout(callback, Math.floor(1000 / 60))
}
)
}
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = (
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.msCancelAnimationFrame ||
window.oCancelAnimationFrame ||
clearTimeout
)
}
- 新建
flake.js
实现礼花碎屑基础类Flake
import { random, randomMax, randomMaxCeil } from './util'
class Flake {
constructor(canvas, size, fallSpeed) {
this.canvas = canvas
this.maxSize = size
this.fallSpeed = fallSpeed
// Y方向速度
this.speed = 1 * random() + fallSpeed
// 用来计算X方向偏移
this.stepSize = random() / 30
this.step = 0
// 背景色
const r = ~~(256 * random())
const g = ~~(256 * random())
const b = ~~(256 * random())
this.rgba = `rgba(${r}, ${g}, ${b}, 1)`
// 旋转角度
this.rot = 0
this.reset()
// 初始化时候,让碎屑随机分布在屏幕上
this.y = randomMax(canvas.height)
}
// 更新礼花碎屑位置
update() {
this.velX *= 0.98
this.velY <= this.speed && (this.velY = this.speed)
this.velX += Math.cos(this.step += 0.05) * this.stepSize
this.y += this.velY
this.x += this.velX
if (
this.x >= this.canvas.width ||
this.x <= 0 ||
this.y >= this.canvas.height ||
this.y <= 0
) {
this.reset()
}
}
// 礼花碎屑重置到最上面,大小和X方向位置重新随机
reset() {
const { canvas, maxSize, speed } = this
this.x = randomMax(canvas.width)
this.y = 0
this.size = random() * maxSize + 2
this.velY = speed
this.velX = 0
}
}
- 修改
flake.js
文件,定义几种礼花类型,并添加各自的绘制方法render()
// 圆形
export class CircularFlake extends Flake {
constructor(canvas, size, fallSpeed) {
super(canvas, size, fallSpeed)
this.radius = randomMaxCeil(size / 2)
}
render(ctx) {
const { x, y, rgba, radius } = this
ctx.save()
ctx.beginPath()
ctx.fillStyle = rgba
ctx.arc(x, y, radius, 0, 2 * Math.PI, true)
ctx.fill()
ctx.restore()
}
}
// 方形
export class SquareFlake extends Flake {
constructor(canvas, size, fallSpeed) {
super(canvas, size, fallSpeed)
this.width = this.height = randomMaxCeil(size)
}
render(ctx) {
const { x, y, rgba, rot, width, height } = this
ctx.save()
ctx.beginPath()
ctx.fillStyle = rgba
ctx.translate(x, y)
ctx.rotate(rot * Math.PI / 180)
ctx.translate(-x, -y)
ctx.fillRect(x - width / 2, y - height / 2, width, height)
ctx.restore()
}
}
// 矩形
export class RectangleFlake extends Flake {
constructor(canvas, size, fallSpeed) {
super(canvas, size, fallSpeed)
this.width = randomMaxCeil(size)
this.height = randomMaxCeil(size)
}
render(ctx) {
const { x, y, rgba, rot, width, height } = this
ctx.save()
ctx.beginPath()
ctx.fillStyle = rgba
ctx.translate(x, y)
ctx.rotate(rot * Math.PI / 180)
ctx.translate(-x, -y)
ctx.fillRect(x - width / 2, y - height / 2, width, height)
ctx.restore()
}
}
// 三角形
export class TriangleFlake extends Flake {
constructor(canvas, size, fallSpeed) {
super(canvas, size, fallSpeed)
this.width = this.height = randomMaxCeil(size)
}
render(ctx) {
const { x, y, rgba, rot, width, height } = this
ctx.save()
ctx.beginPath()
ctx.fillStyle = rgba
ctx.translate(x, y)
ctx.rotate(rot * Math.PI / 180)
ctx.translate(-x, -y)
ctx.moveTo(x - width / 2, y + 0.333 * height)
ctx.lineTo(x + width / 2, y + 0.333 * height)
ctx.lineTo(x, y - 0.666 * height)
ctx.closePath()
ctx.fill()
ctx.restore()
}
}
- 新建
index.js
文件,添加初始化canvas
、生成碎片和绘制方法
import { randomMax } from './util'
import { CircularFlake, SquareFlake, RectangleFlake, TriangleFlake } from './flake'
export default class CanvasFlake {
constructor(opts) {
// const {
// maxFlake = 200,
// flakeSize = 10,
// fallSpeed = 2,
// parentNode = document.body
// } = opts
// Object.assign(this, { maxFlake, flakeSize, fallSpeed, parentNode })
opt = opts || {}
this.maxFlake = opt.maxFlake || 200
this.flakeSize = opt.flakeSize || 10
this.fallSpeed = opt.fallSpeed || 2
this.parentNode = opt.parentNode || document.body
// status: 0 初始状态 1 开始状态 2 停止状态 3 暂停状态 4 重新开始
this.status = 0
this.canvas = null
this.ctx = null
this.flakes = []
}
// 在指定的DOM中插入canvas,并设置相关属性
initCanvas() {
const canvas = document.createElement('canvas')
canvas.id = 'Paperfall'
canvas.width = this.parentNode.clientWidth
canvas.height = this.parentNode.clientHeight
canvas.setAttribute('style', 'position:absolute;top:0;left:0;z-index:0;pointer-events:none;')
this.parentNode.appendChild(canvas)
this.canvas = canvas
this.ctx = canvas.getContext('2d')
window.onresize = function() {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
}
}
// 生成maxFlake个随机类型的碎片
getFlakes() {
const flakeTypes = [
CircularFlake,
SquareFlake,
RectangleFlake,
TriangleFlake
]
for (let i = 0; i < this.maxFlake; i++) {
this.flakes.push(new flakeTypes[randomMax(flakeTypes.length)](this.canvas, this.flakeSize, this.fallSpeed))
}
}
// canvas中渲染所有碎片
render() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.flakes.forEach(item => {
item.update()
item.render(this.ctx)
})
this.loop = window.requestAnimationFrame(() => {
// 用CanvasFlake去执行,否则找不到ctx、flakes等
this.render.apply(this)
})
}
}
- 修改
index.js
,添加方法控制canvas
的绘制、停止、暂停和重新开始
import { randomMax } from './util'
import { CircularFlake, SquareFlake, RectangleFlake, TriangleFlake } from './flake'
export default class CanvasFlake {
// ...
start() {
if (this.status === 1 || this.status === 4) return
this.status = 1
this.initCanvas()
this.getFlakes()
this.render()
}
stop() {
if (this.status === 2 || this.status === 0 || !this.canvas) return
this.pause()
this.status = 2
this.parentNode.removeChild(this.canvas)
this.canvas = null
}
pause() {
if (this.status === 3) return
this.status = 3
window.cancelAnimationFrame(this.loop)
}
resume() {
if (this.status === 3 && this.canvas) {
this.status = 4
this.loop = window.requestAnimationFrame(() => {
this.render.apply(this)
})
}
}
}
- 在页面中使用
<!-- 有一个DOM节点,放canvas -->
<div id="canvasWrapper" style="width: 100vw; height: 100vh"></div>
import CanvasFlake from '@/utils/flake'
export default {
mounted() {
new CanvasFlake({
parentNode: document.getElementById('canvasWrapper'),
maxFlake: 80
}).start()
}
}
发表评论