Vue.js 中实现局部彩条ConfettiEffect动效:采用canvas-confetti库

实现局部彩条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') {
  // 触发动效
}

实际效果

通过以上实现,我们可以获得以下效果:

  1. 精准反馈:彩条只在用户点击的复选框附近出现
  2. 视觉舒适:不会遮挡其他界面元素
  3. 性能良好:减少了粒子数量和动画时间
  4. 体验流畅:用户能清晰感知到自己的操作结果

总结

局部化的动效设计是提升用户体验的重要细节。通过合理使用 canvas-confetti 库和精确的位置计算,我们能够在 Vue.js 项目中轻松实现这种效果。这种设计不仅让界面更加生动有趣,还能提供清晰的操作反馈,提升用户的操作信心和满意度。

在实际项目中,我们可以将这种局部动效应用到各种用户交互场景中,如按钮点击、表单提交、任务完成等,为用户提供更加精致和贴心的交互体验。

相关推荐
爱吃香蕉的阿豪2 小时前
.NET 10正式发布:三年LTS加持,性能与AI开发双重革命
前端·javascript·vue.js
我的div丢了肿么办3 小时前
js中async和await 的详细讲解
前端·javascript·vue.js
种时光的人4 小时前
关于人人开源框架renren-fast-vue前端npm install安装报错的问题解决方法
前端·vue.js·npm
GoFly开发者5 小时前
使用AES加解密在vue3前端加密、Golang后端解密实战教程(后端框架可以为Gin、Goframe等框架使用)
vue.js·aes加密vue·aes解密go
普通码农5 小时前
Vue-Konva 使用(缩放 / 还原 / 拖动) 示例
前端·javascript·vue.js
一 乐6 小时前
运动会|基于SpingBoot+vue的高校体育运动会管理系统(源码+数据库+文档)
java·前端·javascript·数据库·vue.js·学习·springboot
inCBle6 小时前
vue3+ts 封装一个通用流程复用工具函数
前端·vue.js·设计
摇滚侠7 小时前
Vue 项目实战《尚医通》,利用 Qrcode 获取二维码,笔记51
vue.js·笔记
默海笑7 小时前
VUE后台管理系统:定制化、高可用前台样式处理方案
前端·javascript·vue.js