日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

Vue3 實(shí)現(xiàn)最近很火的酷炫功能:卡片懸浮發(fā)光

來(lái)源: 責(zé)編: 時(shí)間:2024-05-22 17:14:11 175觀看
導(dǎo)讀有趣的動(dòng)畫(huà)效果前幾天在網(wǎng)上看到了一個(gè)很有趣的動(dòng)畫(huà)效果,如下,光會(huì)跟隨鼠標(biāo)在卡片上進(jìn)行移動(dòng),并且卡片會(huì)有視差的效果。那么在 Vue3 中應(yīng)該如何去實(shí)現(xiàn)這個(gè)效果呢?圖片基本實(shí)現(xiàn)思路其實(shí)實(shí)現(xiàn)思路很簡(jiǎn)單,無(wú)非就是分幾步:首先,卡

有趣的動(dòng)畫(huà)效果

前幾天在網(wǎng)上看到了一個(gè)很有趣的動(dòng)畫(huà)效果,如下,光會(huì)跟隨鼠標(biāo)在卡片上進(jìn)行移動(dòng),并且卡片會(huì)有視差的效果。aUp28資訊網(wǎng)——每日最新資訊28at.com

那么在 Vue3 中應(yīng)該如何去實(shí)現(xiàn)這個(gè)效果呢?aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

基本實(shí)現(xiàn)思路

其實(shí)實(shí)現(xiàn)思路很簡(jiǎn)單,無(wú)非就是分幾步:aUp28資訊網(wǎng)——每日最新資訊28at.com

  • 首先,卡片是相對(duì)定位,光是絕對(duì)定位
  • 監(jiān)聽(tīng)卡片的鼠標(biāo)移入事件mouseenter,當(dāng)鼠標(biāo)進(jìn)入時(shí)顯示光
  • 監(jiān)聽(tīng)卡片的鼠標(biāo)移動(dòng)事件mouseover,鼠標(biāo)移動(dòng)時(shí)修改光的left、top,讓光跟隨鼠標(biāo)移動(dòng)
  • 監(jiān)聽(tīng)卡片的鼠標(biāo)移出事件mouseleave,鼠標(biāo)移出時(shí),隱藏光

我們先在 Index.vue 中準(zhǔn)備一個(gè)卡片頁(yè)面,光的CSS效果可以使用filter: blur() 來(lái)實(shí)現(xiàn)。aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

可以看到現(xiàn)在的效果是這樣:aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

實(shí)現(xiàn)光源跟隨鼠標(biāo)

在實(shí)現(xiàn)之前我們需要注意幾點(diǎn):aUp28資訊網(wǎng)——每日最新資訊28at.com

1、鼠標(biāo)移入時(shí)需要設(shè)置卡片 overflow: hidden,否則光會(huì)溢出,而鼠標(biāo)移出時(shí)記得還原。aUp28資訊網(wǎng)——每日最新資訊28at.com

2、獲取鼠標(biāo)坐標(biāo)時(shí)需要用clientX/Y而不是pageX/Y,因?yàn)榍罢邥?huì)把頁(yè)面滾動(dòng)距離也算進(jìn)去,比較嚴(yán)謹(jǐn)。aUp28資訊網(wǎng)——每日最新資訊28at.com

剛剛說(shuō)到實(shí)現(xiàn)思路時(shí)我們說(shuō)到了mouseenter、mousemove、mouseleave,其實(shí)mouseenter、mouseleave 這二者的邏輯比較簡(jiǎn)單,重點(diǎn)是 mouseover 這個(gè)監(jiān)聽(tīng)函數(shù)。aUp28資訊網(wǎng)——每日最新資訊28at.com

而在 mouseover 這個(gè)函數(shù)中,最重要的邏輯就是:光怎么跟隨鼠標(biāo)移動(dòng)呢?aUp28資訊網(wǎng)——每日最新資訊28at.com

或者也可以這么說(shuō):怎么計(jì)算光相對(duì)于卡片盒子的 left 和 top。aUp28資訊網(wǎng)——每日最新資訊28at.com

對(duì)此我專門畫(huà)了一張圖,相信大家一看就懂怎么算了。aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

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

知道了怎么計(jì)算,那么邏輯的實(shí)現(xiàn)也很明了了~封裝一個(gè)use-light-card.ts:aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

接著在頁(yè)面中去使用:aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

這樣就能實(shí)現(xiàn)基本的效果啦~aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

卡片視差效果

卡片的視差效果需要用到樣式中 transform 樣式,主要是配置四個(gè)東西:aUp28資訊網(wǎng)——每日最新資訊28at.com

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

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

現(xiàn)在就有了卡片視差的效果啦~aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

給所有卡片添加光源

上面只是給一個(gè)卡片增加光源,接下來(lái)可以給每一個(gè)卡片都增加光源啦!!!aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

讓光源變成可配置

上面的代碼,總感覺(jué)這個(gè) hooks 耦合度太高不太通用,所以我們可以讓光源變成可配置化,這樣每個(gè)卡片就可以展示不同大小、顏色的光源了~像下面一樣。aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

既然是配置化,那我們希望是這么去使用 hooks 的,我們并不需要自己在頁(yè)面中去寫光源的dom節(jié)點(diǎn),也不需要自己去寫光源的樣式,而是通過(guò)配置傳入 hooks 中。aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

所以 hooks 內(nèi)部要自己通過(guò)操作 DOM 的方式,去添加、刪除光源,可以使用createElement、appendChild、removeChild 去做這些事~。aUp28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片aUp28資訊網(wǎng)——每日最新資訊28at.com

完整源碼

