如何使用CSS混合模式和SVG来动态更改产品图片的颜色
前两天在Codepen看到了@Kyle Wetton写的一个示例,使用CSS混合模式和SVG来改变沙发的颜色。非常有意思的一案例。这让我想起了在实际的一些业务场景中的运用,比如说一些美妆的应用中,就有类似的场景。不知道大家是否想深入的了解如何实现这样的效果?如果是,那么请继续往下阅读。
使用CSS混合模式和SVG来改变沙发颜色案例
下面这个Demo是来自于Codepen上@Kyle Wetton写的一个效果:
尝试着改为颜色,你会看到不同的沙发颜色:
是不是很有意思。
其实在实际场景中也有类似的一些效果,比如一些美妆应用:
如果你想了解其中的实现原理或效果,请继续往下阅读。
你需要具备的基础知识
如果希望顺利的实现上面示例的效果,那么需要具备一点点基础知识。比如CSS的混合模、SVG等。
使用CSS的混合模式不同的属性值,我们可以非常轻意的改变一张图片的效果:
CSS混合模式还能实现很多其他的效果,这里就不阐述了。
除此之外,你还需要会点扣图的技巧。不过这一点,我想对于前端来说应该不是难题。
如何实现给沙发换肤
接下来,我们就实战一下,先来分析一下@Kyle Wetton的案例。该案例非常简单,在HTML中有三个部分:
- 有一坨SVG代码,看上去密密麻麻,对于不了解SVG的同学肯定会带来一种恐惧感(亲,莫慌)
- 有一张图片
- 有几个控件,可以让用户选择颜色
简单的分析一下,你看到的一坨SVG代码:
<svg id="js-couch" class="couch__overlay" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" width="1000" height="394">
<defs>
<path d="M996.35 77.55q-1.85-1.95-8.65-3.75l-62.4-17.1q-9.3-2.75-12.15-2.5-1.8.15-2.85.45l-.75.3q2.25-16.3 3.75-22.05 1.15-4.4 1.4-10.8.2-6.6-.7-10.85-1.25-5.65-3.1-7.8-2.95-3.35-9.65-2.7-5.95.6-39.3 1.7-38.3 1.25-39.45 1.3-10.25.5-126.75.5-5.05 0-54.2 1.3-45.8 1.25-54.05.95-19.45-.45-30.4-.7-20.2-.55-23.1-1.3-22.3-5.85-26.5 1.25-2.65 4.55-3.85 7.9-.6 1.7-.7 2.5-.65-2.2-2.05-4.55-2.75-4.65-6.45-5.2-3.85-.55-13.65-.4-7.4.1-12 .4-.4.05-18.7.9-16.55.8-19.15 1.1-3.4.4-14.6 1.1-11.3.75-13.05.65h-9.8q-8.65-.05-11.45-.4-2.85-.35-9.25-.6-6.7-.15-8.5-.25-2.7-.1-27.75-.1-25.1 0-29.6.1-92.35 1.15-99 1.65-5.15.4-20 0-15.3-.4-24.4-1.25-6.75-.6-21-1.55-12.95-.9-14.85-1.1-6.45-1.05-11.05-1.5-8.7-.85-12.85.5-5.45 1.75-8.1 4.65-3.2 3.4-2.9 8.6.25 4.65 2.1 11.8 1 3.8 2.55 9.1 1 3.85 2.35 10.1-.1 1-1.5 1-1.75 0-7.7.85-7.1 1-9.8 2.05-2.4.9-23 4.75-21.2 3.9-22.05 4.15-8.2 1.85-15.05 3.35Q7.4 69.1 5.65 70.3 2.5 72.45 2 73.1.6 75 .75 79.2q.15 4.15 1.3 12.75.9 6.85 1.45 10 .5 2.75 8.55 54 6.65 42.15 7.35 46.85 1.15 7.65 4.9 28.55 4.55 25.2 6.35 31.2 2.45 8.15 3.8 11.75 1.85 4.9 3.2 5.75 1.25.8 6.85.65 2.75-.05 5.3-.25l23.85.35q.1 0 1 .95t2 .95q1.9 0 3.4-1.4l23.1-.25 43.65.4q135.05 2.15 137.9 1.9 1.25-.1 72.9.5 72.45.65 76.85.45 8.1-.35 64 .4 143.35.95 146 1.1.55.05 75.3.3 74.7.3 79.8.6 8.65.5 68.25-.35l51.75.5 1.6.4q1.95.35 3.8.05 1.45-.25 3.5-.2 1.9 0 3.35-.3 2.1-.45 8.25-.8 6.25-.3 8.75-.05 1.7.2 8 1 5.75.3 7.4-1.75 1.75-2.2 4.95-10.85 2.8-7.55 4.05-12.4.65-2.5 3.6-17.2 2.75-13.75 3.15-14.8.45-1.25 4.45-22.85 4.05-22.4 4.4-24.4.3-1.45 3.75-25.2 3.35-23.2 4-26.3 1.15-5.5 2.35-18.8 1.4-15.7.8-23.7-.6-8.35-3.35-11.15z" id="a" />
</defs>
<use xlink:href="#a"/>
</svg>
你不用太担心path
元素中的那些数字,上面的SVG代码实际的效果如下:
第二部分有一张原图(也就是所说的产品图):
第三部分是用于控制颜色的一些控件:
<div class="colours">
<div class="colours__labels">
<span>Sofa</span>
<span>Gradient</span>
<span></span>
</div>
<div class="colours__inputs">
<input id="js-color-1" class="jscolor {onFineChange:'updateCouch(this)'}" value="FCFF4D">
<input id="js-color-2" class="jscolor {onFineChange:'updateBackgroundD(this)'}" value="24243e">
<input id="js-color-3" class="jscolor {onFineChange:'updateBackgroundL(this)'}" value="302b63">
<button class="gen-random" onclick="generateRandom()">Generate random</button>
</div>
</div>
那究竟发生了什么呢?
简单的说,在<img>
上面盖了一个<svg>
元素,并且在希望改变颜色的部分使用SVG的path
绘制出来一个矢量形状。你只需要更改path
元素的fill
值(填充色),并使用CSS的mix-blend-mode:multiply
将该颜色上色到图像上。
使用图像编辑软件描出所需路径
假设你的产品和示例一样,是一张沙发,那么可以直接下载下图到你本地:
然后使用图像编辑软件(我使用的是Sketch软件)打开这张图片:
使用矢量工具根据沙发的边缘,像下面这样进行描图:
现在这样的描边效果有点粗糙,可以增加描点来细化:
这可是细活。
描好之后,用颜色来填充,设置一些参数就可以达到:
把SVG拷贝出来:
拷贝出来的SVG代码如下:
<svg width="1000px" height="285px" viewBox="0 0 1000 285" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M92.359375,15.1601563 C91.5665016,9.75036369 97.6468644,4.55538452 109.370117,4.46289063 C122.16228,4.36196327 141.943594,6.59233589 161.163086,7.42089844 C197.996094,9.00878906 206.387695,8.79101563 233.302734,8.79101563 C244.47692,8.79101563 262.072665,7.43774646 278.09082,7.42089844 C307.797392,7.38965284 337.814716,7.42089844 352.157227,7.42089844 C373.001805,7.42089844 393.757447,7.87557813 414.616211,7.42089844 C431.647409,7.0496521 446.838118,6.33630832 459.914063,5.78125 C478.244201,5.00315736 487.394531,3.11914063 494.027344,5.78125 C500.660156,8.44335938 501.632813,13.6982422 503.053711,13.6982422 C504.474609,13.6982422 504.904297,0.217773438 515.746094,0.217773438 C524.55517,0.217773438 531.708576,3.46640125 560.296875,4.46289063 C566.893711,4.69283354 574.527862,5.78125 583.591797,5.78125 C620.296888,5.78125 654.356171,3.85804874 690.652344,3.11914063 C702.16134,2.88484345 717.608438,3.11914063 729.430664,3.11914063 C742.116211,3.11914063 778.749023,3.11914063 794.581055,3.11914063 C810.413086,3.11914063 836.683429,2.23951661 864.150391,1.56445313 C887.247123,0.996798022 905.637349,1.20954108 907.589844,1.56445313 C923.535156,4.46289063 905.439453,50.9443359 907.589844,53.234375 C909.740234,55.5244141 914.932617,52.2695313 923.547852,55.5244141 C932.163086,58.7792969 947.413933,61.6575642 960.662109,65.3310547 C979.44094,70.5381 999,76.2663676 999,79.2919922 C999,82.2007095 999.54016,112.827542 994.683594,148.322266 C991.88052,168.808823 985.496846,191.792104 981.550781,212.458008 C974.126665,251.338788 965.831581,284.948509 960.662109,284.948509 C947.984375,284.948509 48.4257813,285.09082 36.7978516,277.898438 C25.1699219,270.706055 0.502693332,85.9480599 0.2763156,75.953125 C0.0578535434,65.9581901 18.4550781,64.6064453 36.7978516,61.3828125 C45.9721054,59.7704922 56.3347295,57.2107657 66.1601562,55.5244141 C78.6112562,53.3874145 80.1865234,51.7539062 92.359375,50.1416016 C104.532227,48.5292969 97.7890625,52.2070313 92.359375,15.1601563 Z" id="路径" fill="#0B0808"></path>
</g>
</svg>
也可以把SVG导出来:
现在的代码有点脏,我们可以对SVG代码进行优化。
使用SVGOMG优化SVG代码
这里使用在线工具SVGOMG来优化我们拷贝出来的SVG代码:
点击“Open SVG”导入刚下载下来的.svg
文件:
调整右侧面板参数来优化SVG代码:
优化后的SVG代码会要更干净的多:
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="285">
<path fill="#0B0808" fill-rule="evenodd" d="M92.359375 15.1601563c-.7928734-5.40979261 5.2874894-10.60477178 17.010742-10.69726567 12.792163-.10092736 32.573477 2.12944526 51.792969 2.95800781 36.833008 1.58789062 45.224609 1.37011719 72.139648 1.37011719 11.174186 0 28.769931-1.35326917 44.788086-1.37011719 29.706572-.0312456 59.723896 0 74.066407 0 20.844578 0 41.60022.45467969 62.458984 0 17.031198-.37124634 32.221907-1.08459012 45.297852-1.63964844 18.330138-.77809264 27.480468-2.66210937 34.113281 0 6.632812 2.66210938 7.605469 7.9169922 9.026367 7.9169922 1.420898 0 1.850586-13.48046876 12.692383-13.48046876 8.809076 0 15.962482 3.24862781 44.550781 4.24511719 6.596836.22994291 14.230987 1.31835937 23.294922 1.31835937 36.705091 0 70.764374-1.92320126 107.060547-2.66210937 11.508996-.23429718 26.956094 0 38.77832 0h65.150391c15.832031 0 42.102374-.87962402 69.569336-1.5546875 23.096732-.5676551 41.486958-.35491205 43.439453 0 15.945312 2.8984375-2.150391 49.37988277 0 51.66992187 2.15039 2.2900391 7.342773-.9648437 15.958008 2.2900391 8.615234 3.2548828 23.866081 6.1331501 37.114257 9.8066406C979.44094 70.5381 999 76.2663676 999 79.2919922c0 2.9087173.54016 33.5355498-4.316406 69.0302738-2.803074 20.486557-9.186748 43.469838-13.132813 64.135742-7.424116 38.88078-15.7192 72.490501-20.888672 72.490501-12.677734 0-912.2363277.142311-923.8642574-7.050071C25.1699219 270.706055.50269333 85.9480599.2763156 75.953125c-.21846206-9.9949349 18.1787625-11.3466797 36.521536-14.5703125 9.1742538-1.6123203 19.5368779-4.1720468 29.3623046-5.8583984 12.4511-2.1369996 14.0263672-3.7705079 26.1992188-5.3828125 12.172852-1.6123047 5.4296875 2.0654297 0-34.9814453z"/>
</svg>
可以和前面未优化的代码做个对比。
把优化后的代码要是放到Codepen上的话,大至看到的效果就如下:
如果你感兴趣的话,可以按上面的操作步骤,将沙发换成你想要的图片。
把图片引入进来
按下面的模式,将图片引入进来,并添加一些CSS代码,让SVG盖在图片之上:
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="285" preserveAspectRatio="none">
<defs>
<path fill="var(--fillColor)" fill-rule="evenodd" d="M92.359375 15.1601563c-.7928734-5.40979261 5.2874894-10.60477178 17.010742-10.69726567 12.792163-.10092736 32.573477 2.12944526 51.792969 2.95800781 36.833008 1.58789062 45.224609 1.37011719 72.139648 1.37011719 11.174186 0 28.769931-1.35326917 44.788086-1.37011719 29.706572-.0312456 59.723896 0 74.066407 0 20.844578 0 41.60022.45467969 62.458984 0 17.031198-.37124634 32.221907-1.08459012 45.297852-1.63964844 18.330138-.77809264 27.480468-2.66210937 34.113281 0 6.632812 2.66210938 7.605469 7.9169922 9.026367 7.9169922 1.420898 0 1.850586-13.48046876 12.692383-13.48046876 8.809076 0 15.962482 3.24862781 44.550781 4.24511719 6.596836.22994291 14.230987 1.31835937 23.294922 1.31835937 36.705091 0 70.764374-1.92320126 107.060547-2.66210937 11.508996-.23429718 26.956094 0 38.77832 0h65.150391c15.832031 0 42.102374-.87962402 69.569336-1.5546875 23.096732-.5676551 41.486958-.35491205 43.439453 0 15.945312 2.8984375-2.150391 49.37988277 0 51.66992187 2.15039 2.2900391 7.342773-.9648437 15.958008 2.2900391 8.615234 3.2548828 23.866081 6.1331501 37.114257 9.8066406C979.44094 70.5381 999 76.2663676 999 79.2919922c0 2.9087173.54016 33.5355498-4.316406 69.0302738-2.803074 20.486557-9.186748 43.469838-13.132813 64.135742-7.424116 38.88078-15.7192 72.490501-20.888672 72.490501-12.677734 0-912.2363277.142311-923.8642574-7.050071C25.1699219 270.706055.50269333 85.9480599.2763156 75.953125c-.21846206-9.9949349 18.1787625-11.3466797 36.521536-14.5703125 9.1742538-1.6123203 19.5368779-4.1720468 29.3623046-5.8583984 12.4511-2.1369996 14.0263672-3.7705079 26.1992188-5.3828125 12.172852-1.6123047 5.4296875 2.0654297 0-34.9814453z" id="a"/>
</defs>
<use xlink:href="#a"/>
</svg>
<img src="https://static.fedev.cn/sites/default/files/blogs/2019/1909/couch-8_xrckc2.png" />
</div>
注意,上面的代码把path
放在了defs
元素中,同时给path
显式设置了一个id
值为a
,并且使用use
的xlink:href="#a"
引用path
。另外,借助CSS自定义属性,让后面的事情变得简单一点。
- 在
path
的fill
中使用var()
引用声明好的自定义属性--fillColor
- 在CSS中
:root{}
选择器显式声明--fillColor
的值,比如red
这样可以非常容易的修改path
的填充色:
:root {
--fillColor: red;
}
.container {
position: relative;
transform: scale(.66);
svg {
position: absolute;
top: 0;
left: 0;
}
}
改变--fillColor
的值,就能改变path
的填充色:
离目标越来越近了。
在SVG路径上添加CSS混合模式
接着我们在SVG上添加CSS的混合模式相关的样式:
svg {
mix-blend-mode: multiply;
}
这里使用了mix-blend-mode
属性,设置其属性值为multiply
。接着你再修改--fillColor
的值时,沙发的颜色也会跟着修改:
上面看到的效果是手动的修改--fillColor
的值。我们可以在页面中添加一个控件,可以让事情变得简单的多:
const inputEle = document.querySelector('input')
document.documentElement.style.setProperty('--fillColor', inputEle.value);
inputEle.addEventListener('change', (e)=>{
document.documentElement.style.setProperty('--fillColor', e.target.value);
})
你将看到的效果如下:
使用类似的原理,你还可以实现更复杂的效果,比如@jasonday写的这个这个案例:
其他
看到上面的效果,是否有点成就感。实现@Kyle Wetton写的案例效果并不复杂。感兴趣的话,不仿尝试一下。
其实我们还可以将这样的一个效果封装成一个组件(比如说Vue、React这样的组件)。
小结
实现该效果主要运用了**CSS的混合模式mix-blend-mode: multiply;
**和SVG的path
来扣出符合你需要的填充物。并且将SVG盖在你的产品图片上(注意,定位很重要,如果不稳合,效果就会不佳)。另外还采用了CSS的自定义属性,较好的来控制颜色的更改。是不是很简单,你掌握了?