马黑黑 发表于 2025-8-10 12:06

使用mask属性实现图片转场

本帖最后由 马黑黑 于 2025-8-10 18:10 编辑 <br /><br /><style>
        .artBox { font-size: 18px; }
        .artBox > p { margin: 10px 0; line-height: 30px; }
        .artBox mark { padding: 4px 6px; background: lightblue; }
        .tRed { color: red; }
        .tGreen { color: green; }
</style>

<div id="prevBox"></div>
<div class="artBox">
        <p>CSS 属性 <txt-red>mask</txt-red> 属性允许用户使用图片去遮罩使用了该属性的元素的指定区域。利用此属性,可以实现美妙的图片转场效果。</p>
        <h5>实现思路:</h5>
        <p>多张图片放入数组待用,接着按顺序读取数组图片,第 n 张图片做主元素背景,第 n+1 张图片做伪元素的背景,每间隔一段时间执行一次,如此循环往复。伪元素做图片入场、出场效果,使用 <txt-red>mask</txt-red> 进行线性或其它渐变遮罩(本文使用线性渐变遮罩),非透明渐变区域是伪元素背景图呈现的区域,先从 -2% 开始,0% 开始是透明区域伪元素背景不可见,然后非透明区域递增到 100%,这样伪元素上的背景图片入场方式就是淡入,而上一张图片已是主元素背景图片,它的出场就是淡出。伪元素进出的角度可以随意设计,本文采用矩形四个角作为进出场的地点。</p>
        <p>以下是具体代码,可以点击代码框右上角的预览链接查看效果:</p>
        <div class="codebox"data-prev="1">
&lt;style&gt;
        #pa {
                margin: 30px auto;
                width: 1024px;
                height: 640px;
                background: var(--bg);
                position: relative;
                <txt-green>/* 主元素初始背景图片为JS数组中最后一张 */</txt-green>
                --bg: url('https://638183.freep.cn/638183/t24/w5/g5.webp') no-repeat center/cover;
                --bg1: none; <txt-green>/* 伪元素开始时背景为空 */</txt-green>
        }
        <txt-green>/* 伪元素使用遮罩 */</txt-green>
        #pa::before {
                content: '';
                position: absolute;
                inset: 0;
                background: var(--bg1);
                mask: linear-gradient(var(--a), red var(--per), transparent calc(var(--per) + 2%), transparent);
                <txt-green>/* 兼容版本 &lt; 120 的 Chrome */</txt-green>
                -webkit-mask: linear-gradient(var(--a), red var(--per), transparent calc(var(--per) + 2%), transparent);
        }
        <txt-green>/* 关键帧动画播放控制器 */</txt-green>
        #ma {
                position: absolute;
                left: 40px;
                top: 40px;
                animation: rot 8s linear infinite;
        }
        <txt-green>/* 指针移入时暂停关键帧动画 */</txt-green>
        #ma:hover {
                animation-play-state: paused;
        }
        @keyframes rot {
                to { transform: rotate(1turn); }
        }
&lt;/style&gt;

&lt;div id="pa"&gt;
        &lt;img id="ma" src="https://638183.freep.cn/638183/small/780.webp" alt="" title="移出播放/移入暂停" /&gt;
&lt;/div&gt;

&lt;script&gt;
        <txt-green>// per : 遮罩起始点,step : 递增系数,aniCounter : 关键帧动画运行计数器,</txt-green>
        <txt-green>// raf : requestAnimationFrame API 计数器</txt-green>
        var per = -2, step = 0.5, aniCounter = 0, raf;

        <txt-green>// 图片数组</txt-green>
        var pics = [
                'https://638183.freep.cn/638183/t24/w5/g1.webp',
                'https://638183.freep.cn/638183/t24/w5/g2.webp',
                'https://638183.freep.cn/638183/t24/w5/g3.webp',
                'https://638183.freep.cn/638183/t24/w5/g4.webp',
                'https://638183.freep.cn/638183/t24/w5/g5.webp'
        ];

        </txt-green>// 播放控制器关键帧运行迭代事件<txt-green>
        ma.onanimationiteration = () =&gt; {
                <txt-green>// angle : 渐变角度,picIdx : 图片序号</txt-green>
                var angle = aniCounter % 4, picIdx = aniCounter % pics.length;
                <txt-green>// 更新CSS变量 --a</txt-green>
                pa.style.setProperty('--a', `${45 + (angle * 90)}deg`);
                <txt-green>// 更新伪元素使用的CSS变量 --bg1</txt-green>
                pa.style.setProperty('--bg1', `url(${pics}) no-repeat center/cover`);
                picIdx = (picIdx + 1) % pics.length; <txt-green>// 图片序号变更</txt-green>
                aniCounter ++; <txt-green>// 关键帧动画计数器递增</txt-green>
                changePic(); <txt-green>// 运行图片转场函数</txt-green>
        };

        <txt-green>// 图片转场函数</txt-green>
        function changePic() {
                <txt-green>// 遮罩起始点若大于 100</txt-green>
                if (per &gt; 100) {
                        cancelAnimationFrame(raf); <txt-green>// 取消请求关键帧动画API计数器</txt-green>
                        per = -2; <txt-green>// 遮罩起始点回到原始值</txt-green>
                        var picIdx = aniCounter % pics.length; <txt-green>// 计算当前图片序号</txt-green>
                        picIdx = picIdx &gt; 0 ? picIdx - 1: pics.length - 1; <txt-green>// 防止序号超出范围</txt-green>
                        <txt-green>// 更新主元素背景图</txt-green>
                        pa.style.setProperty('--bg', `url(${pics}) no-repeat center/cover`);
                <txt-green>// 否则</txt-green>
                } else {
                        per += step; <txt-green>// 遮罩起始点递增</txt-green>
                        pa.style.setProperty('--per', per + '%'); <txt-green>// 更新遮罩起始点</txt-green>
                        raf = requestAnimationFrame(changePic); <txt-green>// 递归运行函数自身并更新raf计数器</txt-green>
                }
        }
