官网装B神器-GSAP动画

469 阅读6分钟

介绍

  1. 支持移动缩放渐变等常用动画
  2. 支持多个动画时间线控制
  3. 支持鼠标滚动控制动画
  4. 支持定格 pin
  5. 官网:gsap.com/

基础应用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>gsap</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script> 
</head>
<style> 
.gsap .box {
  display: block;
  width: 70px;
  height: 70px;
  margin: 10px;
  border-radius: 12px;
  line-height: 70px;
  text-align: center;
  color: #fff;
  font-size: 16px
}
.gsap .green {
  border: 1px solid green
}
.gsap .red {
  border: 1px solid red
}
.gsap .blue {
  border: 1px solid blue
}
.gsap .purple {
  border: 1px solid purple
}
.gsap .orange {
  border: 1px solid orange
}
</style>

<body>  
  <div class="gsap">
    <div class="box green"></div> 
    <div class="box orange"></div> 
    <div class="box purple"></div> 
  </div>
</body>
</html>

image.png

旋转

 gsap.to(".green", {
  rotation: 360,
  duration: 3, //动画整体 延迟
  ease: "bounce.out", // 缓冲效果
}); 

Dec-30-2024 15-42-46.gif

移动

to

// 移动 从那里出发
gsap.to(".green", {
   x: 500 ,
   y: 200,
   rotation: 1360, //旋转度数
   duration:3, //动画整体 延迟
   repeat:1, // 
   yoyo:1 // 回弹
});

Dec-30-2024 15-43-32.gif

from

// 从那里过来
gsap.from(".green", {
   x: 500 ,
   y: 200,
   rotation: 1360,
   duration:3,
   repeat:1,
   yoyo:1
}); 

Dec-30-2024 15-44-02.gif

fromTo

y 从 300 到 0 移动

//从那里到那里 
gsap.fromTo(".box",{
  y : 300
} ,{
  duration: 2, 
  y: 0,
  repeat:-1, // 一直循环
  yoyo:true,  
});

Dec-30-2024 15-44-45.gif

时间线

根据配置按顺同步执行

    const t1 = gsap.timeline({defaults: {repeat:2,yoyo:true,rotation:360}});
    t1.to(".green", {
      x: 600,
      duration: 1
    }); 
    t1.to(".orange", {
      x: 600,
      duration: 1,
    } );
    t1.to(".purple", {
      x: 600,
      duration: 1,
    });

Dec-30-2024 15-46-28.gif

时间线 参数控制先后

  // 时间参数 ,控制动画的执行顺序 相对于上一个时间。 
const t1 = gsap.timeline();
t1.to(".green", {  x: 600,   duration: 5  }); 
t1.to(".orange", {  x: 600,   duration: 1  }, '<'); // 相对于上一个green
t1.to(".purple", {  x: 600,   duration: 1  }, '-=3');    // 相对于上一个 orange 提前3秒,即2秒开发

Dec-30-2024 17-32-10.gif

回调事件

//时间线所有动画结束时调用
    const t1 = gsap.timeline({duration: 1 ,onComplete: tlComplete});  //这里的duration 是延迟多久后执行
    function tlComplete() {
      console.log("all is complete"); 
    }

    t1.to(".green", {
      x: 300, 
      onComplete: () => console.log("the green is complete")
    }) 

    t1.to(".orange", {
      x: 200,
      onComplete: () => console.log("the orange is complete")
    }) 
    t1.to(".purple", {
      x: 150,
      onComplete: () => console.log("the purple is complete")
    }) 

Dec-30-2024 15-48-52.gif

对象控制

可以传入对象,动态获取变化的值,相当于通过返回参数 自己实现视图的逻辑

   let obj = { myNum: 10, myColor: "red" };
    gsap.to(obj, {
      myNum: 200,
      myColor: "blue",
      onUpdate: () => console.log(obj.myNum, obj.myColor)
    });

Dec-30-2024 15-48-31.gif

进阶

滚动库

要实现鼠标滚动配合动画,需要额外引入 ScrollTrigger.min.js这个库 由于要测试滚动,所以要在头尾都添加添加一个特别长的内容,才能出现滚动条来测试滚动

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script>
</head>
<style>
 
#header {
  margin-top: 2000px;
}
#footer {
 margin-bottom: 2000px;
} 
.gsap {
  color: #000;
  position: relative;
  box-sizing: border-box;
  padding: 20px;
  display: flex;
  border: 1px solid #000;
  justify-content: center;
  flex-direction: column;
  align-items: center;
} 

