還記得 Dudi 嗎? 在 Day 12 , Dudi 始終沒有接收到來自家鄉的老公的回應,為此她倍感消沈,每日借酒澆愁,好不愉快。隨著酗酒的日子長了, Dudi 開始對周遭的人聲稱時常看到殘影,這個現象已經嚴重影響到她的工作。她的事業合夥人看不下去了,帶她去給醫生看,醫生們紛紛搖頭,不得其解。
直到後來,遇到了一名宣稱可以根治此症狀的名醫,但他缺少一樣工具協助他診斷。醫生需要一個可以模擬 Dudi 眼中看到殘影現象的頁面,來協助他分析!
於是, Dudi 的事業合夥人找上了我們...
在一個頁面上有一行字,字有多個殘影,我們希望殘影能夠隨著滑鼠的移動而改變相對位置。範例連結
下面是HTML,外層類別 hero
代表作用域,滑鼠進到這裡面時,才會讓殘影與滑鼠互動。內層元素 h1
就是我們要產生殘影的字。其中, contenteditable
屬性讓使用者能夠改動其內容。
<div class="hero">
<h1 contenteditable>Dudi 喝醉酒...</h1>
</div>
接著看 JS 程式碼,步驟大致如下:
hero
內移動時,會呼叫自訂函式來控制陰影位置hero
內的座標位置看起來不難,一項一項來吧! 首先建立監聽器與自訂函式。
const hero = document.querySelector('.hero');
const text = document.querySelector('h1');
function shadow(e) {
// 控制陰影位置
}
hero.addEventListener('mousemove', shadow);
接下來是自訂函式的內容,要想讓陰影位置隨著滑鼠而動,首先要計算滑鼠位置。
function shadow(e) {
// 記錄滑鼠位置在 `.hero` 內的位置
let {offsetX: x, offsetY: y} = e;
}
這裡必需用到回傳的事件物件,所以函數內要設參數 e
,負責接收回傳的事件物件。
事件物件中, offsetX
、 offsetY
屬性負責記錄滑鼠在 target
中的位置。 target
上個章節有略提到,是指觸發該滑鼠事件的元素。
滑鼠在 .hero
區塊上時, target
為 .hero
元素, 但當滑鼠移動到 h1
上時, target
就會更改為 h1
元素。做為參考點的元素改變了,因此 offsetX
、 offsetY
的位置也會跟著改變。為了補償此改變,必須對滑鼠在 h1
上的狀況做修正,程式碼如下:
function shadow(e) {
// 記錄滑鼠位置在 `.hero` 內的位置
let {offsetX: x, offsetY: y} = e;
// 若滑鼠不在 `.hero` 上,修正座標
if(this !== e.target) {
x += e.target.offsetLeft;
y += e.target.offsetTop;
}
}
修正的邏輯為,計算 h1
原點位置與 .hero
原點位置的距離,補償給滑鼠在 h1
上的 offsetX
、 offsetY
。
這裡提一下 {offsetX: x, offsetY: y} = e
這串,這是 ES6 新的變數指定值的方法,稱為解構賦值 (Destructuring Assignment)。相當於下列程式碼:
let x = e.offsetX;
let y = e.offsetY;
接著要計算滑鼠相對於字體中心的位移,然後轉換為陰影移動的距離。程式碼如下:
// 自訂的最大陰影移動距離
const walk = 100;
function shadow(e) {
// 記錄滑鼠位置在 `.hero` 內的位置
let {offsetX: x, offsetY: y} = e;
// 若滑鼠不在 `.hero` 上,修正座標
if(this !== e.target) {
x += e.target.offsetLeft;
y += e.target.offsetTop;
}
// 記錄 hero 元素的總長、總寬
const {offsetWidth: width, offsetHeight: height} = hero;
// 計算相對位移,轉換為陰影位置
const xWalk = Math.round((x / width - 0.5) * walk);
const yWalk = Math.round((y / height - 0.5) * walk);
// 陰影擴散範圍
const shadowLevel = (Math.abs(xWalk) + Math.abs(yWalk)) / 2 / walk * 30;
}
在此例,字體中心位置在 .hero
元素的正中心,因此算出滑鼠位置在 .hero
元素內的長度比例後,扣掉 0.5,即為兩者相對位移。
該位移乘上最大陰影移動距離,即為滑鼠在該位置時陰影的距離。陰影擴散範圍則隨個人喜好計算。
最後更新文字陰影位置,程式碼如下:
function shadow(e) {
// 前略
text.style.textShadow = `${-xWalk}px ${-yWalk}px ${shadowLevel}px rgba(0, 0, 0, 0.5),
${xWalk * 3}px ${yWalk * 3}px ${shadowLevel}px rgba(0, 0, 0, 0.3)
`;
}
textShadow
前兩個參數代表陰影位置,第三個參數代表陰影擴散距離,第四個參數代表陰影顏色。另外,使用逗號讓我們可以指定多重陰影。
如此一來, Dudi 眼裏所看到的多重景象得以完整呈現,透過醫生的分析,證明了 Dudi 純粹只是酒喝太多罷了!接受飲酒戒斷後的 Dudi 重獲新生,也放下了她的過去,開始了新的生活,這都要感謝大家的支持,謝謝你們, Dudi 會更好。
以上是 JS30 第十六篇!