现代 CSS

初探GSAP

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

时至今日在Web中开发动效有很多种方式,有纯CSS的方式(AnimationsTransitions),也有Web Animation APISVG Animation,还有很多优秀的JavaScript库(比如,Anime.jsKUTE.jsMo.js等)。在WebGL方面有Three.jsPixiJSBabylon.jsPlayCanvas等。除此之外,业内还有一个制作动画非常出色的平台,那就是GSAPGreenSock Animation Platform的简称)。在接下来的日子中,将会不间断的发布一些关于GSAP制作动效相关的教程,今天这篇文章,主要和大家先探讨一些GSAP的基础知识。

W3cplus小站上关于Web动画相关的教程有很多,如果你对动画其他方面的知识感兴趣的话,可以猛击这里进行了解

GSAP是什么?

GSAP是GreenSock提供的一个制作动画的成熟的JavaScript库,该库源于Flash的动画。这也意味着GreenSock背后的人对Web动画了如指掌,这个库已经存在很长的时间了,而且不会很快消失。

GSAP中有很多工具和插件,我们可以利用它们来快速开发Web动画和快速处理开发Web动画遇到的任何挑战。除了GSAP具有强大的特性、工具和插件之外,而且学习曲线也相对较浅,因为它在所有不同的实现和插件中使用了直观且一致的语法。此外,GSAP论坛还提供了很棒的文档、教程等。

为什么选择GSAP?

文章开头就提到过,在Web中构建中动画效果有很多种方式,也有很多优秀的库或工具,那么为什么要选择GSAP呢?那是因为,GSAP有着下面几个明显的特征:

  • 速度快: GSAP专门优化了动画性能,使用实现和CSS一样的高性能动效
  • 轻量和模块化: 模块化与插件式的结构保持了GSAP核心引擎的轻量,TweenLite包非常的小。GSAP还提供了 TweenLiteTimelineLiteTimelineMaxTweenMax等功能动画模块,在使用的时候可以按需加载
  • 没有依赖: 使用GSAP开发动效时不需要依赖于任何第三方库或插件
  • 灵活控制: 不用受限于线性序列,可以重叠动画序列,你可以通过精确时间控制,灵活地使用最少的代码实现动效
  • 任何对象都可以实现动画,只要能运行JavaScript脚本的地方就可以使用GSAP

GSAP也非常的灵活,可以适用于你给它的任何东西。也就是,GSAP可以让下面这些东东都动起来:

  • CSS: CSS中的2D3D方面的transformcolorwidthopacityborder-radiusmargin等,几乎所有的CSS属性都可以
  • SVG: SVG中的一些属性,比如viewBoxwidthheightfillstrokecxropacity等,而且借助像 MorphSVGDrawSVG这样的GSAP插件,还可以实现一些高级的效果
  • 任何数值,比如<canvas>的对象,3D动画场景中的摄像机位置和滤镜值

一旦你学习了GSAP相关的基本语法,就能够在任何可运行JavaScript脚本的地方使用GSAP。如果你也比较关注DOM元素的CSS属性的动画,在使用GSAP时,还可以在React、Vue等前端框架中使用。

GSAP核心模块

GSAP有几大核心模块,它们是:

  • TweenLite:GSAP的基础,一个轻量级和快速的HTML5动画库
  • TweenMax:TweenLite的扩展,除了包括TweenLite本身之外,还包括TimelineLite、TimelineMax、CSSPlugin、AttrPlugin、RoundPropsPlugin、DirectionalRotationPlugin、BezierPlugin和EasePack
  • TimelineLite:一种轻量级的Timeline,用于控制多个Tween和(或)其他Timeline
  • TimelineMax:一个增强版的TimelineLite,它提供了额外的、非必要的功能,如repeatrepeatDelayyoyo等等

在实际使用的时候,如果你所需要的功能不是过强,可以直接使用GSAP自己内置的API(TweenLite),但如果你所需要的功能过强的话,可以根据自己的需要加载相应的功能模块。在后面介绍GSAP使用的时候,我们会详细介绍这方面。

值得注意的是,GSAP除了提供上述所列的功能模块之外,还提供了一些额外的功能(也有付费的额外功能),比如使用 DrawSVGPlugin创建线条绘制的动画效果,MorphSVHPlugin可以将一个SVG形状变形为另一个,以及其他功能。

GSAP如何工作?