.gsap .box {
  display: block;
  width: 70px;
  height: 70px;
  margin: 10px;
  border-radius: 12px;
  line-height: 70px;
  text-align: center;
  color: #fff;
  font-size: 16px
}
.gsap .green {
  border: 1px solid green
}
.gsap .red {
  border: 1px solid red
}
.gsap .blue {
  border: 1px solid blue
}
.gsap .purple {
  border: 1px solid purple
}
.gsap .orange {
  border: 1px solid orange
}
.gsap .box.active {
  background-color: red;
}
 
</style>
<body> 
  <div id="header">
  </div>
  <div class="gsap">
    <div class="box green"></div>
    <div class="box purple"></div>
     <div class="box orange"></div>
  </div>
  <div id="footer">
  </div>
  <script>
 
  </script>
</body>

</html>

image.png

ScrollTrigger.create

重要的参数:

  • animation:对应的时间线
  • markers:是否打开调试模式
  • trigger:需要绑定滚动的元素
  • scrub:只有打开才会跟鼠标滚动同步
  • start :两个参数, 注意 start :top center 第一个参数 top 是指traget的检测边距,第二个参数是指 浏览器的检测边距center,当两者相交后触发。
  • end : 两个参数, 对应动画结束的位置,bottom为target元素的底部,center为浏览器的中间为检查线。

Dec-30-2024 16-58-54.gif

ScrollTrigger 写法 
let tl2 = gsap.timeline()
tl2.to('.green', {scale: 2, duration: 1})
ScrollTrigger.create({
    animation: tl2,
    markers: true,
    start: "top center", // top为target元素的顶部,center为浏览器的中间位置
    end: "bottom center", //bottom为target元素的顶部,center为浏览器的中间位置
    trigger: ".gsap", // 整个 div.gsap为触发对象
    scrub: true, // 是否与鼠标同步
    pin: false, // 是否触发时,动画固定不动
});

我们可以看到当触发 整个div.gsap元素的top与浏览器中线相交时候,触发滚动逻辑。动画开始跟鼠标同步播放。 注意:动画变大后是不会还原,但是当鼠标回滚的时候会还原

pin 固定

Dec-30-2024 17-00-11.gif

let tl2 = gsap.timeline()
tl2.to('.green', {scale: 2, duration: 1})
ScrollTrigger.create({
  animation: tl2,
  markers: true,
        start: "top center",
        end: "bottom center",
        trigger: ".gsap",
        scrub: true,
        pin: true,
});

当我们设置pin:true后 当触发动画时,内容会一直固定在触发的位置,直到动画结束

特别长的滚动

Dec-30-2024 17-00-54.gif 可以通过start/end设置,来实现特别长的滚动效果

start

  • "top -100px" //元素顶部要超过顶部100px才出发
  • "100px center" //元素顶部接触浏览器中线后100px才触发

end

  • "+=5000px" 等价于 5000px center
  • "+=5000px" 等价于 top + 5000px center 就是元素顶部接触浏览器中线后 要经过5000px才触发

let tl1 = gsap.timeline()
tl1.to('.purple', {scale: 2, duration: 1})
ScrollTrigger.create({
  animation: tl1,
        markers: true,
        start: "top center", 
        end: "+=5000px", // 当前的top 再加上 5000px 的距离
        trigger: ".gsap",
        scrub: true,
        pin: ".gsap",
});

gsap trigger写法

 gsap.to(".purple", {
      rotation: 360, 
      backgroundColor: "orange",
      scrollTrigger: {
        markers: true,
        start: "top center",
        end: "bottom center",
        trigger: ".purple",
        scrub: 1,
      },
    });

样式开关

Dec-30-2024 17-01-44.gif

gsap.to(".green", {
    scrollTrigger: {
    trigger: ".green",
    start: "top center",
    end: "bottom center",
    markers: true,
    scrub: true,
    toggleClass: {
        targets: ".green",
        className: "active", // 激活时候背景为红色
    }
    }, 
    duration: 1000, // 这里延迟意义不大,是根据实际滚动的帧来决定时间  
});

多个动画时间线

Dec-30-2024 17-02-18.gif

const tl = gsap.timeline({
    scrollTrigger: {
    trigger: ".green",
    scrub: true,
    markers: true,
    start: "top center",
    end: "bottom center",
    },
});
tl.to(".green", {
    x: 100,
    scale: 1.5,
});
tl.to(".purple", {
    scale: 2,
    x: 200,
});
tl.to(".orange", {
    scale: 2.5,
    x: 300,
});