<!-- Index.vue --><template>  <div class="container">    <!-- 方塊盒子 -->    <div class="item" ref="cardRef1"></div>    <!-- 方塊盒子 -->    <div class="item" ref="cardRef2"></div>    <!-- 方塊盒子 -->    <div class="item" ref="cardRef3"></div>  </div></template><script setup lang="ts">import { useLightCard } from './use-light-card';const { cardRef: cardRef1 } = useLightCard();const { cardRef: cardRef2 } = useLightCard({  light: {    color: '#ffffff',    width: 100,  },});const { cardRef: cardRef3 } = useLightCard({  light: {    color: 'yellow',  },});</script><style scoped lang="less">.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);  }}</style>
// use-light-card.tsimport { onMounted, onUnmounted, ref } from 'vue';interface IOptions {  light?: {    width?: number; // 寬    height?: number; // 高    color?: string; // 顏色    blur?: number; // filter: blur()  };}export const useLightCard = (option: IOptions = {}) => {  // 獲取卡片的dom節(jié)點(diǎn)  const cardRef = ref<HTMLDivElement | null>(null);  let cardOverflow = '';  // 光的dom節(jié)點(diǎn)  const lightRef = ref<HTMLDivElement>(document.createElement('div'));  // 設(shè)置光源的樣式  const setLightStyle = () => {    const { width = 60, height = 60, color = '#ff4132', blur = 40 } = option.light ?? {};    const lightDom = lightRef.value;    lightDom.style.position = 'absolute';    lightDom.style.width = `${width}px`;    lightDom.style.height = `${height}px`;    lightDom.style.background = color;    lightDom.style.filter = `blur(${blur}px)`;  };  // 設(shè)置卡片的 overflow 為 hidden  const setCardOverflowHidden = () => {    const cardDom = cardRef.value;    if (cardDom) {      cardOverflow = cardDom.style.overflow;      cardDom.style.overflow = 'hidden';    }  };  // 還原卡片的 overflow  const restoreCardOverflow = () => {    const cardDom = cardRef.value;    if (cardDom) {      cardDom.style.overflow = cardOverflow;    }  };  // 往卡片添加光源  const addLight = () => {    const cardDom = cardRef.value;    if (cardDom) {      cardDom.appendChild(lightRef.value);    }  };  // 刪除光源  const removeLight = () => {    const cardDom = cardRef.value;    if (cardDom) {      cardDom.removeChild(lightRef.value);    }  };  // 監(jiān)聽(tīng)卡片的鼠標(biāo)移入  const onMouseEnter = () => {    // 添加光源    addLight();    setCardOverflowHidden();  };  // use-light-card.ts  // 監(jiān)聽(tīng)卡片的鼠標(biāo)移動(dòng)  const onMouseMove = (e: MouseEvent) => {    // 獲取鼠標(biāo)的坐標(biāo)    const { clientX, clientY } = e;    // 讓光跟隨鼠標(biāo)    const cardDom = cardRef.value;    const lightDom = lightRef.value;    if (cardDom) {      // 獲取卡片相對(duì)于窗口的x和y坐標(biāo)      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`;      //   設(shè)置動(dòng)畫(huà)效果      const maxXRotation = 10; // X 軸旋轉(zhuǎn)角度      const maxYRotation = 10; // Y 軸旋轉(zhuǎn)角度      const rangeX = 200 / 2; // X 軸旋轉(zhuǎn)的范圍      const rangeY = 200 / 2; // Y 軸旋轉(zhuǎn)的范圍      const rotateX = ((clientX - x - rangeY) / rangeY) * maxXRotation; // 根據(jù)鼠標(biāo)在 Y 軸上的位置計(jì)算繞 X 軸的旋轉(zhuǎn)角度      const rotateY = -1 * ((clientY - y - rangeX) / rangeX) * maxYRotation; // 根據(jù)鼠標(biāo)在 X 軸上的位置計(jì)算繞 Y 軸的旋轉(zhuǎn)角度      cardDom.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; //設(shè)置 3D 透視    }  };  // 監(jiān)聽(tīng)卡片鼠標(biāo)移出  const onMouseLeave = () => {    // 鼠標(biāo)離開(kāi)移出光源    removeLight();    restoreCardOverflow();  };  onMounted(() => {    // 設(shè)置光源樣式    setLightStyle();    // 綁定事件    cardRef.value?.addEventListener('mouseenter', onMouseEnter);    cardRef.value?.addEventListener('mousemove', onMouseMove);    cardRef.value?.addEventListener('mouseleave', onMouseLeave);  });  onUnmounted(() => {    // 解綁事件    cardRef.value?.removeEventListener('mouseenter', onMouseEnter);    cardRef.value?.removeEventListener('mousemove', onMouseMove);    cardRef.value?.removeEventListener('mouseleave', onMouseLeave);  });  return {    cardRef,  };};

aUp28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-90040-0.htmlVue3 實(shí)現(xiàn)最近很火的酷炫功能:卡片懸浮發(fā)光

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: Python 并發(fā)編程的 12 個(gè)實(shí)用技巧

下一篇: ViewRootImpl如何負(fù)責(zé)管理繪制視圖樹(shù)和刷新界面

標(biāo)簽:
  • 熱門焦點(diǎn)
Top 主站蜘蛛池模板: 龙里县| 保亭| 大丰市| 五原县| 杨浦区| 凭祥市| 连州市| 开鲁县| 石首市| 靖州| 高邮市| 秀山| 台北市| 安达市| 陆川县| 潞城市| 兴化市| 巴林右旗| 江西省| 南澳县| 邻水| 罗山县| 息烽县| 湖南省| 广汉市| 通许县| 永康市| 安多县| 平南县| 肥东县| 莫力| 石阡县| 凤城市| 桂阳县| 二连浩特市| 乾安县| 谷城县| 册亨县| 锡林郭勒盟| 邹城市| 安义县|