今天给大家带来的是一个简单的小球抛物线动画效果

场景⚡

假如我们有一个按钮以及一个终点目标,在点击按钮的时候出现一个类似于购物车的小球入库的动画效果

js 复制代码
<template>
  <div ref="boxWrapRef" class="animation_demo">
    <div>
      <a-button @click="handleClick" class="btn_class">点击我</a-button>
    </div>
    <div class="destination_wrap" ref="divRef">
      <a-button type="primary" class="destination_btn">目标点</a-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
const divRef = ref();
const boxWrapRef = ref();
</script>

<style lang="scss">
.animation_demo {
  padding: 80px;
  width: 800px;
  height: 600px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
}
.destination_wrap {
  align-self: flex-end;
}
.box {
  width: 20px;
  height: 20px;
  position: absolute;
  border-radius: 50%;
  background: red;
}
</style>

步骤分解✨

1️⃣获取小球需要到达的目标终点endXendY坐标.

2️⃣创建2个一个X方向一个Y方向的关键帧动画.

3️⃣在点击的时候获取小球初始化的startXstartY坐标并创建小球应用动画

第1步-获取目标终点坐标

js 复制代码
const getStartXY = computed(() => {
  return {
    left: divRef.value.getBoundingClientRect().left,
    top: divRef.value.getBoundingClientRect().top,
    width: destinationRef.value.getBoundingClientRect().width,
    height: destinationRef.value.getBoundingClientRect().height,
  };
})
const left = computed(() => {
  return getStartXY.value.left + getStartXY.value.width / 2 - 10 + "px";
});
const top = computed(() => {
  return getStartXY.value.top + getStartXY.value.height / 2 - 10 + "px";
});

第2步-创建X和Y方向2个关键帧动画

js 复制代码
@keyframes leftChange {
  to {
    left: v-bind(left);
  }
}
@keyframes topChange {
  to {
    top: v-bind(top);
  }
}

第3步-点击创建小球并且应用上动画

js 复制代码
<script setup lang="ts">
...
const handleClick = (e: MouseEvent) => {
   const target = e.target as HTMLImageElement;
  const { x, y, width, height } = target.getBoundingClientRect();
  const startX = x + width / 2;//在中间创建小球需要加上宽度和高度的一般
  const startY = y + height / 2;
  useAnimation(startX, startY);
};
const useAnimation = (startX: number, startY: number) => {
  const div = document.createElement("div");
  div.style.top = startY - 10 + "px";
  div.style.left = startX - 10 + "px";
  div.classList.add("box");
  boxWrapRef.value.appendChild(div);
  div.onanimationend = () => {
    div.remove();
  };
};
...
<script>
<style>
...
.box {
  width: 20px;
  height: 20px;
  position: absolute;
  border-radius: 50%;
  background: red;
  animation: leftChange 1s linear forwards,
    topChange 1s linear forwards;
}
...
</style>

效果♦️

最后一步实现抛物线⭐

将Y轴方向的动画函数换成贝塞尔函数,抛物线就是先上后下,把animationend先注释掉,用谷歌浏览器工作实时调整下

大概调整为cubic-bezier(0.5, -0.5, 1, 1)就差不多了

效果❤️

全部代码

js 复制代码
<template>
  <div ref="boxWrapRef" class="animation_demo">
    <div>
      <a-button @click="handleClick" class="btn_class">点击我</a-button>
    </div>
    <div class="destination_wrap" ref="destinationRef">
      <a-button type="primary" class="destination_btn">目标点</a-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
const destinationRef = ref();
const boxWrapRef = ref<HTMLDivElement>();
const getStartXY = computed(() => {
  return {
    left: destinationRef.value.getBoundingClientRect().left,
    top: destinationRef.value.getBoundingClientRect().top,
    width: destinationRef.value.getBoundingClientRect().width,
    height: destinationRef.value.getBoundingClientRect().height,
  };
});
const left = computed(() => {
  return getStartXY.value.left + getStartXY.value.width / 2 - 10 + "px";
});
const top = computed(() => {
  return getStartXY.value.top + getStartXY.value.height / 2 - 10 + "px";
});
const handleClick = (e: MouseEvent) => {
  const target = e.target as HTMLImageElement;
  const { x, y, width, height } = target.getBoundingClientRect();
  const startX = x + width / 2;
  const startY = y + height / 2;
  useAnimation(startX, startY);
};
const useAnimation = (startX: number, startY: number) => {
  const div = document.createElement("div");
  div.style.top = startY - 10 + "px";
  div.style.left = startX - 10 + "px";
  div.classList.add("box");
  boxWrapRef.value.appendChild(div);
  div.onanimationend = () => {
    div.remove();
  };
};
</script>

<style lang="scss">
.animation_demo {
  padding: 80px;
  width: 800px;
  height: 600px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
}
.destination_wrap {
  align-self: flex-end;
}
.box {
  width: 20px;
  height: 20px;
  position: absolute;
  border-radius: 50%;
  background: red;
  animation: leftChange 0.5s linear forwards,
    topChange 0.5s cubic-bezier(0.5, -0.5, 1, 1) forwards;
}
@keyframes leftChange {
  to {
    left: v-bind(left);
  }
}
@keyframes topChange {
  to {
    top: v-bind(top);
  }
}
</style>

总结✨✨✨

这个简单的效果可以根据场景更换小球为图片或其他样式、修改贝塞尔函数参数的方式可以应用在点击下载等效果上

相关推荐
光影少年2 分钟前
react navite高频手写/实操题
前端·javascript·react native·react.js·前端框架
hunterandroid15 分钟前
HarmonyOS 弱网与离线优先架构实战:请求队列、本地缓存与增量同步
前端·前端框架
lhldsg20 分钟前
宠物同城领养平台开发实战:从需求分析到上线部署指南
java·前端·小程序·需求分析·宠物
hunterandroid20 分钟前
Android 内存泄漏排查实战:从 LeakCanary 报警到根因定位
android·前端
AI视觉网奇21 分钟前
3d子部件高亮选中
开发语言·javascript·3d
leoZ23122 分钟前
AI+前端提效-12 AI辅助前端性能优化与监控:从开发到线上全流程提效
前端·人工智能·神经网络·自然语言处理·性能优化·keras·知识图谱
阿图灵24 分钟前
LangGraph 实战 03:Workflows 与 Agents——六种工作流模式与智能体实战(附 6 个可运行示例)
java·前端·javascript·工作流·ai agent·智能体·langgraph
狂炫冰美式29 分钟前
电脑合盖之后 Cursor 还在偷我电?看看为啥
前端·人工智能·后端
leeyi29 分钟前
微前端:14 个子应用是怎么做到不散架的——DeepFlux 前端工程拆解(第102篇)
前端·aigc·agent
2603_965896621 小时前
JavaScript 对象零基础详解|属性、方法、遍历、增删改查
开发语言·javascript·ecmascript