GSAP学习笔记

GSAP

最近在学习GSAP动画库,它是一个非常强大和专业的动画库,并且兼容性极强。拥有很多有趣的插件,比如滚动动画控制等。这是我所作的笔记,因为GSAP没有中文文档,而且中文资料不多,我相信我所做的笔记会给我带来极大的遍历,也希望对读者们有所帮助

  • js动画库
  • 浏览器兼容性极强
  • 性能非常高

入门

npm install gsap

import { gsap } from "gsap";

下面的文章百分百有帮助

GSAP 入门 - 学习中心 - GreenSock

GSAP从入门到精通 - 掘金 (juejin.cn)

一、创建动画

gsap.to(".box", { x: 200 })//或者
gsap.to(domElement,{ x: 200 });

语法:

gsap.Methods(target,variables);

  • Methods:方法

  • target:目标元素

  • variables:变量参数

补间动画:

这里有四种类型的动画补间:

  • gsap.to()这个是最常见的,指元素从当前状态开始,到定义的状态结束的动画
gsap.to(".circle", { x: 40, fill: 'blue', })
  • gsap.from()元素从from定义的状态开始移动到当前现有状态结束动画
gsap.from(".circle", { x: -40, fill: 'blue', });
  • gsap.fromTo()定义起始点状态和终点状态进行动画
gsap.fromTo( ".circle",{ x: -40, fill: 'blue', }, { x: 40, fill: 'green' });
  • gsap.set()直接跳转到set的状态,无动画化
gsap.set(".circle", { x: 40, fill: 'blue', });

目标元素获取:

target可以使用选择器或者获取的dom元素,甚至一个包含多个dom元素数组

gsap.to(".box",{x:20});

let box = document.querySelector(".box");
gsap.to(box,{x:20});

let box1 = document.querySelector(".box1");
let box2 = document.querySelector(".box2");
gsap.to([box1,box2],{x:200});

变量参数:

变量参数可以是影响动画的任意属性或者特殊行为,例如duration\onComplete\repeat

gsap.to(target, {
  // this is the vars object
  // it contains properties to animate
  x: 200,
  rotation: 360,
  // and special properties
  duration: 2
})

gsap可以改变几乎所有的动画有关的属性,包括css属性,自定义的对象的属性,甚至css变量和复杂的字符串

这里是一张GSAP属性和css动画属性对应的表

GSAP变量参数等同于css的属性
x: 100transform: translateX(100px)
y: 100transform: translateY(100px)
xPercent: 50transform: translateX(50%)
yPercent: 50transform: translateY(50%)
scale: 2transform: scale(2)
scaleX: 2transform: scaleX(2)
scaleY: 2transform: scaleY(2)
rotation: 90transform: rotate(90deg)
rotation: “1.25rad”这里使用弧度则没有对应的css
skew: 30transform: skew(30deg)
skewX: 30transform: skewX(30deg)
skewY: “1.23rad”这里使用弧度则没有对应的css
transformOrigin: “center 40%”transform-origin: center 40%
opacity: 0adjust the elements opacity
autoAlpha: 0不透明度和可见性的简写
duration: 1animation-duration: 1s
repeat: -1animation-iteration-count: infinite 重复无限次
repeat: 2animation-iteration-count: 2
delay: 2animation-delay: 2
yoyo: true 来回往复动画animation-direction: alternate

单位

GSAP默认单位是px和deg,可以使用字符串形式设定自己想要的单位,甚至相对计算

x: 200, // 默认px
x: "+=200" // 相对计算
x: '40vw', // 字符串
x: () => window.innerWidth / 2, //用函数值计算
rotation: 360 // 默认deg
rotation: "1.25rad" // 使用弧度单位rad

远不止如此

gsap可以做的远不止上面的属性,他几乎可以做所有css的变换,只要你记住css属性,去尽情尝试吧,注意css属性需要驼峰写法

gsap.to(".box", { 
  duration: 2,
  backgroundColor: '#8d3dae',
});

但是像filter和boxShadow这种cpu密集型的属性还是谨慎动画化

SVG和JS对象、canvas

svg

svg可以被gsap动画化,他的诸多属性,例如width、height、fill、stroke、cx、opacity,甚至svg的viewBox本身使用的attr对象都可以成为补间动画的参数

<template>
    <svg id="svg" viewBox="0 0 100 100">
       <rect class="svgBox" fill="#28a92b" x="0" y="35" width="30" height="30" rx="2" />
    </svg>
</template>
<script>
    gsap.to(".svgBox", { 
      duration: 2,
      x: 100, // use transform shorthand (this is now using SVG units not px, the SVG viewBox is 100 units wide)
      xPercent: -100,
      // or target SVG attributes
      attr: {
        fill: '#8d3dae',
        rx: 50, 
      },
    });
</script>

js对象

GSAP曾提到任何东西都可以被它动画化,这是真的。GSAP动画变量属性时甚至可以不需要获取DOM元素。我们可以创建一个对象并用对象的任意的属性去动画化。

//create an object
let obj = { myNum: 10, myColor: "red" };

