前幾天在網上看到了一個很有趣的動畫效果,如下,光會跟隨鼠標在卡片上進行移動,并且卡片會有視差的效果。
那么在 Vue3 中應該如何去實現這個效果呢?
圖片
其實實現思路很簡單,無非就是分幾步:
我們先在 Index.vue 中準備一個卡片頁面,光的CSS效果可以使用filter: blur() 來實現。
圖片
可以看到現在的效果是這樣:
圖片
在實現之前我們需要注意幾點:
1、鼠標移入時需要設置卡片 overflow: hidden,否則光會溢出,而鼠標移出時記得還原。
2、獲取鼠標坐標時需要用clientX/Y而不是pageX/Y,因為前者會把頁面滾動距離也算進去,比較嚴謹。
剛剛說到實現思路時我們說到了mouseenter、mousemove、mouseleave,其實mouseenter、mouseleave 這二者的邏輯比較簡單,重點是 mouseover 這個監聽函數。
而在 mouseover 這個函數中,最重要的邏輯就是:光怎么跟隨鼠標移動呢?
或者也可以這么說:怎么計算光相對于卡片盒子的 left 和 top。
對此我專門畫了一張圖,相信大家一看就懂怎么算了。
圖片
知道了怎么計算,那么邏輯的實現也很明了了~封裝一個use-light-card.ts:
圖片
接著在頁面中去使用:
圖片
這樣就能實現基本的效果啦~
圖片
卡片的視差效果需要用到樣式中 transform 樣式,主要是配置四個東西:
圖片
現在就有了卡片視差的效果啦~
圖片
上面只是給一個卡片增加光源,接下來可以給每一個卡片都增加光源啦!!!
圖片
圖片
上面的代碼,總感覺這個 hooks 耦合度太高不太通用,所以我們可以讓光源變成可配置化,這樣每個卡片就可以展示不同大小、顏色的光源了~像下面一樣。
圖片
既然是配置化,那我們希望是這么去使用 hooks 的,我們并不需要自己在頁面中去寫光源的dom節點,也不需要自己去寫光源的樣式,而是通過配置傳入 hooks 中。
所以 hooks 內部要自己通過操作 DOM 的方式,去添加、刪除光源,可以使用createElement、appendChild、removeChild 去做這些事~。
圖片
<!-- 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節點 const cardRef = ref<HTMLDivElement | null>(null); let cardOverflow = ''; // 光的dom節點 const lightRef = ref<HTMLDivElement>(document.createElement('div')); // 設置光源的樣式 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)`; }; // 設置卡片的 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); } }; // 監聽卡片的鼠標移入 const onMouseEnter = () => { // 添加光源 addLight(); setCardOverflowHidden(); }; // use-light-card.ts // 監聽卡片的鼠標移動 const onMouseMove = (e: MouseEvent) => { // 獲取鼠標的坐標 const { clientX, clientY } = e; // 讓光跟隨鼠標 const cardDom = cardRef.value; const lightDom = lightRef.value; 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(); }; onMounted(() => { // 設置光源樣式 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, };};
本文鏈接:http://www.tebozhan.com/showinfo-26-90040-0.htmlVue3 實現最近很火的酷炫功能:卡片懸浮發光
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com