页面上两张照片,每张中间有一条可拖动的竖线:线左边是原图,线右边只保留一种颜色,其余全部变成黑白。照片里的红裙、红大衣依然鲜艳,背景的石板、树影、墙面则褪成灰调。竖线可以直接拖,也可以在图片上任意位置点一下让它跳过去。页面底部还有一根彩虹渐变的滑杆,拖动它,被保留下来的那个颜色会实时变成别的色相——红裙可以变成蓝裙、绿裙,而背景始终保持黑白。
这不是提前做好的两张图叠在一起,也没有用 Canvas 逐像素处理。整个效果由一条 SVG 滤镜链实时完成,图片仍然是普通的 img 标签,右键仍能正常保存。
这套局部保色特效的四个部分
颜色筛选靠一个颜色矩阵。矩阵最后一行把红、绿、蓝三个分量按不同权重加权到透明度上,于是只有满足特定色彩关系的像素才被保留,其余被判为“背景”。
分割线是一层平铺出来的遮罩。滤镜里用透明度变换配合平铺生成一条竖向的选区,右侧内容据此与去色版本合成,分割位置由一个 CSS 变量控制,拖动滑杆就是在改这个变量。
色相旋转是滤镜里的一个节点。脚本把底部滑杆的值直接写进色相旋转滤镜的属性,浏览器立即重算,无需重绘图片。
滑杆被做成了图片上的“把手”。原生 range 控件的滑块被样式改造成一个带左右箭头的圆环,轨道被改造成那条竖线,控件本体关掉了指针事件,只有滑块可点——这样图片的右键菜单不受影响。
三处硬功夫
其一,那个颜色矩阵才是核心。它的最后一行大致是这样的权重:
8 -9 -9 0 0
含义是:新的透明度等于红分量乘以正权重,再减去绿和蓝的贡献。红色多而绿蓝少的像素得到高透明度值(被选中),灰色像素三分量接近相等因而相互抵消(不被选中)。这一行就是“选红色”的全部逻辑。想改成选蓝色,把权重的位置换一换即可,比在脚本里逐像素判断快得多,也不受图片跨域限制。
其二,滤镜里有一堆为浏览器差异准备的补丁。源码在生成选区之后先做了一次偏移叠加,再做腐蚀与膨胀。注释说得很直白:单靠腐蚀加膨胀本来就能同时消除发丝般的细线并填补缝隙,但在极小缩放下某些浏览器会先执行腐蚀,导致整幅画面消失,所以必须先叠一层偏移垫底。这类经验在文档里查不到,是实测出来的。
其三,控件的可用性被认真处理过。滑块用重复的锥形渐变画出左右两个三角箭头,再用三层遮罩做差集把多余部分抠掉;滑杆本体设为不可点击、滑块单独恢复可点击,从而保证点击穿透到图片上——脚本里那段监听点击并换算百分比的代码,就是为了让“点图片任意处,线跳过去”也能工作。
拿到源码后能改什么
换图片只改路径,另外每张图上还写了两个内联变量:宽高比与裁剪定位,用来控制显示区域,换成竖图时记得同步调整。
要保留的颜色由那个颜色矩阵决定。最快的调法是先用底部的色相滑杆试出你想要的效果,再把对应的角度写死成滤镜的初始值。若原图里的目标颜色不够纯,可以适当加大矩阵的权重差值,让判定更严格。
分割线的样式(粗细、颜色、把手大小)都在滑杆相关的样式规则里,把手直径由字号推导。底部彩虹滑杆的渐变用了较新的色彩插值语法,能画出完整的一圈色相环,这段可以单独拿去复用在任何取色控件上。
运行提示:效果依赖 SVG 滤镜与较新的 CSS 颜色函数,Chrome、Edge、Firefox、Safari 近年版本均可运行,但不同浏览器对滤镜的实现细节有差异,源码中已针对性地做了处理。页面可直接双击 index.html 打开,无需服务器。详细运行方法请参见压缩包内的帮助文档。
关于局部保留色彩的常见问题
可以保留绿色或蓝色吗?
可以。调整颜色矩阵最后一行三个权重的分配即可,正权重给你要保留的通道,另外两个给负值。也可以直接用底部的色相滑杆实时试。
图片必须是本地的吗?
不必。SVG 滤镜作用在渲染结果上,不需要读取像素数据,因此不受跨域画布污染的限制——这是它相对 Canvas 方案的一大优势。
移动端能拖吗?
能。用的是原生的 range 控件,触摸拖动天然支持;点击图片跳转分割线的逻辑在触摸屏上同样有效。窄屏下布局会自动从并排改为堆叠。
为什么某些浏览器里分割线附近有一条细缝?
这正是滤镜链中偏移叠加与形态学操作要解决的问题。若你修改了滤镜结构后重新出现细缝,检查是否保留了偏移叠加那一步,以及腐蚀与膨胀的半径是否匹配。
它适合用在哪里
“前后对比”是它最直接的用途:修图与调色服务的效果展示、产品翻新前后对比、医美或家装的案例页。另一类用途是视觉聚焦——在长图或首屏里,用局部保色把观众的视线引到主体上。颜色矩阵、选区生成、跨浏览器补丁与控件改造的完整写法都在源码里,注释非常详细,下载后可查看。