《fun-widgets:简单却有趣的触点交互(TouchCard)》

Hi guys, 最近在用豆包的时候发现一个有意思的交互应用,下面我们一起来看下怎么实现

样例

为了更好得展示最终效果,直接使用豆包的界面作为样例

UI上可以看出:

  • 光标进入时PersonInfo模块会出现"下陷"效果
  • "下陷"效果延后触发
  • 右侧Memojis"悬浮"效果

分解开始

一、"下陷"效果复刻

通过肉眼观看,当光标移入时,整个卡片会出现下陷,这是一种视觉欺骗。

我们可以通过mousemove和csstransformrotateX&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>
  • handleMouseMoveFn
    @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为正角

    rotateXrotateY属性可以控制元素倾角

注意1: 最终为3d效果,添加transform-3d属性让transform支持此效果。

注意2: 在操控倾斜时倾斜会被视距所影响,perspective属性可以替我们解决这个问题。将视距设置为2000px,避免畸变。

CSS 属性 perspective 指定了观察者与 z=0 平面的距离,使具有三维位置变换的元素产生透视效果。z>0 的三维元素比正常大,而 z<0 时则比正常小,大小程度由该属性的值决定。

观察原型发现倾斜极值为[-6,6]。接下来就是一道简单的数学题:x与width如何得到[-6,6]区间值?

逻辑很简单:

  1. w/2为原点
  2. <math xmlns="http://www.w3.org/1998/Math/MathML"> x − w / 2 x-w/2 </math>x−w/2得到所处正负极
  3. <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)得到所处坐标比例
  4. <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]区间
  5. 简化下公式就是 <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>

题外话

  1. 欢迎分享更多有趣交互
  2. 动图制作by:kapwing
  3. 源码已传至Github:github.com/WZJvearyfun...

我是小王饿了 。一个前端开发,曾任职500强车企前端负责人、新零售互联网、事业单位。

好捣鼓点东西吃,有点手艺。爱看小说,爱旅游,人生不受限。

29岁的人和19岁的人其实没差别,除了体重...

相关推荐
互联网搬砖老肖37 分钟前
Web 架构之 CDN 加速原理与落地实践
前端·架构
会飞的鱼先生39 分钟前
javascript中Cookie、BOM、DOM的使用
前端·javascript·chrome
OpenTiny社区42 分钟前
开源之夏·西安电子科技大学站精彩回顾:OpenTiny开源技术下沉校园,点燃高校开发者技术热情
前端·开源
多多*1 小时前
基于rpc框架Dubbo实现的微服务转发实战
java·开发语言·前端·redis·职场和发展·蓝桥杯·safari
灏瀚星空1 小时前
用HTML5 Canvas打造交互式心形粒子动画:从基础到优化实战
前端·html·html5
Jackson__1 小时前
聊一下HTTP 与 HTTPS 的区别,以及HTTPS 的加密方式
前端·面试
好运yoo2 小时前
npm install的原理
前端·npm
Jiaberrr2 小时前
uniapp 安卓 APP 后台持续运行(保活)的尝试办法
android·前端·javascript·uni-app·app·保活
不老刘2 小时前
uniapp+vue3实现CK通信协议(基于jjc-tcpTools)
前端·javascript·uni-app
蓝婷儿2 小时前
第二章支线八 ·CSS终式:Tailwind与原子风暴
前端·css