vue 加入购物车抛物线动画

效果图


购物车主要是两张图,分别是盖子和篮子组成一个图,点击事件默认能获取页面点击X和Y

.vue

typescript 复制代码
<template>
  <view
    v-for="ball in balls"
    :key="ball.id"
    class="ball"
    :style="{
      '--x': `${ball.x - 30}px`,
      '--y': `${ball.y - 20}px`,
    }" />
  <view class="btn wxy-button flex-between">
    <view class="flex">
      <view class="btn-item">
        <image class="btn-item-lid" :src="imgUrl('home/lid')"
               :class="{'btn-item-lid-active': balls.length > 0}" />
        <image class="btn-item-basket" :src="imgUrl('home/basket')" />
      </view>
      <wxy-text-price :num="2.5" :digit="2" />
    </view>
    <view class="wxy-btn flex-center btn-button bg-main round">
      去结算
    </view>
  </view>
</template>

<script setup lang='ts'>
import { imgUrl } from '@/modules/tool'
import { watch, ref } from 'vue'

interface Ball {
  id: number
  x: number
  y: number
}

const props = defineProps({
  location: {
    type: Object,
    default: () => ({
      x: 0,
      y: 0,
    }),
  },
})

const balls = ref<Ball[]>([])
let ballId = 0

watch(() => props.location, (newLoc) => {
  if (newLoc.x !== 0 || newLoc.y !== 0) {
    const newBall: Ball = {
      id: ++ballId,
      x: newLoc.x,
      y: newLoc.y,
    }
    balls.value.push(newBall)

    setTimeout(() => {
      const index = balls.value.findIndex((b) => b.id === newBall.id)
      if (index > -1) {
        balls.value.splice(index, 1)
      }
    }, 800)
  }
})
</script>

<style lang='scss' scoped>
.ball {
  position: absolute;
  top: var(--y);
  left: var(--x);
  z-index: 3;
  width: 48rpx;
  height: 48rpx;
  background-color: var(--main);
  border-radius: 50%;
  animation: fly-to-cart 1s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
  pointer-events: none;
  will-change: transform;
}

@keyframes fly-to-cart {
  to{
    transform: translate(calc(30rpx - var(--x)), calc(100vh - 100rpx - var(--y))) scale(0.6);
  }
}

.btn{
  padding: 20rpx 30rpx calc(20rpx + (env(safe-area-inset-bottom) / 2));

  &-button{
    width: 280rpx;
    height: 84rpx;
    font-weight: 500;
  }

  &-item{
    --size:166rpx;
    --top:-30rpx;

    width: 110rpx;

    &-lid{
      position: absolute;
      top: var(--top);
      left: 0;
      z-index: 1;
      width: var(--size);
      height: var(--size);
      backface-visibility: hidden;
      transition: .4s ease-in-out;
      will-change: transform;
    }

    &-lid-active{
      transform: rotate(-60deg) translateY(-30rpx) translateX(20rpx);
    }

    &-basket{
      position: absolute;
      top: var(--top);
      left: 0;
      width: var(--size);
      height: var(--size);
    }
  }
}
</style>

遇到问题可以看我主页加我Q,很少看博客,对你有帮助别忘记点赞收藏。

相关推荐
anOnion7 小时前
构建无障碍组件之Menu Button pattern
前端·html·交互设计
用户47949283569157 小时前
claude Fable用不了?把Gpt 5.5pro接到你的claude code里
前端·后端
JieE2128 小时前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
冬奇Lab10 小时前
AI Workflow 定义的四次演进:从 Markdown 到 JS 脚本,再到分布式多 Agent
javascript·人工智能·agent
zhangxingchao10 小时前
Kotlin常用的Flow 操作符整理
前端
IT_陈寒12 小时前
React的useState居然还有这种坑?我差点删库跑路
前端·人工智能·后端
Pedantic13 小时前
SwiftUI 手势笔记
前端·后端
橙子家13 小时前
浏览器缓存之【结构化数据库与缓存】: IndexedDB、Cache storage 和 Storage buckets
前端
user205855615181313 小时前
X6 中边悬浮置顶,规避 `mouseleave` 事件丢失问题
前端
李明卫杭州13 小时前
CSS aspect-ratio 属性完全指南
前端