视频控制

通过currentTime参数控制视频

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script>
  <link href="./gsap.css" rel="stylesheet">
</head>
<style>
  #header {
    margin-top: 2000px;
  }

  #footer {
    margin-bottom: 2000px;
  }

  .gsap {
    color: #000;
    position: relative;
    box-sizing: border-box;
    padding: 20px;
    display: flex;
    border: 1px solid #000;
    justify-content: center;
    flex-direction: column;
    align-items: center;
  } 
</style>

<body>
  <div id="header">
  </div>
  <div class="gsap">
    <video muted="" webkit-playsinline="" playsinline="" preload="auto" name="media" class="myvideo"
      poster="https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png">
      <source src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/av1/360/Big_Buck_Bunny_360_10s_1MB.mp4"
        type="video/mp4">
    </video>
  </div>
  <div id="footer">
  </div>

  <script>
    var tl3 = gsap.timeline(); 
    ScrollTrigger.create({
      trigger: '.gsap',
      animation: tl3,
      start: 'top center',
      end: "bottom center",
      pin: true,
      scrub: true,
      markers: true
    })

    //移动端兼容处理 
    var video1 = document.querySelector('.myvideo');
    if (window.innerWidth < 1200) {
      video1.play().then(function () {
        video1.pause();
      })
    }
    tl3.to(video1, {
      currentTime: video1.duration || 10,
      ease: "none",
      duration: 10
    })
  </script>
</body>

</html>

Dec-30-2024 17-57-17.gif

视频2 + 文字

通过事件onEnter,onComplete和onReverseComplete在对应的地方做视频的播放控制

Jan-02-2025 13-43-13.gif

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
</head>
<style>
  #header {
    margin-top: 2000px;
  }

  #footer {
    margin-bottom: 2000px;
  }

  .container {
    position: relative;
    height: 80vh;
  }

  .video-wrapper {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }

  .video-wrapper .video-box {
    width: 100%;
    height: 100%;
  }

  .video-wrapper video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    position: absolute;
  }
  .tips {
    z-index: 9999;
    position: absolute;
    color: red;
    font-size: 80px;
    opacity: 0;
  }
</style>

<body>
  <div id="header">
  </div>
  <div class="container">
    <div class="tips t1">
      test1
    </div>
    <div class="tips t2">
      test2
    </div> 
    <div class="video-wrapper">
      <div class="video-box">
        <video muted="" webkit-playsinline="" playsinline="" preload="auto" name="media" class="video part1">
          <source src="https://cdn.pixabay.com/video/2020/05/08/38521-418027189_tiny.mp4?download" type="video/mp4">
        </video>
        <video muted="" webkit-playsinline="" playsinline="" preload="auto" name="media" class="video part2"
          loop="true">
          <source src="https://test-videos.co.uk/vids/bigbuckbunny/mp4/av1/360/Big_Buck_Bunny_360_10s_1MB.mp4" type="video/mp4">
        </video> 
        <video muted="" webkit-playsinline="" playsinline="" preload="auto" name="media" class="video part3"
          loop="true"> 
          <source src="https://cdn.pixabay.com/video/2024/11/17/241802_tiny.mp4?download" type="video/mp4">
        </video>
      </div>
    </div>
  </div>

  <div id="footer">
  </div>

  <script>

    var endPosition = '+=1800px'; 
    var video1 = document.querySelector('video.part1');
    var video2 = document.querySelector('video.part2');
    var video3 = document.querySelector('video.part3');
    let videoList = [video1, video2, video3]

    function video1play(isPlay) {
      $('.video.part1').show();
      $('.video.part2').hide();
      $('.video.part3').hide();
      isPlay ? video1.play() : video1.pause()
      video2.pause()
      video3.pause()
    }
    function video2play() {
      $('.video.part1').hide();
      $('.video.part2').show();
      $('.video.part3').hide();
      video1.pause()
      video2.play()
      video3.pause()
    }
    function video3play() {
      $('.video.part1').hide();
      $('.video.part2').hide();
      $('.video.part3').show();
      video1.pause()
      video2.pause()
      video3.play()
    }

    var tl50 = gsap.timeline({ default: { ease: "power3.inOut" } });
    tl50.fromTo(`.tips.t1`, { y: 10, opacity: 0 }, { // 显示第一个标签
      y: 0, opacity: 1, duration: 1, onComplete: () => {
        video2play()
      }, onReverseComplete: function () {
        video1play(true)
      }
    })  
    .to('.tips.t1', {  opacity: 0, display: 'none', duration: 1 }) // 隐藏第1个标签
    .fromTo('.tips.t2', { y: 10, opacity: 0 }, {  y: 0, opacity: 1, duration: 1}) // 显示第3个标签
    .to('.tips.t2 ', {
        opacity: 0, display: 'none', duration: 1, onComplete: () => {
          video3play()

        }, onReverseComplete: function () {
          video2play()
        }
      })
    ScrollTrigger.create({
      trigger: '.container',
      animation: tl50,
      start: 'center center',
      end: endPosition,
      pin: true,
      markers: true,
      scrub: true,
      onEnter: function () {
        video1play(true)
      },
      onEnterBack: function () {
        // video1play(false) 
      }
    }) 
  </script>
