惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
Spread Privacy
Spread Privacy
T
Threatpost
C
Cisco Blogs
P
Palo Alto Networks Blog
AI
AI
Cyberwarzone
Cyberwarzone
NISL@THU
NISL@THU
P
Privacy & Cybersecurity Law Blog
G
GRAHAM CLULEY
Simon Willison's Weblog
Simon Willison's Weblog
T
Tor Project blog
Latest news
Latest news
AWS News Blog
AWS News Blog
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
WordPress大学
WordPress大学
Vercel News
Vercel News
S
Securelist
爱范儿
爱范儿
J
Java Code Geeks
Know Your Adversary
Know Your Adversary
S
Schneier on Security
Hugging Face - Blog
Hugging Face - Blog
F
Fortinet All Blogs
Last Week in AI
Last Week in AI
D
DataBreaches.Net
宝玉的分享
宝玉的分享
D
Darknet – Hacking Tools, Hacker News & Cyber Security
MongoDB | Blog
MongoDB | Blog
Engineering at Meta
Engineering at Meta
K
Kaspersky official blog
美团技术团队
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
量子位
博客园_首页
Attack and Defense Labs
Attack and Defense Labs
S
Secure Thoughts
Google Online Security Blog
Google Online Security Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
腾讯CDC
T
Threat Research - Cisco Blogs
雷峰网
雷峰网
有赞技术团队
有赞技术团队
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
S
Security Affairs

张鑫旭-鑫空间-鑫生活

