前端动画库之gsap

一、GSAP基础入门

1.动画方法
javascript 复制代码
import gsap from "gsap";
const fun1 = () => {
  // gsap.to()的意思是从当前位置移动到设置的位置
  gsap.to(".red_box", {
    x: 500, //沿x轴移动500px
    duration: 1, //动画时长2秒
    ease: "power1.inOut", //运动效果,匀速、加速、减速等等
  });
};
const fun2 = () => {
  // gsap.from()的意思是从设置位置移动到当前位置
  gsap.from(".red_box", {
    x: 500, //沿x轴移动500px
    duration: 1, //动画时长2秒
    ease: "power1.inOut", //运动效果,匀速、加速、减速等等
  });
};
const fun3 = () => {
  // gsap.fromTo()的意思是从设置位置移动到目标位置
  gsap.fromTo(
    ".red_box",
    {
      x: 500,
    },
    { x: 200 }
  );
};
const fun4 = () =>{
  // 和gsap.to()一样,设置属性值,但不会执行动画
  gsap.set(".red_box",{
    x: 500,
  })
}
2.动画配置项
javascript 复制代码
  gsap.to(".red_box", { //这个选择器支持数组['.red_box','.red_box2'],用于选择多个元素执行相同动画
    x: "random(-100,100)", // any properties (not limited to CSS)
    backgroundColor: "red", // 背景颜色
    duration: 1, // 动画时长
    delay: 0.5, // 0.5秒后开始执行动画,延迟时间,默认0
    ease: "none", // 缓动效果,具体值查看gsap文档https://gsap.com/docs/v3/Eases
    // stagger: 0.1, // 因为.red_box可能是多个元素,stagger属性可以设置多个元素动画开始的间隔时间
    stagger: {
      //each: 0.1,
      amount:1,  //交错的总量,总体间隔1s内完成动画,不管有多少个元素,10个也是1s,100个也是1s
      from:"start", //注意,这个是从第一个元素开始交错动画,可选值有end(最后一个元素) center(中间的元素)edges(两侧元素) random(随机某个元素)
      grid:[4,3], //弹性布局和grid布局时,上面的from的center可能并不是布局上的center,可以通过[4,3]告诉gsap当前布局是4列3行的布局,它会去找真正的center
      axis:"y",//也是弹性布局和grid布局,设置按行或者列,整体移动,可选值有x y auto
      repeat: -1,
      yoyo: true,  //这里面的yoyo和repeat是单独针对每一个动画的,外面的yoyo和repeat是针对整个动画结束后再yoyo和重复
    },
    paused: false, // 动画是否暂停,默认是false
    // overwrite: "auto", // default is false
    repeat: -1, // 动画重复次数,默认是0,-1为无限循环,如果yoyo等于true,返回的那次动画也算一次repeat
    repeatDelay: 0, // seconds between repeats 重复动画之间的延迟时间
    repeatRefresh: false, // 设置为true时,每次动画都会重新计算开始结束值,在使用动态值(相对值、随机值或基于函数的值)时非常有用,比如上面的x
    yoyo: true, // if true > A-B-B-A, if false > A-B-A-B  是否反向,就是要不要回来
    yoyoEase: true, // or ease like "power2"
    immediateRender: false,
    onComplete: () => {
      // 很多回调,看官网文档了
      console.log("finished");
    },
  });
3.控制方法
javascript 复制代码
anim.play() // 播放动画 如果运动方向是A -> B,调用reverse()方法后,运动方向是B -> A,执行play()方法后,运动方向是A -> B
  .pause()  // 暂停动画
  .resume() // 播放动画 如果运动方向是A -> B,调用reverse()方法后,运动方向是B -> A,执行resume()方法后,运动方向是B -> A
  .reverse()  //反向播放动画
  .restart()  // 重新从初始位置播放动画
  .timeScale(2) // 动画播放倍速
  .seek(1.5) // 动画直接跳转到某个时间点
  .progress(0.5) // 直接跳转到某个百分比播放动画,不包括repeats
  .totalProgress(0.8) //直接跳转到某个百分比播放动画,包括repeats
  // when used as setter, returns animation (chaining)

  // other useful methods (tween and timeline)
  .kill() // 立即销毁动画,这里有很多传参,可以销毁动画里面的某个属性,比如x,具体看官网
  .isActive() // 当前动画是否被激活
  .then() // 当动画完成会触发这个then
  .invalidate() // 刷新任何内部记录的开始/结束值
  .eventCallback() // 设置或获取某个时间的回调函数

  // timeline-specific methods
  // add label, tween, timeline, or callback
  .add(thing, position)
  // calls function at given point
  .call(func, params, position)
  // get an Array of the timeline's children
  .getChildren()
  // empties the timeline
  .clear()
  // animate playhead to a position linearly
  .tweenTo(timeOrLabel, {vars})
  // ^^ with both start and end positions
  .tweenFromTo(from, to, {vars})
