共用的 HTML 與 CSS 說明
範例中共同的部份只有 HTML 與 CSS,如下。
1 2 | <div id="cursor"></div> <h1>這是內容</h1> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | body { margin: 0; height: 100vh; font-family: 'Bebas Neue', sans-serif; display: grid; place-content: center; overflow-x: hidden; } #cursor { position: absolute; width: 60px; height: 60px; border-radius: 50%; background: rgb(175, 200, 200); pointer-events: none; // 滑鼠點按不會觸發事件 mix-blend-mode: darken; // CSS 色混合模式 } h1 { font-size: 20vw; color: blue; pointer-events: none; } |
特別說明的部份:
- mix-blend-mode: darken:CSS 色混合模式,有點類似 PhotoShop 的圖層的圖層濾鏡。
- pointer-events: none:穿越上層的元素,當滑鼠點按不會觸發事件。
監聽 mousemove 事件來觸發取得 xy 座標,做為樣式 translate 屬性變化時所產生的 transform 動畫
JavaScript 的部份如下。
1 2 3 4 5 6 7 | const cursor = document.getElementById("cursor"); document.addEventListener("mousemove", (e) => { // 取得滑鼠座標 clientY clientX // console.log('cursor e.clientX', e.clientX, 'e.clientY', e.clientY); cursor.style.transform = `translate(${e.clientX}px, ${e.clientY}px)`; }); |
運行的結果如下,在觸發上會以連續觸發的方式,讓跟著移動的色塊很緊密的跟著,比較沒有運動上的快慢變化。
See the Pen 滑鼠跟隨的元素-1-監聽 mousemove 觸發取得 xy 座標,做為樣式 translate 屬性變化所產生的動畫 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
增加 requestAnimationFrame 方法讓函式連續觸發自已產生微廷遲 + 元素中心定位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | const cursor = document.getElementById("cursor"); // get the dimensions of the cursor let cursorWidth = cursor.offsetWidth; let cursorHeight = cursor.offsetHeight; let settings = { mouseX: 0, mouseY: 0, xPos: 0, yPos: 0, speed: 15, // speed factor }; const animate = () => { settings.xPos += (settings.mouseX - settings.xPos) / settings.speed; settings.yPos += (settings.mouseY - settings.yPos) / settings.speed; // use transform to change the position and center the cursor cursor.style.transform = `translate(${settings.xPos - cursorWidth / 2}px, ${ settings.yPos - cursorHeight / 2 }px)`; /** * (cursorWidth / 2) 與 (cursorHeight / 2):為了元素座標中心以滑鼠游標為中心 * settings.xPos:x 座標 * settings.yPos:y 座標 * settings.mouseX:滑鼠 x 座標 * settings.mouseY:滑鼠 y 座標 */ requestAnimationFrame(animate); /** requestAnimationFrame() 瀏覽器執行函數取得動畫幀數 * requestAnimationFrame() 本身不會執行 * 透過 function animate() 包裝 requestAnimationFrame() 與呼叫執行,讓 requestAnimationFrame() 不斷的重覆執行 */ } animate(); document.addEventListener("mousemove", (e) => { settings.mouseX = e.pageX; settings.mouseY = e.pageY; }); |
重點解說:
- offsetWidth:元素的佈局寬度,測量包含元素的邊框 (border)、水平線上的內邊距 (padding)、豎直方向捲軸 (scrollbar)(如果存在的話)、以及 CSS 設定的寬度 (width) 的值。
- offsetHeight:與上面的 offsetWidth 差不多,只是用來取得元素垂值的資訊。
- settings.mouseX = e.pageX 與 settings.mouseY = e.pageY:透過 document.addEventListener("mousemove") 的監聽方法,會將滑鼠取得的座標位置傳向 settings 物件下的 mouseX 與 mouseY
- const animate = () => {} 的函式與執行,會因為 requestAnimationFrame(animate) 動畫方法的觸發,本身這個方法只會執行一次而本身不會執行,但把 animate 函式當成參數傳入執行後,函式會自已不斷的觸發執行,讓動畫進行更新。
- settings.xPos += (settings.mouseX - settings.xPos) / settings.speed;:
- settings.mouseX 是由 document.addEventListener("mousemove") 觸發所傳入的滑鼠當前的座標。
- 自訂義的 settings.xPos 是動畫元素的座標,受到滑鼠移動後會先減去計算,接著在除於所設定的速度 settings.speed (值是 15),會將計算的結果傳向 settings.xPos 動畫元素的座標做賦值的動作。
- cursor.style.transform = `translate(${settings.xPos - cursorWidth / 2}px, ${settings.yPos - cursorHeight / 2}px)`:
- settings.xPos 因為值的計算而改變後,就可以傳向 transform 的 CSS 動畫屬性,當 translate 屬性產生變化後就會產生動畫了,也因為定位因 translate (位移) 將 x 與 y 的部份計算過,所以產生有點類似延遲的效果產生。
- cursorWidth / 2 與 cursorHeight / 2,是用來取跟隨元素自身的高度與寬度的正中間。
- 取得跟隨元素自身的高度與寬度的正中間後,接著向 settings.xPos 水平與 settings.yPos 垂直相減,這樣就會讓跟隨元素的定位扣掉自先的高和寬。
完整範例如下:
See the Pen 滑鼠跟隨的元素-2-增加 transform by Jimmy_Wu (@Jimmy_Wu) on CodePen.
參考資料: