当前位置:首页 > 科技  > 软件

使用 React Hooks 实现鼠标悬浮卡片发光的动画效果

来源: 责编: 时间:2024-06-05 17:43:48 259观看
导读有趣的动画效果前几天在网上看到了一个很有趣的动画效果,如下,光会跟随鼠标在卡片上进行移动,并且卡片会有视差的效果,那么在 React 中应该如何去实现这个效果呢?基本实现思路其实实现思路很简单,无非就是分几步:首先,卡片是

有趣的动画效果

前几天在网上看到了一个很有趣的动画效果,如下,光会跟随鼠标在卡片上进行移动,并且卡片会有视差的效果,那么在 React 中应该如何去实现这个效果呢?RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

基本实现思路

其实实现思路很简单,无非就是分几步:RoH28资讯网——每日最新资讯28at.com

  • 首先,卡片是相对定位,光是绝对定位
  • 监听卡片的鼠标移入事件mouseenter,当鼠标进入时显示光
  • 监听卡片的鼠标移动事件mouseover,鼠标移动时修改光的left、top,让光跟随鼠标移动
  • 监听卡片的鼠标移出事件mouseleave,鼠标移出时,隐藏光

我们先在 Index.tsx 中准备一个卡片页面,光的CSS效果可以使用filter: blur() 来实现:RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

卡片视差效果

卡片的视差效果需要用到样式中 transform 样式,主要是配置四个东西:RoH28资讯网——每日最新资讯28at.com

  • perspective:定义元素在 3D 变换时的透视效果
  • rotateX:X 轴旋转角度
  • rotateY:Y 轴旋转角度
  • scale3d:X/Y/Z 轴上的缩放比例

RoH28资讯网——每日最新资讯28at.com

给所有卡片添加光源

上面只是给一个卡片增加光源,接下来可以给每一个卡片都增加光源啦!!!RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

让光源变成可配置

上面的代码,总感觉这个 hooks 耦合度太高不太通用,所以我们可以让光源变成可配置化,这样每个卡片就可以展示不同大小、颜色的光源了~像下面一样:RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

既然是配置化,那我们希望是这么去使用 hooks 的,我们并不需要自己在页面中去写光源的dom节点,也不需要自己去写光源的样式,而是通过配置传入 hooks 中:RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

所以 hooks 内部要自己通过操作 DOM 的方式,去添加、删除光源,可以使用createElement、appendChild、removeChild 去做这些事~RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

RoH28资讯网——每日最新资讯28at.com

完整源码

// use-light-card.tsimport { useEffect, useRef } from 'react';interface IOptions {  light?: {    width?: number; // 宽    height?: number; // 高    color?: string; // 颜色    blur?: number; // filter: blur()  };}export const useLightCard = (option: IOptions = {}) => {  // 获取卡片的dom节点  const cardRef = useRef<HTMLDivElement | null>(null);  let cardOverflow = '';  // 光的dom节点  const lightRef = useRef<HTMLDivElement>(document.createElement('div'));  // 设置光源的样式  const setLightStyle = () => {    const { width = 60, height = 60, color = '#ff4132', blur = 40 } = option.light ?? {};    const lightDom = lightRef.current;    lightDom.style.position = 'absolute';    lightDom.style.width = `${width}px`;    lightDom.style.height = `${height}px`;    lightDom.style.background = color;    lightDom.style.filter = `blur(${blur}px)`;  };  // 设置卡片的 overflow 为 hidden  const setCardOverflowHidden = () => {    const cardDom = cardRef.current;    if (cardDom) {      cardOverflow = cardDom.style.overflow;      cardDom.style.overflow = 'hidden';    }  };  // 还原卡片的 overflow  const restoreCardOverflow = () => {    const cardDom = cardRef.current;    if (cardDom) {      cardDom.style.overflow = cardOverflow;    }  };  // 往卡片添加光源  const addLight = () => {    const cardDom = cardRef.current;    if (cardDom) {      cardDom.appendChild(lightRef.current);    }  };  // 删除光源  const removeLight = () => {    const cardDom = cardRef.current;    if (cardDom) {      cardDom.removeChild(lightRef.current);    }  };  // 监听卡片的鼠标移入  const onMouseEnter = () => {    // 添加光源    addLight();    setCardOverflowHidden();  };  // use-light-card.ts  // 监听卡片的鼠标移动  const onMouseMove = (e: MouseEvent) => {    // 获取鼠标的坐标    const { clientX, clientY } = e;    // 让光跟随鼠标    const cardDom = cardRef.current;    const lightDom = lightRef.current;    if (cardDom) {      // 获取卡片相对于窗口的x和y坐标      const { x, y } = cardDom.getBoundingClientRect();      // 获取光的宽高      const { width, height } = lightDom.getBoundingClientRect();      lightDom.style.left = `${clientX - x - width / 2}px`;      lightDom.style.top = `${clientY - y - height / 2}px`;      //   设置动画效果      const maxXRotation = 10; // X 轴旋转角度      const maxYRotation = 10; // Y 轴旋转角度      const rangeX = 200 / 2; // X 轴旋转的范围      const rangeY = 200 / 2; // Y 轴旋转的范围      const rotateX = ((clientX - x - rangeY) / rangeY) * maxXRotation; // 根据鼠标在 Y 轴上的位置计算绕 X 轴的旋转角度      const rotateY = -1 * ((clientY - y - rangeX) / rangeX) * maxYRotation; // 根据鼠标在 X 轴上的位置计算绕 Y 轴的旋转角度      cardDom.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; //设置 3D 透视    }  };  // 监听卡片鼠标移出  const onMouseLeave = () => {    // 鼠标离开移出光源    removeLight();    restoreCardOverflow();  };  useEffect(() => {    // 设置光源样式    setLightStyle();    // 绑定事件    cardRef.current?.addEventListener('mouseenter', onMouseEnter);    cardRef.current?.addEventListener('mousemove', onMouseMove);    cardRef.current?.addEventListener('mouseleave', onMouseLeave);    return () => {        // 解绑事件        cardRef.current?.removeEventListener('mouseenter', onMouseEnter);        cardRef.current?.removeEventListener('mousemove', onMouseMove);        cardRef.current?.removeEventListener('mouseleave', onMouseLeave);    }  })  return {    cardRef,  };};// Index.tsximport './Index.less'import { useLightCard } from './use-light-card'const Index = () => {    const { cardRef: cardRef1 } = useLightCard()    const { cardRef: cardRef2 } = useLightCard({        light: {            color: '#ffffff',            width: 100        }    })    const { cardRef: cardRef3 } = useLightCard({        light: {            color: 'yellow'        }    })    return <div className='light-card-container'>        {/* 方块盒子 */}        <div className='item' ref={cardRef1}></div>        {/* 方块盒子 */}        <div className='item' ref={cardRef2}></div>        {/* 方块盒子 */}        <div className='item' ref={cardRef3}></div>    </div>}export default Index// Index.less.light-card-container {    background: black;    width: 100%;    height: 100%;    padding: 200px;    display: flex;    justify-content: space-between;      .item {      position: relative;      width: 125px;      height: 125px;      background: #1c1c1f;      border: 1px solid rgba(255, 255, 255, 0.1);      // 不需要了      // .light {      //   width: 60px;      //   height: 60px;      //   background: #ff4132;      //   filter: blur(40px);      //   position: absolute;      // }    }}

