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

vue2 + ts 实现透视卡片 + 瀑布上下移动效果

vue2 + ts 实现透视卡片 + 瀑布上下移动效果

vue2 + ts 实现透视卡片 + 瀑布上下移动效果,跟随鼠标透视卡片,附带上下缓慢移动,鼠标移入后暂停,移出后继续,传入delayIndex可实现瀑布上下移动效果。

效果(只是卡片组件功能,内容的样式是通过插槽放进来的):
在这里插入图片描述在这里插入图片描述

完整代码:

<!-- 透视卡片组件-CardEffect -->
<template><div class="poster-con-wrapper":class="{ 'glass-card': glassCard }"ref="cardRef":style="{ borderColor: `rgba(${rgb}, 0.5)`, borderRadius: borderRadius }"@mouseenter="onMouseEnter"@mouseleave="onMouseLeave"><slot></slot></div>
</template><script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import abpbase from 'geofly-framework-web-common/libs/abpbase';@Component({name: 'CardEffect',components: {},
})
export default class CardEffect extends abpbase {@Prop({ type: String, default: '0, 160, 160' }) rgb!: string;@Prop({ type: Boolean, default: true }) glassCard!: boolean;@Prop({ type: String, default: '8px' }) borderRadius!: string;@Prop({ type: Number, default: 10 }) maxTilt!: number;// 新增:延迟索引@Prop({ type: Number, default: 0 }) delayIndex!: number;private handleMouseMove!: (e: MouseEvent) => void;private handleMouseLeave!: () => void;private floatY = 0;private targetY = 0;// 最大向下移动距离private floatMaxDown = 20;private floatMaxUp = 5;private floatAnimationId = 0;// 移动速度private floatSpeed = 0.03;mounted() {console.log('mounted-delayIndex', this.delayIndex);this.addMouseTiltEffect();// 延迟启动浮动动画const delayTime = this.delayIndex * 300; // 每张卡片延迟 的ms,可调整setTimeout(() => {this.startFloatEffect();}, delayTime);}beforeDestroy() {console.log('beforeDestroy-delayIndex', this.delayIndex);this.removeMouseTiltEffect();this.stopFloatEffect();this.onMouseLeave();}onClick() {this.$emit('toList');}// ----------------- 鼠标倾斜 -----------------addMouseTiltEffect() {const el = this.$refs.cardRef as HTMLElement;if (!el) return;const { maxTilt } = this;this.handleMouseMove = (e: MouseEvent) => {const rect = el.getBoundingClientRect();const x = (e.clientX - rect.left) / rect.width;const y = (e.clientY - rect.top) / rect.height;const rotateY = (0.5 - x) * 2 * maxTilt;const rotateX = (y - 0.5) * 2 * maxTilt;el.style.transform = `perspective(800px)translateZ(0)translateY(${this.floatY}px)rotateX(${rotateX}deg)rotateY(${rotateY}deg)`;};this.handleMouseLeave = () => {el.style.transform = `perspective(800px) translateZ(0) translateY(${this.floatY}px) rotateX(0deg) rotateY(0deg)`;};el.addEventListener('mousemove', this.handleMouseMove);el.addEventListener('mouseleave', this.handleMouseLeave);}removeMouseTiltEffect() {const el = this.$refs.cardRef as HTMLElement;if (!el) return;el.removeEventListener('mousemove', this.handleMouseMove);el.removeEventListener('mouseleave', this.handleMouseLeave);}// ----------------- 浮动动画 -----------------startFloatEffect() {const el = this.$refs.cardRef as HTMLElement;if (!el) return;let direction = 1; // 1向下 -1向上const animate = () => {if (!this.$root.$data.pauseFloat) {if (direction === 1) {this.targetY = this.floatMaxDown / 1.5;} else {this.targetY = -this.floatMaxUp;}this.floatY += (this.targetY - this.floatY) * this.floatSpeed;if (Math.abs(this.floatY - this.targetY) < 0.1) {direction *= -1;}el.style.transform = `perspective(800px)translateZ(0)translateY(${this.floatY}px)`;}this.floatAnimationId = requestAnimationFrame(animate);};this.floatAnimationId = requestAnimationFrame(animate);}stopFloatEffect() {cancelAnimationFrame(this.floatAnimationId);}// ----------------- 鼠标移入/移出 -----------------onMouseEnter() {this.$root.$data.pauseFloat = true;}// 鼠标移出onMouseLeave() {this.$root.$data.pauseFloat = false;}
}
</script><style lang="less" scoped>
.poster-con-wrapper {position: relative;height: 100%;width: 100%;transition: all 0.1s ease;transform-style: preserve-3d;border: 2px solid rgba(0, 160, 160, 0.7);perspective: 800px;box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);&:hover {box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);}&::after {content: "";position: absolute; top: 0; left: 0;width: 100%; height: 100%;border-radius: inherit;pointer-events: none;background: linear-gradient(135deg,rgba(255,255,255,0.25) 0%,rgba(255,255,255,0) 60%);}
}.poster-con {background-color: #fff;color: @primary-color;height: 100%;width: 100%;padding: 16px;border-radius: 8px;.poster-content {margin-top: 16px;font-size: 14px;}
}
</style>