gsap.to(obj, {
  myNum: 200,
  myColor: "blue",
  duration:4,//持续时间,这里仍然可以用gsap的特殊属性
  onUpdate: () => console.log(obj.myNum, obj.myColor)
});

canvas

在GSAP制作canvas动画时,我们一般都会去封装一个对象来存放canvas里元素的位置等信息,不仅如此这种方式还经常用在threejs,Pixijs上

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#28a92b";

let position = { x: 0, y: 0 };

function draw() {
  // erase the canvas
  ctx.clearRect(0, 0, 300, 300);
  // redraw the square at it's new position
  ctx.fillRect(position.x, position.y, 100, 100);
}

//animate x and y of point
gsap.to(position, { 
  x: 200, 
  y: 200, 
  duration: 4,
  // unlike DOM elements, canvas needs to be redrawn and cleared on every tick
  onUpdate: draw 
});

GSAP特殊属性

例如duration、yoyo这样的属性被称为GSAP的特殊属性,他们可以帮助我们去改变动画的一些行为方式

属性描述
duration动画持续时间 (s) 默认值: 0.5
delay动画延迟时间 (s)
repeat动画重复次数,-1代表无限重复
yoyo就像他的名字一样yoyo,也就是动画往复 Default: false
stagger每个元素之间的动画开始时间间隔(s),前提是提供了多个元素
ease缓和和更改动画的速度,让动画更具个性和感觉 Default: “power1.out”
onComplete动画完成时的回调函数

二、Easing缓动效果

Easing简介

easing可能是动画中最重要的设计之一,一个好的easing可能会给动画带来更个性更有趣的感觉

看下面的例子,尤其是这个bounce

gsap.to(".green", { 
  rotation: 360,
  duration: 2,
  repeat: -1,
  repeatDelay: 2,
  ease: 'none'
});


gsap.to(".purple", { 
  rotation: 360,
  duration: 2,
  repeat: -1,
  repeatDelay: 2,
  ease: 'bounce.out'
});

easing_linear vs bounce

ease底层是数学计算,但是我们可以依靠gsap提供的可视化工具去调整挑选合适的ease

Easing | GSAP docs

image-20231012101124549

Ease types

ease有三种类型:in out inout

ease: "power1.in"
// 从慢到快, 像重物落下

ease: "power1.out"
// 开始快,结束慢,就像一个滚动的球慢慢停下来

ease: "power1.inOut"
// 开始慢,中间快,结束慢,就像汽车加速和减速一样

注意:“SlowMo” ease, “RoughEase”, “ExpoScaleEase”, 和自定义ease (“CustomEase”, “CustomBounce”, 和"CustomWiggle")不在gsap的核心中,使用他们需要先导入,详情看installation page

三、Staggers交替动画

Staggers | GSAP docs

在gsap中交替动画的效果是可以配置的,并且功能超级强大

stagger

gsap.to(".box", {
  y: 100,
  stagger: 0.1, // 给每一个".box" 元素之间添加开始动画间隔为0.1s
});

stagger_0.1s

网格布局交替动画

Staggers | GSAP docs simple-configuration

当使用网格布局时,可以配置stagger,设置网格的行和列来控制 网格中项目的交替动画

gsap.to(".box", {
  duration: 1,
  scale: 0.1,
  y: 40,
  ease: "power1.inOut",
  stagger: {
    grid: [7,15],
    from: 11,//从何处开始扩散延迟动画 center\end\edges\random\l1\[index]直接选取第index元素开始向外扩散交替
    axis:null,//动画轴,null(both)\x\y
    ease: "power2.in",
    amount: 1.5 //运行完成交替中的所有元素动画的所有总时间
  }
});

stagger配置项

属性描述
amount完成交替动画总时间,如果想单独设置交替的时间请用each
each每个元素交替动画开始之间的时间间隔
from交替动画在数组中开始的位置,可以用index来选取,另外可以使用"start",“center”,“edges”,“random”, or“end”
grid[行,列数]或者"auto"自行计算grid行列数
axis指定交错的扩散方式,x为基于x轴,y为基于y轴,null为基于x和y轴
ease分配动画开始时间的复杂度。因此,“power2”开始时会有较大的时间间隙,然后在接近尾声时聚集得更紧密。默认值:“none”。

Function自定义stagger

只有需要自定义stagger逻辑时才使用函数的方式定义stagger

Staggers | GSAP docs function

gsap.to(".box", {
  y: 100,
  stagger: function (index, target, list) {
    // your custom logic here. Return the delay from the start (not between each)
    return index * 0.1;
  },
});

这个函数会在数组中的每个目标元素中调用一次,并且返回起始位置到该元素的总延迟(不是上一个元素的开始延迟),可以用下面三个给予的参数来进行所需的计算

  • index 数组中的索引值
  • target 数组中该索引值对应的元素
  • list 目标数组

repeat、yoyo、回调函数的位置影响

repeat、yoyo、回调函数,这三个可以放在stagger外面或者里面,在外面则会控制整体的交替动画,若在里面则会控制交替动画中的单个元素的动画行为