1分钟速度了解text-align match-parent声明 « 张鑫旭-鑫空间-鑫生活 光标的形状也能设置了,就是CSS caret-shape属性 « 张鑫旭-鑫空间-鑫生活 pretext与文字四面环绕效果的实现 « 张鑫旭-鑫空间-鑫生活 JSON.rawJSON方法的作用是什么? « 张鑫旭-鑫空间-鑫生活 如何使用CSS判断鼠标从哪个方向进入元素? « 张鑫旭-鑫空间-鑫生活 借助mediabunny纯JS实现视频水印、剪裁、合成等功能 « 张鑫旭-鑫空间-鑫生活 务必谨慎使用JS WeakRef弱引用 « 张鑫旭-鑫空间-鑫生活 CSS六边形头像的实现与蜂巢布局 « 张鑫旭-鑫空间-鑫生活 CSS corner-shape与背景底纹技术 « 张鑫旭-鑫空间-鑫生活 浅学WebTransport API:下一代Web双向通信技术 « 张鑫旭-鑫空间-鑫生活 HTML dialog元素新支持了closedBy属性 « 张鑫旭-鑫空间-鑫生活 HTML interestfor属性与悬停popover交互效果 « 张鑫旭-鑫空间-鑫生活 CSS3模拟window7炫酷界面效果展示 « 张鑫旭-鑫空间-鑫生活 图片旋转+剪裁js插件(兼容各浏览器) « 张鑫旭-鑫空间-鑫生活 纯js无图片无外链CSS图片幻灯片播放插件 « 张鑫旭-鑫空间-鑫生活 CSS border三角、圆角图形生成技术简介 « 张鑫旭-鑫空间-鑫生活 CSS “渐进增强”在web制作中常见应用举例 « 张鑫旭-鑫空间-鑫生活 让IE6/IE7/IE8浏览器支持CSS3属性 « 张鑫旭-鑫空间-鑫生活 jQuery – 高亮动态显示页面HTML代码插件 « 张鑫旭-鑫空间-鑫生活 翻译 – 逐渐消失的Flash网站 « 张鑫旭-鑫空间-鑫生活 CSS reset的重新审视 – 避免样式重置 « 张鑫旭-鑫空间-鑫生活 新浪微博插入话题后部分文字选中的js实现 « 张鑫旭-鑫空间-鑫生活 JavaScript实现http地址自动检测并添加URL链接 « 张鑫旭-鑫空间-鑫生活 CSS实现兼容性的渐变背景(gradient)效果 « 张鑫旭-鑫空间-鑫生活 CSS content内容生成技术以及应用 « 张鑫旭-鑫空间-鑫生活 CSS gradient渐变之webkit核心浏览器下的使用 « 张鑫旭-鑫空间-鑫生活 CSS渐变之CSS3 gradient在Firefox3.6下的使用 « 张鑫旭-鑫空间-鑫生活 区分IE8/IE7/IE6及其他浏览器-CSS “\9″hack « 张鑫旭-鑫空间-鑫生活 CSS实现跨浏览器兼容性的盒阴影效果 « 张鑫旭-鑫空间-鑫生活 CSS3&HTML5各浏览器支持情况一览表 « 张鑫旭-鑫空间-鑫生活 翻译 – CSS Sprites:实用技术还是生厌之物? « 张鑫旭-鑫空间-鑫生活 纯CSS实现易拉罐3D滚动效果 « 张鑫旭-鑫空间-鑫生活 纯CSS实现侧边栏/分栏高度自动相等 « 张鑫旭-鑫空间-鑫生活 CSS+JavaScript实现页面不同布局的切换 « 张鑫旭-鑫空间-鑫生活 JavaScript实现最简单的拖拽效果 « 张鑫旭-鑫空间-鑫生活 自己写的无图片版jQuery zxxbox弹出框插件 « 张鑫旭-鑫空间-鑫生活 中国古代道家思想与网页重构的思考 « 张鑫旭-鑫空间-鑫生活 让所有浏览器支持HTML5 video视频标签 « 张鑫旭-鑫空间-鑫生活 纯CSS实现各类气球泡泡对话框效果 « 张鑫旭-鑫空间-鑫生活 JS HEX十六进制与RGB, HSL颜色的相互转换 « 张鑫旭-鑫空间-鑫生活 CSS实现兼容性的渐变、高光等文字效果 « 张鑫旭-鑫空间-鑫生活 去除冗余 – 精简您的CSS样式代码 « 张鑫旭-鑫空间-鑫生活 对overflow与zoom”清除浮动”的一些认识 « 张鑫旭-鑫空间-鑫生活 jQuery Pagination Ajax分页插件中文详解 « 张鑫旭-鑫空间-鑫生活 absolute绝对定位的非绝对定位用法 « 张鑫旭-鑫空间-鑫生活 网页布局思想浅议-淘宝新版首页为实例 « 张鑫旭-鑫空间-鑫生活 回流与重绘:CSS性能让JavaScript变慢? « 张鑫旭-鑫空间-鑫生活 CSS float浮动的深入研究、详解及拓展(二) « 张鑫旭-鑫空间-鑫生活 CSS float浮动的深入研究、详解及拓展(一) « 张鑫旭-鑫空间-鑫生活 60个极富创造性的震撼的电影海报设计 « 张鑫旭-鑫空间-鑫生活 jQuery Lightbox(balupton版)图片展示插件讲解 « 张鑫旭-鑫空间-鑫生活 博客内嵌flash音乐播放器代码、下载与使用 « 张鑫旭-鑫空间-鑫生活 CSS3 animate实现图片墙3D翻转效果 « 张鑫旭-鑫空间-鑫生活 图片旋转效果的一些研究、jQuery插件及实例 « 张鑫旭-鑫空间-鑫生活 CSS3 border-image详解、应用及jQuery插件 « 张鑫旭-鑫空间-鑫生活 jQuery-两款不同原理的圆角插件讲解 « 张鑫旭-鑫空间-鑫生活 页面可用性之outline轮廓外框的一些研究 « 张鑫旭-鑫空间-鑫生活 CSS3 transition实现超酷图片墙动画效果 « 张鑫旭-鑫空间-鑫生活 HTML CSS列表元素ul,ol,dl的研究与应用 « 张鑫旭-鑫空间-鑫生活 jQuery图片文本滚动切换插件jCarousel中文翻译与详解 « 张鑫旭-鑫空间-鑫生活 酷酷的jQuery鼠标悬停图片放大切换显示效果 « 张鑫旭-鑫空间-鑫生活 IE6下z-index犯癫不起作用bug的初步研究 « 张鑫旭-鑫空间-鑫生活 checkbox复选框的一些深入研究与理解 « 张鑫旭-鑫空间-鑫生活 jQuery之使用slideToggle实现垂直下拉菜单 « 张鑫旭-鑫空间-鑫生活 jQuery-innerfade内部列表自动淡入淡出插件 « 张鑫旭-鑫空间-鑫生活 jQuery-火焰灯效果导航菜单 « 张鑫旭-鑫空间-鑫生活 IE下css bug集合-翻译自haslayout.net « 张鑫旭-鑫空间-鑫生活 翻译-IE7/8@font-face嵌入字体与文字平滑 « 张鑫旭-鑫空间-鑫生活 jQuery-马化腾产品设计与用户体验的一些技术实现 « 张鑫旭-鑫空间-鑫生活 像素的世界及其在web开发制作中的应用 « 张鑫旭-鑫空间-鑫生活 css行高line-height的一些深入理解及应用 « 张鑫旭-鑫空间-鑫生活 CSS实现圆角六色渐变自适应按钮详解 « 张鑫旭-鑫空间-鑫生活 我熟知的三种三栏网页宽度自适应布局方法 « 张鑫旭-鑫空间-鑫生活 jQuery照片图像剪裁插件Jcrop中文翻译详解 « 张鑫旭-鑫空间-鑫生活 Ajax Upload多文件上传插件翻译及中文演示 « 张鑫旭-鑫空间-鑫生活 告别图片—使用字符实现兼容性的圆角尖角效果beta版 « 张鑫旭-鑫空间-鑫生活 大小不固定的图片、多行文字的水平垂直居中 « 张鑫旭-鑫空间-鑫生活 jQuery boxy弹出层对话框插件中文演示及讲解 « 张鑫旭-鑫空间-鑫生活 JavaScript实现新浪微博文字放大显示动画效果 « 张鑫旭-鑫空间-鑫生活 翻译:让网络更快一些——最小化浏览器中的回流(reflow) « 张鑫旭-鑫空间-鑫生活 jQuery之addClasas与removeClass使用实例 « 张鑫旭-鑫空间-鑫生活 关于Google圆角高光高宽自适应按钮及其拓展 « 张鑫旭-鑫空间-鑫生活 JavaScript实现图片幻灯片滚动播放动画效果 « 张鑫旭-鑫空间-鑫生活 jQuery-鼠标经过显示大图并跟随鼠标效果插件 « 张鑫旭-鑫空间-鑫生活 关于gif图片(或png8)杂边锯齿的问题 « 张鑫旭-鑫空间-鑫生活 对html与body的一些研究与理解 « 张鑫旭-鑫空间-鑫生活 鲜为人知的一个解决兼容性问题的利器——小数 « 张鑫旭-鑫空间-鑫生活 搜狐白社会似iphone短信对话框效果的优化 « 张鑫旭-鑫空间-鑫生活 关于文字内容溢出用点点点(…)省略号表示 « 张鑫旭-鑫空间-鑫生活 jquery之append与insertBefore使用实例 « 张鑫旭-鑫空间-鑫生活 jQuery之图片关联伸缩效果 « 张鑫旭-鑫空间-鑫生活 jquery之图片左右切换动画效果 « 张鑫旭-鑫空间-鑫生活 jQuery之replace字符串替换实现不同尺寸图片切换 « 张鑫旭-鑫空间-鑫生活 jQuery-实现图片的放大镜显示效果 « 张鑫旭-鑫空间-鑫生活 jQuery-单击文字或图片内容放大显示效果插件 « 张鑫旭-鑫空间-鑫生活 reflection.js-实现图片投影倒影效果js插件 « 张鑫旭-鑫空间-鑫生活 jQuery-很酷的弹出层效果js插件 « 张鑫旭-鑫空间-鑫生活 复选框单选框与文字对齐问题的研究与解决 « 张鑫旭-鑫空间-鑫生活 css margin的相关属性,问题及应用 « 张鑫旭-鑫空间-鑫生活 IE6下png背景不透明问题的综合拓展 « 张鑫旭-鑫空间-鑫生活
深入理解SVG feDisplacementMap滤镜及实际应用 « 张鑫旭-鑫空间-鑫生活
张鑫旭,zhangxinxu · 2017-12-14 · via 张鑫旭-鑫空间-鑫生活

