如何在Safari中替换SVG元素上的mix-blend-mode属性?
问题
我有一段包含SVG元素的HTML代码:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="style.css" type="text/css"> <title>Page</title> </head> <body> <svg style="width:0;height:0;"> <filter id='f'> <feTurbulence type='fractalNoise' baseFrequency='8.5'/> </filter> </svg> </body> </html>
对应的CSS样式代码如下:
body::before, body::after { position: fixed; inset: 0; z-index: -1; --grad: radial-gradient(at 25%, red, rgba(255, 0, 0, .2) 50%, transparent 70%); --mask: var(--grad), var(--grad); -webkit-mask: var(--mask); -webkit-mask-composite: source-in; -webkit-mask: var(--mask); mask: var(--mask); -webkit-mask-composite: source-in, xor; mask-composite: intersect; mix-blend-mode: color; content: ''; } body::before { background: #000; -webkit-filter: url(#f); filter: url(#f); } body::after { background: -webkit-gradient(linear, left top, right top, from(purple), color-stop(20%, navy), to(aqua)); background: -o-linear-gradient(left, purple, navy 20%, aqua); background: linear-gradient(90deg, purple, navy 20%, aqua); }
为了实现最佳浏览器兼容性,我已经给相关属性加了各浏览器前缀,但遇到问题:mix-blend-mode属性在搭配SVG滤镜使用时不被Safari支持,有没有能实现完全一致效果的替代方案?
解决方案
直接把混合逻辑内置到SVG滤镜里就能绕开Safari的兼容问题,最终效果和原写法完全一致,具体做法:
- 原实现的问题点在于,Safari对「元素应用SVG滤镜+CSS
mix-blend-mode」的组合场景支持有缺陷,只要把混合步骤从CSS挪到SVG滤镜内部执行,就不会触发这个兼容bug - 首先修改内联SVG代码,在
<defs>标签里先定义好你需要的紫-海军蓝-水蓝横向渐变,再给原有噪点滤镜补全混合逻辑:用feTurbulence生成噪点、feColorMatrix提取层亮度、feComposite完成层合成,完全复刻mix-blend-mode: color的效果——这个混合模式的本质就是保留上层的色相、饱和度,采用下层的亮度,用SVG原生滤镜原语实现没有跨浏览器兼容问题 - 再调整CSS代码:删掉两个伪元素上的
mix-blend-mode: color声明,移除body::after上的渐变背景,直接给容器应用修改后的SVG滤镜即可,原来写的径向渐变遮罩逻辑完全不需要改动,视觉效果不会有偏差 - 如果需要做双保险兼容,可以加简单的特性判断:非Safari环境保留原有写法,Safari环境自动切换到SVG内置混合的版本,两边效果可以做到完全对齐
修改后的核心SVG滤镜参考代码:
<svg style="width:0;height:0;"> <defs> <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" stop-color="purple" /> <stop offset="20%" stop-color="navy" /> <stop offset="100%" stop-color="aqua" /> </linearGradient> <filter id='compatFilter'> <feTurbulence type='fractalNoise' baseFrequency='8.5' result="noise"/> <feFlood flood-color="#000" result="blackLayer"/> <feComposite in="blackLayer" in2="noise" operator="in" result="noiseTexture"/> <feColorMatrix in="SourceGraphic" type="luminance" result="sourceLum"/> <feComposite in="bgGrad" in2="sourceLum" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="colorLayer"/> <feBlend in="noiseTexture" in2="colorLayer" mode="normal"/> </filter> </defs> </svg>
注意:如果不需要兼容特别老旧的Safari版本,甚至不用写两套代码,直接全量用SVG内置混合的方案即可,Chrome、Firefox对SVG滤镜原语的支持比CSS混合模式早很多,兼容性更稳定。
内容的提问来源于stack exchange,提问作者levensta
相关产品推荐
相关产品推荐

