CSS 探照燈動畫 ( CSS Spotlight )
這篇教學會使用 CSS 的裁切路徑 clip-path 和影像遮罩 mask,將虛擬元素轉換成「探照燈」,做出有趣又吸睛的 CSS 探照燈動畫,最後還會搭配 JavaScript 讓探照燈可以跟隨滑鼠移動。
快速導覽:
探照燈動畫 ( 虛擬元素 + 裁切路徑 clip-path )
製作探照燈的第一種方法為「虛擬元素 + 裁切路徑」,透過 clip-path
樣式屬性,將蓋在背景圖上的虛擬元素 ::before
裁切出一個圓形,搭配半透明的黑色 ::after
,就能做出探照燈,最後只要搭配動畫,就能讓探照燈動起來,詳細說明寫在下方範例註解中。
- 這種做法如果要加入文字,必須得將文字寫在虛擬元素的
content
裡,換行時需要使用white-space: pre;
搭配換行符\A
。- 參考:虛擬元素選擇器 ( 偽元素 )、裁切路徑 clip-path。
<!-- HTML 程式碼 -->
<div></div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative; /* 讓虛擬元素定位參考 */
width: 450px;
height: 300px;
background: url("https://steam.oxxostudio.tw/image/index-css.jpg");
background-size: contain; /* 背景填滿設定 */
}
div::before, div::after {
position: absolute; /* 絕對定位產生重疊 */
white-space: pre; /* 讓 content 支援換行符 */
content: "Hello World\A\A\A oxxo.studio"; /* 加入文字 */
line-height: 1.5;
width: 100%; /* 尺寸與底圖大小相同 */
height: 100%;
text-align: center;
font-size: 50px;
}
div::before {
color: #fff3; /* 文字較暗,像是被遮住 */
background: #000c; /* 半透明黑色遮住底圖 */
}
div::after {
color: #fff; /* 文字較亮,像是被燈照到 */
background: url("https://steam.oxxostudio.tw/image/index-css.jpg"); /* 和底圖相同的圖 */
background-size: contain; /* 和底圖相同的填滿設定 */
clip-path: circle(120px at 50% 50%); /* 使用圓形裁切路徑 */
animation: oxxo 3s alternate infinite ease-in-out;
}
@keyframes oxxo {
0% {clip-path: circle(120px at 10% 50%);} /* 動畫改變圓心位置 */
100% {clip-path: circle(120px at 90% 50%);}
}
</style>
探照燈動畫 ( 虛擬元素 + 影像遮罩 mask )
製作探照燈的第二種方法為「虛擬元素 + 影像遮罩」,透過 mask-image
樣式屬性讀取圓形漸層 radial-gradient
,將蓋在背景圖上黑色半透明的虛擬元素 ::before
裁切出一個圓形,就能做出探照燈,最後只要搭配動畫,就能讓探照燈動起來,詳細說明寫在下方範例註解中。
- 這種做法非常直覺,且可以在元素裡加上其他內容,內容也會一併套用探照燈效,但要額外使用子定屬性
@property
,才能透過動畫改變漸層位置。- 參考:虛擬元素選擇器 ( 偽元素 )、影像遮罩 mask、radial-gradient()、@property 自訂屬性值。
<!-- HTML 程式碼 -->
<div>
<h1>Hello World</h1>
<h2>oxxo.studio</h2>
</div>
<!-- CSS 程式碼 -->
<style>
/* 百分比自訂屬性 */
@property --x {
syntax: "<percentage>";
inherits: true;
initial-value: 10%;
}
/* 百分比自訂屬性 */
@property --y {
syntax: "<percentage>";
inherits: true;
initial-value: 50%;
}
/* 放在探照燈效果中的內容 */
h1, h2 {
text-align: center;
margin: 0;
padding: 0;
font-size: 50px;
line-height: 1;
}
h2 {margin-top: 160px;}
div {
position: relative; /* 提供虛擬元素定位參考 */
width: 450px;
height: 300px;
background: url("https://steam.oxxostudio.tw/image/index-css.jpg");
background-size: contain;
box-sizing: border-box; /* 寬度包含 padding */
padding: 20px;
color: #fff;
}
div::before {
position: absolute; /* 絕對定位覆蓋底圖 */
content: "";
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000c; /* 半透明黑色 */
mask-mode: luminance; /* 設定遮罩圖片為亮度模式 */
mask-image: radial-gradient(circle at var(--x) var(--y), #000 120px, #fff 120px); /* 遮罩效果 */
animation: oxxo 3s alternate infinite ease-in-out;
}
@keyframes oxxo {
0% {--x: 10%;} /* 動畫改變圓心位置 */
100% {--x: 90%;}
}
</style>
JavaScript 滑鼠控制探照燈 ( 裁切版 )
延伸上述「虛擬元素 + 裁切路徑 clip-path」範例程式碼,先將 CSS 中圓心位置改用「CSS 變數」,接著就能透過 JavaScript 把滑鼠座標的值賦予給 CSS 變數,探照燈也就會跟著滑鼠移動,詳細說明參考下方範例註解。
參考:CSS 變數
<!-- HTML 程式碼 -->
<div></div>
<!-- CSS 程式碼 -->
<style>
div {
position: relative;
width: 450px;
height: 300px;
background: url("https://steam.oxxostudio.tw/image/index-css.jpg");
background-size: contain;
box-sizing: border-box;
--x: 50%;
--y: 50%;
}
div::before, div::after {
box-sizing: border-box;
position: absolute;
white-space: pre;
content: "Hello World\A\A\A oxxo.studio";
line-height: 1.5;
width: 100%;
height: 100%;
text-align: center;
font-size: 50px;
}
div::before {
color: #fff3;
background: #000c;
}
div::after {
color: #fff;
background: url("https://steam.oxxostudio.tw/image/index-css.jpg");
background-size: contain;
clip-path: circle(120px at var(--x) var(--y));
animation: oxxo 3s alternate infinite ease-in-out;
}
</style>
<script>
const item = document.querySelector('div');
let ox = item.offsetLeft; // 取得這個元素的座標,方便轉換為遮罩相對位置
let oy = item.offsetTop;
item.addEventListener('mousemove', function(e){
let x = e.pageX - ox; // 計算滑鼠與座標的相對位置,也就是實際點擊圖片的位置
let y = e.pageY - oy; // 計算滑鼠與座標的相對位置,也就是實際點擊圖片的位置
item.style.setProperty('--x',`${x}px`); // 替換 CSS 變數內容
item.style.setProperty('--y',`${y}px`); // 替換 CSS 變數內容
});
</script>
JavaScript 滑鼠控制探照燈 ( 遮罩版 )
延伸上述「虛擬元素 + 影像遮罩 mask」範例程式碼,因為已經使用 CSS 自訂屬性作圓心位置,可以直接裡用 JavaScript 把滑鼠座標的值賦予給 CSS 屬性,探照燈也就會跟著滑鼠移動,詳細說明參考下方範例註解。
需要特別注意自訂屬性的 syntax 要改為「
length
」,參考:@property 自訂屬性值
<!-- HTML 程式碼 -->
<div>
<h1>Hello World</h1>
<h2>oxxo.studio</h2>
</div>
<!-- CSS 程式碼 -->
<style>
/* 長度自訂屬性 */
@property --x {
syntax: "<length>";
inherits: true;
initial-value: 0px;
}
/* 長度比自訂屬性 */
@property --y {
syntax: "<length>";
inherits: true;
initial-value: 0px;
}
h1, h2 {
text-align: center;
margin: 0;
padding: 0;
font-size: 50px;
line-height: 1;
}
h2 {margin-top: 160px;}
div {
position: relative;
width: 450px;
height: 300px;
background: url("https://steam.oxxostudio.tw/image/index-css.jpg");
background-size: contain;
box-sizing: border-box;
padding: 20px;
color: #fff;
}
div::before {
position: absolute;
content: "";
top: 0;
left: 0;
background: #000c;
width: 100%;
height: 100%;
mask-mode: luminance; /* 設定遮罩圖片為亮度模式 */
mask-image: radial-gradient(circle at var(--x) var(--y), #000 120px, #fff 120px);
}
</style>
<script>
const item = document.querySelector('div');
let ox = item.offsetLeft; // 取得這個元素的座標,方便轉換為遮罩相對位置
let oy = item.offsetTop;
item.addEventListener('mousemove', function(e){
let x = e.pageX - ox; // 計算滑鼠與座標的相對位置,也就是實際點擊圖片的位置
let y = e.pageY - oy; // 計算滑鼠與座標的相對位置,也就是實際點擊圖片的位置
item.style.setProperty('--x',`${x}px`); // 替換 CSS 屬性內容
item.style.setProperty('--y',`${y}px`); // 替換 CSS 屬性內容
});
</script>
小結
這篇教學介紹了兩種製作遮罩的方法,不外乎都是需要將黑色或圖形裁切成圓形,相信只要運用得當,一定能在網站中產生有趣又吸睛的效果喔。
意見回饋
如果有任何建議或問題,可傳送「意見表單」給我,謝謝~