4.实用工具方法
javascript 复制代码
// accessible through gsap.utils.foo()
	checkPrefix() // 指定任何CSS属性名,它将返回该属性的适当的浏览器前缀版本
	clamp(min,max,x) // 如何x在范围内就直接返回x,大于max就返回max,小于min就返回min
	distribute() // distribute value among and array
	getUnit("50%") // 获取单位 %
	interpolate("20px", "40px", 0.5); // "30px" 详情看文档,用法很多
	mapRange(-10, 10, 100, 200, 0) //0在[-10 10]的50%处,所以返回150,因为[100,200]的50%是150 将数字在一个范围内的相对位置映射到另一个范围中的等效位置
	normalize(0, 100, 25) //0.25 获取当前值在给定范围的百分比值,返回值范围0-1
	pipe() // sequence function calls
	random(1, 491, 6) // 随机获取[1,491]范围内的值,如果传递了第三个参数,就随机获取[1-491]范围内的6倍数的整数
	selector() // get a scoped selector function
	shuffle([1,2,3,4,5]) // 洗牌,随机打乱数组,但是不会返回一个新的数组,是把原数组打乱
	snap(10, 23.5) // 20 返回第二个参数最接近第一个参数倍数的值
	splitColor("red")) // splits color into RGB array [255, 0, 0]
	toArray() // convert array-like thing to array
	unitize() // adds specified unit to function results
	wrap(["red", "green", "yellow"], 6) //red第二个参数是下标,如果超过了数组长度就从头继续数
	wrapYoyo(["red", "green", "yellow"], 6); // 和wrap差不多,但是每次都是来回数,不是都是从头数
5.动画默认值非动画配置项
javascript 复制代码
//修改动画的全局默认值
gsap.defaults({
	duration:1
})
//非动画配置项
// you only need to define the configuration settings you want to CHANGE. Omitted properties won't be affected.
gsap.config({
  autoSleep: 60, //在查看GSAP是否应该关闭内部计时器以节省系统资源和移动设备上的电池寿命的内部检查之间应该经过多少帧?默认值是120(大约每2秒一次)
  force3D: false, //默认auto,可选 false true 是否开始tranlate3d()属性,auto是动画过程中开启,结束关闭, false是一直关闭,true是一直开启
  nullTargetWarn: false,
  trialWarn: false,
  units: { left: "%", top: "%", rotation: "rad",x:"vw" }, //修改属性的默认单位
});
6.创建时间线
javascript 复制代码
// Create a timeline
let tl = gsap.timeline({
	delay: 0.5,
	paused: true, // default is false
	repeat: 2, // number of repeats (-1 for infinite)
	repeatDelay: 1, // seconds between repeats
	repeatRefresh: true, // invalidates on each repeat
	yoyo: true, // if true > A-B-B-A, if false > A-B-A-B
	defaults: {
		// 这里面的配
		duration: 1,
		ease: 'none',
		repeat:2,
		yoyo:true,
	},
	smoothChildTiming: true,
	autoRemoveChildren: true,
	onComplete: () => {
		console.log("finished")
  	},
	// other callbacks:
	// onStart, onUpdate, onRepeat, onReverseComplete
});

时间线动画的用法也是to,from,fromTo,set,这里主要讲一下第三个位置参数

javascript 复制代码
// position parameter (controls placement)
tl.to(target, { toVars }, positionParameter);
tl.addLabel("myLabel", 0);  //给时间线上的某个时间点打一个标签,设置动画位置的时候可以用
0.7; // 时间线的0.7s处开始运动,绝对值
('-=0.7'); // 在上一个动画的结束位置提前0.7秒运动,+=0.7就是等待0.7秒运动
('myLabel'); // 使用myLabel标签时间点,如果不存在这个标签,就加到时间线的最后面
('myLabel+=0.2'); // myLabel标签时间点等待0.2秒
('<'); // 上一个动画的起始位置开始运动
('>'); // 上一个动画的结束位置开始运动,默认值就是这个
('<0.2'); // 上一个动画的起始位置等待0.2秒运动,<-0.2就是提前0.2秒运动
('-=50%'); // overlap half of inserting animation's duration
('<25%'); // 25% into the previous animation (from its start)
7.对象属性变化