接下来,我们来通过真实的实例来向大家阐述GSAP是如何工作的。

加载GSAP

如果你要使用GSAP来制作Web动画,那么首先要加载GSAP。加载GSAP的方法有很多种,其中最简单的就是加载一个CDN的地址:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.1/gsap.min.js"></script>

如果你还需要一些其他的功能,还可以点击这里,在CDN选项中选择自己所需要的功能模块

这个时候会多加载相应功能模块或插件的JavaScript脚本文件:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.1/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.1/CSSRulePlugin.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.1/EaselPlugin.min.js"></script>

这种方式和其他制作动画效果的JavaScript库相似。这样一来,就可以在你的页面中使用GSAP来开发动效了。

如果你是使用像React或Vue这样的前端框架来开发Web应用或Web页面的话,可以使用NPM或Yarn来安装:

npm install gsap

安装完gsap之后,也可以在这里的“Modules/NPM”选项中选择所需要的插件和功能模块:

然后将对应的代码复制到要使用GSAP的页面或组件中:

import { gsap } from "gsap";
import { CSSRulePlugin } from "gsap/CSSRulePlugin";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";

gsap.registerPlugin(CSSRulePlugin, DrawSVGPlugin);

如果你像我一样是GSAP的初级使用者,我更建议在Codepen上引入GSAP核心库和所需要的插件及功能模块对应的JavaScript。可以在“CodePen”选项中选择所需要的插件等,并且复制所需的脚本地址:

接下来需要在Codepen新建的页面中的JavaScript设置项中添加所复制的JavaScript文件地址:

这样一来就可以在Codepen使用GSAP开发动画效果了。

就我个人而言,我比较喜欢在Codepen上做一些小Demo的开发,方便而且易于分享。接下来的示例,一般都会在Codepen上开发和展示。

第一个GSAP制作的动画

咱们先用CSS AnimationWeb Animation API来构建一个简单的动画效果,然后使用GSAP开发出一个等同效果的动画。

比如我们用一个div绘制了一个圆形,然后给这个圆添加一个简单动效:

向右移动200px,并且移动过程中放大2

如果用CSS Animation来构建的话,会使用@keyframes来声明一个动画:

@keyframes ball {
    to {
        transform: translateX(200px) scale(2);
    }
}

.ball {
    animation: ball 2s linear both
}

效果如下:

点击示例中的“Play”按钮,你看到的动画效果如下:

上面的动画效果换成Web Animation API也不复杂:

const ballKeyframes = [
    {
        transform: 'translateX(0) scale(1)'
    },
    {
        transform: 'translateX(200px) scale(2)'
    }
]

const ballOptions = {
    duration: 2000,
    easing: 'linear',
    fill: 'both'
}

const ball = document.querySelector(".ball");
const handlePlay = document.getElementById("play");

const ballAnimation = ball.animate(ballKeyframes, ballOptions)

ballAnimation.pause();

handlePlay.addEventListener("click", () => {
    ballAnimation.play();
});

效果如下:

接下来,使用GSAP来实现上面的动画效果。实现这样的一个简单地动画效果,我们只使用GSAP的核心功能就可能了,它自身就带有一些实现动画效果的API,比如其中的gsap.to()gsap.fromTo()gsap.from()等。

const handlePlay = document.getElementById("play");

const tween = gsap.to(".ball", {
    duration: 2,
    x: 200,
    scale: 2,
    paused: true
});

handlePlay.addEventListener("click", () => {
    tween.play();
});

效果如下:

最后的效果和前面使用CSS Animation和Web Animation API是等同的:

你打开浏览器开发者工具,你会发现点击示例中的“Play”按钮之后,会发现div元素会动态加载一个行内样式style,就该示例而言是transfrom属性的值在不断的变化,最终停止在gsap.to()设置的值:

<div class="ball" style="transform: translate(200px, 0px) scale(2, 2);"></div>

你可能发现了,这个最终值和前面示例中@keyframes中的to{}中的值是相同的。

特别声明,你可能还不太了解GSAP中的gsap.to()方法的使用,但不用过于担心,阅读到后面,你会了解GSAP中内置的一些方法、属性等实际含义和作用!

GSAP基本API

GSAP库内置了一些属性和方法

有关于GSAP更多的API,可以点击这里下载

我们来看几个常用的API。

gsap.to()

在上面的示例中,用的就是gsap.to()方法:

