起点活动应用之 —— 复杂素材换色技巧

1,114 阅读4分钟

前言

我们在日常开发工作中,有时会遇到一些固化模板页面的需求。譬如设计稿样式和尺寸固化,在后台只需将主题通过配置项换色达到换肤效果,再配上不一样的头图,就可以将整个活动切换成另外一种样式风格,如下图展示:

image (21).png

配置后的几个结果页

image (22).png

image (30).png

一般情况下,这种换色并不是什么难事,简单的 CSS 样式通过 color background-color 都可以轻松实现,但本案例中遇到了一些特殊情况,我们的设计师需要保留足够的设计感,所以导出了一些看起来并没有能那么简单实现的纹理,我们在经过一番尝试后总结出了几个思路,本文将分享给大家列举。

先来看几个遇到的问题

  1. 带有杂质纹理的不规则边框

image (23).png

仔细观察一番,会发现边框有一些毛毛的细微缺口,并不是我们用常规 CSS 就能实现的边框。当时我们首先想到的是使用 SVG 导出后改色,但单个模块的边框在设计软件中导出后,达到了惊人的 1.2MB ,这么大的素材容量在页面里使用我们显然是无法接受的,何况有此模块边框的数量有 4-5 个。

放大后的边框:

image (24).png

image (25).png

  1. 带有相同风格的进度条和圆

image (26).png 推测出现这么大容量的 SVG 应该是目前设计软件对于素材的导出较为死板,没有按照前端角度进行优化所致。

几种可行性方案

  1. 素材使用 PNG,使用 CSS 实现,但兼容性较差

使用 drop-shadowborder-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>

上一张调试图便于大家理解: 演示.gif

但请注意:经过实测,iOS 版本 15 之前都有兼容性问题,移动端 safari 内核对于 filter:drop-shadow 渲染方式与其他浏览器不同,它只能渲染元素可视的部分,所以移出屏幕这个方法就行不通了,如果设置了 overflow:hidden 也会遇到相同的情况。但亲测 iOS 又在 16 版本修复掉了这个问题,看来官方自己也意识到了。

image (27).jpeg

(iOS 14-15 不渲染的效果)

  1. 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 上抽离成配置项改色。

  1. 背景混合模式(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%;
}

但是有一些小限制:那就是素材必须是黑色,元素需要设置一个浅色的底色,不然混合模式就会有问题。 输出结果:

image (28).png

  1. 本案例中的复杂型素材,通过 SVG 滤镜实现

对于复杂型 SVG例如本文遇到的的杂质纹理案例。首先我们基于设计稿要做一些小改动,可以让设计师帮忙把素材重新还原成纯直线,再用代码去实现杂质纹理效果,或者有条件的也可以自己使用 SVG 路径生成工具导出这样的边框线素材。

其次,通过 feTurbulence 滤镜来实线直线中的随机噪点,在 SVG 精髓这本书的第 158 页有提到这个属性:

调整噪点.gif

感谢(公众号:前端侦探 )提供 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);
}

最后,htmlSVG 元素上的颜色也可抽离成配置项实现改色。

<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 滤镜有着比较强大的特效能力,建议大家今后可以多关注一下,有时候也可以做出一些意想不到的效果。

以上就是本次分享的内容,希望能给大家开拓一些思路。