gsap不仅仅是对css属性做动画,对象也可以

javascript 复制代码
import gsap from "gsap";
import { onMounted, ref } from "vue";

let counter = ref(0)
onMounted(() => {
 gsap.to(counter,{
  value: 100,
  duration: 5,
  ease: "power1.out",
  snap:{
    value:5 //设置value属性每次变化都是5的倍数
  }
 })
});
8.Media响应式
javascript 复制代码
  let mm = gsap.matchMedia(); //创建响应式媒体查询
  // 宽度大于500px才执行动画
  // mm.add("(max-width: 500px)", () => {
  //   gsap.to(counter, {
  //     value: 100,
  //     duration: 1,
  //     snap: {
  //       value:5
  //     },
  //   });
  // });

  // mm.add("(width >= 500px)", () => {
  //   gsap.to(".red_box", {
  //     x: 100,
  //     duration: 1,
  //     snap: {
  //       value: 5,
  //     },
  //   });
  // },".box");  //第三个参数是作用范围,表示只有在box下的元素才会执行动画

  /**
   * PC端执行一个动画,移动端执行另一个动画
   */
  // mm.add({
  //   isDesktop: "(width >= 500px)",  // 判断是否是PC端
  //   isMobile: "(width < 500px)", // 移动端
  // },(context) =>{
  //   const {isDesktop,isMobile} = context.conditions;
  //   gsap.to(".red_box",{
  //     x: isDesktop ? 100 : 200,
  //     duration: 1,
  //   })
  // });

  mm.add("(width >= 500px)", () => {
    gsap.to(".red_box", {
      x: 100,
      duration: 1,
      snap: {
        value: 5,
      },
    });
    return () => {
      // 这个函数的执行时机是,从触发动画条件变成不满足的时候
      //如果一开始就不满足动画条件,那么这个函数不会执行
      mm.revert();  //让响应式动画失效
      console.log("clean");
    };
  });

二、GSAP插件

1.GSDevTools

动画时间轴,可以很直观的调试动画,灰常有用

javascript 复制代码
import gsap from "gsap";
import { GSDevTools } from "gsap/GSDevTools";
import { onMounted, ref } from "vue";
gsap.registerPlugin(GSDevTools);
onMounted(() => {
  const lt = gsap.timeline({
    id: "1",
  });
  const lt2 = gsap.timeline({
    id: "2",
  });
  lt.to(".red_box", {
    id: "red1", //可以用于插件左下角选择项
    x: 300,
    duration: 1,
    ease: "power1.inOut",
  }).to(".red_box", {
    id: "red2", //可以用于插件左下角选择项
    rotate: 360,
    duration: 1,
  });
  GSDevTools.create({
    animation: lt,  //绑定某个timeline
    css: {
      bottom: 50,  //设置插件的位置
    },
  });
  GSDevTools.create({
    animation: lt2,
    css: {
      bottom: 0,
    },
  });
});
2.ScrollTrigger滚动

滚动相关的插件,适用于和滚动条位置有关的动画

javascript 复制代码
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { onMounted, ref } from "vue";
gsap.registerPlugin(ScrollTrigger);
onMounted(() => {
  gsap.to(".red_box", {
    x: 300,
    duration: 1,
    ease: "power1.inOut",
    scrollTrigger: ".red_box", //目标元素进入到可视区域内才触发动画,这里的.red_box不一定等于第一个参数
  });
  gsap.to(".blue_box", {
    x: 800,
    duration: 8,
    ease: "none",
    // scrollTrigger: ".blue_box",
    scrollTrigger:{ //进阶用法
      trigger:".blue_box",  //等同于上面的直接写的.blue_box
      // start: 100,  //设置触发点,滚动条走到100px的时候触发动画,这种方式用的较少
      start:"top center", //第一个参数是目标元素,第二个参数是可视区,这里的意思是目标元素的顶部到达可视区顶部触发动画
      markers:true, //参考线
      toggleActions:"pause none none pause", //触发器的行为,分别是start进入 end离开 end进入 start离开,这里的start end是目标元素的参考线
      // scrub: true, // 让动画跟随滚动条一起滚动,配置了这个选项,toggleActions就失效了
      scrub: 1, //在上面的基础上加1s的惯性
      pin: true, //在动画的持续时间里,y轴位置固定
    },  
  });
  gsap.to(".yellow_box", {
    x: 300,
    duration: 1,
    ease: "power1.inOut",
    scrollTrigger: ".yellow_box",
  });

  //   const lt = gsap.timeline({
  //   id: "1",
  //   scrollTrigger:".red_box"
  // });
  // lt.to(".red_box", {
  //   id: "red1",
  //   x: 300,
  //   duration: 1,
  //   ease: "power1.inOut",
  // }).to(".red_box", {
  //   id: "red2",
  //   rotate: 360,
  //   duration: 1,
  // });
});
3.ScrollSmoother插件

