开始
html2particle 是一个复制 DOM 元素为 canvas 粒子效果的库。预设两种粒子动画效果,支持传入自定义粒子类效果。
安装
纯 TS 开发,依赖于 html2canvas 将 DOM 转为 canvas 。 核心代码只有200多行,不到 7kb 。不过由于依赖了 html2canvas
,压缩后的代码有 200+ kb。
sh
$ npm i html2particle
sh
$ pnpm add html2particle
sh
$ yarn add html2particle
sh
$ bun add html2particle
Usage
简单的从 html2particle 导出方法就行,传入 DOM ,会返回一个 startAnimation
方法,调用该方法即可开始动画。
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import html2particle from 'html2particle'
const containerRef = ref<HTMLElement>()
let handleClick = () => {}
const isShow = ref(true)
onMounted(() => {
const { startAnimation } = html2particle(containerRef.value!, {
type: 'Particle',
})
handleClick = () => {
isShow.value = false
startAnimation()
}
})
</script>
<template>
<div ref="containerRef" class="container" @click="handleClick">
<img v-show="isShow" src="/default.webp" alt="">
</div>
</template>
<style scoped>
.container{
width: 888px;
height: 592px;
cursor: pointer;
}
img{
object-fit: cover;
width: 100%;
height: 100%;
}
</style>
自定义
首先是有 TS类型限制,所以自定义的时候会有些许帮助。
这里有个自定义的示例如下:
ts
import type { IParticleInstance } from '../../../packages/html2particle/src/index'
function genNormalizedVal() {
return ((Math.random() + Math.random() + Math.random() + Math.random() + Math.random() + Math.random() - 3)) / 3
}
export class CustomParticle implements IParticleInstance {
name: string
animationDuration: number
speed: { x: number; y: number; ax: number; ay: number }
radius: number
startX: number
startY: number
rgbaArray: any
disWidth: number
disHeight: number
disLeft: number
disTop: number
yPosFunc: (t: any) => any
heightScaler: number
constructor() {
this.name = 'ExplodingParticle'
this.animationDuration = 1000
this.speed = {
x: Math.random() * 20 - 10, // [-20, 20]
y: Math.random() * 20 - 10,
ax: 0,
ay: 0.98,
}
this.radius = 10
this.startX = 0
this.startY = Math.random() * 40 - 20
this.rgbaArray = [0, 0, 0, 0]
this.disWidth = 0
this.disHeight = 0
this.disLeft = 0
this.disTop = 0
this.heightScaler = Math.round(65 * (genNormalizedVal() + 1) / 2) + 10
this.yPosFunc = (t: any) => t
}
draw(ctx: CanvasRenderingContext2D, percent: number): void {
if (this.radius > 0) {
if (this.rgbaArray[3] !== 0)
this.drawEmojiHeart(ctx, this.startX, this.startY, '🍑', this.radius)
this.startX += this.speed.x / 10
this.startY = this.startY + this.yPosFunc(percent) * this.heightScaler / 10 + this.speed.y / 10
}
}
/**
* 绘制 Emoji
* @param ctx 图层
* @param centerX 位置 X
* @param centerY 位置 Y
* @param emoji emoji
* @param size 大小
*/
drawEmojiHeart(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, emoji: string, size: number) {
ctx.font = `${size}px serif`
ctx.fillText(emoji, centerX - size / 2, centerY + size / 4)
}
/**
* 绘制五角星
* @param ctx 图层
* @param centerX 位置 X
* @param centerY 位置 Y
* @param radius 大小
* @param rgbaArray 颜色
*/
drawFivePointedStar(ctx: CanvasRenderingContext2D, centerX: number, centerY: number, radius: number, rgbaArray: number[]) {
ctx.beginPath()
const points = 5
for (let i = 0; i < points * 2; i++) {
const angle = (i * Math.PI) / points - Math.PI / 2
const distance = i % 2 === 0 ? radius : (radius * 2) / 3 // 判断奇偶,奇数为外角点,偶数为内角点
const x = centerX + Math.cos(angle) * distance
const y = centerY + Math.sin(angle) * distance
ctx.lineTo(x, y)
}
ctx.closePath()
ctx.fillStyle = `rgba(${rgbaArray[0]},${rgbaArray[1]},${rgbaArray[2]}, ${rgbaArray[3]})`
ctx.fill()
}
}