gsap.to(".ball", {
    duration: 2,
    x: 200,
    scale: 2,
    paused: true
});

gsap.to()中的第一个参数.ball是需要动起来的元素(在该示例中,就是div.ball元素),第二个参数是一个object,该对象中是用来设置元素动画的一些属性。比如:

  • x:相当于CSS的transform: translateX(),即元素在x轴移动
  • scale:相当于CSS的transform: scale(),即元素放大或缩小
  • duration:相当于CSS的animation-duration,动画播放的持续时间
  • paused:是设置动画的状态

object中可以是控制元素样式的属性参数,也可以是控制动画的一些属性参数,比如:

x: 100                      // ──> transform: translateX(100px)
y: 100                      // ──> transform: translateY(100px)
z: 100                      // ──> transform: translateZ(100px)

scale: 2                    // ──> transform: scale(2)
scaleX: 2                   // ──> transform: scaleX(2)
scaleY: 2                   // ──> transform: scaleY(2)
scaleZ: 2                   // ──> transform: scaleZ(2)

skew: 15                    // ──> transform: skew(15deg)
skewX: 15                   // ──> transform: skewX(15deg)
skewY: 15                   // ──> transform: skewY(15deg)

rotation: 180               // ──> transform: rotate(180deg)
rotationX: 180              // ──> transform: rotateX(180deg)
rotationY: 180              // ──> transform: rotateY(180deg)
rotationZ: 180              // ──> transform: rotateZ(180deg)

perspective: 1000           // ──> transform: perspective(1000px)
transformOrigin: '50% 50%'  // ──> transform-origin: 50% 50%

上面我们看到的是CSS中transform中对应的一些CSS属性,其实在gsap.to()中可设置的属性还可以是其他的CSS属性。换句话说,可以运用于animationtransition中的CSS属性都可以运用于gsap.to()中。比如:

gsap.to('element', {
    backgroundColor: 'red',
    borderRadius: 15
})

除了在gsap.to()中可以运用改变元素特性的CSS属性之外,还可以是控制动效的一些CSS属性,比如:

duration: 2     // ──> animation-duration: 2s
delay: 1        // ──> animation-delay: 1s
ease: 'linear'  // ──> animation-timing-function: linear
repeat: 2       // ──> animation-iteration-count: 2

除了这些能和CSS属性对应上的之外,还有一些其他的属性,比如immediateRenderlazyonCompleteoverwriterepeatDelay等,有关于这方面更详细的介绍,可以查阅gsap.to()官方文档

gsap.from()

gsap.from()可以用来设置动画元素动画开始的位置(结束的位置是元素当前状态,即动画元素在屏幕上的状态)。可能这样不太好理解,我们用Animation.css中的slideInLeft动画效果:

@keyframes slideInLeft {
    from {
        transform: translate3d(-100%, 0, 0);
        visibility: visible;
    }

    to {
        transform: translate3d(0, 0, 0);
    }
}

gsap.from()就可以很好的实现一个类似的动画效果:

const handlePlay = document.getElementById("play");

const tween = gsap.from(".ball", {
    duration: 2,
    x: "-100vw",
    visibility: "visible",
    paused: true
});

handlePlay.addEventListener("click", () => {
    tween.play();
});

效果如下:

点击示例中的“Play”按钮,球会从屏幕外移进来:

注意,能用于gsap.to()的属性和方法同样可以运用于gsap.from()

gsap.from()gsap.to()

如果将两个结合起来,就能实现动画对象从哪里到哪里,其中gsap.from()是用来指定动画对象开始的状态,gsap.to()是用来指定动画对象结束的状态。比如下面这个示例:

const tween = gsap.from(".item", {
    opacity: 1,
    x: 10,
    y: -30,
    rotate: 45,
    duration: 1,
    paused: true
});

const tween2 = gsap.to(".item", {
    opacity: 1,
    x: -30,
    y: 0,
    rotate: 0,
    borderRadius: "50%",
    backgroundColor: "rgb(42 173 210)",
    duration: 4,
    delay: 1,
    paused: true
});

效果如下:

gsap.fromTo()

GSAP中还有一个gsap.fromTo()方法,它允许你定义动画对象初始状态和结束状态。相当于gsap.from()gsap.to()两个方法的结合物。比如上面的示例,我们可以用gsap.fromTo()来实现:

