Hi guys, 最近在用豆包的时候发现一个有意思的交互应用,下面我们一起来看下怎么实现
样例
为了更好得展示最终效果,直接使用豆包的界面作为样例
UI上可以看出:
- 光标进入时
PersonInfo
模块会出现"下陷"效果 - "下陷"效果延后触发
- 右侧
Memojis
"悬浮"效果
分解开始
一、"下陷"效果复刻
通过肉眼观看,当光标移入时,整个卡片会出现下陷,这是一种视觉欺骗。
我们可以通过mousemove
和csstransform
的rotateX
&rotateY
属性可以达到此效果。 逻辑通了,那就开始吧。
- 使用
vue
&tailwindcss
快速搭建一个组件。给予属性@mousemove
函数handleMouseMove
HTML
<template>
<div
class="w-screen h-screen flex justify-center items-center bg-neutral-200"
>
<div
id="card"
class="w-1/2 h-60 relative z-1 flex items-center"
>
<div
ref="touchCardRef"
class="w-full h-full backdrop-blur-[.58594rem] rounded-2xl border-1 border-white shadow-lg shadow-gray-400 flex flex-col justify-center card"
@mousemove="handleMouseMove"
>
<div class="ml-20 text-4xl text-[#141413] font-bold">@小王饿了</div>
<div class="ml-20 mt-6 text-xl text-sky-500 font-bold">
#fun-widgets/TouchCard
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.card {
backdrop-filter: blur(0.58594rem);
background: linear-gradient(
113deg,
hsla(0, 0%, 100%, 0.72) 10.29%,
hsla(0, 0%, 100%, 0) 100.35%
);
}
</style>
-
handleMouseMove
Fn
@mousemove
会返回一个MouseEvent
类型(点击查看内容)。此类型有诸多关于光标在浏览器内坐标的参数,我们取光标在touchCardRef
内的参数offsetX
&offsetY
。使用
@vueuse/core/useElementSize
函数获取touchCardRef
节点的宽高,得到一个
{x:[-width/2, width/2],y:[-height/2, height/2]}
的坐标体
由此可以推断:-
当光标所处区域为正
y
时,卡片向上倾斜,rotateX
为正角,反之rotateX
为负角 -
当光标所处区域为正
x
时,卡片向右倾斜,rotateY
为负角,反之rotateY
为正角
rotateX
和rotateY
属性可以控制元素倾角 -
注意1: 最终为3d效果,添加
transform-3d
属性让transform
支持此效果。
注意2: 在操控倾斜时倾斜会被视距所影响,perspective
属性可以替我们解决这个问题。将视距设置为2000px,避免畸变。
CSS 属性
perspective
指定了观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果。z>0 的三维元素比正常大,而 z<0 时则比正常小,大小程度由该属性的值决定。
观察原型发现倾斜极值为[-6,6]。接下来就是一道简单的数学题:x与width如何得到[-6,6]区间值?
逻辑很简单:
- 以
w/2
为原点 - <math xmlns="http://www.w3.org/1998/Math/MathML"> x − w / 2 x-w/2 </math>x−w/2得到所处正负极
- <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x − w / 2 ) / ( w / 2 ) (x-w/2)/(w/2) </math>(x−w/2)/(w/2)得到所处坐标比例
- <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x − w / 2 ) / ( w / 2 ) ∗ 6 (x-w/2)/(w/2)*6 </math>(x−w/2)/(w/2)∗6得到[-6,6]区间
- 简化下公式就是 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ( x / w / 2 ) − 1 ) ∗ 6 ((x/w/2)-1)*6 </math>((x/w/2)−1)∗6
js
const touchTransform = (x: number, y: number): void => {
const rotateX = -(y / (height.value / 2) - 1) * 6;
const rotateY = (x / (width.value / 2) - 1) * 6;
transform.value = `perspective(2000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
};
到此,"下陷"效果的最后一块拼图完成了。看下整体代码:
js
<script lang="ts" setup>
import { ref } from "vue";
import { useElementSize } from "@vueuse/core";
const transform = ref<string>("");
const touchCardRef = ref<HTMLElement>();
const { width, height } = useElementSize(touchCardRef);
const touchTransform = (x: number, y: number): void => {
const rotateX = -(y / (height.value / 2) - 1) * 10;
const rotateY = (x / (width.value / 2) - 1) * 10;
transform.value = `perspective(2000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
};
const handleMouseMove = (event: MouseEvent) => {
const { offsetX: x, offsetX: y } = event;
touchTransform(x, y);
};
</script>
<template>
<div
class="w-screen h-screen flex justify-center items-center bg-neutral-200"
>
<div
class="w-1/2 h-60 relative z-1 flex items-center transform-3d"
:style="{ transform }"
>
<div
ref="touchCardRef"
class="w-full h-full backdrop-blur-[.58594rem] rounded-2xl border-1 border-white shadow-lg shadow-gray-400 flex flex-col justify-center card"
@mousemove="handleMouseMove"
>
<div class="ml-20 text-4xl text-[#141413] font-bold">@小王饿了</div>
<div class="ml-20 mt-6 text-xl text-sky-500 font-bold">
#fun-widgets/TouchCard
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.card {
backdrop-filter: blur(0.58594rem);
background: linear-gradient(
113deg,
hsla(0, 0%, 100%, 0.72) 10.29%,
hsla(0, 0%, 100%, 0) 100.35%
);
}
</style>
二、"下陷"效果延后触发
接着处理"下陷"效果的延后触发,这块很简单,添加transition
属性就行。
css
transition: transform 0.15s linear;
三、右侧Memojis
"悬浮"效果
在translate
中有3个属性可以让节点偏移
transform: translateX(2em)
transform: translateY(3in)
transform: translateZ(2px)
想要实现浮于上层的效果,这里选择translateZ
。借鉴写tailwind的图片,设置对应的参数可以让图片悬于上方。
- 增加图片,添加
translate-z-12
属性,进化至完全体 。(BGM:Butterfly)
js
<template>
<div
class="w-screen h-screen flex justify-center items-center bg-neutral-200"
>
<div
class="w-1/2 h-60 relative z-1 transform-3d transition-transform flex items-center"
:style="{ transform }"
>
<div
ref="touchCardRef"
class="w-full h-full backdrop-blur-[.58594rem] rounded-2xl border-1 border-white shadow-lg shadow-gray-400 flex flex-col justify-center card"
@mousemove="handleMouseMove"
@mouseout="handleMouseOut"
>
<div class="ml-20 text-4xl text-[#141413] font-bold">@小王饿了</div>
<div class="ml-20 mt-6 text-xl text-sky-500 font-bold">
#fun-widgets/TouchCard
</div>
</div>
<img
class="absolute pointer-events-none translate-z-12 w-1/4 -right-1/8 rounded-full border-1 border-white"
src="https://p3-flow-imagex-sign.byteimg.com/user-avatar/f9e7748c4fea045de8a774d0836ad177~tplv-a9rns2rl98-image.jpeg?rk3s=98c978ad&x-expires=1743502443&x-signature=YLBy9quP5%2B3%2BsPEza3K4SfeRfNg%3D"
/>
</div>
</div>
</template>
题外话
- 欢迎分享更多有趣交互
- 动图制作by:kapwing
- 源码已传至Github:github.com/WZJvearyfun...
我是小王饿了 。一个前端开发,曾任职500强车企前端负责人、新零售互联网、事业单位。
好捣鼓点东西吃,有点手艺。爱看小说,爱旅游,人生不受限。
29岁的人和19岁的人其实没差别,除了体重...