如何使用CSS混合模式和SVG来动态更改产品图片的颜色

今天跟大家分享一个与业务场景紧密结合的案例,即动态更改产品图片的颜色,它非常适用于诸如美妆的应用中。我曾在《现代 CSS》小册的《CSS 图像处理与特效指南》和《深入浅出 SVG》小册的《实战篇:SVG 与 Web 开发之使用 CSS 混合模式增强 SVG 图形》中介绍了多种不同方式实现动态更改产品图片颜色的技术方案。今天,在这里,我将向大家分享如何使用 CSS 混合模式和 SVG 来实现。如果感兴趣,请继续往下阅读:

使用CSS混合模式和SVG来改变沙发颜色案例

我们接下来要实现的效果如下:

Demo 地址:codepen.io/kylewetton/...

是不是很有意思。其实在实际场景中也有类似的一些效果,比如一些美妆应用:

你需要具备的基础知识

如果希望顺利的实现上面示例的效果,那么需要具备一点点基础知识。比如CSS的混合模SVG等。使用CSS的混合模式不同的属性值,我们可以轻易的改变一张图片的效果:

CSS混合模式还能实现很多其他的效果,这里就不阐述了。除此之外,你还需要会点扣图的技巧。不过这一点,我想对于前端来说应该不是难题。

如何实现给沙发换肤

接下来,我们就实战一下。该案例非常简单,在HTML中有三个部分:

  • 有一坨SVG代码,看上去密密麻麻,对于不了解SVG的同学肯定会带来一种恐惧感(亲,莫慌)

  • 有一张图片

  • 有几个控件,可以让用户选择颜色

简单的分析一下,你看到的一坨SVG代码:

ini 复制代码
<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代码实际的效果如下:

第二部分有一张原图(也就是所说的产品图):

第三部分是用于控制颜色的一些控件:

xml 复制代码
<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代码如下:

arduino 复制代码
<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代码

URL: jakearchibald.github.io/svgomg/

点击"Open SVG"导入刚下载下来的.svg文件:

调整右侧面板参数来优化SVG代码:

优化后的SVG代码会要更干净的多:

arduino 复制代码
<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���的话,大至看到的效果就如下:

Demo 地址:codepen.io/airen/full/...

如果你感兴趣的话,可以按上面的操作步骤,将沙发换成你想要的图片。

把图片引入进来

按下面的模式,将图片引入进来,并添加一些CSS代码,让SVG盖在图片之上:

ini 复制代码
<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 href="#a"/>
    </svg>
    <img src="/pathto/couch-8_xrckc2.png" />
</div>

注意,上面的代码把path放在了defs元素中,同时给path显式设置了一个id值为a,并且使用usehref="#a"引用path。另外,借助CSS自定义属性,让后面的事情变得简单一点。

  • pathfill中使用var()引用声明好的自定义属性--fillColor

  • 在CSS中:root{}选择器显式声明--fillColor的值,比如red

这样可以非常容易的修改path的填充色:

css 复制代码
:root {
    --fillColor: red;
}

.container {
    position: relative;
    transform: scale(.66);
    svg {
        position: absolute;
        top: 0;
        left: 0;
    }
}

改变--fillColor的值,就能改变path的填充色:

离目标越来越近了。

在SVG路径上添加CSS混合模式

接着我们在SVG上添加CSS的混合模式相关的样式:

css 复制代码
svg {
    mix-blend-mode: multiply;
}

这里使用了mix-blend-mode属性,设置其属性值为multiply。接着你再修改--fillColor的值时,沙发的颜色也会跟着修改:

上面看到的效果是手动的修改--fillColor的值。我们可以在页面中添加一个控件,可以让事情变得简单的多

javascript 复制代码
const inputEle = document.querySelector('input')

document.documentElement.style.setProperty('--fillColor', inputEle.value);

inputEle.addEventListener('change', (e)=>{
    document.documentElement.style.setProperty('--fillColor', e.target.value);
})

你将看到的效果如下:

Demo 地址:codepen.io/airen/full/...

注意,这里我们演示的是 mix-blend-mode 属性值为 multiply 的效果,但这并不意味着,只能将 mix-blend-mode 属性设置为 multiply ,你也可以设置为其他值,这些值会根据你输入的填充颜色,动态改变产品图颜色。请尝试着调整下面示例中的混合模式,查看产品图颜色的变化:

Demo 地址:codepen.io/airen/full/...

使用类似的原理,你还可以实现更复杂的效果:

Demo 地址:codepen.io/airen/full/...

看到上面的效果,是否有点成就感。感兴趣的话,不妨尝试一下。

小结

实现该效果主要运用了 CSS的混合模式mix-blend-mode: multiply;SVG的path来扣出符合你需要的填充物。并且将SVG盖在你的产品图片上(注意,定位很重要,如果不吻合,效果就会不佳)。另外还采用了CSS的自定义属性,较好的来控制颜色的更改。是不是很简单,你掌握了?


如果你觉得该教程对你有所帮助,请给我点个赞。要是你喜欢 CSS ,或者想进一步了解和掌握 CSS 相关的知识,请关注我的专栏,或者移步阅读下面这些系列教程:

相关推荐
m0_7482463526 分钟前
前端通过new Blob下载文档流(下载zip或excel)
前端·excel
半糖112238 分钟前
将本地项目提交到远程仓库
前端·vue.js
web150850966415 小时前
【React&前端】大屏适配解决方案&从框架结构到实现(超详细)(附代码)
前端·react.js·前端框架
理想不理想v5 小时前
前端项目性能优化(详细)
前端·性能优化
CodeToGym5 小时前
使用 Vite 和 Redux Toolkit 创建 React 项目
前端·javascript·react.js·redux
Cachel wood6 小时前
Vue.js前端框架教程8:Vue消息提示ElMessage和ElMessageBox
linux·前端·javascript·vue.js·前端框架·ecmascript
桃园码工7 小时前
4_使用 HTML5 Canvas API (3) --[HTML5 API 学习之旅]
前端·html5·canvas
桃园码工7 小时前
9_HTML5 SVG (5) --[HTML5 API 学习之旅]
前端·html5·svg
人才程序员8 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
m0_548514778 小时前
前端三大主流框架:React、Vue、Angular
前端·vue.js·react.js