前言
我们在日常开发工作中,有时会遇到一些固化模板页面的需求。譬如设计稿样式和尺寸固化,在后台只需将主题通过配置项换色达到换肤效果,再配上不一样的头图,就可以将整个活动切换成另外一种样式风格,如下图展示:
配置后的几个结果页
一般情况下,这种换色并不是什么难事,简单的 CSS
样式通过 color
background-color
都可以轻松实现,但本案例中遇到了一些特殊情况,我们的设计师需要保留足够的设计感,所以导出了一些看起来并没有能那么简单实现的纹理,我们在经过一番尝试后总结出了几个思路,本文将分享给大家列举。
先来看几个遇到的问题
- 带有杂质纹理的不规则边框
仔细观察一番,会发现边框有一些毛毛的细微缺口,并不是我们用常规 CSS
就能实现的边框。当时我们首先想到的是使用 SVG
导出后改色,但单个模块的边框在设计软件中导出后,达到了惊人的 1.2MB
,这么大的素材容量在页面里使用我们显然是无法接受的,何况有此模块边框的数量有 4-5 个。
放大后的边框:
- 带有相同风格的进度条和圆
推测出现这么大容量的
SVG
应该是目前设计软件对于素材的导出较为死板,没有按照前端角度进行优化所致。
几种可行性方案
- 素材使用 PNG,使用 CSS 实现,但兼容性较差
使用 drop-shadow
和 border-image
结合,将 CSS
实体样式移出屏幕,用“剪影”取代素材,通过其颜色属性修改主题色。
例子:
.box {
border: 1rem solid transparent;
border-image: url('./border.png') 88 stretch;
width: 5.26rem;
height: 2.5rem;
/* 将主体移出屏幕 */
transform: translateX(-7.5rem) rotate(2deg);
}
然后我们在 dom
元素上动态绑上 drop-shadow
计算样式
<div class="box" :style="getBorderColor"/>
在 computed
中写上从后台拿到的配置项颜色
getBorderColor () {
// 这里的 7.5rem 是 drop-shadow 的 offset-x 属性,从屏幕外拉回原来的位置替代原来素材图,达到换色效果
return `filter: drop-shadow(${this.color} 7.5rem 0);`
},
得到的渲染结果如下
<div class="box" style="filter: drop-shadow(rgb(220, 95, 95) 7.5rem 0);"></div>
上一张调试图便于大家理解:
但请注意:经过实测,iOS
版本 15 之前都有兼容性问题,移动端 safari
内核对于 filter:drop-shadow
渲染方式与其他浏览器不同,它只能渲染元素可视的部分,所以移出屏幕这个方法就行不通了,如果设置了 overflow:hidden
也会遇到相同的情况。但亲测 iOS
又在 16 版本修复掉了这个问题,看来官方自己也意识到了。
(iOS 14-15 不渲染的效果)
- mask (SVG、PNG 均可)
这个实现方法比较简单,本文中的杂质边框最终也可以使用 PNG
代替。
.box {
width: 394px;
height: 266px;
background-color: lightcoral;
-webkit-mask: url("./border.svg") no-repeat 50% 50%;
-webkit-mask-size: cover;
/* 遮罩尺寸和位置可按实际情况微调 */
mask: url("./border.svg") no-repeat 50% 50%;
mask-size: cover;
}
这样,背景颜色就可以任意修改,最后绑到 dom
上抽离成配置项改色。
- 背景混合模式(SVG、PNG 均可)
.box {
width: 394px;
height: 266px;
color: lightcoral;
background-image: linear-gradient(currentColor, currentColor), url(./black.png);
background-color: #FFF;
background-blend-mode: lighten, normal;
background-size: 100%;
}
但是有一些小限制:那就是素材必须是黑色,元素需要设置一个浅色的底色,不然混合模式就会有问题。 输出结果:
- 本案例中的复杂型素材,通过 SVG 滤镜实现
对于复杂型 SVG
,例如本文遇到的的杂质纹理案例。首先我们基于设计稿要做一些小改动,可以让设计师帮忙把素材重新还原成纯直线,再用代码去实现杂质纹理效果,或者有条件的也可以自己使用 SVG
路径生成工具导出这样的边框线素材。
其次,通过 feTurbulence
滤镜来实线直线中的随机噪点,在 SVG
精髓这本书的第 158 页有提到这个属性:
感谢(公众号:前端侦探 )提供 DEMO 和方法
我们只需在 html
中加入一段 SVG
滤镜,记得隐藏掉,在页面上就不会占位
<svg style="display: none;">
<defs>
<filter id="nosieFilter" filterUnits="userSpaceOnUse" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="1.04" result="noise"></feTurbulence>
<feDisplacementMap scale="7" xChannelSelector="R" yChannelSelector="G" in="SourceGraphic" in2="noise" result="noise2"></feDisplacementMap>
</filter>
</defs>
</svg>
在 css
中写上与之匹配的滤镜 ID nosieFilter
,所有元素都可以复用这个滤镜。
.box {
filter: url(#nosieFilter);
}
最后,html
中 SVG
元素上的颜色也可抽离成配置项实现改色。
<svg width="394" height="266" viewBox="0 0 394 266" :fill="red">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.2659 26.0066C18.413 23.2491 20.7676 21.1329 23.5251 21.28L388.539 40.7474C391.297 40.8945 393.413 43.2491 393.266 46.0066C393.119 48.7641 390.764 50.8803 388.007 50.7332L22.9926 31.2658C20.235 31.1187 18.1189 28.7641 18.2659 26.0066Z"></path>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.265938 216.007C0.413005 213.249 2.76763 211.133 5.52513 211.28L370.539 230.747C373.297 230.894 375.413 233.249 375.266 236.007C375.119 238.764 372.764 240.88 370.007 240.733L4.99255 221.266C2.23505 221.119 0.118871 218.764 0.265938 216.007Z"></path>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.0098 240.689C22.2538 240.516 20.159 238.143 20.3311 235.387L34.6924 5.36699C34.8645 2.61093 37.2382 0.5162 39.9943 0.688275C42.7504 0.86035 44.8451 3.23407 44.673 5.99013L30.3117 236.01C30.1396 238.766 27.7659 240.861 25.0098 240.689Z"></path>
<path fill-rule="evenodd" clip-rule="evenodd" d="M359.005 265.781C356.246 265.661 354.108 263.326 354.229 260.567L364.747 20.5558C364.868 17.797 367.202 15.6586 369.961 15.7795C372.72 15.9004 374.858 18.2348 374.738 20.9936L364.219 261.005C364.098 263.764 361.764 265.902 359.005 265.781Z"></path>
</svg>
总结
从维护性和扩展性角度来看,mask
方法最利于维护,代码量也最少。而 SVG
滤镜方法在扩展性上更有优势,它不仅可以让其他元素复用此滤镜,还能通过修改滤镜的数值,让素材得到实时变化的效果。SVG
滤镜有着比较强大的特效能力,建议大家今后可以多关注一下,有时候也可以做出一些意想不到的效果。
以上就是本次分享的内容,希望能给大家开拓一些思路。