结语

RoH28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-92131-0.html使用 React Hooks 实现鼠标悬浮卡片发光的动画效果

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 25个每个开发人员都应该知道的CSS 技巧

下一篇: C# 异常处理:每个 .NET 开发者都应掌握的核心知识点

标签:
  • 热门焦点
  • iPhone卖不动了!苹果股价创年内最大日跌幅:市值一夜蒸发万亿元

    8月5日消息,今天凌晨美股三大指数高开低走集体收跌,道指跌0.41%;纳指跌0.36%;标普500指数跌0.52%。热门科技股也都变化极大,其中苹果报181.99美元,跌4.8%,创
  • 从 Pulsar Client 的原理到它的监控面板

    背景前段时间业务团队偶尔会碰到一些 Pulsar 使用的问题,比如消息阻塞不消费了、生产者消息发送缓慢等各种问题。虽然我们有个监控页面可以根据 topic 维度查看他的发送状态,
  • 一篇聊聊Go错误封装机制

    %w 是用于错误包装(Error Wrapping)的格式化动词。它是用于 fmt.Errorf 和 fmt.Sprintf 函数中的一个特殊格式化动词,用于将一个错误(或其他可打印的值)包装在一个新的错误中。使
  • 从零到英雄:高并发与性能优化的神奇之旅

    作者 | 波哥审校 | 重楼作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得
  • 得物宠物生意「狂飙」,发力“它经济”

    作者|花花小萌主近日,得物宣布正式上线宠物鉴别,通过得物App内的&ldquo;在线鉴别&rdquo;,可找到鉴别宠物的选项。通过上传自家宠物的部位细节,就能收获拥有专业资质认证的得物鉴
  • 品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现&ldquo;美团直播&rdquo;的固定入口。在直播聚合页面,外卖&ldquo;神枪手&rdquo;直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • 大厂卷向扁平化

    来源:新熵作者丨南枝 编辑丨月见大厂职级不香了。俗话说,兵无常势,水无常形,互联网企业调整职级体系并不稀奇。7月13日,淘宝天猫集团启动了近年来最大的人力制度改革,目前已形成一
  • 机构称Q2全球智能手机出货量同比下滑11% 苹果份额依旧第2

    7月20日消息,据外媒报道,研究机构的报告显示,由于需求下滑,今年二季度全球智能手机的出货量,同比下滑了11%,三星、苹果等主要厂商的销量,较去年同期均有下
  • 上海举办人工智能大会活动,建设人工智能新高地

    人工智能大会在上海浦江两岸隆重拉开帷幕,人工智能新技术、新产品、新应用、新理念集中亮相。8月30日晚,作为大会的特色活动之一的上海人工智能发展盛典人工
Top