四、Timelines 时间线

创建时间线

时间线的主要作用是在多个动画的情况下给动画发生时间进行排序和控制,这比单纯的用delay控制更加方便可靠

// create a timeline
let tl = gsap.timeline()

// add the tweens to the timeline - Note we're using tl.to not gsap.to
tl.to(".green", { x: 600, duration: 2 });
tl.to(".purple", { x: 600, duration: 1 });
tl.to(".orange", { x: 600, duration: 1 });

timeline

控制时间线

1、delay

我们可以利用delay来延迟或抵消时间线的开始时间,但这并不灵活

let tl = gsap.timeline()

tl.to(".green", { x: 600, duration: 2 });
tl.to(".purple", { x: 600, duration: 1, delay: 1 });
tl.to(".orange", { x: 600, duration: 1 });

timeline_delay

2、位置参数

通过位置参数我们可以精确的控制时间线

tl.to(domElem,{...},位置参数)

let tl = gsap.timeline()

// start at exactly 1 second into the timeline (absolute)
tl.to(".green", { x: 600, duration: 2 }, 1);

// insert at the start of the previous animation
tl.to(".purple", { x: 600, duration: 1 }, "<");//'<'表示上一个动画的开始,>则是结尾

// insert 1 second after the end of the timeline (a gap)
tl.to(".orange", { x: 600, duration: 1 }, "+=1");

timeline_position_params

位置参数有多种形式

  • 绝对时间 tl.to(".class", {x: 100}, 3);从时间线开始测量的绝对时间(s)
  • 相对间隔
    • tl.to(".class", {x: 100}, "+=1");前面所有元素动画结束后1秒开始
    • tl.to(".class", {x: 100}, "+=50%");前面所有元素动画结束后延迟自身动画时间50%后开始
    • 时间重叠:
      • tl.to(".class", {x: 100}, "-=1");前面所有元素动画结束前1s开始动画
      • tl.to(".class", {x: 100}, "-=25%");

组合:’’<+=2", “>-=3”

标签:可以用add(“标签名”,“位置参数”)来给位置起别名,之后可以在时间线补间中使用该名作为位置

  
//tweens are inserted at and relative to a label's position
var tl = gsap.timeline();
tl.to("#green", {duration: 1, x: 750})//可以用链式的形式添加不同动画到时间线
  //add blueGreenSpin label 1 second after end of timeline
  .add("blueGreenSpin", "+=1")
  //add tween at blueGreenSpin label
  .to("#blue", {duration: 2, x: 750, rotation: 360}, "blueGreenSpin")
  //insert tween 0.5 seconds after blueGreenSpin label
  .to("#orange", {duration: 2, x: 750, rotation: 360}, "blueGreenSpin+=0.5");

时间线特殊属性

repeat,repeatDelay等,可以在创建时间线的时候加入,从而控制时间线行为

let tl = gsap.timeline({repeat: -1, repeatDelay: 1, yoyo: true})

tl.to(".green", { rotation: 360 });
tl.to(".purple", { rotation: 360 });
tl.to(".orange", { rotation: 360 });

defaults

在创建时间线时键入defaults属性,defaults中定义的属性会被其to等补间方法共有

var tl = gsap.timeline({
    defaults: {
        duration: 1
    }
});

//no more repetition of duration: 1!
tl.to(".green", {x: 200})
  .to(".purple", {x: 200, scale: 0.2})
  .to(".orange", {x: 200, scale: 2, y: 20});

关键帧

利用关键帧我们可以给一个目标设置多个补间动画,这些补间动画的持续时间会在duration时间内被平均分配

gsap.to(".box", {
  keyframes: {
    x: [0, 100, 100, 0, 0],
    y: [0, 0, 100, 100, 0],
    ease: "power1.inOut"
  },
  duration: 2
});

对象关键帧

可以设置更具体的属性,可以把每个对象看成是一个补间动画,还可以对整体做缓动ease

gsap.to(".elem", {
 keyframes: [
  {x: 100, duration: 1, ease: 'sine.out'}, // finetune with individual eases
  {y: 200, duration: 1, delay: 0.5}, // create a 0.5 second gap
  {rotation: 360, duration: 2, delay: -0.25} // overlap by 0.25 seconds
 ],
 ease: 'expo.inOut' // ease the entire keyframe block
});


百分比关键帧

这种写法更像css,不需要关注每个补间的持续时间,只需要在整体设置duration。而且百分比关键帧允许为每个补间动画(子对象)设置自己的缓动

gsap.to(".elem", {
 keyframes: {
  "0%":   { x: 100, y: 100},
  "75%":  { x: 0, y: 0, ease: 'sine.out'}, // finetune with individual eases
  "100%": { x: 50, y: 50 },
   easeEach: 'expo.inOut' // ease between keyframes
 },
 ease: 'none' // ease the entire keyframe block
 duration: 2,
})

回调

对象关键帧和百分比关键帧的行为和正常补间动画类似,可以利用回调函数