文章转载自:

http://tydMf3zC.yxyyp.cn
http://uZShFMkK.yxyyp.cn
http://guVFEyLq.yxyyp.cn
http://Joxs1jwh.yxyyp.cn
http://2lOwVfX6.yxyyp.cn
http://8Hl17Wu6.yxyyp.cn
http://b5eZXeVo.yxyyp.cn
http://JCR7doWg.yxyyp.cn
http://AIqBrt3d.yxyyp.cn
http://NzmcnoLw.yxyyp.cn
http://aLPn8jTZ.yxyyp.cn
http://HeUz88pW.yxyyp.cn
http://74prPgVo.yxyyp.cn
http://TL8MdKrb.yxyyp.cn
http://BNjKk7Hk.yxyyp.cn
http://KHZ9ykew.yxyyp.cn
http://86yOFitW.yxyyp.cn
http://9X4AKPia.yxyyp.cn
http://JYxtBoNl.yxyyp.cn
http://dfWcWgut.yxyyp.cn
http://oKncICoU.yxyyp.cn
http://2q2QZQgn.yxyyp.cn
http://cnuXDdJa.yxyyp.cn
http://fdZ5fU2Y.yxyyp.cn
http://RmopYe8Z.yxyyp.cn
http://oFHpfFdH.yxyyp.cn
http://0CwR8u1B.yxyyp.cn
http://QxYl3guM.yxyyp.cn
http://rig5pbC4.yxyyp.cn
http://6ix1qI7T.yxyyp.cn
http://www.dtcms.com/a/364274.html

相关文章:

  • 计算机网络---CA证书体系(Certificate Authority)
  • FPGA离群值剔除算法
  • 【C++】在 Windows 系统调用第三方程序(创建进程)
  • 校园外卖点餐系统(代码+数据库+LW)
  • LeetCode 刷题【62. 不同路径】
  • 【Linux】Linux开发必备:Git版本控制与GDB调试全指南
  • ESXI8多网卡链路聚合
  • Nature Machine Intelligence 基于强化学习的磁性微型机器人自主三维位置控制
  • 【正则表达式】 正则表达式运算法优先级的先后是怎么排序的?
  • Elasticsearch(高性能分布式搜索引擎)01
  • 从“看见”到“行动”:一场机器视觉与机器人的软硬件共舞
  • 动态IP和静态IP配置上有什么区别
  • 云手机中的三大核心技术主要是指什么?
  • 都2025年了,还有人用Python 2吗
  • 华为HCIE数通含金量所剩无几?考试难度加大?
  • 【开题答辩全过程】以 垃圾分类和废物管理系统的设计与实现为例,包含答辩的问题和答案
  • AI会“胡说八道”?探秘AI幻觉背后的真相
  • C++并发编程指南14 使用future
  • MySQL知识点3
  • 电子病历空缺句的语言学特征描述与自动分类探析(以GPT-5为例)(中)
  • Cookie、Session、登录
  • 空调噪音不穿帮,声网虚拟直播降噪技巧超实用
  • 【论文阅读】-《THE JPEG STILL PICTURE COMPRESSION STANDARD》
  • 【论文阅读】LightThinker: Thinking Step-by-Step Compression (EMNLP 2025)
  • 自然语言处理深层语义分析中公理化体系的可行性、挑战与前沿进展
  • 华为HCIP、HCIE认证:自学与培训班的抉择
  • 《探索C++11:现代语法的性能优化策略(中篇)》
  • 分布式系统的 CAP 原则与 BASE 原则理解
  • 精密板料矫平机:让金属“心平气和”的科技
  • 多场景对练数据的 Excel 横向导出方案(EasyExcel 动态表头实践)