使用 GreenSock 来制作有趣的 SVG 动画

3,616 阅读6分钟
原文链接: svgtrick.com

最近从Dribbble shot 看到@thorstenbeeck发布的一个设计效果图得到灵感。根据这个设计,使用GreenSock这个动画平台实现了一个SVG的动画效果,下面就来聊聊我是怎么实现这个动画效果的。

GreenSock

GreenSock是一个无需多介绍了,是一个专门用来开发动效的平台,并且提供了javascript版本。更多关于GreenSock的一个入门介绍可以去我整理的简明教程看看,这里就不再介绍它的基本知识。

GreenSock在制作SVG动画方面也非常强悍,并且还专门提供了用于加强SVG动画制作的相关插件,比如Greensock DrawSVG plugin。它可以实现任何SVG图形的绘制,也可以操作诸如stroke-dashoffset和stroke-dasharray等CSS属性来制作一些描边的动画效果,下面来一个简单的实例:

详细代码地址

使用SVG来绘制图形

这一步将使用SVG来实现Dribble shot上的效果图。从效果图可以看到整个图形都是由简单的一些几何图形组成的,而绘制图形正是SVG所擅长的,使用SVG来实现这样的形状非常简单。

首先来为整个图形创建一个画布。创建一个SVG元素。然后创建一个跟画布一样大的矩形元素 rect 然后使用一个圆形的 clipPath 元素来遮住整个画布使其内容只显示在圆形的遮罩内。代码如下所示:

<svg viewBox="0 0 500 500">

<g class="canvas">
<defs>
<clipPath id="circle">
<circle class="mask" cx="250" cy="250" r="100" />

</clipPath>
</defs>

<g clip-path="url(#circle)">

<rect class="bg" x="0" y="0" width="500" height="500" />

</g>
</g>
</svg>

一个基本的布局就完成了。下面是来添加一些样式。当然你可以使用行内样式或者是内联样式也可以是外链样式。


<circle class="mask" cx="250" cy="250" r="100" fill="red" />

<circle class="mask" cx="250" cy="250" r="100" style="fill: red;" />

一般样式建议使用外链的方式来引入样式,这样可以保证html的整洁以及可维护性。当然也可以使用诸如SASS等预编译语言来编写CSS,可以利用预编译语言提供的变量功能来管理颜色或者是字号等全局样式,非常方便。

由于我使用的是SASS,我把颜色都在SASS中定义好对应颜色名称的变量里面:

// Color palette

$red: #E9214F;
$burnt-sienna: #F06B4B;

$goldenrod: #F6DA71;
$scooter: #349597;
$midnight-express: #20283B;

$marzipan: #FCDC9F;
$fruit-salad: #49934E;

$goblin: #3D7C42;
$chambray: #44557E;
$port-gore: #384668;

$white: #ffffff;
$silver-sand: #BBBBBB;
// Set background color

body {
background-color: $midnight-express;

}

// Center the svg horizontally and vertically

svg {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);

width: 500px;
height: 500px;
backface-visibility: hidden;

}

// Color styles