gsap.to(".elem", {
 keyframes: [
  {x: 100, duration: 1},
  {y: 200, duration: 1, onComplete: () => { console.log('complete')}},
  {rotation: 360, duration: 2, delay: -0.25, ease: 'sine.out'}
 ]
});

gsap.to(".elem", {
 keyframes: {
  "0%":   { x: 100, y: 100},
  "75%":  { x: 0, y: 0, ease: 'power3.inOut'},
  "100%": { x: 50, y: 50, ease: 'none', onStart: () => { console.log('start')} }
 },
 duration: 2,
})

五、控制和回调

1、控制

gsap提供了控制动画播放的方法

//首先将一个补间动画赋值给一个变量,
let tween = gsap.to("#logo", {duration: 1, x: 100});
//暂停
tween.pause();
//恢复播放动画并且不改变方向
tween.resume();
//重新播放动画
tween.restart();
//反转播放方向
tween.reverse();

//跳转到特定的播放时间,不影响实例是否暂停或者反转
tween.seek(0.5);

//获取或者设置动画的进度
tween.progress(0.25);

//获取或者设置动画的时间缩放比例,这里设置减速为1/2
tween.timeScale(0.5);

//两倍速
tween.timeScale(2);

//杀死动画,并且会被垃圾回收机制回收内存
tween.kill();

// 可以采用链式编程
tween.timeScale(2).reverse();

2、动画回调函数

  • onComplete:动画完成时调用

  • onStart:动画开始

  • onUpdate:动画更新每一帧

  • onRepeat:动画重复

  • onReverseComplete:动画反转后再次达到起点时调用

