马黑黑 发表于 2025-8-15 20:28

radial+mask实现图片转场效果

本帖最后由 马黑黑 于 2025-8-15 20:31 编辑 <br /><br /><div class="codebox" data-prev="1">
&lt;style&gt;
        #ma {
                position: relative;
                margin: 30px auto;
                width: 400px;
                height: 530px;
                background: var(--bg);
                color: red;
                <txt-green>/* 主元素背景使用JS图片数组中的第 1 张图片 */</txt-green>
                --bg: url('https://638183.freep.cn/638183/t22/hl/bw1.jpg') no-repeat center/cover;
                <txt-green>/* 伪元素背景使用JS图片数组中的第 2 张图片 */</txt-green>
                --bg1: url('https://638183.freep.cn/638183/t22/hl/bw2.jpg') no-repeat center/cover;
                --per: 0%; <txt-green>/* 色标边界变量 */</txt-green>
        }
        #ma::before {
                position: absolute;
                content: '';
                inset: 0;
                background: var(--bg1);
                <txt-green>/* 遮罩设定 : 圆形遮罩(椭圆遮罩则将 circle, 删掉) */</txt-green>
                mask: radial-gradient(circle, red var(--per), transparent var(--per) 0);
                --webkit-mask: radial-gradient(circle, red var(--per), transparent var(--per) 0);
        }
&lt;/style&gt;

&lt;div id="ma" title="点击转场"&gt;&lt;/div&gt;

&lt;script&gt;
        var per = 0,                        <txt-green>// 遮罩渐变色标边界</txt-green>
                step = 1,                        <txt-green>// 色标边界变化歩幅</txt-green>
                picIdx = 0,                        <txt-green>// 图片序号</txt-green>
                isPlaying = false,        <txt-green>// 遮罩动画是否运行中</txt-green>
                raf;                                <txt-green>// 请求关键帧动画计数器</txt-green>
        <txt-green>// 图片数组(多少任意)</txt-green>
        var pics = [
                'https://638183.freep.cn/638183/t22/hl/bw1.jpg',
                'https://638183.freep.cn/638183/t22/hl/bw2.jpg',
                'https://638183.freep.cn/638183/t22/hl/bw3.jpg',
                'https://638183.freep.cn/638183/t22/hl/bw4.jpg',
                'https://638183.freep.cn/638183/t22/hl/bw5.jpg',
                'https://638183.freep.cn/638183/t22/hl/bw6.jpg'
        ];

        <txt-green>// 更新图片(背景图片序号的计算需要做取余数处理)</txt-green>
        const update = () =&gt; {
                if (isPlaying) return; <txt-green>// 若请求关键帧动画正在运行中则不执行此函数</txt-green>
                <txt-green>// 如果当前使用的图片序号为偶数 :</txt-green>
                if (picIdx % 2 === 0) {;
                        <txt-green>// 主元素背景使用伪元素的背景图片</txt-green>
                        ma.style.setProperty('--bg', `url(${pics}) no-repeat center/cover`);
                        <txt-green>// 伪元素背景使用下一张图片</txt-green>
                        ma.style.setProperty('--bg1', `url(${pics[(picIdx + 1) % pics.length]}) no-repeat center/cover`);
                <txt-green>// 否则假如是奇数 :</txt-green>
                } else {
                        <txt-green>// 伪元素背景使用当前图片序号对应的图片</txt-green>
                        ma.style.setProperty('--bg1', `url(${pics}) no-repeat center/cover`);
                        <txt-green>// 主元素背景使用下一张图片</txt-green>
                        ma.style.setProperty('--bg', `url(${pics[(picIdx + 1) % pics.length]}) no-repeat center/cover`);
                }
                picIdx ++; <txt-green>// 图片序号递增</txt-green>
                animate(); <txt-green>// 运行遮罩动画</txt-green>
        };

        <txt-green>// 遮罩动画</txt-green>
        const animate = () =&gt; {
                per += step; <txt-green>// 色标边界变量递增(或递减)</txt-green>
                <txt-green>// 若色标变量小于 0 或 大于 100 :</txt-green>
                if (per &lt; 0 || per &gt; 100) {
                        cancelAnimationFrame(raf); <txt-green>// 取消请求关键帧动画计数器</txt-green>
                        step = -step; <txt-green>// 歩幅正负互变</txt-green>
                        isPlaying = false; <txt-green>// 此时遮罩动画运行状态为假</txt-green>
                <txt-green>// 否则 :</txt-green>
                } else {
                        ma.style.setProperty('--per', per + '%'); <txt-green>// 将 per 变量传递给CSS变量 --per</txt-green>
                        raf = requestAnimationFrame(animate); <txt-green>// 递归运行函数自身(以持续改变色标边界)</txt-green>
                        isPlaying = true; <txt-green>// 此时遮罩动画运行状态为真</txt-green>
                }
        };

        ma.onclick = () =&gt; update();
&lt;/script&gt;
</div>

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

马黑黑 发表于 2025-8-15 20:28

本帖最后由 马黑黑 于 2025-8-15 20:42 编辑

实现原理简单解释:

(一)打开页面即初始化之时,通过CSS设定,主元素和伪元素分别使用JS图片数组中的第一张和第二张图片做背景,伪元素被全遮罩,主元素呈现图片;