.canvas {
.bg { fill: $scooter; }

.ground { fill: $marzipan; }

.sky1 { fill: $goldenrod; }

.sky2 { fill: $burnt-sienna; }

.sky3 { fill: $red; }

.tree-left { fill: $fruit-salad; }

.tree-right { fill: $goblin; }

.mountain-left { fill: $chambray; }

.mountain-right { fill: $port-gore; }

.mountain-top-left { fill: $white; }

.mountain-top-right { fill: $silver-sand;

}

现在整个是静态的,我们使用一点点GreenSock代码来实现一个从小到大缩放动画效果:

// 定义好要操作相关元素的变量

const svg = document.querySelector('svg')

const canvas = {

wrapper: svg.querySelector('.canvas'),
mask: svg.querySelector('.canvas .mask')

}

// 创建一个新的GASP的时间轴

const tl = new TimelineMax({

repeat: -1

})

// 使用 GASP的from方法来定义mask元素半径从0到1的缩放动画效果

tl.from(canvas.mask, 1, {

attr: {
r: 0
},
ease: Elastic.easeOut.config(3, 1)

})

几行代码,一个缩放的动画效果就完成了:

详细代码地址

绘制天空

接下来是绘制天空和地面元素。使用SVG中的矩形元素 rect 来绘制,三个用来绘制天空一个用来绘制地面:

<svg viewBox="0 0 500 500">

<g class="canvas">
<defs>
<clipPath id="circle">
<circle class="mask" cx="250" cy="250" r="100" />

</clipPath>

<clipPath id="ground">
<rect x="50" y="50" width="400" height="260" />

</clipPath>
</defs>

<g clip-path="url(#circle)">

<rect class="bg" x="0" y="0" width="500" height="500" />

<g clip-path="url(#ground)">

<rect class="sky3" x="50" y="130" width="350" height="70" />

<rect class="sky2" x="50" y="200" width="350" height="50" />

<rect class="sky1" x="50" y="250" width="350" height="50" />

</g>
<rect class="ground" x="100" y="300" width="350" height="10" />

</g>
</g>
</svg>

同样这里也需要为它们增加一点动效,首先是地面的动效,地面的动效是从下往上冒出的。

tl.from(canvas.ground, 0.5, {

autoAlpha: 0,
attr: {
y: '+=200'
},
ease: Power4.easeOut
}, 0.1)

天空的动效跟地面一样的。由于天空有3个元素,并且是依次出现而不是同从下往上冒出来,可以使用GreenSock中的 stagger 方法来实现:

tl.staggerFrom([canvas.sky1, canvas.sky2, canvas.sky3], 0.5, {

autoAlpha: 0,
skewY: 0,
attr: {
y: '+=90'
},
ease: Elastic.easeOut.config(1, 3)

}, 0.075, 0.25)

为了使动画更加细腻,做完动画后还需要调整下动画的运动曲线,可以使用GreenSock提供的一个可视化动画曲线调整工具来选择合适的动画曲线 ease visualizer

详细代码地址

下面添加地面上的树,树由两个三角形组成。因为数是一个一个挨着排列的,所以使用 g 元素来包裹树元素编成一个组,这样方便来布局:

<g class="tree tree1" transform="translate(150 255)">

<polygon class="tree-left" points="15,0 15,50 0,50" />

<polygon class="tree-right" points="15,0 15,50 30,50" />

</g>

重复复制七份同样的代码,并且使用transform来改变树的位置,从而使之依次排列。然后是使用GreenSock来编写树的动画效果,树的动画效果是从45度旋转到0度旋转的效果并且同时从0到1的缩放效果:

tl.staggerFrom([canvas.tree1, canvas.tree2, canvas.tree3, canvas.tree4, canvas.tree5, canvas.tree6, canvas.tree7], 0.5, {

rotation: 45,
scale: 0,
transformOrigin: 'bottom center',

ease: Back.easeOut.config(2, 1.55)

}, 0.05, 0.4)

效果如下所示:

详细代码地址

最后是后面的大山以及大山的动画效果。

大山由四个三角形元素组成,代码如下:

<g class="mountain" transform="translate(150 185)">

<polygon class="mountain-left" points="100,0 100,120 0,120" />

<polygon class="mountain-right" points="100,0 200,120 100,120" />

<polygon class="mountain-top-left" points="100,0 100,30 75,30" />

<polygon class="mountain-top-right" points="99,0 125,30 99,30" />

</g>

它的动画效果和树的效果差不多,只不过是把旋转的动效变成翻转的效果。

tl.staggerFrom([canvas.mountain], 0.75, {

y: '+=50',
skewX: -200,
scale: 0,
transformOrigin: 'bottom center',

ease: Back.easeOut.config(1, .2)

}, 0.0125, 0.5)

一个有趣的SVG的动画效果就完成了,代码地址使用GreenSock 配合 SVG 来制作动画效果如此简单。

原文地址,根据自己理解整理了下这个教程,主要是来学习下使用GreenSock来制作SVG动画效果的思路和方法。