const tween = gsap.fromTo(".item", 
    {
        opacity: 1,
        x: 10,
        y: -30,
        rotate: 45
    },
    {
        opacity: 1,
        x: -30,
        y: 0,
        rotate: 0,
        borderRadius: "50%",
        backgroundColor: "rgb(42 173 210)",
        duration: 4,
        paused: true
    }
);


playAni.addEventListener("click", () => {
    tween.play();
});

restartAni.addEventListener("click", () => {
    tween.restart();
});

效果如下:

gasp.set()

如果你想立即设置一些属性,可以使用gasp.set()方法。其实它的本质就是一个持续时间为0gsap.to()的补间动画。比如下面两个方法,实现的动画效果是一样的:

gsap.set(".class", {x: 100, y: 50, opacity: 0.5});
gsap.to(".class", {duration: 0, x: 100, y: 50, opacity: 0.5});

gsap.defaults()gsap.config()

GSAP中的gsap.defaults()方法你设置所有补间动效所需要的参数。比如,如果你想改变所有补间动效的easeduration,可以像下面这样做:

gsap.defaults({
    ease: "power2.in", 
    duration: 1
});

但如果你在特定的补间动画中重新显式设置了与gsap.defaults()相同的属性,则会覆盖,比如:

gsap.to('.class',{
    ease: 'linear',
    duration: 2
})

有的时候,使用GSAP制作动效,需要做一些统一的配置,比如说单位的处理,那么就可以使用GSAP中的gsap.config()方法

gsap.config({
    autoSleep: 60,
    force3D: true,
    nullTargetWarn: false,
    units: { 
        x: "vw", 
        y: "vh" 
    }
});

gsap.config()主要有autoSleepforce3DnullTargetWarnunits等配置选项,有关于这几个配置详细的说明,可以查阅官方文档

gsap.timeline()

时间线(Timeline)是一个强大的时序控制工具,它包括了Tweens和时间线容器,使用它可以简单地控制多个动画之间的时间。如果没有Timeline的话,构建复杂的序列动画就会很麻烦,因为只能靠每个动效的延迟时间和持续时间来控制,比如:

gsap.to("#id", {x: 100, duration: 1});
gsap.to("#id", {y: 50, duration: 1, delay: 1});
gsap.to("#id", {opacity: 0, duration: 1, delay: 2});

为了更好的理解,还是从我们最熟悉的CSS动画开始:

<div class="container">
    <div class="dot"></div>
    <div class="dot"></div>
    <div class="dot"></div>
</div>

/* CSS */
@keyframes dot {
    0% {
        transform: translateY(0) scale(1);
    }
    25% {
        transform: translateY(100px) scale(1.1);
    }
    50% {
        transform: translateY(0) scale(0.9);
    }
    75% {
        transform: translateY(-100px) scale(1.1);
    }
    100% {
        transform: translateY(0) scale(1);
    }
}

.play .dot {
    animation: dot 1s linear both;
}

.play .dot:nth-child(1) {
    animation-duration: 1s;
    animation-delay: 0s;
}

.play .dot:nth-child(2) {
    animation-duration: 1s;
    animation-delay: 1s;
}

.play .dot:nth-child(3) {
    animation-duration: 1s;
    animation-delay: 2s;
}

效果如下:

这三个.dot的动画时序如下图所示:

如果你点击了示例中的“Play”按钮,动画效果如下:

在GSAP中,我们可以使用gasp.timeline()实现一个类似的效果:

const blue = document.querySelector(".blue");
const pink = document.querySelector(".pink");
const yellow = document.querySelector(".yellow");

const tl = gsap.timeline();

tl.to(blue, { scale: 2 })
    .to(blue, { rotation: 360 })
    .to(blue, { scale: 1 })
    .to(pink, { y: -50 })
    .to(pink, { rotation: 360 })
    .to(pink, { y: 0 })
    .to(yellow, { scale: 0.5 })
    .to(yellow, { rotation: 360 })
    .to(yellow, { scale: 1 });

效果如下:

Timeline是一个复杂的体系,在后续的内容中将会花更多的时间和大家一起来探讨这方面的知识

GSAP控制动画的方法

Web Animation API中Animation提供了多个方法用来控制动画,比如cancel()finish()pause()play()等。在GSAP中,同样有一些类似的方法,用来控制动画。

// 创建一个动画
let tween = gsap.to('.ball',{duration: 1, x: 100}); // 或 gsap.timeline(...) 创建动画;