by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=6626
本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。

补充于2018-05-07
应该就是最近一两个版本的Chrome吧,居然feDisplacementMap滤镜实时效果都没了,不支持了,匪夷所思,Firefox还是有的。
补充于2019-04-19
Chrome浏览器现在又支持良好了,不知道哪个版本修正了这个问题。

一、先看一个feDisplacementMap滤镜效果

在Chrome或者Firefox浏览器下点击下面的按钮或者图片,就会看到,从点击位置开始,有水波荡漾的特效:

上面的按钮和图片就是普通的<button><img>元素,也就意味着基本上这个效果可以作用于几乎任何的HTML元素,适用场景非常广泛。

如何应用上面波纹效果呢?

本文后面会介绍

//zxx :如果对效果感兴趣,对feDisplacementMap不感兴趣,可以直接点击上面文字链接定位到下面。

二、SVG feDisplacementMap滤镜简介

CSS3中有很多基本的滤镜,例如模糊,下阴影,反相,灰度,亮度控制等等,使用非常方便,以至于Chrome和Firefox等浏览器(目前Safari 11并未支持)的canvas也增加了同语法的filter API,例如:

context.filter = "blur(15px)";

很棒!然而,对于SVG滤镜,总要一分为二得看待。

首先,CSS3支持的所有这些滤镜SVG都是可以实现的,注意自己的措辞,“可以实现”。但是如果要问容不容易实现呢,那和CSS比,那就是雅思跟四级的区别,除了高斯模糊等少数几个滤镜,其他滤镜效果只要自己重新进行组合定制。

