Skip to content

GSAP

Scroll插件:ScrollTrigger、ScrollTo、Observer
SVG插件:MotionPath
UI插件:Flip、Draggable
Text插件:Text
针对其他框架的插件:Easel、Pixi

svg动画

javascript
gsap.to('.ball', {
  duration: 3, 
  repeat: -1,
  ease: 'none',
  // svg动画
  motionPath: {
    // path: 'M 10 80 C 80 10, 130 10, 190 80 S 300 150, 360 80', // svg图像的path标签或者path路径值
    path: '#motion-path',
    align: '#motion-path', // 一般和path保持一致 让目标对象和路径x,y坐标合二为一
    autoRotate: true, // 按路径方向自动旋转角度
    alignOrigin: [0.5, 0.5], // 一般设置为[0.5, 0.5],即目标对象中心点
    // start: 0.3, // 路径动画起点, 默认即0
    // end: 0.8, // 路径动画结束点, 默认即1,可以大于1
  }
});

gsap的svg动画需要安装插件,gsap有好几个svg插件,其中MotionPath是免费的,其他插件需要付费使用。MotionPath常用的几个属性如上面motionPath所示。更多知识点参考文档即可

时间轴动画

javascript
  const tl = gsap.timeline({
    repeat: 0,
    onComplete: () => {
      // 移出时间轴动画元素,重复动画,再重新执行动画
      tl
      .killTweensOf('.item2')
      .repeat(-1)
      .restart()
    }
  })

  tl
  .to('.item1', {
    duration: 0.5,
    translateY: 260,
    ease: 'bounce.inOut',
  })
  .to('.item2', {
    duration: 0.5,
    translateY: 260,
  }, '+=0.001')
  .to('.item2', {
    duration: 0.5,
    rotate: 180,
  }, '+=0.001')
  .to('.item3', {
    duration: 0.5,
    translateY: 260,
    scale: 2
  }, '+=0.001')

无缝滚动

如果不需要单步停顿,实现会更容易

javascript
tl = gsap.timeline({
  repeat: -1
})

tl
.to('.wrap-inner', {
  duration: 12,
  ease: 'none',
  translateX: `-${scrollWidth}`,
  onComplete: () => {
    console.log('haha')
  }
})

// 单步停顿
tl = gsap.timeline({})
const animeteFuc = () => {
  tl
  .to('.wrap-inner', {
    duration: 3,
    ease: 'none',
    translateX: `-${scrollWidth}`, // 单步移动的距离
    onComplete: () => {
      if (scrollIndex >= scrollLength) {
        scrollIndex = 1
        scrollWidth = itemWidth
        // 重置至起始位置
        tl
        .set('.wrap-inner', {
          translateX: 0
        })
      } else {
        scrollIndex += 1
        scrollWidth = itemWidth * scrollIndex
      }
      // 单步停顿的无缝滚动
      setTimeout(() => {
        animeteFuc()
      }, 1000)
    }
  })
}
animeteFuc()

完整示例

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>gsap单步停顿的无缝滚动</title>
  <style>
    .wrap {
      width: 600px;
      height: 200px;
      border: 1px solid #333;
      margin: 0 auto;
      padding: 10px;
      box-sizing: border-box;
      overflow: hidden;
    }
    .wrap-inner {
      font-size: 0px;
      height: 180px;
    }
    .item {
      width: 180px;
      height: 180px;
      display: inline-block;
      font-size: 12px;
      color: #fff;
      text-align: center;
      line-height: 180px;
      background-color: burlywood;
      margin-right: 10px;
    }
  </style>
</head>
<body>
<div class="wrap">
  <div class="wrap-inner" onmouseenter="onEnter()" onmouseleave="onLeave()">
    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item3">4</div>

    <div class="item item1">1</div>
    <div class="item item2">2</div>
    <div class="item item3">3</div>
    <div class="item item3">4</div>
  </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script>
  let tl = null
  document.addEventListener('DOMContentLoaded', (event) => {

    const wrapEl = document.getElementsByClassName('wrap-inner')
    const itemEl = document.getElementsByClassName('item')
    const rect = itemEl[0].getBoundingClientRect()
    const marginEl = window.getComputedStyle(itemEl[0], null)
    const itemWidth = rect.width + parseFloat(marginEl.marginLeft) + parseFloat(marginEl.marginRight)
    wrapEl[0].style.width = `${itemWidth * itemEl.length}px`

    let scrollWidth = itemWidth
    let scrollIndex = 1
    const scrollLength = itemEl.length / 2

    tl = gsap.timeline({})

    const animeteFuc = () => {
      tl
      .to('.wrap-inner', {
        duration: 3,
        ease: 'none',
        translateX: `-${scrollWidth}`,
        onComplete: () => {
          if (scrollIndex >= scrollLength) {
            scrollIndex = 1
            scrollWidth = itemWidth
            // 重置至起始位置
            tl
            .set('.wrap-inner', {
              translateX: 0
            })
          } else {
            scrollIndex += 1
            scrollWidth = itemWidth * scrollIndex
          }
          // 单步停顿的无缝滚动
          setTimeout(() => {
            animeteFuc()
          }, 1000)
        }
      })
    }
    animeteFuc()
  });

  const onEnter = () => {
    tl.pause()
  }

  const onLeave = () => {
    tl.play()
  }
</script>
</body>
</html>

Designed & Powerd by liujun