使用mask-image實現星球大戰場景過渡效果
前言
大家有看過星球大戰這部電影嗎,里面有許多場景間的過渡效果看起來非常的絲滑,那我們能不能使用CSS來模擬實現一下呢?
漸變體驗
如果mask-image以圖像或漸變的形式出現,則我們可以控制與元素的哪些像素是可見的,哪些像素是不可見的(透明)。當mask-image應用于元素時,它充當一種映射,確定每個元素的可見性。
下面嘗試將兩張圖片使用mask-image實現交叉淡入淡出效果
<template>
<div :class="$style.xq_outer">
<div :class="$style.xq_bg"></div>
<div :class="$style.xq_women"></div>
</div>
</template>
<style lang="scss" module>
.xq_outer {
position: relative;
width: 600px;
height: 360px;
margin: 20px auto;
background: red;
}
.xq_bg {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_bg.jpeg");
background-size: 100% 100%;
}
.xq_women {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_women.jpeg");
background-size: 100% 100%;
-webkit-mask-image: linear-gradient(to right, transparent 30%, #fff 44%);
}
</style>
效果是這樣的:
這里第一眼看上去你不會以為這就是一張圖片吧,其實這是由星球背景 + 人物兩張圖片“合成”的。
是不是有點PS的感覺了。
「原理是:第一個場景是星球,第二個場景是人物,直接位于第一個場景之上。在第二個場景中使用漸變蒙版使其左側透明,從而顯示出第一個場景。」
-webkit-mask-image為linear-gradient()從左到右漸變。
- 前三分之一是完全透明的,所以這部分人物圖片是不可見的,底下的星球圖片露出。
- 中間的三分之一從透明變為不透明的白色,場景逐漸淡入。
- 最后三分之一是完全不透明的白色,導致這部分人物圖片完全可見,底下的星球完全被蓋住。
水平擦除過渡
了解了上面這個效果的實現原理,我們就可以來著手實現第二個水平擦除效果了,從上面這個例子我們不難想象到只要蒙版拉伸到比實際場景更寬,然后動畫水平滑動,就能夠實現這個水平擦除效果了。
.xq_outer {
position: relative;
width: 600px;
height: 360px;
margin: 20px auto;
}
.xq_bg {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_bg.jpeg");
background-size: 100% 100%;
}
.xq_women {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_women.jpeg");
background-size: 100% 100%;
// -webkit-mask-image: linear-gradient(to right, transparent 30%, #fff 44%);
-webkit-mask-image: linear-gradient(to right, transparent 48%, #fff 52%);
-webkit-mask-size: 210%;
-webkit-mask-position: left;
}
.xq_outer:is(:hover) .xq_women {
-webkit-mask-position: right;
transition: -webkit-mask-position 2s linear;
}
效果如下:
「原理:蒙版的大小為-webkit-mask-size: 210%- 這為場景提供了 100% 的寬度,最初是完全透明的 + 10% 的淡入淡出 + 另一個 100% 的場景最終完全不透明。」
光圈擦除過渡
下面再來看一種光圈擦除效果,這里將會使用到@property自定義屬性,由于這個屬性還處于實驗階段,所以在將其用于生產之前,請仔細檢查瀏覽器兼容性表格。
大致的思路是:使用radial-gradient()在其中定義一個自定義屬性。然后我們可以對該自定義屬性進行動畫處理以對漸變進行動畫處理。但在此之前,我們需要將自定義屬性注冊到@property.
@property --r {
syntax: "<percentage>";
inherits: true;
initial-value: -5%;
}
.xq_women {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_women.jpeg");
background-size: 100% 100%;
// -webkit-mask-image: linear-gradient(to right, transparent 30%, #fff 44%);
// -webkit-mask-image: linear-gradient(to right, transparent 48%, #fff 52%);
// -webkit-mask-size: 210%;
// -webkit-mask-position: left;
-webkit-mask-image: radial-gradient(
circle,
#fff calc(var(--r) - 5%),
transparent calc(var(--r) + 5%)
);
}
@keyframes circle {
to {
--r: 105%;
}
}
.xq_outer:is(:hover) .xq_women {
// -webkit-mask-position: right;
// transition: -webkit-mask-position 2s linear;
animation: circle 2s linear forwards;
}
再來看看此時的效果:
「原理:隨著 --radius 的動畫化,radial-gradient() 中的色標位置也是如此。它們被計算為 --radius 值的 -5% 和 +5%,以創建漸變淡入淡出,為擦拭提供柔和的邊緣。」
時鐘擦除過渡
再來看一個星球大戰中最具標志性的擦除過渡——時鐘擦除過渡。這次我們使用conic-gradient()和動畫化一個自定義屬性angle值。
@property --angle {
syntax: "<angle>";
inherits: true;
initial-value: -10deg;
}
.xq_women {
position: absolute;
width: 100%;
height: 100%;
background-image: url("/public/xq_women.jpeg");
background-size: 100% 100%;
-webkit-mask-image: conic-gradient(
#fff 0deg,
#fff calc(var(--angle) - 10deg),
transparent calc(var(--angle) + 10deg),
transparent 360deg
),
conic-gradient(transparent 350deg, #fff 360deg);
z-index: -1;
}
@keyframes ang {
to {
--angle: 370deg;
}
}
.xq_outer:is(:hover) .xq_women {
animation: ang 2s linear forwards;
z-index: 1;
}
效果如下:
「原理:第一個conic-gradient()動畫以創建時鐘擦除效果,但它在其起點(在 處0deg)留下了硬邊。這就是為什么要conic-gradient()在硬邊緣之前創建一個小的淡入淡出并將其軟化的原因。漸變結合在一起為場景創建蒙版。」
總結
現在的CSS功能越來越強大了,在本文中介紹了很多 CSS 成分,包括不同類型的漸變、注冊自定義屬性,當然還有遮罩和動畫。這在以前,僅僅靠CSS是不可能實現的效果!