当前位置: 首页 > news >正文

美团购物车小球动画效果

美团购物车小球动画效果

  • 前言
  • 先来看看效果
  • 具体实现逻辑
  • 核心代码
  • 源码分享
  • 结语

前言

最近做了一个小需求,记得之前写过这个美团购物车小球动画效果还发在csdn里面了,但需要的时候却找不到,不知道什么时候给删了,今天再给大家分享一下这个功能的源码,以便以后的使用。

先来看看效果

请添加图片描述

具体实现逻辑

每次点击时,拿到点击的位置作为小球的开始位置,再获取到购物车的结束位置。确定了两端位置之后,给小球设置css的path路径(使用贝塞尔曲线),最后通过animate方法执行动画效果,即可实现。

核心代码

// 小球的css设置
.ball {width: 20px;height: 20px;background: linear-gradient(135deg, #ff3000, #ff9000);border-radius: 50%;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);position: absolute;top: 0;right: 0;
}// 添加购物车方法
const addToCart = (item, event) => {cart.count++;cart.total += item.price;// 获取点击位置const startX = event.clientX;const startY = event.clientY;createBall(startX, startY);};// 创建小球const createBall = (startX, startY) => {let endEle = document.querySelector(".cart").getBoundingClientRect();let endX = Math.floor(endEle.left + endEle.width / 2);let endY = Math.floor(endEle.top + endEle.height / 2);let fatherEle = document.querySelector(".container");let ball = document.createElement("div");ball.classList.add("ball");ball.style.left = startX + "px";ball.style.top = startY + "px";// 贝塞尔曲线路径ball.style.offsetPath = `path('M${0} ${0} C${100} ${-100}, ${endX - startX} ${endY - startY}, ${endX - startX} ${endY - startY}')`;fatherEle.appendChild(ball);setTimeout(() => {fatherEle.removeChild(ball);}, Number(animationSpeed.value) - 100);ball.animate(// 将偏移路径动画化{ offsetDistance: [0, "100%"] },{duration: Number(animationSpeed.value),iterations: 1,easing: "cubic-bezier(.667,0.01,.333,.99)",direction: "alternate",});};

源码分享

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>美团购物车小球动画</title><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;background-color: #f5f5f5;padding: 20px;color: #333;}.container {max-width: 800px;margin: 0 auto;background: white;border-radius: 12px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);padding: 20px;}h1 {text-align: center;color: #ffd000;margin-bottom: 20px;}.description {text-align: center;margin-bottom: 30px;color: #666;}.items-container {display: flex;flex-wrap: wrap;gap: 15px;justify-content: center;margin-bottom: 30px;}.item {width: 100px;padding: 10px;background: #f8f8f8;border-radius: 8px;text-align: center;cursor: pointer;transition: transform 0.2s;}.item:hover {transform: translateY(-3px);box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);}.item-img {width: 60px;height: 60px;background: linear-gradient(135deg, #ffd000, #ffb800);border-radius: 50%;margin: 0 auto 8px;}.cart-container {position: fixed;bottom: 20px;right: 20px;}.cart {width: 60px;height: 60px;background: #ffd000;border-radius: 50%;display: flex;align-items: center;justify-content: center;position: relative;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);}.cart-count {position: absolute;top: -5px;right: -5px;background: #ff3000;color: white;font-size: 12px;width: 20px;height: 20px;border-radius: 50%;display: flex;align-items: center;justify-content: center;}.ball-container {position: absolute;pointer-events: none;}.ball {width: 20px;height: 20px;background: linear-gradient(135deg, #ff3000, #ff9000);border-radius: 50%;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);position: absolute;top: 0;right: 0;}.control-panel {background: #f9f9f9;padding: 15px;border-radius: 8px;margin-top: 20px;}.slider-container {margin: 10px 0;}label {display: block;margin-bottom: 5px;font-weight: bold;color: #555;}input[type="range"] {width: 100%;}.value-display {text-align: center;font-size: 14px;color: #777;}.code-example {background: #2d2d2d;color: #f8f8f2;padding: 15px;border-radius: 8px;margin-top: 20px;overflow-x: auto;font-family: "Fira Code", monospace;}</style></head><body><div id="app"></div><script type="module">const { createApp, ref, reactive } = Vue;const App = {setup() {const items = ref([{ id: 1, name: "美食", price: 25 },{ id: 2, name: "饮料", price: 15 },{ id: 3, name: "水果", price: 20 },{ id: 4, name: "甜品", price: 18 },{ id: 5, name: "快餐", price: 22 },{ id: 6, name: "小吃", price: 12 },]);const cart = reactive({count: 0,total: 0,});const balls = ref([]);const ballIndex = ref(0);const animationSpeed = ref(600);const addToCart = (item, event) => {cart.count++;cart.total += item.price;// 获取点击位置const startX = event.clientX;const startY = event.clientY;createBall(startX, startY);};// 创建小球const createBall = (startX, startY) => {let endEle = document.querySelector(".cart").getBoundingClientRect();let endX = Math.floor(endEle.left + endEle.width / 2);let endY = Math.floor(endEle.top + endEle.height / 2);let fatherEle = document.querySelector(".container");let ball = document.createElement("div");ball.classList.add("ball");ball.style.left = startX + "px";ball.style.top = startY + "px";// 贝塞尔曲线路径ball.style.offsetPath = `path('M${0} ${0} C${100} ${-100}, ${endX - startX} ${endY - startY}, ${endX - startX} ${endY - startY}')`;fatherEle.appendChild(ball);setTimeout(() => {fatherEle.removeChild(ball);}, Number(animationSpeed.value) - 100);ball.animate(// 将偏移路径动画化{ offsetDistance: [0, "100%"] },{duration: Number(animationSpeed.value),iterations: 1,easing: "cubic-bezier(.667,0.01,.333,.99)",direction: "alternate",});};return {items,cart,balls,ballIndex,animationSpeed,addToCart,createBall,};},template: `<div class="container"><h1>美团购物车小球动画</h1><p class="description">点击商品将生成飞向购物车的小球动画,多个小球实例互不干扰</p><div class="items-container"><divv-for="item in items":key="item.id"class="item"@click="addToCart(item, $event)"><div class="item-img"></div><div>{{ item.name }}</div><div>¥{{ item.price }}</div></div></div><div class="control-panel"><h3>动画控制</h3><div class="slider-container"><label>动画速度: {{ animationSpeed }}ms</label><inputtype="range"min="500"max="1000"v-model="animationSpeed"><div class="value-display">调整小球飞行的速度</div></div></div><div class="cart-container"><div class="cart"><span>购</span><div class="cart-count">{{ cart.count }}</div></div></div>`,};const app = createApp(App);app.mount("#app");</script></body>
</html>

结语

关注我,不迷路。
不定时分享前端相关问题以及解决方案。
希望能帮助每个在开发类似功能的小伙伴。

http://www.dtcms.com/a/351469.html

相关文章:

  • Docker Compose 使用指南 - 1Panel 版
  • 国产化芯片ZCC3790--同步升降压控制器的全新选择, 替代LT3790
  • 第17章|PowerShell 安全警报——高分学习笔记(运维实战向)
  • Tableau Server高危漏洞允许攻击者上传任意恶意文件
  • 数据库云平台:提升运维效率与降低成本的有效工具
  • 【Ubuntu系统实战】一站式部署与管理MySQL、MongoDB、Redis三大数据库
  • WPS 智能文档,5分钟上手!
  • React学习教程,从入门到精通, React教程:构建你的第一个 React 应用(1)
  • 电力时序预测相关论文
  • 物流配送路径规划项目方案
  • yggjs_rbutton React按钮组件v1.0.0 最佳实践指南
  • 从陪聊到客服,声网如何支撑AI实时交互?
  • Rust 登堂 之 函数式编程(三)
  • 面试之JVM
  • CentOS 7 服务器初始化:从 0 到 1 的安全高效配置指南
  • 使用 flutter_tts 的配置项
  • C# 13 中的新增功能实操
  • 深入了解AWS Auto Scaling
  • OpenAI API Python实战教程:如何稳定获取结构化 JSON 输出(简易/复杂 双示例)
  • Nginx Ubuntu vs CentOS 常用命令对照表---详解笔记
  • AR技术引领航空制造迈向智能化新时代
  • Java标识符命名规则与规范
  • 32.Attention-注意力机制
  • 【算法--链表题2】19.删除链表的倒数第 N 个节点:通俗详解
  • A股大盘数据-20250826 分析
  • Java大厂面试实战:从Spring Boot到微服务架构的全链路技术剖析
  • 英伟达jetson开发板Ubuntu系统配置显示屏系统脱离手动输入指令自动编译执行操作
  • InnoDB详解2
  • 从混乱到高效:企业如何构建可持续发展的IT工单系统
  • 清分系统在电商中的一些案例