给滚动添加一个平滑滚动效果,和ScrollTrigger搭配使用

html 复制代码
  <div class="home_container">
    <button @click="fun1">按钮</button>
    <!-- smooth-wrapper smooth-content是ScrollSmoother插件的默认id,可以修改,但是没必要-->
    <div id="smooth-wrapper">
      <div id="smooth-content">
        <!-- 这里面放需要平滑滚动的内容 -->
        <div class="box red_box" data-speed="1">1</div>
        <div class="box blue_box" data-speed="1">2</div>
        <div class="box yellow_box" data-speed="1">3</div>
      </div>
    </div>
  </div>
javascript 复制代码
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { ScrollSmoother } from "gsap/ScrollSmoother";
import { onMounted, ref } from "vue";
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
let smooth = ScrollSmoother.create({
  // smooth: 1,  //1s滑动效果
  // effects: true, //支持一些功能的启动:speed->速度,需要通过在元素上添加自定义属性开启  lag->滞后,也是在元素上加自定义属性
})
// smooth.effects(".box", {
//   // speed: 1,  //也可以通过这里设置speed和lag
//   // lag: 1,
//   speed: (i) =>{return i * 2},
//   lag:  (i) =>{return i * 2},  //支持函数,更加灵活
// });
const fun1 = () =>{
  smooth.scrollTo(".yellow_box",true,"center top");  //目标元素,第二个参数表示是否平滑滚动,第三个参数分别表示目标元素的顶部滚到可视区域的顶部
}
4.Flip插件

给dom结构变化添加动画

javascript 复制代码
import gsap from "gsap";
import { Flip } from "gsap/Flip";
import { onMounted, ref } from "vue";
gsap.registerPlugin(Flip);
const fun1 = () => {
  let red = document.querySelector(".red_box");
  let blue = document.querySelector(".blue_box");
  //把元素的状态存起来
  let state = Flip.getState([blue, red]);
  //原生js的before方法,将blue元素插入red元素之前
  red?.before(blue);
  // red.style.width = "200px";
  // 添加动画
  Flip.from(state, {
    duration: 1,
    repeat: -1,
    yoyo: true,
    ease: "power1.inOut",
    scale: true,  //缩放,插件特有
    spin: true, //旋转,插件特有
  });
};
5.TextPlugin

用于文字转换

javascript 复制代码
import gsap from "gsap";
import { TextPlugin } from "gsap/TextPlugin";
import { onMounted, ref } from "vue";
gsap.registerPlugin(TextPlugin);
const fun1 = () => {
  gsap.to(".text", {
    duration: 1,
    text: {
      value: "hello bitch fuck you ! ! ! ! ! ", //要替换成的内容
      delimiter: " ", //分隔符,对英文有用,这样可以一个单词一个单词的替换,中文就没必要了
      /**
       * 新添加的类名,注意它会给文字加一个span标签,这里的类名是添加在这个span标签上面的,
       * 在<style scoped>中定义这个新的类名,样式会不生效,至于为啥,好好思考思考
>
       */
      newClass: "red",
      oldClass: "blue", //旧的类名
      padSpace:true, //当要新文字多旧文字的时候,会给就旧文字添加空格成一样的长度
      rtl: true, //变化方向,默认是从左到右,这个是从右到左
    },
  });
};
6.SplitText

用于文字分割

