最近学到svg滤镜就用图片结合滤镜做了个荷塘月色的效果。下面是具体的实现思路。
一、月亮
实现思路:
月亮的发光效果这里其实是画了三个圆,加上不同的滤镜叠加的效果。
1、先画一个以(100,100)为圆心,半径为70px的圆,添加模糊滤镜feGaussianBlur,实现月亮边缘的发光效果。如下所示:
2、画第二个以(100,100)为圆心,半径为60px的圆,添加线性渐变,实现黄色由深到浅的效果。两个圆叠加效果,如下所示:
3、画第三个以(100,100)为圆心,半径为60px的圆,添加镜面光照反射滤镜,实现月亮中间发光的立体效果。如下所示:
代码如下:
<svg class="wrap" width="500" height="500">
<defs>
<!-- 模糊滤镜 -->
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="8" />
</filter>
<!-- 线性渐变-->
<linearGradient id="moonColor" x1="0%" y1="0%" x2="100%" y2="80%">
<stop offset="10%" stop-color="#fbe70e" />
<stop offset="100%" stop-color="#fbfedf" />
</linearGradient>
<!-- 镜面反射 -->
<filter id="lightMoon">
<feSpecularLighting in="SourceGraphic" specularExponent="5" lighting-color="white" result="light">
<fePointLight x="65" y="70" z="30"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="light" operator="arithmetic" k1="1" k2="0" k3="0" k4="0"/>
</filter>
</defs>
<svg class="content">
<!-- 第一个圆 -->
<circle cx="100" cy="100" r="70" fill="#fbfedf" filter="url(#blur)"/>
<circle cx="100" cy="100" r="60" fill="url(#moonColor)"/>
<circle cx="100" cy="100" r="60" fill="#fbfedf" filter="url(#lightMoon)"/>
</svg>
</svg>
相关滤镜的介绍:
1、feGaussianBlur 模糊滤镜
属性:
- in:SourceGraphic 表示图形元素自身将作为 原始输入。SourceAlpha 图形元素自身将作为 原语的原始输入,只使用元素的非透明部分。
- stdDeviation:模糊量
2、feSpecularLighting 镜面光线反射滤镜
定义一个光照滤镜,需要指定以下几个因素:
- 被照射的几何物体: in 属性传入 SourceGraphic
- 光照反射类型:所用的光照反射滤镜
- 光的颜色: lighting-color属性
- 光照源: 光照反射滤镜内定义光源滤镜
- 这里只是定义了光照,如果需要作用于物体上的效果。需要通过feComposite进行图形合成。
属性:
- specularExponent:控制光源的焦点。从最后图像的焦点来看,值越大图形越亮。
- surfaceScale:定义图形基底的高度。可以用来控制图形与光源的距离。可填任意数字,默认值为1
- specularConstant:控制镜面反射率。值越高,镜面反射越明显。从图像结果来看,值越高颜色越接近原色,越低颜色越白(无论原图像和光的颜色是什么。可填任意正数,默认值为1
- result:滤镜的名称,用于之后其他滤镜上引用(所有滤镜中都有该属性)
3、fePointLight 光点效果
x,y指的是在光源中心在当前用户坐标系统中的位置,z指的是光源距离xy平面的距离 (距离越远光点看起来越大,类似手电筒照墙面的效果)
4、feComposite 合成滤镜
光照滤镜一般情况下需要与feComposite一起使用然后使得引用该滤镜的元素产生一些光照的效果,一般在与feComposite合作的时候使用的是feComposite的operator='arithmetic'的时候的混合算法,此时feComposite中的k1,k2,k3,k4将可以设置并使得滤镜起作用。镜面反射(feSpecularLighting)设置为:k1=0;k2=1;k3=1;k4=0
二、荷花倒影以及水波效果
实现思路:这里我先用了三张图片,第一张有水波纹的图片,第二张图片是背景透明的荷花,第三张图片是倒过来的荷花,用来实现荷花的倒影。
三张图片通过位移组合,效果如下:
这时添加feTurbulence 纹理滤镜,再通过动画去改变滤镜的baseFrequency的值,就实现了水波的效果。
代码如下:
<template>
<svg class="night-wrap">
<defs>
<!-- 水波滤镜 -->
<filter id="displacementFilter">
<feTurbulence type="turbulence" baseFrequency="0.01 .1" result="turbulence" seed="23">
<animate id="ani1" attributeName="baseFrequency" from="0.007 0.09" to="0.015 0.14" begin="0s; ani2.end" dur="15s" fill="freeze" />
<animate id="ani2" attributeName="baseFrequency" from="0.015 0.14" to="0.007 0.09" begin="ani1.end" dur="15s" fill="freeze"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="turbulence" scale="20" xChannelSelector="R" yChannelSelector="R" />
</filter>
</defs>
<svg>
<!-- 水波、倒影-->
<g class="ripple" filter="url(#displacementFilter)">
<!-- 水图片-->
<image class="ripple-img" xlink:href="../assets/01.png" width="100%" height="100%" x="-10px" y="190px"/>
<!-- 花倒影图片-->
<image xlink:href="../assets/02.png" width="500" height="200" x="50px" y="320px"/>
</g>
</svg>
</svg>
</template>
<style scoped>
.night-wrap {
width: 500px;
height: 660px;
background: #04191c;
}
</style>
相关滤镜的介绍:
1、feTurbulence 纹理滤镜
相关属性:
- baseFrequency(默认值:0):噪声的频率。频率越大,相同显示区域下可以显示的噪声就越密集。baseFrequency属性可以接受两个值,这两个值分别会为x轴和y轴上的基础频率,由此,我们可以生成在某一个方向拉伸的噪声。
- seed (默认值:0):伪随机数生成器的起始编号,不同seed生成效果。
- type (默认值:turbulence):把位于同一个子集的两个功能合并在一个滤镜里,type的取值是turbulence和fractalNoise。turbulence类型时图像会有一些尖锐效果,形似湍流。fractalNoise则是在原来的噪声中叠加白噪声,让最终的结果呈现出高斯模糊的效果。
2、feDisplacementMap
位置替换滤镜,就是改变元素和图形的像素位置的。该滤镜主要是对图形进行形变,扭曲,液化
属性:
- in:in表示输入的原始图形
- in2:in2表示用来映射的图形
- scale: 值越大,偏移越大
- xChannelSelector:x轴的使用的偏移颜色可取值R,G,B,A。
- yChannelSelector:y轴的使用的偏移颜色可取值R,G,B,A。
3、animate
- attributeName:定义发生变化的元素属性名
- from:变化属性的初始值
- to:变化属性的结束值
- begin:动画开始时间,单个值 beigin="3s":3秒后开始; 多个值:beigin="3s, 6s , 8s": 3秒开始一次,6s开始一次...动画没有执行完也会重头重新执行
- dur:定义动画的所需时间
- fill:可选值,freeze,remove,默认remove,freeze:动画终止时,发生变化的元素属性停留在动画终止时的状态,remove,动画终止时,发生变化的元素属性回复到动画起始时的状态。
三、兔子和船
兔子和船是用svg画的,这样方便缩小和放大以及组合。 核心代码:
<svg class="night-wrap">
<defs>
<g id="boat-rabbit">
<boat :width="200" :height="100" x="100px" y="440px"/>
<rabbit :width="100" :height="100" x="150px" y="412px" />
</g>
</defs>
<svg>
<use xlink:href="#boat-rabbit" x="60px" y="-100px">
<animateMotion dur="8s" repeatCount="indefinite" path="M 0, 20 L 10 30 z" />
</use>
</svg>
</svg>
这里用g标签包裹兔子和船主要是为了方便同时加动画效果。use标签引用boat-rabbit,包裹animateMotion标签,path属性主要是船摆动的路径,让船有上下摆动的效果,实现船和兔子也随着水波荡漾效果。
鱼也是同样是方式,但是鱼用的是背景透明的图片,给鱼添加了一点透明效果,加了路径动画,以及水波滤镜,就实现鱼在水中游动的效果。
以上的代码都只是核心部分,完整的代码:点击查看