这并不是说SVG滤镜不好,而是本身定位和策略的差异。

SVG滤镜提供的是更基础,更底层的控制手段;而CSS的这些滤镜是看成是经过封装处理后暴露的高度定制的API。你可以看成是原生JavaScript和jQuery的区别。

例如SVG滤镜可以对特定通道颜色进行细致的处理,比方说把图片里面所有红色全部去掉。但是在CSS中,目前却没有这样的功能。

所以SVG滤镜要更加强大,更加灵活,虽然学习成本有点高,但是一旦熟练掌握,深入了解,我们就能自己创造出很多非常精湛的效果。尤其现在现代浏览器下普通HTML元素也能直接使用CSS filter属性应用SVG滤镜,这是说我们的web交互动效可以更上一层楼。可见好好学习SVG还是很有价值和前景的。

不如就先从本文的feDisplacementMap滤镜开刀。

SVG本质是XML,和HTML元素同宗,因此,其所有的滤镜都是使用标签和属性实现的。所以,这里的feDisplacementMap滤镜实际上指的是<feDisplacementMap>元素。

在SVG中,所以的滤镜元素标签都是以字母fe打头,例如高斯模糊滤镜<feGaussianBlur>等。

配合一些SVG元素属性,就能实现我们想要的滤镜效果了。

实际上,学习SVG滤镜难的并不是这些属性的掌握和了解,而是并不知道滤镜本身是个什么东西,作用是什么?原理是什么?

例如这里的feDisplacementMap,看名称这么长,吓都吓死了,谁还敢学?

feDisplacementMap滤镜简介

feDisplacementMap实际上是一个位置替换滤镜,就是改变元素和图形的像素位置的。

map含义和ES5中数组的map方法是一样的,遍历原图形的所有像素点,使用feDisplacementMap重新映射替换一个新的位置,形成一个新的图形。

因此,feDisplacementMap滤镜在业界的主流应用是对图形进行形变,扭曲,液化。

例如本文实现的波纹效果,实际上就是将原始图形按照水波的波纹形状进行扭曲实现。

而这个水波效果的SVG滤镜实际上就一小撮代码,你往页面上任意位置一放,CSS就这么一应用,效果就出来了。SVG和CSS代码如下:

<svg style="position:absolute;height:0;clip:rect(0 0 0 0);">
    <defs>
        <filter id="filter-ripple">
            <feImage xlink:href="./map.png" x="0" y="0" width="512" height="512" result="ripple"></feImage>
            <feDisplacementMap xChannelSelector="G" yChannelSelector="R" color-interpolation-filters="sRGB" in="SourceGraphic" in2="ripple" scale="80"></feDisplacementMap>
            <feComposite operator="in" in2="ripple"></feComposite>
        </filter>
    </defs>
</svg>

.element {
  filter: url(#filter-ripple);
}

哈哈,是不是有种看天书的感觉,不要紧张,其实都是纸老虎。

三、SVG feDisplacementMap滤镜深入了解

feDisplacementMap对图形进行位置隐射,那一定有一个映射公式,只要我们理解这个映射公式,那基本上你掌握就不远了。

公式如下(摘自MDN文档):

P'(x,y) ← P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))

解释下:

· P'(x,y)指的是转换之后的x, y坐标。
· x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5)指的是具体的转换规则。
· XC(x,y)表示当前x,y坐标像素点其X轴方向上设置的对应通道的计算值,范围是0~1。
· YC(x,y)表示当前x,y坐标像素点其Y轴方向上设置的对应通道的计算值,范围是0~1。
· -0.5是偏移值,因此XC(x,y) - 0.5范围是-0.5~0.5YC(x,y) - 0.5范围也是-0.5~0.5
· scale表示计算后的偏移值相乘的比例,scale越大,则偏移越大。