(二)随后的图片转场,主元素和伪元素一次轮换呈现图片,以变量 picIdx 取 2 的余数值为依据分别给主元素、伪元素设置背景;

(三)转场总是通过遮罩、揭开遮罩依次进行。

马黑黑 发表于 2025-8-15 20:44

radial-gradient 径向渐变默认使用椭圆形状,当元素高一致时是为圆形形状,不一致时默认是为椭圆形状,可以像本例 21、22 行代码那样强制使用圆形形状。

杨帆 发表于 2025-8-15 20:57

新的图片转场效果又诞生了,谢谢马老师精彩示范与讲授

马黑黑 发表于 2025-8-15 21:50

<p>一楼的 update 函数写的比较罗嗦,修改一下:</p>
<div class="codebox">
const update = () => {
        if (isPlaying) return;
        var idx = (picIdx % 2 === 0 ? picIdx : picIdx + 1) % pics.length,
                idx1 = (picIdx % 2 === 0 ? picIdx + 1 : picIdx) % pics.length;
        ma.style.setProperty('--bg', `url(${pics}) no-repeat center/cover`);
        ma.style.setProperty('--bg1', `url(${pics}) no-repeat center/cover`);
        picIdx ++;
        animate();
};
</div>

红影 发表于 2025-8-15 21:55

马黑黑 发表于 2025-8-15 20:28
实现原理简单解释:

(一)打开页面即初始化之时,通过CSS设定,主元素和伪元素分别使用JS图片数组中的 ...

分别是在奇偶数的转换过程中,遮罩半径在0%和100%之间转换,这思路真巧妙{:4_199:}

红影 发表于 2025-8-15 21:57

马黑黑 发表于 2025-8-15 20:44
radial-gradient 径向渐变默认使用椭圆形状,当元素高一致时是为圆形形状,不一致时默认是为椭圆形状,可以 ...

原来还可以把径向渐变的椭圆强制改变成圆形{:4_204:}

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

红影 发表于 2025-8-15 21:57
原来还可以把径向渐变的椭圆强制改变成圆形

就这两种形状,默认椭圆,还可以设置中心点:

radiao-gradient(at 20% 20, red, transparent,green);

或使用圆形形状:

radial-gradient(circle at 20% 20%, red, transparent, red);

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

红影 发表于 2025-8-15 21:55
分别是在奇偶数的转换过程中,遮罩半径在0%和100%之间转换,这思路真巧妙

笨人用巧法{:4_170:}

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

杨帆 发表于 2025-8-15 20:57
新的图片转场效果又诞生了,谢谢马老师精彩示范与讲授

{:4_191:}

小辣椒 发表于 2025-8-15 22:38

小辣椒欣赏加学习{:4_187:}

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

小辣椒 发表于 2025-8-15 22:38
小辣椒欣赏加学习

{:4_190:}

花飞飞 发表于 2025-8-15 22:53

这次有方向区别了,由内到外,或者由外到内。。
关键是--per的变化,0至100或者100至0,
所以若色标变量小于 0 或 大于 100,就歩幅正负互变进行step = -step;
太喵了喵喵。。。。。{:4_173:}

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

花飞飞 发表于 2025-8-15 22:53
这次有方向区别了,由内到外,或者由外到内。。
关键是--per的变化,0至100或者100至0,
所以若色标变量 ...

这是巧妙完成预设目标的方法

花飞飞 发表于 2025-8-15 22:54

马黑黑 发表于 2025-8-15 20:28
实现原理简单解释:

(一)打开页面即初始化之时,通过CSS设定,主元素和伪元素分别使用JS图片数组中的 ...

表面看着只是形状的 变化,实际上运行根本不同{:4_173:}

花飞飞 发表于 2025-8-15 22:55

马黑黑 发表于 2025-8-15 22:33
就这两种形状,默认椭圆,还可以设置中心点:

radiao-gradient(at 20% 20, red, transparent,green);


椭圆的也好玩,横竖斜方向随机的吧

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

花飞飞 发表于 2025-8-15 22:54
表面看着只是形状的 变化,实际上运行根本不同

不同的渐变遮罩以及遮罩设计方案,实现的细节自然不一样。

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

花飞飞 发表于 2025-8-15 22:55
椭圆的也好玩,横竖斜方向随机的吧

中心点默认在元素的中心。如果需要随机中心点,可以参考 conic 设置随机开始角度的方法设置随机圆心:

mask: radial-gradient(circle at var(--cx) var(--cy), 颜色...);

然后在JS相应的地方处理 --cx 和 --cy 变量

花飞飞 发表于 2025-8-15 22:59

马黑黑 发表于 2025-8-15 22:54
这是巧妙完成预设目标的方法

这灵巧的超标了,厉害。。
主要是这么难的东东被你写得小白也能有点点看懂。。更厉害。。{:4_170:}

花飞飞 发表于 2025-8-15 23:00

马黑黑 发表于 2025-8-15 22:56
不同的渐变遮罩以及遮罩设计方案,实现的细节自然不一样。

每种都有自己的独特风格,举一反三是行不通了{:4_173:}
页: [1] 2 3 4 5
查看完整版本: radial+mask实现图片转场效果