精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

使用 React Hooks 實現鼠標懸浮卡片發光的動畫效果

開發
在網上看到了一個很有趣的動畫效果:光會跟隨鼠標在卡片上進行移動,并且卡片會有視差的效果。那么在 React 中應該如何去實現這個效果呢?

有趣的動畫效果

前幾天在網上看到了一個很有趣的動畫效果,如下,光會跟隨鼠標在卡片上進行移動,并且卡片會有視差的效果,那么在 React 中應該如何去實現這個效果呢?

基本實現思路

其實實現思路很簡單,無非就是分幾步:

  • 首先,卡片是相對定位,光是絕對定位
  • 監聽卡片的鼠標移入事件mouseenter,當鼠標進入時顯示光
  • 監聽卡片的鼠標移動事件mouseover,鼠標移動時修改光的left、top,讓光跟隨鼠標移動
  • 監聽卡片的鼠標移出事件mouseleave,鼠標移出時,隱藏光

我們先在 Index.tsx 中準備一個卡片頁面,光的CSS效果可以使用filter: blur() 來實現:

可以看到現在的效果是這樣:

實現光源跟隨鼠標

在實現之前我們需要注意幾點:

  • 鼠標移入時需要設置卡片 overflow: hidden,否則光會溢出,而鼠標移出時記得還原
  • 獲取鼠標坐標時需要用clientX/Y而不是pageX/Y,因為前者會把頁面滾動距離也算進去,比較嚴謹

剛剛說到實現思路時我們說到了mouseenter、mousemove、mouseleave,其實mouseenter、mouseleave 這二者的邏輯比較簡單,重點是 mouseover 這個監聽函數

而在 mouseover 這個函數中,最重要的邏輯就是:光怎么跟隨鼠標移動呢?

或者也可以這么說:怎么計算光相對于卡片盒子的 left 和 top

對此我專門畫了一張圖,相信大家一看就懂怎么算了:

  • left = clientX - x - width/2
  • height = clientY - y - height/2

知道了怎么計算,那么邏輯的實現也很明了了~封裝一個use-light-card.ts

接著在頁面中去使用:

這樣就能實現基本的效果啦~

卡片視差效果

卡片的視差效果需要用到樣式中 transform 樣式,主要是配置四個東西:

  • perspective:定義元素在 3D 變換時的透視效果
  • rotateX:X 軸旋轉角度
  • rotateY:Y 軸旋轉角度
  • scale3d:X/Y/Z 軸上的縮放比例

現在就有了卡片視差的效果啦~

給所有卡片添加光源

上面只是給一個卡片增加光源,接下來可以給每一個卡片都增加光源啦!!!

讓光源變成可配置

上面的代碼,總感覺這個 hooks 耦合度太高不太通用,所以我們可以讓光源變成可配置化,這樣每個卡片就可以展示不同大小、顏色的光源了~像下面一樣:

既然是配置化,那我們希望是這么去使用 hooks 的,我們并不需要自己在頁面中去寫光源的dom節點,也不需要自己去寫光源的樣式,而是通過配置傳入 hooks 中:

所以 hooks 內部要自己通過操作 DOM 的方式,去添加、刪除光源,可以使用createElement、appendChild、removeChild 去做這些事~

完整源碼

// use-light-card.ts

import { 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.tsx
import './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;
      // }
    }
}

結語

責任編輯:趙寧寧 來源: 前端之神
相關推薦

2017-02-06 13:00:49

Android翻轉卡片動畫效果

2024-05-22 08:47:41

2020-10-28 09:12:48

React架構Hooks

2022-04-16 20:10:00

React Hookfiber框架

2022-08-21 09:41:42

ReactVue3前端

2022-07-18 09:01:58

React函數組件Hooks

2022-02-10 19:15:18

React監聽系統模式

2019-08-20 15:16:26

Reacthooks前端

2011-07-08 10:15:15

IPhone 動畫

2023-11-06 08:00:00

ReactJavaScript開發

2009-09-22 12:59:58

ibmdwDojo

2021-03-09 09:52:55

技術React Hooks'數據

2021-03-18 08:00:55

組件Hooks React

2022-03-31 17:54:29

ReactHooks前端

2020-09-19 17:46:20

React Hooks開發函數

2009-09-03 16:50:35

C#鼠標形狀

2021-02-24 07:40:38

React Hooks閉包

