目录
🤺🤺🤺前期回顾
🪂 演示
演示地址https://www.kirilv.com/canvas-confetti/
🛹 目录树
1:封装canvas
2:封装彩屑 Ts
3:抽离逻辑判断 Ts
4:页面调用
src
|--- types
|--- global.d.ts
|--- components
|--- zkConfettiCanvas.vue
|--- utils
|--- confetti.ts
|--- confettiIndex.ts
|--- views
|---- home.vue
🐔 安装
pnpm install --save canvas-confetti
🤖 global.d.ts
// 申明外部 npm 插件模块
declare module 'canvas-confetti';
😷 zkConfettiCanvas.vue
<template>
<canvas
id="confetti"
style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
"
></canvas>
</template>
🌍 confetti.ts
import { nextTick } from "vue";
interface Origin {
x?: number;
y?: number;
}
// 注释需要放在函数上面,不然调用鼠标放上去没提示
/**
* 显示彩屑效果
* @param { number } type - 彩屑效果类型
* @type 1: 默认屏幕中间喷射
* @type 2: 满屏随机炸裂
* @type 3: 屏幕中间随机位置喷射
* @type 4: 屏幕两端向内喷射
* @param { Origin } origin - 彩屑的原点坐标
* x: x轴位置,默认值为0.5
* y: y轴位置,默认值为0.7
* @url https://www.kirilv.com/canvas-confetti/
* @returns { Promise<void> } - Promise对象
*/
export async function showConfetti(
type = 1,
origin: Origin = { x: 0.5, y: 0.7 }
): Promise<void> {
await import("canvas-confetti").then((confetti) => {
const canvasEle: HTMLCanvasElement | null =
document.querySelector("#confetti");
if (!canvasEle) return;
canvasEle.style.zIndex = "9999";
const myConfetti = confetti.create(canvasEle, {
resize: true,
useWorker: true,
disableForReducedMotion: true,
});
import("./confettiIndex").then((confettiIndex) => {
confettiIndex.getUserDescribe(type, myConfetti, origin);
});
nextTick(() => {
canvasEle.style.zIndex = "-1";
myConfetti.reset();
});
});
}
🎋 confettiIndex.ts
interface Origin {
x?: number;
y?: number;
}
export function getUserDescribe(type: number, myConfetti: any, origin: Origin) {
const describeForNameMap = [
[
() => type === 1, // 判断条件
() =>
myConfetti({
particleCount: 100,
spread: 70,
// 默认屏幕中间喷射,可以在引入的时候传入参数,如 showConfetti({x:0.1,y:0.5})
origin: origin,
}), // 执行函数
],
[
() => type === 2,
() => {
const duration = 3 * 1000;
const animationEnd = Date.now() + duration;
const defaults = {
startVelocity: 30,
spread: 360,
ticks: 60,
zIndex: 0,
};
function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min;
}
const interval: any = setInterval(function () {
const timeLeft = animationEnd - Date.now();
if (timeLeft <= 0) {
return clearInterval(interval);
}
const particleCount = 50 * (timeLeft / duration);
myConfetti(
Object.assign({}, defaults, {
particleCount,
origin: { x: randomInRange(0.1, 0.3), y: Math.random() - 0.2 },
})
);
myConfetti(
Object.assign({}, defaults, {
particleCount,
origin: { x: randomInRange(0.7, 0.9), y: Math.random() - 0.2 },
})
);
}, 250);
},
],
[
() => type === 3,
() => {
function randomInRange(min: number, max: number) {
return Math.random() * (max - min) + min;
}
myConfetti({
angle: randomInRange(55, 125),
spread: randomInRange(50, 70),
particleCount: randomInRange(50, 100),
origin: origin,
});
},
],
[
() => type === 4,
() => {
const duration = performance.now() + 1 * 1000;
const confettiColors = ["#6967fe", "#85e9f4", "#e16984"];
void (function frame() {
myConfetti({
particleCount: confettiColors.length,
angle: 60,
spread: 55,
origin: { x: 0 },
colors: confettiColors,
});
myConfetti({
particleCount: confettiColors.length,
angle: 120,
spread: 55,
origin: { x: 1 },
colors: confettiColors,
});
if (performance.now() < duration) {
requestAnimationFrame(frame);
}
})();
},
],
];
function myError() {
throw new Error("没有找到对应的效果");
}
// 通过find方法找到子数组中的第一个函数(判断条件)为true的子数组
const getDescribe = describeForNameMap.find((item) => item[0]());
// 子数组存在则运行子数组中的第二个元素(执行函数)
getDescribe ? getDescribe[1]() : myError();
}
😷 index.vue
<template>
<div>
<zw-confetti-canvas />
<el-button type="success" @click="onTrigger">发射彩屑</el-button>
</div>
</template>
<script setup lang="ts">
import { showConfetti } from '/@/utils/confetti';
const onTrigger = () => {
showConfetti();
};
</script>
完毕!以上为全部代码,复制即用,默认封装四种特效、可以按照需求自定义奥~
🥰😉 谢谢观看
_______________________________ 期待再见 _______________________________