
实现局部彩条ConfettiEffect动效,发个博客记录一下
技术实现方案
1. 选择合适的动效库
我们选择 canvas-confetti 这个轻量级库,它专门用于创建纸屑效果,具有以下优势:
- 体积小(约 4KB)
- 效果丰富且可定制
- 性能优秀
- 支持多种发射模式
安装方式:
bash
npm install canvas-confetti
2. 创建动效组件
首先创建一个可复用的动效组件:
vue
<!-- ConfettiEffect.vue -->
<template>
<div></div>
</template>
<script>
import confetti from 'canvas-confetti';
export default {
name: 'ConfettiEffect',
methods: {
trigger(duration = 3000, position = null) {
// 如果提供了位置信息,则在该位置附近创建局部效果
if (position) {
confetti({
particleCount: 50, // 减少粒子数量
spread: 45, // 缩小扩散角度
startVelocity: 20, // 设置初始速度
decay: 0.95, // 设置衰减率
gravity: 1.5, // 调整重力
scalar: 0.8, // 缩放粒子大小
ticks: 100, // 减少动画持续时间
origin: {
x: position.x, // 设置X轴起始位置
y: position.y // 设置Y轴起始位置
}
});
} else {
// 默认全屏效果
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 }
});
}
}
}
}
</script>
3. 在触发点计算位置
在需要触发动效的地方(如复选框点击事件)计算位置并触发效果:
vue
<template>
<div class="task-item">
<input
type="checkbox"
:checked="task.status === 'done'"
@change="toggleTaskStatus(task, $event)"
/>
<!-- 其他任务内容 -->
</div>
</template>
<script>
export default {
methods: {
toggleTaskStatus(task, event) {
// 业务逻辑处理...
// 只在任务完成时触发动效
if (task.status === 'done') {
// 获取触发元素的位置
const checkbox = event.target;
const rect = checkbox.getBoundingClientRect();
const position = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 2) / window.innerHeight
};
// 触发局部彩条效果
this.$refs.confettiEffect.trigger(3000, position);
}
}
}
}
</script>
4. 在首页组件中集成应用
在首页组件中集成彩条效果,主要应用于任务完成场景:
js
<!-- dashboard.vue -->
<template>
<div class="dashboard">
<!-- 其他页面内容 -->
<!-- 今日任务清单 -->
<div class="tasks-section">
<div class="tasks-list">
<div
v-for="task in todayTasks"
:key="task.taskId"
class="task-item"
:class="{ completed: task.status === 'done' }"
>
<input
type="checkbox"
:checked="task.status === 'done'"
@change="toggleTaskStatus(task, $event)"
/>
<span class="task-name" :class="{ 'completed-task': task.status === 'done' }">
{{ task.taskName }}
</span>
<span class="task-time" v-if="task.planTime">
{{ formatTime(task.planTime) }}
</span>
</div>
</div>
</div>
<!-- 添加彩条效果组件 -->
<ConfettiEffect ref="confettiEffect" />
</div>
</template>
<script>
import ConfettiEffect from '@/components/ConfettiEffect.vue'
export default {
name: 'Dashboard',
components: {
ConfettiEffect
},
methods: {
async toggleTaskStatus(task, event) {
try {
const newStatus = task.status === 'done' ? 'todo' : 'done';
await tasksApi.updateTask(task.taskId, {
status: newStatus
});
task.status = newStatus;
this.todayFinishedTasks = this.todayTasks.filter(t => t.status === 'done');
// 只在任务完成时触发动效
if (newStatus === 'done') {
// 计算复选框位置并触发动效
this.triggerConfettiAtPosition(event);
}
} catch (error) {
console.error('更新任务状态失败:', error);
alert('更新任务状态失败');
}
},
triggerConfettiAtPosition(event) {
// 获取触发元素的位置
const checkbox = event.target;
const rect = checkbox.getBoundingClientRect();
const position = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 2) / window.innerHeight
};
// 触发局部彩条效果
this.$refs.confettiEffect.trigger(3000, position);
}
}
}
</script>
关键优化点
1. 参数调优
实现局部化效果的关键在于参数调整:
- particleCount: 从 150 减少到 50,避免过度渲染
- spread: 从 70 减少到 45,限制扩散范围
- ticks: 从 200 减少到 100,缩短动画时间
- origin: 使用计算得到的精确位置
2. 位置计算
通过 getBoundingClientRect() 获取元素相对于视窗的位置,然后转换为 canvas-confetti 所需的比例坐标:
javascript
const position = {
x: (rect.left + rect.width / 2) / window.innerWidth,
y: (rect.top + rect.height / 2) / window.innerHeight
};
3. 条件触发
只在特定状态转换时触发效果(如未完成 → 完成),避免在逆向操作时误触发:
javascript
if (newStatus === 'done') {
// 触发动效
}
实际效果
通过以上实现,我们可以获得以下效果:
- 精准反馈:彩条只在用户点击的复选框附近出现
- 视觉舒适:不会遮挡其他界面元素
- 性能良好:减少了粒子数量和动画时间
- 体验流畅:用户能清晰感知到自己的操作结果
总结
局部化的动效设计是提升用户体验的重要细节。通过合理使用 canvas-confetti 库和精确的位置计算,我们能够在 Vue.js 项目中轻松实现这种效果。这种设计不仅让界面更加生动有趣,还能提供清晰的操作反馈,提升用户的操作信心和满意度。
在实际项目中,我们可以将这种局部动效应用到各种用户交互场景中,如按钮点击、表单提交、任务完成等,为用户提供更加精致和贴心的交互体验。