gsap.to(".class", {
  duration: 1, 
  x: 100, 
  //箭头函数(只有一行时比较简洁,当然箭头函数的this需要注意)
  onComplete: () => console.log("the tween is complete")
}
        
gsap.timeline({onComplete: tlComplete}); //普通函数(适合多行函数)

function tlComplete() {
  console.log("the tl is complete");
  // more code
}

插件

一、介绍

插件为GSAP的核心增加了额外的功能,当我们需要的时候去引入插件即可。

插件部分免费,有一部分需要收费

Plugins | GSAP docs

1、导入插件

类似于下面的形式,在npm或者yarn之类的导入gsap时插件已经被安装,至于不同的插件如何导入可以看这个Installation | GSAP docs — 安装| GSAP 文档

import { ScrollTrigger } from "gsap/ScrollTrigger";

2、注册插件

gsap.registerPlugin(插件1,插件2,...)//插件名不需要引号

多次注册插件不会产生问题,但同时也没啥用

二、ScrollTrigger 基于滚动触发的动画

ScrollTrigger 使任何人都可以用最少的代码创建令人惊叹的基于滚动的动画。无限灵活。拖动、固定、捕捉或只是触发任何与滚动相关的内容,即使它与动画无关。

简单示例

gsap.to(".box", {
  scrollTrigger: ".box", //  当".box"进入视口的时候触发动画(仅一次)
  x: 500,
});

上面的示例中,.box一旦暴露在视口中(不论是否完整暴露)就会直接执行动画,那么如何控制动画的在何时激活呢?

1、start控制动画激活时机

start需要两个值:触发器元素的位置,浏览器视口位置

  • 触发器元素的位置,指定以触发器元素的哪个部位为触发位置
  • 浏览器视口位置:当触发器元素的指定位置到达指定的浏览器视口位置时动画会被触发

位置可以用百分比、关键字或者数值表示

“20%”,"-80%",“top+=300px”,“bottom”

gsap.to(".box", {
  x: 500,
  scrollTrigger:{
      trigger:'.box',//这里代表触发器元素
      start:'center center'//指定触发位置为中央
  } 
});

2、markers标记

markers可以帮助我们在开发时更好的观察动画触发时机

只需在scrollTrigger配置项中将其设置为true

3、end,scrub-控制动画结束位置,动画进度绑定于滚动条

之前我们用start控制了动画激活位置,而end可以控制动画结束位置,

在讲end之前我们需要提到scrub这个配置项。

  • scrub可以将动画的进度直接链接到滚动条。这样我们就不用担心滚动条滚完了,动画却还远没有播放完毕
  • scrub:true将动画的进度直接和滚动条绑定
  • scrub:NumberNumber指的是播放头进度赶上滚动条进度所需要的时间(秒),比如scrub:0.5表示动画的进度需要0.5秒才能赶上滚动条位置。

现在我们就可以放心的用end来指定动画结束的位置了,利用scrub也不用担心动画能否完整。

gsap.to('.box1', {
  x: 400,
  scrollTrigger: {
    trigger: '.container',
    start: 'center 70%',
    end: 'center 30%', //指定动画结束位置
    scrub: true, // 
  }
});

注意:我们已经说过scrub会将动画进度绑定到滚动条上,因此滚动条回退时动画也会回退

4、pin 固定滚动时的元素位置

pin用于将元素位置固定不动,当然只会在触发期间(start-end)才会固定,当触发期间过去之后,元素位置也不会突然跳转,而是仍然随着滚动条移动而移动。

scroll_pin

pin很酷,很多网页都用到它这样的效果

步骤

我们仅仅需要做的大致是三步:

  • 选定trigger元素
  • 设置start\end位置
  • 应用pin:true

pinSpacing

默认情况下,pin底部(或者右侧)会被添加一个padding以便将其他元素向下(向右)推,以便当固定元素取消固定时,之后的内容可以正常的显示,否则后面的内容极有可能会出现在固定元素的下方。当然你可以手动调整为false(默认为true)

pinSpacing这会在大多数情况下起作用,但是遇到一些特殊情况可能你需要去看看文档或者想其他的解决方案

5、四个回调函数和toggleActions

回调函数描述
onEnter动画(上到下/左到右)进入
onLeave动画(上到下/左到右)离开
onEnterBack动画(下到上/右到左)回退到最后一刻时
onLeaveBack动画(下到上/右到左)回退到起始点

toggleActions

控制上面四个不同时刻位置的动画状态

顺序为onEnter、onLeave、onEnterBack 和 onLeaveBack(顺序依次为滚轮向下滚动一遍再向上滚动一遍而触发的四个时刻)

toggleActions: "play pause resume reset"

三、Draggable拖动

暂未学习

四、MotionPath沿路径移动

暂未学习

五、Observer观察者

暂未学习

深入探索

一、处理冲突的补间动画

当一个对象属性被赋予多个补间动画时,在多个补间动画有相同属性的变换,之间就会产生冲突,我们必须考虑发生冲突时如何处理

gsap有三种覆盖模式来处理冲突overwrite

  • false(默认):不发生覆盖,多个补间可以尝试同时对同一目标的相同属性进行动画处理。简单来说就是摆烂,让补间动画互相打架直到一个结束
  • true:对同一目标进行动画处理的任何现有补间都将立即终止。
  • “auto”:只有现有补间的冲突部分才会被杀死,如果补间1对目标的x和旋转属性进行动画处理,然后补间2开始仅对相同目标的x属性进行动画处理,并在第二个补间上设置,则补间1的旋转部分将被保留,但其中的x部分将被终止。
//设置重复覆盖
gsap.to(".line", { x: 200, overwrite: true });

// 设置全局重复覆盖
gsap.defaults({ overwrite: true });

// 时间线设置重复覆盖
const tl = gsap.timeline({ defaults: { overwrite: true } });

总之,尽量不要让动画冲突

二、focus

三、即时渲染

四、SVG动画

GSAP在SVG上的2D转换的工作方式和任何其他DOM元素完全相同

并且GSAP在兼容性上完全没问题,没有一个动画库可以比它考虑的更全面。在GSAP的加持下,甚至连IE和Opera都可以支持CSS的transform转换,

因此css绝不是2d变换的一种选择

1、使用2D变换 缩放、旋转、倾斜、移动

gsap.to("#gear", {duration: 1, x: 100, y: 100, scale: 0.5, rotation: 180, skewX: 45});

2、相对元素本身设置转换原点transformOrigin

当我们需要旋转变换是时需要设置旋转原点,因为我们都知道SVG中的旋转默认是围绕画布的坐标原点来旋转的

当然,不仅仅是旋转,缩放也需要设置缩放的原点。

下面是使用transformOrigin来设置转换原点的三种方式,transformOrigin的坐标系统是以于物体本身的左上角为原点,也就是相对于元素本身的

gsap.to("rect", {duration: 1, rotation: 360, transformOrigin: "50% 50%"}); //百分比
gsap.to("rect", {duration: 1, rotation: 360, transformOrigin: "center center"}); //关键字
gsap.to("rect", {duration: 1, rotation: 360, transformOrigin: "50px 50px"}); //像素坐标

你也可以直接用set来设置转换原点

gsap.set("#circle", {transformOrigin:"50% 50%"})

3、启用smoothOrigin

在svg动画中改变transformOrigin的值时会出现一些尴尬的情况、当你改变transformOrigin时,元素首先会回到初始位置再根据新的转换原点的值重新设置转换原点再进行转换。可能不太好理解,直接看下面的链接中的示例

SVG | GSAP docs 转换原点跳跃

而当我们启用smoothOrigin:true,一切都变得合理了

注意:仅适用于svg

4、在全局坐标系定义转换原点svgOrigin

gsap.to(svgElement, {duration: 1, rotation: 270, svgOrigin: "250 100"}); 

但是svgOrigin只支持px来定位

5、改变标签内部的属性值

GSAP可以处理几乎所有的css属性,当然也可以处理标签内部的属性值,可以用attr对象来处理任何的数字型属性,例如svg标签中的fill stroke width height等

gsap.to("#rect", {duration: 1, attr: {x: 100, y: 50, width: 100, height: 100}, ease: "none"});

注意:改变前后的单位必须相同,这一点很重要,因为attr不进行单位间的转换

6、基于百分比的x,y位移

SVG在其规范中并没有考虑基于百分比的变换,然而当我们在构建响应式网站时,百分比变换可以是我们更方便的定位元素。

我们使用xPercent\yPercent来基于相对元素自身的百分比位移

gsap.to("rect", {duration:0.5, xPercent:100});

但是:请注意,百分比位移后不能再改变元素的宽高。因为GSAP为了实现百分比变换必须进行数学计算。它不像正常的基于百分比的css转换那样‘实时’

7、鼠标拖动SVG的实现(精准而优雅)

GSAP提供了一个Draggable插件来拖动svg元素,我们可以将移动限制在任何定义的轴或者边界内部,进行hitTest()测试(碰撞检测),以及投掷或者旋转元素(inertiaPlugin)

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
   width="600px" height="300px" viewBox="0 0 600 300" enable-background="new 0 0 600 300" xml:space="preserve">
<path id="container" d="M286.131,285.808H38.515c-6.6,0-12-5.4-12-12V26.192c0-6.6,5.4-12,12-12h247.616c6.6,0,12,5.4,12,12v247.616
  C298.131,280.408,292.731,285.808,286.131,285.808z"/>
<g id="box">
  <path fill="#88ce02" stroke="#000000" stroke-miterlimit="10" d="M261.677,205.856H169.52c-6.6,0-12-5.4-12-12v-92.157
    c0-6.6,5.4-12,12-12h92.157c6.6,0,12,5.4,12,12v92.157C273.677,200.456,268.277,205.856,261.677,205.856z"/>
  <text transform="matrix(1 0 0 1 194.1124 149.8997)" font-family="'MyriadPro-Regular'" font-size="12">throw me</text>
</g>
<g id="circle">
  <g>
    <circle fill="#88ce02" stroke="#000000" stroke-miterlimit="10" cx="420.681" cy="149.9" r="80.349"/>
  </g>
  <text transform="matrix(1 0 0 1 400.2934 153.4218)" font-family="'MyriadPro-Regular'" font-size="12">spin me</text>
  <circle fill="#DDEAB0" cx="484.594" cy="149.9" r="10.48"/>
</g>
<text transform="matrix(1 0 0 1 41.5878 29.476)" fill="#FFFFFF" font-family="'ArialMT'" font-size="12">green box will stay within bounds of container</text>
</svg>
gsap.registerPlugin(Draggable, InertiaPlugin);//注册Draggable, InertiaPlugin两个插件

gsap.set("#circle", {transformOrigin:"50% 50%"})//设置svg元素circle的旋转中心

Draggable.create("#box", {
  type:"x,y",
  bounds:"#container",
  overshootTolerance:0,
  inertia:true
})

Draggable.create("#circle", {
  type:"rotation",
  inertia:true
})

具体配置看插件相关的配置文档

8、设定路径来移动svg元素

GSAP的 MotionPathPlugin插件使得沿路径的svg或者DOM动画制作变得轻而易举,

例子看resources | GSAP docs — 沿着路径移动任何东西

9、Stroke 动画

DrawSVGPlugin插件可以逐步显示或隐藏svg的Stroke

例子:resources | GSAP docs —SVG Stroke

10、不同svg形状之间的变换

MorphSVGPlugin还提供了对SVG路径变形的补间高级控制

resources | GSAP docs — path点数变换

五、如何用gsap对dom元素做CSS3的3d变换

在我实际操作中发现,GSAP并不能正确的直接用rotationX,rotaionY,rotationZ属性进行元素的3d变换。因此我只能通过字符串不断改变transform的值来用gsap进行3d变换。

下面,我创建了一个rotation对象,并通过gsap去补间Z的旋转角度,再通过onUpdate回调函数不断的给元素的transform属性赋值rotateZ的旋转值。这样我们就可以通过gsap实现3d变换,当然也可以直接通过css实现,特定场景特定分析吧

onMounted(()=>{
  const rect = document.querySelector('.rect')
  const rotation={
    X:75,
    Y:0,
    Z:0
  }
  const transformStr = "rotateX(75deg) translateY(10px)"
  rect.style.transform=transformStr
  gsap.to(rotation,{
    Z:360,
    duration:10,
    repeat:-1,
    onUpdate:()=>{
      rect.style.transform=`rotateX(75deg) translateY(10px) rotateZ(${rotation.Z}deg)`
      console.log(rotation.Z)
    }
  })
})

高级

这部分是我对gsap文档中部分方法和属性的笔记,之前的则是我对gsap教程的笔记(learn模块)

一、注册gsap补间动画

我们知道gsap有to、from、fromTo、set四个自带的补间动画。

同时gsap提供了一个registerEffect方法来注册自己配置好的补间动画,这样对于相似的动画我们可以避免代码的重复

以及一个effects属性来调用注册的补间动画。下面是具体使用方法

// 注册补间:
gsap.registerEffect({
  name: "fade",//给这个补间起别名
  effect: (targets, config) => {//编写补间逻辑
    return gsap.to(targets, { duration: config.duration, opacity: 0 });//返回补间动画
  },
  defaults: { duration: 2 }, //设置默认值
  extendTimeline: true, //true则表示可以直接在timeline中调用
});

// 通过effects属性调用注册的补间动画:
gsap.effects.fade(".box");

// 或者直接在timeline中调用:
let tl = gsap.timeline();
tl.fade(".box", { duration: 3 })
  .fade(".box2", { duration: 1 }, "+=2")
  .to(".box3", { x: 100 });
  • effect将targets作为数组传入,即使传入的是选择器字符串或者包含多个字符串的数组,targets仍然是一个包含dom元素的数组。
  • defaults属性用于设置默认值
  • extendTimeline该属性如果为true,那么就意味着可以将该effects效果像其他普通补间一样在任何时间轴上使用,并且可以配置position参数

二、侦听每次动画更新

gsap动画更新是基于requestAnimationFrame的,因此它和浏览器的渲染周期完美同步,(但这也意味着每次更新动画的时间间隔并不相同。也就是如果设置每次更新移动10px那么可能会导致每1ms内移动的距离不一样,因为每1ms内每次更新动画的次数是不一定的。所以我们一般会设置速度,然后通过速度乘以时间来决定每次更新动画时移动的距离。当然gsap已经考虑到这方面,所以我们一般情况下只需正常使用gsap。)

如果我们需要监听每次动画更新,那么gsap.ticker属性可以帮助我们

  • 添加监听函数:gsap.ticker.add(myFunction)函数可以是箭头也可以是普通

  • 移除监听函数gsap.remove(myFunction)

  • gsap.ticker.add(myFunction);
    myfunction fnName(time,deltaTime,frame ){
    	//...
    }
    
    • time:自代码启动以来的总时间
    • deltaTime:自上一个刻度以来经过的毫秒数(可以通过gsap.ticker.deltaRatio()来获取fps
    • frame:帧编号,在每个时刻会递增
  • add高级配置:gsap.ticker.add(myCallback, once, prioritize);

    • myCallback回调函数
    • once:布尔类型,true表示回调函数只调用一次然后自动删除
    • prioritize:布尔类型,true则该回调函数会在全局时间线之前触发

三、gsap.utils

utiles提供了很多有用的工具,比如random,等gsap.utils() | GSAP docs — gsap.utils() | gsap.utils() | GSAP 文档

四、控制一大堆gsap动画gsap.context()

假设有一大块的gsap代码创建不同的动画,我们可以使其在gsap.context中创建以便我们去整体的操作他们

let ctx = gsap.context(() => {
 gsap.to(...);
 gsap.from(...);
 gsap.timeline().to(...).to(...);
 ...
});

ctx.kill();//杀死动画

1、应用于特定dom元素

通过context我们可以将内部动画的选择器应用于特定的dom元素范围内

let ctx = gsap.context(() => {

  gsap.to(".box", {...}) // <- normal selector text, automatically scoped to myRefOrElement
  gsap.from(".circle", {...});

}, myRefOrElement); //这里可以是选择器文本,也可以是dom元素

2、添加新的动画到context

// create context
let ctx = gsap.context(() => {...});

// then later, add to it:
ctx.add(() => {
  gsap.to(...); // now all these get added to the Context.
  gsap.from(...);
});

3、清理函数

您可以选择返回一个“清理函数”,如果/当上下文恢复时应该调用该函数。这可以包含你自己的自定义清理代码:

let ctx = gsap.context(() => {
  ...
  return () => {
    //我的自定义清理代码。在触发ctx.revert()时调用。
  };
});

tip:你也可以在任何.add()函数中返回一个清理函数;当Context的revert()被调用时,它们都将被调用。

revert()用于还原到动画前位置,并且会杀死动画

四、gsap.defaults()

该方法用于给所有的补间动画设置默认值

五、补间数组

我们之前用gsap补间过js的对象,同时数组也是可以被补间的,但是需要使用endArray

const arr = [1, 2, 3];

gsap.to(arr, {
  endArray: [5, 6, 7],
  onUpdate() {
    console.log(arr);
  },
});

六、gsap.Modifiers

七、gsap.Snap

LAST: 常见的 GSAP 错误

此链接必读。https://gsap.com/resources/mistakes

LAST:实践中遇到的问题及解决方法

一、如何做CSS3的3d变换

在我实际操作中发现,GSAP并不能正确的直接用transform属性进行元素的3d变换。因此我只能通过不断改变transform的值来用gsap进行3d变换。

下面,我创建了一个rotation对象,并通过gsap去补间Z的旋转角度,再通过onUpdate回调函数不断的给元素的transform属性赋值rotateZ的旋转值。这样我们就可以通过gsap实现3d变换,当然也可以直接通过css实现,特定场景特定分析吧

onMounted(()=>{
  const rect = document.querySelector('.rect')
  const rotation={
    X:75,
    Y:0,
    Z:0
  }
  const transformStr = "rotateX(75deg) translateY(10px)"
  rect.style.transform=transformStr
  gsap.to(rotation,{
    Z:360,
    duration:10,
    repeat:-1,
    onUpdate:()=>{
      rect.style.transform=`rotateX(75deg) translateY(10px) rotateZ(${rotation.Z}deg)`
      console.log(rotation.Z)
    }
  })
})

二、动画管理

这里谈谈我在学习中遇到的动画管理相关的问题,但是很多问题后来发现在https://gsap.com/resources/mistakes这个链接中是可以找到答案的

1、当事件改变动画行为时产生的动画冲突解决——overwrite

我们经常会用到事件去改变动画,但这常常会产生冲突

对于动画冲突我们可以用overwrite:true或者overwrite:'auto’的形式防止冲突,这两者的区别在处理冲突动画这一章节中已讲,不再赘述

<script setup>
    function handlerClick(){
       gsap.to('domEle',{
            x:100,
            duration:1,
            overwrite:'auto'
       })
    }
    onMounted(()=>{
       gsap.to('domEle',{
            x:1000,
            duration:1
        })
    })
</script>

2、from, yoyo等基于当前状态的值的动画问题

这里以from举例,我们知道from是从设定值到当前值的补间动画。我们抛出一个问题,当我们设定当前元素的值opacity为1,然后创建一个from补间动画opacity为0.那么当动画开始时元素就会从opacity0向1过渡。但是当动画过渡到比如0.5时我们再次触发opacity为0的from补间动画会怎么样(这里假设不需要overwrite:true)? 明显,动画会再次发生,而且因为触发时动画opacity为0.5,此时动画会从0过渡到0.5结束,这在一般情况下是错误的。因为我们想要的是它再次回到opacity为1。

当然这种情况是好解决的,我们只需要把from改成fromTo就行了

当出现类似的问题以及使用repeatRefresh时出现类似问题都可以看看改成fromTo能否解决问题,要记住gsap中补间是相对的

3、同一个函数内可以通过变值来改变

虽然之前说多个gsap对同一属性进行动画会产生冲突,但当我在同一个函数中进行参数变换却能正确的切换动画,

我不知道什么原因,除非在这个函数中每次调用的gsap应用的to方法是同一个to方法对象(因为我们知道一个gsap可以应用多个to方法)

<script setup>
import {gsap} from "gsap";
import {ref} from 'vue';
let X = 1000
function handlerClick(){
       gsap.to('.box',{
            x:X,
            duration:1,
       })
      X-=200
    }
</script>

3、gsap.killTweensOf()

这个方法用于杀死一个dom元素的所有补间动画

参数为dom元素或者dom元素的选择器名

unmounted() {
    gsap.killTweensOf(this.$refs.myElement); // 停止名为 "myElement" 的动画
  }

4、gsap.quickTo()和gsap.quickSetter()

这是我在gsap文档中发现的两个方法。当需要修改同一dom元素的同一属性值时便可以使用这两个方法,而这两者的区别在于setter是直接设置属性没有过渡,而to则可以产生补间动画。下面是用quickTo制作的鼠标跟随动画

gsap.set(".ball", {xPercent: -50, yPercent: -50});

let xTo = gsap.quickTo(".ball", "x", {duration: 0.6, ease: "power3"}),
    yTo = gsap.quickTo(".ball", "y", {duration: 0.6, ease: "power3"});

window.addEventListener("mousemove", e => {
  // xTo(e.pageX);
  // yTo(e.pageY);
  xTo(e.clientX);
  yTo(e.clientY);
});

画opacity为0.5,此时动画会从0过渡到0.5结束,这在一般情况下是错误的。因为我们想要的是它再次回到opacity为1。

当然这种情况是好解决的,我们只需要把from改成fromTo就行了

当出现类似的问题以及使用repeatRefresh时出现类似问题都可以看看改成fromTo能否解决问题,要记住gsap中补间是相对的

3、同一个函数内可以通过变值来改变

虽然之前说多个gsap对同一属性进行动画会产生冲突,但当我在同一个函数中进行参数变换却能正确的切换动画,

我不知道什么原因,除非在这个函数中每次调用的gsap应用的to方法是同一个to方法对象(因为我们知道一个gsap可以应用多个to方法)

<script setup>
import {gsap} from "gsap";
import {ref} from 'vue';
let X = 1000
function handlerClick(){
       gsap.to('.box',{
            x:X,
            duration:1,
       })
      X-=200
    }
</script>

3、gsap.killTweensOf()

这个方法用于杀死一个dom元素的所有补间动画

参数为dom元素或者dom元素的选择器名

unmounted() {
    gsap.killTweensOf(this.$refs.myElement); // 停止名为 "myElement" 的动画
  }

4、gsap.quickTo()和gsap.quickSetter()

这是我在gsap文档中发现的两个方法。当需要修改同一dom元素的同一属性值时便可以使用这两个方法,而这两者的区别在于setter是直接设置属性没有过渡,而to则可以产生补间动画。下面是用quickTo制作的鼠标跟随动画

gsap.set(".ball", {xPercent: -50, yPercent: -50});

let xTo = gsap.quickTo(".ball", "x", {duration: 0.6, ease: "power3"}),
    yTo = gsap.quickTo(".ball", "y", {duration: 0.6, ease: "power3"});

window.addEventListener("mousemove", e => {
  // xTo(e.pageX);
  // yTo(e.pageY);
  xTo(e.clientX);
  yTo(e.clientY);
});
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值