再用一句话解释就是,根据设定的通道颜色对原图的x, y坐标进行偏移。

下面,我们回到SVG <feDisplacementMap>滤镜代码部分:

<feDisplacementMap xChannelSelector="G" yChannelSelector="R" color-interpolation-filters="sRGB" in="SourceGraphic" in2="ripple" scale="80"></feDisplacementMap>

此时,这段SVG代码就容易理解多了,一个一个来:

  • xChannelSelector对应XC(x,y),表示X轴坐标使用的是哪个颜色通道进行位置偏移。我们应该都知道,颜色有RGBA四个通道,R表示red红色,G表示green绿色,B表示blue蓝色,A表示alpha可以理解为透明度。因此,xChannelSelector属性值可以是R、G、B、A中的任意一个,默认是A,基于透明度进行位置偏移。

    假设xChannelSelector="A",请问,如果用来map映射图片像素点是完全不透明,也就是A的值是1,请问,最终映射后的图片是如何位置偏移的?

    是原地不动吗?不是的!会看到原始图片所有的像素点都往左移动了。因为此时的XC(x,y)1,套用公式,x + scale * (XC(x,y) - 0.5) = x + scale * 0.5,此时scale80。也就是原来的0,0坐标变成了40,0坐标,于是,原始图片所有点都往左移动了40像素。

    注意这里的位移逻辑,偏移为正,是反向的位移。

    什么时候图片原地不动呢,那就是图片透明度是0.5的时候,也就是50%透明。此时scale * (XC(x,y) - 0.5)的计算值是0.

  • yChannelSelectorxChannelSelector类似,只是一个是x轴(横轴)方向,一个是y轴(纵轴)方向,其它都类似,不赘述。
  • color-interpolation-filters表示滤镜对颜色进行计算时候采用的颜色模式类型。分为linearRGB(默认值)和sRGBsRGB是我们平常用的RGB颜色,因此,这里设置为sRGB方便我们理解;

    翌日更新
    测试发现目前最新版本Safari 11并不支持sRGB,只能以linearRGB渲染。

    加上目前Safari 11仅支持SVG元素滤镜。因此,若想使Safari浏览器下有完全一致的效果,需要:1. 使用SVG元素;2. linearRGB通道值转换成对应的sRGB值。

    对于单一图片元素,简单图形,可行(如最后的鲸鱼游动效果)。复杂HTML元素,建议放弃Safari。

    更多linearRGB和sRGB知识可以参见这篇文章:“了解LinearRGB和sRGB以及之间的JS相互转换”。

  • inin2都表示输入,支持的属性值也都是一样的,包括固定的属性值关键字SourceGraphicSourceAlphaBackgroundImageBackgroundAlphaFillPaintStrokePaint;以及自定义的滤镜的原始引用,例如这里的ripple,引用的就是<feImage>元素输出的result值。

    inin2有什么区别呢?

    这个需要看SVG元素的,对于<feDisplacementMap>元素,in表示输入的原始图形,in2表示用来映射的图形。

    至于它们的属性值,目前阶段,大家无需深入了解每个属性值的作用,记住使用in="SourceGraphic"就好了,也就是使用该filter元素的图形作为原始图像。in2属性值也一定是<feImage>元素的result属性值就好了。

  • scale很好理解,就是公式里面的缩放比例,可正可负,默认是0。通常使用正数值处理,值越大,偏移越大。

下面我们通过一个案例,加深对<feDisplacementMap>滤镜各个属性值的理解,主要是xChannelSelectoryChannelSelectorscale这3个属性。

假设我们的Map映射图片是下面这张图:

映射图片

左边一半颜色是RGB(255,127,127),也就是R通道值是1,G和B通道计算值是0.5,右半边则是完全透明。

套用下面的SVG代码:

<svg>
    <defs>
        <filter id="filter-ripple">
            <feImage xlink:href="./map.png" x="0" y="0" width="256" height="256" result="ripple"></feImage>
            <feDisplacementMap xChannelSelector="R" yChannelSelector="G" color-interpolation-filters="sRGB" in="SourceGraphic" in2="ripple" scale="80"></feDisplacementMap>
            <feComposite operator="in" in2="ripple"></feComposite>
        </filter>
    </defs>