&lt;/script&gt;
        </div>
        <p><txt-red>mask</txt-red> 遮罩所使用的线性渐变角度可以使用数组替代算式以设计更具个性化的变换方式。此外,可以考虑其它方式的渐变背景,甚至可以使用图片、SVG路径。</p>
</div>

<script type="module">
        import linenumber from 'https://638183.freep.cn/638183/web/js/linenumber.js';
        linenumber();
</script>

红影 发表于 2025-8-10 13:03

图片的转场效果很漂亮,分别依次从四个角点开始的呢,都是沿着45度方向行进。同时背景图也依次轮换着,这样的转场顺畅而自然,真漂亮{:4_199:}

红影 发表于 2025-8-10 13:05

鼠标放在小播上,也会让转场暂停呢{:4_204:}

花飞飞 发表于 2025-8-10 16:50

中午时看了一眼,有事先跑了,现在来仔细学一下这个漂亮的转场。。。。
新画面淡入自然流畅,边缘有微微的羽化效果,好看

花飞飞 发表于 2025-8-10 16:55

转场在视频里常见。。
之前白老师在贴子里也做过一些图片转场效果,还有用各种不同的形状实现。
今天从mask 属性角度实现,也很新奇。。
角度可以任意变换,上下平铺,或者左右平铺也都很漂亮。。{:4_199:}

花飞飞 发表于 2025-8-10 16:57

这个二楼代码又有改变吧。。看着特别舒适
主代码与背景黑白分明,经典色系。。
注释全部用彩色,一目了然。。。

杨帆 发表于 2025-8-10 17:30

本帖最后由 杨帆 于 2025-8-10 17:31 编辑

美妙的图片转场效果,谢谢马老师经典讲授、精彩分享{:4_191:}

马黑黑 发表于 2025-8-10 17:46

杨帆 发表于 2025-8-10 17:30
美妙的图片转场效果,谢谢马老师经典讲授、精彩分享

这类设计,还有很多可能

梦江南 发表于 2025-8-10 18:09

老师辛苦了!{:4_190:}

马黑黑 发表于 2025-8-10 18:09

梦江南 发表于 2025-8-10 18:09
老师辛苦了!

{:4_180:}

杨帆 发表于 2025-8-10 18:38

马黑黑 发表于 2025-8-10 17:46
这类设计,还有很多可能

是的,只有明白其原理才可尝试一下{:4_190:}

马黑黑 发表于 2025-8-10 22:09

杨帆 发表于 2025-8-10 18:38
是的,只有明白其原理才可尝试一下

比如角度,你可以从其它角度开始,然后不是加90度,而是N。

转一圈应该是几步,用 360 / N 作为取余数的标准即可

杨帆 发表于 2025-8-10 22:13

马黑黑 发表于 2025-8-10 22:09
比如角度,你可以从其它角度开始,然后不是加90度,而是N。

转一圈应该是几步,用 360 / N 作为取余数 ...

谢谢马老师细致入微的指导,我好好琢磨一下{:4_190:}

马黑黑 发表于 2025-8-10 22:15

杨帆 发表于 2025-8-10 22:13
谢谢马老师细致入微的指导,我好好琢磨一下

这里面需要感受的是角度的划分,都是以圆的度数为参照

杨帆 发表于 2025-8-10 22:18

马黑黑 发表于 2025-8-10 22:15
这里面需要感受的是角度的划分,都是以圆的度数为参照

有点明白了,谢谢老师{:4_190:}

马黑黑 发表于 2025-8-10 22:28

杨帆 发表于 2025-8-10 22:18
有点明白了,谢谢老师

你可以先尝试开始的角度,去体会它从哪儿开始

杨帆 发表于 2025-8-10 23:09

马黑黑 发表于 2025-8-10 22:28
你可以先尝试开始的角度,去体会它从哪儿开始

好的,谢谢老师{:4_190:}

马黑黑 发表于 2025-8-18 19:24

杨帆 发表于 2025-8-10 23:09
好的,谢谢老师

{:4_191:}
页: [1]
查看完整版本: 使用mask属性实现图片转场