2012-01-10 14:59:42

jQuery

2010-09-09 12:49:58

鼠標懸停tip效果CSS

2019-03-13 10:10:26

React組件前端
點贊
收藏

51CTO技術棧公眾號

欧美日韩国产中文字幕| 男男视频亚洲欧美| 亚洲大尺度美女在线| 黄色av网址在线播放| 可以在线观看的av网站| 精品中文字幕一区二区| 欧美多人乱p欧美4p久久| 色天使在线视频| 日本国产亚洲| 婷婷亚洲久悠悠色悠在线播放| 日本一区二区三区视频在线播放 | 久久五月天小说| 日韩一卡二卡三卡四卡| 日韩黄色片视频| 成人福利片网站| 久久亚区不卡日本| 亚洲xxx视频| 亚洲国产成人精品女人久久| 一区二区蜜桃| 亚洲人a成www在线影院| 久久久久久久久久久影视| 欧美成人a交片免费看| 亚洲人成影院在线观看| 欧美影视一区二区| 韩国av免费在线观看| 青娱乐精品视频| 91精品国产高清久久久久久久久| 三上悠亚作品在线观看| 精品国产乱码久久久久久果冻传媒| 日韩精品最新网址| 亚洲免费999| 欧美xxx性| 欧美日韩黄色大片| 久久这里只有精品8| 欧美r级在线| 国产婷婷色一区二区三区四区| 国产精品国模大尺度私拍| 91精品国自产| 全部av―极品视觉盛宴亚洲| 热99精品里视频精品| 日本熟伦人妇xxxx| 欧美日本二区| 精品视频9999| 国产性xxxx| 国产精品久久观看| 色婷婷av一区二区三区在线观看| 91玉足脚交白嫩脚丫| 中文字幕久久精品一区二区| 制服丝袜在线91| 久久6免费视频| 日韩毛片网站| 麻豆一区一区三区四区| 亚洲在线视频网站| av动漫免费观看| 自拍视频在线网| 日本一区二区动态图| 色一情一乱一伦一区二区三区丨| 能在线看的av| 久久精品免费在线观看| 欧美一区二区在线视频观看| 日本在线视频1区| 久久亚洲精华国产精华液 | 国产精品久久久久婷婷| 少妇免费毛片久久久久久久久| 韩国三级在线观看久| 久久久久成人黄色影片| 日韩高清三级| 成人亚洲综合天堂| 亚洲欧洲精品一区二区三区不卡| 亚洲欧洲中文| av毛片在线免费看| 亚洲第一福利视频在线| 久久久999免费视频| 免费看av不卡| 欧美日韩国产综合一区二区三区| 日本中文字幕影院| 这里视频有精品| 精品亚洲一区二区三区| 亚洲黄色免费视频| 亚洲大全视频| 久久久久久噜噜噜久久久精品| 亚州国产精品视频| 日韩精品久久久久久| 91精品国产自产在线老师啪| 国产不卡av在线播放| 成人h动漫精品一区二| 久久国产精品精品国产色婷婷| 免费动漫网站在线观看| 国产精品久久久久aaaa樱花| 粉嫩av一区二区三区天美传媒| 国产在线精彩视频| 精品1区2区3区| 国产精久久久久| 国产麻豆精品久久| 久久国产精品久久久| 久久夜靖品2区| 日本成人在线不卡视频| 99久久国产免费免费| 久久这里精品| 亚洲自拍偷拍av| 15—17女人毛片| 91久久精品无嫩草影院 | 中文字幕 日本| 精品久久久久久久| 久久久久在线观看| 中文字幕无线码一区 | 深爱五月激情网| 亚洲成人三区| 国产精品福利在线观看网址| 国产黄色av片| 国产精品三级电影| 黑人糟蹋人妻hd中文字幕| 中文字幕综合| 亚洲日韩中文字幕| 日本一二三区不卡| 国产一区二区三区不卡在线观看| 乱一区二区三区在线播放| 色婷婷在线播放| 欧美人与z0zoxxxx视频| 国产精品无码午夜福利| 欧美午夜在线| 91在线高清视频| www.视频在线.com| 欧美日韩亚洲激情| 亚洲精品乱码久久久久久蜜桃欧美| 日韩在线观看电影完整版高清免费悬疑悬疑| 久久久女人电视剧免费播放下载| 国产免费黄色大片| 欧美国产激情一区二区三区蜜月| 大陆极品少妇内射aaaaa| 视频一区中文字幕精品| 久久久成人av| 中文字幕一区二区人妻痴汉电车| 91网站在线播放| 成品人视频ww入口| 日本一区二区三区播放| 久久精品国产一区| 国产精品永久久久久久久久久| 国产日韩精品一区二区三区在线| 一本大道熟女人妻中文字幕在线 | ririsao久久精品一区| 欧美一区二区网站| 国产在线免费看| 麻豆视频一区二区| 亚洲精品欧洲精品| aaaa欧美| 中文字幕无线精品亚洲乱码一区| 久久久久久无码午夜精品直播| 26uuu精品一区二区三区四区在线 26uuu精品一区二区在线观看 | 国产精品久久久久国产精品日日| 我要看一级黄色大片| 成人网18免费网站| 国产欧美久久久久久| av资源种子在线观看| 在线观看91视频| 黑人と日本人の交わりビデオ| 欧美aaaaa成人免费观看视频| 日韩三级电影| 电影亚洲精品噜噜在线观看| 亚洲天堂影视av| 中文无码av一区二区三区| 国产精品亲子伦对白| 欧美在线aaa| 欧美激情777| 91gao视频| 国产自产自拍视频在线观看| 精品中文字幕久久久久久| 日韩在线视频不卡| 国产精品亲子乱子伦xxxx裸| 成 人 黄 色 小说网站 s色| 你懂的一区二区| 国产伦精品一区| 不卡av影片| 日韩小视频在线观看| 亚洲AV无码成人片在线观看| 精品国产精品自拍| 熟女少妇内射日韩亚洲| 国模一区二区三区白浆| 国产freexxxx性播放麻豆| 九色精品国产蝌蚪| 国产日韩换脸av一区在线观看| av在线app| 亚洲国产另类久久精品| 国产精品自拍第一页| 亚洲欧美一区二区三区极速播放 | 国产情人综合久久777777| 亚洲天堂网2018| 夜夜嗨一区二区| 亚洲一区二区精品在线观看| 亚洲精品国产九九九| 欧日韩在线观看| 高潮毛片在线观看| 亚洲美女在线看| 99精品人妻无码专区在线视频区| 午夜电影网亚洲视频| 久久久久久久麻豆| 99久久久久久| 国产精品嫩草影视| 久久一区二区三区四区五区| 7777在线视频| 精品国产精品| 国产一区二区三区高清| 宅男噜噜噜66国产精品免费| 欧美又大又硬又粗bbbbb| 欧美69xxx| 亚洲欧美日韩一区二区在线 | 亚洲老板91色精品久久| 国产a级免费视频| 在线观看免费亚洲| 日韩美女一级片| 日韩理论片一区二区| 在线观看日本中文字幕| 成人国产精品免费网站| 九九热免费在线观看| 玖玖精品视频| 久久黄色片视频| 亚洲最大av| 亚洲精品第一区二区三区| 日韩中文av| 国产一区二区三区黄| 日韩精品三级| 成人在线免费观看视视频| 欧美天堂视频| 欧美亚洲国产精品| 超碰在线最新网址| 欧美成人中文字幕在线| 欧美精品hd| 亚洲夜晚福利在线观看| 日本不卡免费播放| 日韩av影院在线观看| 精品人妻一区二区三区麻豆91| 欧美精品自拍偷拍| 国产精品国产精品国产| 在线观看国产91| 波多野结衣在线电影| 一本到高清视频免费精品| 全部毛片永久免费看| 亚洲一区二区在线免费看| 可以直接看的黄色网址| 日韩毛片在线免费观看| 国产成人精品视频免费| 中文字幕一区免费在线观看| 国产又粗又黄又猛| 国产精品久久久久影院色老大 | 色噜噜狠狠一区二区三区狼国成人| 日本女人一区二区三区| 最近中文字幕一区二区| 蜜臀久久久99精品久久久久久| 国产一区二区视频免费在线观看| 国产精品一级| 日韩欧美xxxx| 青青国产91久久久久久| 日韩欧美国产片| 久久成人免费网站| 男人午夜视频在线观看| 国产精品123区| 国产香蕉精品视频| 99r国产精品| 99久久精品免费视频| 国产色一区二区| 亚洲女人毛茸茸高潮| 亚洲视频在线一区观看| 欧美激情一区二区视频| 婷婷久久综合九色综合伊人色| 国产精品va无码一区二区三区| 日韩欧美精品中文字幕| 18国产免费视频| 51久久夜色精品国产麻豆| 亚洲AV无码精品自拍| 亚洲国产毛片完整版| 国产精品免费播放| 日韩午夜在线视频| aa级大片免费在线观看| 国产成人综合一区二区三区| 日韩成人综合网站| 99久热re在线精品视频| 日韩电影在线观看完整免费观看| 欧美午夜精品理论片a级大开眼界 欧美午夜精品久久久久免费视 | 成人免费视频网站入口| 丝袜美腿一区二区三区动态图| 日韩精品国内| 综合五月婷婷| 六月丁香婷婷激情| 国内精品视频666| 一级性生活大片| 综合色天天鬼久久鬼色| 久久老司机精品视频| 欧美怡红院视频| 成人激情四射网| 亚洲视频综合网| 日本伦理一区二区| 国产精品扒开腿做爽爽爽的视频| 欧美成人精品一级| 欧美第一黄网| 欧美久久99| 亚洲一级免费观看| av一区二区三区在线| 精品在线观看一区| 黑人巨大精品欧美一区二区| 99热精品在线播放| 亚洲午夜未删减在线观看| 免费毛片在线看片免费丝瓜视频 | 国产精品8888| 日韩av电影天堂| 中文成人无字幕乱码精品区| 一色屋精品亚洲香蕉网站| 九九精品免费视频| 日韩精品中文字幕一区| 在线播放毛片| 欧美壮男野外gaytube| 97se亚洲| 亚洲第一页在线视频| 麻豆亚洲精品| 亚洲熟女一区二区| 一区二区在线看| 伊人精品一区二区三区| 日韩av在线免播放器| 亚洲小说区图片区都市| 国产精品一区二区三区免费视频 | 9i看片成人免费看片| 欧美va亚洲va在线观看蝴蝶网| 99中文字幕一区| 日本欧美国产在线| 久久免费视频66| 男人c女人视频| 国产麻豆欧美日韩一区| 最新av电影网站| 欧美亚洲综合色| 国产福利小视频在线| 国产成人免费av| 日日狠狠久久偷偷综合色| 欧美,日韩,国产在线| 国产成人av一区| 久久久久久激情| 欧美成人三级在线| 成人在线观看亚洲| 成人国产精品一区二区| 久久国产亚洲| 91制片厂毛片| 国产精品久久久久久久久免费桃花| 国产一区免费看| 亚洲午夜精品久久久久久性色| 在线黄色的网站| 欧美精品一区二区三区在线看午夜 | 亚洲最新av在线网站| 二吊插入一穴一区二区| 日本一区二区精品| 美女久久久精品| 日韩欧美在线视频播放| 制服丝袜亚洲网站| 18+视频在线观看| 成人片在线免费看| 亚洲成人资源| 日本黄色网址大全| 在线免费不卡视频| 色三级在线观看| 亚洲a在线播放| 亚洲午夜一级| 亚洲av无码一区二区三区网址| 大荫蒂欧美视频另类xxxx| 日韩a在线观看| 国产精品国语对白| 久久久久午夜电影| 日本黄色大片在线观看| 午夜精品一区二区三区电影天堂 | 亚洲性视频在线| 欧美 日韩 亚洲 一区| 91麻豆国产在线观看| 秋霞av一区二区三区| 最近日韩中文字幕中文| 国产专区精品| 欧美一区二区中文字幕| 国产拍欧美日韩视频二区 | 精品在线小视频| 国产91在线精品| 久久免费一级片| 26uuu国产日韩综合| 91久久国语露脸精品国产高跟| 九色成人免费视频| 久9久9色综合| 中文字幕av一区二区三区人妻少妇| 午夜亚洲福利老司机| 成人欧美亚洲| 成人性色av| 免费看精品久久片| 久久久久久激情| 一色桃子一区二区| 91成人精品在线| 爱情岛论坛亚洲首页入口章节| 亚洲一区二区三区精品在线| 毛片网站在线| av一区二区三区四区电影| 老司机午夜精品视频在线观看| 黄色精品视频在线观看| 亚洲黄色成人网| 国产精一区二区| www.超碰com| 午夜激情一区二区三区| 老司机精品影院| 欧美日韩亚洲一区二区三区四区|