</body>

</html>

swiper + 滚动

通过动态创建gsap.timeline()并且根据当前显示的宽度动态设置 x的坐标

  • 即: x启始位置 = 当前浏览器宽度 - 实际元素的x - 实际元素的宽度/2
  • 使用fromTo进行动画移动效果
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.6.0/ScrollTrigger.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@latest/swiper-bundle.min.css">
  <script src="https://cdn.jsdelivr.net/npm/swiper@latest/swiper-bundle.min.js"></script>
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>

</head>
<style>
  #header {
    margin-top: 2000px;
  }

  #footer {
    margin-bottom: 2000px;
  } 

  .main-container {
      position: relative; 
      background: none no-repeat center center;
      background-size: cover;
      height: calc(100vh - 70px);
      color: #fff;
      overflow: hidden; 
  }  
  .container {
      color: #000;
      text-align: center;
      padding: 0 0 1.5em; 
      color: #fff;
  }
  .container .swiper-slide img {
      width: 100%;
      border-radius: 0.28em;
  }
  .container  .wrapper {
      max-width: 800px;
      padding: 0 20px; 
  }
  .container .swiper-container {
      padding: 0.74em 0;
  }
  .main-container .swiper-pagination {
      top: auto !important;
      bottom: 0;
      left: 50% !important;
      transform: translateX(-50%);
      max-width: 24%;
      background-color: #EFEDE9;
      height: 3px;
  }  
</style>

<body>
  <div id="header">
  </div> 
  <div class="main-container container">
    <div style="position: relative;">
        <div class="wrapper">
            <div class="swiper-container">
                <div class="swiper-wrapper">
                    <div class="swiper-slide swiper-slide1">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide2">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide3">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide4">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide5">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide6">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide7">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                    <div class="swiper-slide swiper-slide8">
                        <img src="https://picsum.photos/200" alt="">
                    </div>
                </div>
                <div class="swiper-pagination"></div>
            </div>
        </div>
        <span class="icon-arrow-right arrow-left"></span>
        <span class="icon-arrow-right arrow-right"></span>
    </div>
  </div>

  <div id="footer">
  </div>

  <script>
 
      var mySwiper = new Swiper('.container .swiper-container', {
          slidesPerView: 6,
          slidesPerGroup: 6,
          spaceBetween: 15,
          pagination: {
              el: '.container .swiper-pagination',
              type : 'progressbar',
          },
          breakpoints: {
              1023: {
                  slidesPerView: 4,
                  slidesPerGroup: 4,
              },
              767: {
                  slidesPerView: 3,
                  slidesPerGroup: 3,
              }
          },
          navigation: {
              nextEl: '.container .arrow-right',
              prevEl: '.container .arrow-left',
          },
      })
      var num = 6;
      // if($(window).width() < 1023) { // 可以根据当前浏览器的宽度进行判断
      //     num = 4;
      // } 

      for(i = 1;i<=num;i++){
          window['timelineItem'+ i] =  gsap.timeline()
          window['timelineItem'+ i].fromTo('.container .swiper-slide'+i, { x: $(window).width()/2 - $('.container .swiper-slide'+i).offset().left - $('.container .swiper-slide').width()/2 },
          { x: 0 })
          .to('.container .icon-arrow-right', { display: 'block' });
          ScrollTrigger.create({
              trigger: '.container',
              animation: window['timelineItem'+ i],
              start: 'top center',
              end: "bottom top",
              pin: true,
              scrub: true,
              markers :true
          })
      }
  </script>
</body>

</html>

Dec-31-2024 09-49-42.gif

源码地址

github.com/mjsong07/gs…