網站特效黏黏的滑鼠追蹤~輕鬆理解效果原理

滑鼠追蹤是一個非常多變又好玩的技術,甚至不只有追蹤,還能判斷拖曳、移動速度,做出更多變得特效,常見有小東西追著滑鼠,今天就來特別講解一下,將滑鼠追蹤應用到會動的臉。

基本原理

首先我們要取得滑鼠在螢幕上的 X 與 Y 的數值,滑鼠在網頁的畫面起始位置是 X=0、Y=0,也就是畫面的最左上。接著我們要監聽當滑鼠進入了目標元素,透過計算滑鼠位置與目標元素的長寬,來算出上下左右,並且加上滑鼠與中心點的距離。

不囉唆直接實作吧~

我們先來建立一個 animate 的 Function,因為這個 Function 最後會放入監聽 mousemove,所以就要先帶入變數 eventitemevent 是這個監聽事件會自動給的,那我們要使用它就要宣告出來。

從這個 event 我們可以拿到滑鼠的 x y 數值,因為我們是監聽該元素,所以此時拿到 x y 值不會像是平常從螢幕最左上開始計算 0 0,而是從該元素的最左上 0 0 開始計算。

再從帶入的 item 取得該元素的寬高值。拿到必要的值後,我們就要算出滑鼠與元素的相對位置,並且給他一個要追的距離。x / widthy / height 是在計算滑鼠在元素之中的相對位置。因為原本的位置是 0~1 之間,我們想要增加移動的距離,所以要能達到以下目的。

  • 當滑鼠位於元素的左邊緣 (x = 0) 時,xMove 等於 -10。
  • 當滑鼠位於元素的右邊緣 (x = width) 時,xMove 等於 10。
  • 當滑鼠位於元素的中央 (x = width / 2) 時,xMove 等於 0。

所以就有了 move * 2 再 - move 這個公式。

const animate = (event, item) => {
    const   { offsetX: x, offsetY: y } = event,
            { offsetWidth: width, offsetHeight: height } = item,
            move = 10,
            xMove = x / width * (move * 2) - move,
            yMove = y / height * (move * 2) - move;
}

再來就幾乎結束了,將算好的賦予上 css

這邊有個奇妙的細節,我們不是直接給到 lef top 或者 translate,而是用了一個新的方法,給到變數上面去,這是為了讓之後子層元件同樣能吃到這個 css 變數值,就不用透過 js 再去計算子層,大大增加效能。

const animate = (event, item) => {
    const   { offsetX: x, offsetY: y } = event,
            { offsetWidth: width, offsetHeight: height } = item,
            move = 10,
            xMove = x / width * (move * 2) - move,
            yMove = y / height * (move * 2) - move;

    item.style.setProperty('--mouseX', `${xMove}px`);
    item.style.setProperty('--mouseY', `${yMove}px`);
    if (e.type === 'mouseleave') {
        item.style.setProperty('--mouseX', '0px');
        item.style.setProperty('--mouseY', '0px');
    }
}

最後裝上監聽,最困難的部分就完成囉!


const FaceItems = document.querySelectorAll('.facewrap__item')

FaceItems.forEach((item) => {
  item.addEventListener('mousemove', (e) => {
    animate(e, item)
  })
  item.addEventListener('mouseleave', (e) => {
    animate(e, item)
  })
})

開始寫上 CSS

以上都是邏輯面,接下來是我自己最愛做的事情,就是特效的細節,就是這些微小的細節才能造就整個動畫的質感提升。我們是做多層的臉,有眼睛、嘴巴、臉這三個圖層,要給他們有不同的移動速度與距離,才能讓臉追蹤看起來更有層次感,更生動。

&__item {

  svg {
    width: 25rem;
    height: 25rem;

    #face {
      transition: transform 0.1s linear;
      transform: translate3d(calc(var(--mouseX) * 0.5), calc(var(--mouseY) * 0.5), 0);
    }

    #mouth {
      transition: transform 0.1s linear;
      transform: translate3d(calc(var(--mouseX) * 2), calc(var(--mouseY) * 2), 0);
    }

    #eyein {
      transition: transform 0.1s linear;
      transform: translate3d(calc(var(--mouseX) * 1.6), calc(var(--mouseY) * 1.6), 0);
    }

可以看到我使用了 var(--mouseX),這個就是拿來吃父層剛剛被 js 所賦予的 mouseX mouseY property,所以子層也都需要跟著掛上,再用 calc() 算出你想要的距離即可。

其實整個最奇妙的還是 property,當我發現它可以直接由子層繼承父層時,真的像是發現新大陸一樣呢~希望未來我還能用這個玩出更多不同花樣。

這裡附上整個程式碼 Codepen 的連結~ https://codepen.io/esdesignstudio/pen/BaeBMBe?editors=1111