// GSAP控制动画的方法
tween.play()        // ──> 播放
.pause()            // ──> 暂停
.resume()           // ──> 继续播放
.reverse()          // ──> 反方向播放
.restart()          // ──> 重新播放
.timeScale(2)       // ──> 2 = 两倍速度, 0.5 = 半速
.seek(1.5)          // ──> 跳转到时间(秒)或标签
.progress(0.5)      // ──> 跳转到一半
.totalProgress(0.8) // ──> 包括重复

// 当用作setter时,返回动画(链)

// 其他有用的方法 (tween 和 timeline)
.kill()           // ──> 立即销毁
.isActive()       // ──> 如果当前正在播放动画,返回true
.then()           // ──> Promise
.invalidate()     // ──> 清除记录的开始、结束值
.eventCallback()  // ──> 获取或设置一个事件回调

// timeline:具体方法
// add 标签, tween, timeline 或 callback
.add(thing, position)

// 在指定点调用函数
.call(func, params, position)

// 获取时间轴的子元素数组
.getChildren()

// 空的timeline
.clear()

// 动画播放头到一个位置线性
.tweenTo(timeOrLabel, {vars})

// ^^ 有开始和结束的位置
.tweenFromTo(from, to, {vars})

我们来看一个简单的地示例:

var tween = gsap.to(".green", {
    duration: 4, 
    x: 750, 
    rotation: 360, 
    borderRadius: '50%',
    backgroundColor: '#f36',
    boxShadow: '0 0 10px 20px #f90',
    ease: "none", 
    paused: true
});

document.querySelector("#play").addEventListener('click', () => tween.play()) 
document.querySelector("#pause").addEventListener('click', () => tween.pause())
document.querySelector("#resume").addEventListener('click', () => tween.resume())
document.querySelector("#reverse").addEventListener('click', () => tween.reverse())
document.querySelector("#restart").addEventListener('click', () => tween.restart())

效果如下:

GSAP中的缓动函数

在CSS的animationtransition中可以使用animation-timing-functiontransition-timing-function来指定动效的缓动效果。简单地说就是用来控制动效在过渡期间的变化速度。在CSS的animationtransition中有几个固定的缓动函数,即lineareaseease-inease-in-outease-outcubic-bezier()等。

在GSAP中,控制动效的缓动函数ease要强大的多,除了常见的函数之外,还可以使用下面这个交互工具,获取更多的缓动函数:

来看一个简单地示例:

document.querySelector("#bounce").addEventListener("click", () => {
    gsap.to("#logo", { 
        duration: 1, 
        y: 100, 
        scale: 0.5, 
        ease: "bounce" 
    });
});

document.querySelector("#linear").addEventListener("click", () => {
    gsap.to("#logo", { 
        duration: 1, 
        x: 100, 
        scale: 0.8, 
        ease: "linear" 
    });
});

document.querySelector("#elastic").addEventListener("click", () => {
    gsap.to("#logo", {
        duration: 1,
        y: 100,
        x: 30,
        scale: 0.5,
        ease: "elastic.out(1, 0.3)"
    });
});

document.querySelector("#circ").addEventListener("click", () => {
    gsap.to("#logo", { 
        duration: 1, 
        y: 100, 
        scale: 1.2, 
        ease: "circ.out" 
    });
});

document.querySelector("#expo").addEventListener("click", () => {
    gsap.to("#logo", {
        duration: 1,
        y: 100,
        x: 100,
        scale: 0.5,
        ease: "expo.out"
    });
});

效果如下:

小结

如果你阅读到这里的话,我想你对GSAP有了一个初步的认识。

这篇文章中,介绍了GSAP的一些基础知识,通过对这些基础知识的了解,已经可以使用GSAP的一些基本API,比如,gsap.to()gsap.from()gsap.fromTo();一些控制动画的方法,比如play()restart()等,还有Timeline以及缓动函数等来构建一个简单地动画效果。正如文章中所说的,GSAP的功能是非常强大的,只要我们掌握GSAP核心模块的知识,我们制作动画效果就能如鱼得水。如果你对这方面感兴趣的话,请持续关注后续相关内容的更新。在后面将会和大家一起探讨GSAP中更深奥知识,比如Timeline,TweeLite、TweenMax等。

最后希望这篇文章对你有所帮助,如果你在这方面有更多的经验或建议,欢迎在下面的评论中与我们一起共享。

返回顶部