</svg>
<svg width="256" height="192" style="outline:1px dotted;">
    <image xlink:href="https://image.zhangxinxu.com/image/study/s/s256/mm1.jpg" width="256" height="192" filter="url(#filter-ripple)"></image> 
</svg>

结果,当scale0的时候,效果这样:

scale为0的时候效果

scale200的时候,效果这样:

200 scale时候效果

效果表现为2个特点:

  1. 右半区域一直都是透明的。当应用feDisplacementMap滤镜的通道不是透明度时候,映射效果会把这个透明度继承过来。例如,假设map.png右半区域颜色是rgba(255,127,127,.1),则效果会是下图这样:

    有透明度时候的效果

  2. 应用滤镜的图片只是单纯的水平移动。为什么呢?

    这是因为xChannelSelector="R"yChannelSelector="G",水平方向基于"R"颜色通道进行位移,垂直方向基于"G"颜色通道进行位移。

    由于RGB(255,127,127)颜色的R值是1,G值是0.5,也就是XC(x,y)1YC(x,y)0.5,套用公式P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5)),可以得到终坐标为:P( x + scale * 0.5, y + 0),于是随着scale变化,最终图片仅在水平方向发生了移动。

眼见为实,您可以狠狠地点击这里:feDisplacementMap滤镜作用原理示意demo

更多滤镜效果展示

为了方便大家更直观感受滤镜带来的扭曲效果,我制作了很多各式各样的映射图,大家可以体验下应用滤镜后的效果。

您可以狠狠地点击这里:feDisplacementMap滤镜效果走马观花demo

某效果截图:

某滤镜应用后效果截图

白色RGB值最大255,黑色RGB值最小0,。也就是对于黑白图片而言,颜色越深,对应的图片区域越往右下方移动;颜色越浅,对应的图片区域越往左上方移动。

因此,上面人物的扭曲效果就好理解了。

什么时候图片不移动呢?

那就是我们的<feImage>颜色是50度灰的时候,RGB表示为rgb(127,127,127),16进制色值为#7f7f7f

因此,在feDisplacementMap世界里,五十度灰可以等同于“纹丝不动”的意思,例如,人被捆绑的时候是不能动的。

最终效果还与<feImage>尺寸和位置有关

假如说我们图片很小,<feImage>尺寸很大,则仅原图和<feImage>对应的像素位置会发生位移。所以,通常,我们会设置<feImage>尺寸和被应用图形元素尺寸一致,当然,这并不绝对。

四、水波特效的实现

实现水波特效难点不在于滤镜,而在于<feImage>图片。

如果对最终的水波特效要求不是很高,我们可以直接使用SVG绘制一个嵌套环形渐变,完整滤镜代码如下:

<svg width="256" height="192">
  <defs>
    <radialGradient id="rg" r=".9"> 
      <stop offset="0%" stop-color="#f00"></stop>
      <stop offset="10%" stop-color="#000"></stop>
      <stop offset="20%" stop-color="#f00"></stop>
      <stop offset="30%" stop-color="#000"></stop>
      <stop offset="40%" stop-color="#f00"></stop>
      <stop offset="50%" stop-color="#000"></stop>
      <stop offset="60%" stop-color="#f00"></stop>
      <stop offset="70%" stop-color="#000"></stop>
      <stop offset="80%" stop-color="#f00"></stop>
      <stop offset="90%" stop-color="#000"></stop> 
      <stop offset="100%" stop-color="#f00"></stop>
    </radialGradient>                        
    <rect id="witness" width="256" height="192" fill="url(#rg)"></rect>

     <filter id="filter-ripple">
          <feImage result="pict2" xlink:href="#witness" x="0" y="0" width="256" height="192"></feImage>
          <feDisplacementMap scale="10" xChannelSelector="R" yChannelSelector="R" in2="pict2" in="SourceGraphic">
       </feDisplacementMap>
    </filter>    
  </defs>
</svg>

x轴,y轴均采用红色作为映射通道,绘制的渐变环效果如下:

红色渐变环水波

然后10倍scale后的扭曲效果大概这样子:

应用简易波纹图后的效果

您可以狠狠地点击这里:简易水波扭曲demo

然后配合JS改变渐变偏移或者feDisplacementMapscale值,都能有水波效果。

然而上面实现的这个水波效果实际上是比较粗糙的,真正业界用来实现水波效果几乎都是用的下面这张<feImage>图:

业界使用水波特效图