javascript 复制代码
import gsap from "gsap";
import { SplitText } from "gsap/SplitText";
import { onMounted, ref } from "vue";
gsap.registerPlugin(SplitText);
const fun1 = () => {
  /**
   * type:lines  一行为标准
   * type:words  一个单词为标准
   * type:chars  一个英文字母为标准
   * mask  遮罩的意思,只能看到选中元素可视范围内的动画,超过的看不到
   */
  const split = new SplitText(".text", { type: "lines",mask:"lines" });  //创建SplitText实例
  gsap.from(split.lines, {
    duration: 1,
    y: 300,
    stagger: 0.5,
    onComplete: () => {
      split.revert();  //动画结束后,把元素还原,因为这个动画会把原来的文字进行切割
    },
  });
};
7.Observer监听
javascript 复制代码
import gsap from "gsap";
import { Observer } from "gsap/Observer";
import { onMounted, ref } from "vue";
gsap.registerPlugin(Observer);
onMounted(() => {
  Observer.create({
    target: ".text",  //默认是window
    /**
     * wheel 监听滚轮
     * touch 监听触摸,移动端用
     * pointer 监听鼠标
     * scroll 监听滚动条
     */
    type: "pointer",
    onDown: () => {
      console.log("向下滑动");
    },
    onUp: () => {
      console.log("向上滑动");
    },
    onLeft: () => {
      console.log("向左滑动");
    },
    onRight: () => {
      console.log("向右滑动");
    },
    onChangeX: () => {
      // 包括左右滑动
      console.log("X轴滑动");
    },
    onChangeY: () => {
      // 包括上下滑动
      console.log("Y轴滑动");
    },
    onMove: () => {
      // 鼠标移动
      console.log("鼠标移动");
    },
    lockAxis:true, //锁定滑动方向,比如一开始就y轴滑动,那么就只能y轴滑动,x轴滑动不会触发回调了
  });
});
8.Draggable拖拽和InertiaPlugin惯性
javascript 复制代码
import gsap from "gsap";
import { Draggable } from "gsap/Draggable";
import { InertiaPlugin } from "gsap/InertiaPlugin";
import { onMounted, ref } from "vue";
gsap.registerPlugin(Draggable, InertiaPlugin);
onMounted(() => {
  Draggable.create(".red", {
    type: "x", //限制拖拽,只能x轴平移
    inertia: true, //惯性
  });
  Draggable.create(".blue", {
    type: "y", //限制拖拽,只能y轴平移
    inertia: true, //惯性
  });
  Draggable.create(".yellow", {
    type: "rotation", //旋转
    inertia: true, //惯性
    snap:(value) => {
      return Math.round(value / 90) * 90;  //限制为最终旋转角度为90°
    }
  });
});
9.DrawSVGPlugin

svg绘制

html 复制代码
  <div class="container">
    <button @click="fun1">动画按钮</button>
    <svg
      width="200"
      height="200"
      viewBox="0 0 200 200"
      xmlns="http://www.w3.org/2000/svg"
    >
      <!-- 模板路径 -->
      <path
        d="M 50,150 
       A 80,80 0 0,1 150,150"
        stroke="black"
        stroke-width="5"
        fill="none"
      />
      <!-- 动画路径 -->
      <path
        d="M 50,150 
       A 80,80 0 0,1 150,150"
        stroke="blue"
        stroke-width="20"
        fill="none"
        id="path"
      />
    </svg>
  </div>
  <style>
  #path {
  stroke-dasharray: 0px 10000px; //虚线 第一个参数是虚线的大小 第二个参数是虚线间隔
}
  </style>
javascript 复制代码
import { gsap } from "gsap";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";
import { onMounted } from "vue";
gsap.registerPlugin(DrawSVGPlugin);

const fun1 = () => {
  gsap.to("#path", {
    duration: 2,
    // drawSVG: "100%", //绘制svg,就是更改stroke-dasharray第一个参数
    drawSVG:"40% 70%",  //两个值就是绘制svg的开始和结束位置,但是这样会有一个从0开始的动画
    ease: "power2.inOut",
  });
};
onMounted(() => {
  gsap.set("#path", {
    drawSVG:"40% 40%",  //这样结合上面的动画,就可以实现直接从40%开始绘制,不要取class里面设置40%,那里的40%并不是整个svg的40%
    ease: "power2.inOut",
  });
})
10.MorphSVGPlugin

svg变化形状

javascript 复制代码
import { gsap } from "gsap";
import { MorphSVGPlugin } from "gsap/MorphSVGPlugin";
import { onMounted } from "vue";
gsap.registerPlugin(MorphSVGPlugin);

const fun1 = () => {
  gsap.to("#path", {
    duration: 2,
    morphSVG: "M0,0 L0,100 L100,100 L100,0 Z",  //要变化的svg,可以直接写路径,也可以写id来选中某一个路径
  });
};