中间区域使用五十度灰色,红色色带锥形交错,中间是黑色带。x轴,y轴采用红色和绿色通道来映射,最终形成的水波效果就非常接近于自然世界中的水波涟漪。

然而,业界通常解决方案就是使用一张PNG图片来解决问题,但会带来另外一个问题,那就是这张图片的尺寸实在是太大了,如果是512*512规格,则PNG图片大小由212K,这是非常惊人的,对于一个小小的水波特效,如果加载的资源非常大,那是性价比非常低的一件事情。而且由于这张图片色彩非常丰富,是无法进行PNG压缩的。导致无法在实际项目中大规模应用。

那有什么办法解决这个问题呢?

有!

我们可以使用canvas把这个经典的水波映射图绘制在画布上,然后转换成base64地址,赋予<feImage>元素的xlink:href属性即可!

绘制分三步:1. 50度灰色背景;2. 红绿相间锥形渐变;3. 交错暗色条纹。

具体细节不表。然后下图就是我绘制出来的最终效果:

张鑫旭使用canvas绘制映射图

于是<feImage>图片现在只需要几十行JS代码就可以得到了,实际项目中大规模应用水波特效的成本就大大降低了,基本上可以在任何项目中渐进增强使用。

项目中应用水波特效

很简单,我已经把水波特效功能写成一段JS了,ripple-min.js,直接引入就可以使用了。代码示意如下:

1. 引入JS

<script src="./ripple-min.js"></script>

2. 调用rippleEffect方法,语法为:

rippleEffect(dom);

dom表示页面上希望点击出现水波的DOM元素,就这么简单。例如下面效果使用JS就是:

rippleEffect(document.getElementById('button'));
rippleEffect(document.getElementById('img'));

水波特效基本原理
插入SVG滤镜相关代码,当点击目标元素时候,根据目标元素尺寸,确定<feDisplacementMap>元素的合适的scale大小,以及<feImage>的偏移位置和大小,借助我在这个项目(https://github.com/zhangxinxu/Tween)中的animation.js和线性运动算法,实现属性值的连续变化。

于是波纹效果达成。

水波特效兼容性
PC端,Chrome和Firefox浏览器支持很好!Safari浏览器仅仅支持SVG元素应用filter属性的效果,对于普通元素,虽然能够识别filter样式,但是渲染上是有明显问题的,元素会直接看不见,也就是并不支持。PC和移动端均作了测试,版本11,均不支持。应该以后会支持。

因此特效最大的受益网站还是PC端网页项目,因为现在很多网站七八成用户都是chrome内核。对于不支持此特效的浏览器,对原有的功能和效果并无任何影响,因此可以放心大胆使用。

ripple-min.js大小仅几K大小,对于如今的web应用而言,大小基本上可以忽略不计。可以说是一个低成本,低影响,高收益,适用广泛的交互效果。

五、其它滤镜效果的实现

知道了feDisplacementMap滤镜作用原理,我们就能在这个基础上进行创造,实现其它一些我们想实现的效果。例如:

点击此链接:https://www.cssworld.cn/

可以看到中间的鲸鱼尾巴是上下游动的。

游弋的鲸鱼

实现原理:
使用canvas绘制下图所示<feImage>图:

鲸鱼游动使用的映射图

左侧大片区域是50度灰,也就是鲸鱼的左边大部分是位置不动的,右侧是一个渐变,从灰色到rgb(255,127,0)的渐变。

这里的渐变色值rgb(255,127,0)一看就知道不是随便设置的数值,中间的G绿色为127,由于通道设置为xChannelSelector="G" yChannelSelector="B",也就是X轴使用绿色位移,由于正好127色值为0.5,所以鲸鱼水平方向是没有任何移动的。但垂直方向采用的通道是B,随着渐变进行,从127->0,也就是越往尾部,其上下偏移越大,于是实现视觉上的尾巴上下游动效果。

六、结束语

本文就是最近相关知识研究的一点小心得。

如何在项目中应用水波效果,这个是即插即用的,当下的就能带来收益。

至于feDisplacementMap相关知识,是留给后来人的,那些致力于在图形交互领域有所成长的小伙伴。

feDisplacementMap滤镜的原理和特性在不同的语言环境中,其实都是相通的,例如WebGL中有类似滤镜,以后,很有可能CSS也会类似功能的属性。

怎么讲呢!图形图像领域的基础知识吧!

感谢阅读,行文仓促,出错难免,欢迎指出!

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=6626

(本篇完)