GSAP插件做的动效按钮

文章介绍了如何利用GSAP,特别是其MorphSVGPlugin模块,在React项目中创建复杂的SVG动画和按钮交互效果。GSAP是一个强大的动效平台,能实现多种前端动画,包括SVG和canvas。虽然部分插件付费,但提供了丰富的功能。文中给出了一个动态按钮的实现代码示例,涉及元素变换、路径变形等动画效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

推荐一个强大的前端动效插件:GSAP。

也许你见过tweenMax、TweenLite等插件,而GSAP就是整合了这些插件之后的一个完整版动效平台。

他的强大之处在于:非常完善。你能想到的所有前端动画,几乎都能实现。包括svg动画、canvas动画。

但缺点是部分plugins是收费的,所以商用的话就要小心点了。

下面是我用他的MorphSVGPlugin模块做的一个小按钮。

 最终实现效果视频可以看我Dou音,DY号:G_console

SVG形状可以用adobe illustrator编辑。

代码里的gsapMorphSVGPlugin需要自行安装,可以看下官方文档,代码里放了链接。

React:

import React from 'react';
import styles from './index.less';
import gsap from "gsap";  //自行install
import { MorphSVGPlugin } from "";  //自行install: https://greensock.com/docs/v3/Plugins/MorphSVGPlugin
gsap.registerPlugin(MorphSVGPlugin); 
const color1 = 'rgb(34 198 107)';
interface ViewProps extends React.HTMLAttributes<HTMLSpanElement> {
  active?: boolean;
}
export default class ButtonSvg1 extends React.Component<ViewProps> {
  public render() {
    const { active, className, children, ...restProps } = this.props;
    return (
      <div className={styles.snakeBtn} gsap-id='snakeBtn'>
        <div className={styles.hoverBox} onClick={this.clickSnkBtn}></div>
        <div className={styles.txts} gsap-id='txt1'>
          <span>点</span>
          <span>我</span>
          <span>点</span>
          <span>我</span>
        </div>
        <div className={styles.txts2} gsap-id='txt2'>
          <span>失</span>
          <span>败</span>
          <span className={styles.b}>!</span>
        </div>
        <svg viewBox="0 0 340 80" gsap-id='svg' fill={color1}>
          <path className='path1' d="M106,25c0-3.9,3.1-7,7-7c3.9,0,30.7,0,57,0s53.1,0,57,0s7,3.1,7,7v30c0,3.9-3.1,7-7,7s-30.7,0-57,0s-53.1,0-57,0
c-3.9,0-7-3.1-7-7V25z"/>
        </svg>
        <svg viewBox="0 0 10 10" className={styles.eye} gsap-id='eye' fill={color1}>
          <path d="M4.4,6.6c-6.3-0.1-2-5.4,2-5S13.3,6.7,4.4,6.6z" fill="#fff"/>
          <circle cx="5.7" cy="4.1" r="1.1"/>
        </svg>
      </div>
    );
  }

  private clickSnkBtn = () => {
    const button:any = document.querySelector("[gsap-id='snakeBtn']") || {};
    if(button.active) {
      return;
    }
    button.active = true
    
    const path = button.querySelector("[gsap-id='svg'] .path1");
    const txt1 = button.querySelectorAll("[gsap-id='txt1'] span");
    const txt2 = button.querySelectorAll("[gsap-id='txt2'] span");
    const eye = button.querySelectorAll("[gsap-id='eye']");

    const reset = () => {
      gsap.to(path, {
        fill: 'rgb(34 198 107)',
        morphSVG: "M106,25c0-3.9,3.1-7,7-7c3.9,0,30.7,0,57,0s53.1,0,57,0s7,3.1,7,7v30c0,3.9-3.1,7-7,7s-30.7,0-57,0s-53.1,0-57,0 c-3.9,0-7-3.1-7-7V25z",
        duration: 0,
        onComplete: () => {
          gsap.to(txt2, {
            stagger: {
              from: "end",
              amount: 0.1
            },
            scale: 0.2,
            opacity: 0,
            translateX: '0px',
            translateY: '0px',
            rotateZ: '0deg',
            duration: 0.2,
            onComplete: () => {
              gsap.to([...txt1], {
                stagger: 0.1,
                scale: 1,
                opacity: 1,
                translateX: '0px',
                duration: 0.2,
                onComplete: () => {
                  button.active = false;
                }
              })
            }
          })  
        }
      })
    }

    gsap.to([...txt1], {
      stagger: {
        from: "end",
        amount: 0.15
      },
      keyframes: [
        {
          scale: 1.15,
          duration: 0.15 
        }, {
          scale: 0.2,
          opacity: 0,
          translateX: '20px',
          duration: 0.2 
        }
      ],
      onComplete: () => {
        gsap.to(eye, {
          keyframes: [{
            translateX: '-18px',
            translateY: '5px',
            rotate: '0deg',
            opacity: 0.2,
            duration: 0.2,
          }, {
            translateX: '-71px',
            translateY: '0px',
            rotate: '45deg',
            opacity: 1,
            duration: 0.25
          }, {
            translateX: '-71px',
            translateY: '13px',
            rotate: '-25deg',
            opacity: 1,
            duration: 0.3
          }, {
            translateX: '-71px',
            translateY: '0px',
            rotate: '45deg',
            opacity: 1,
            duration: 0.3
          }, {
            translateX: '-71px',
            translateY: '13px',
            rotate: '-25deg',
            opacity: 1,
            duration: 0.3
          }, {
            translateX: '-18px',
            translateY: '0px',
            rotate: '0deg',
            opacity: 0.5,
            duration: 0.25,
          }, {
            translateX: '0px',
            translateY: '0px',
            rotate: '0deg',
            opacity: 0,
            duration: 0.15,
          }]
        })
        const svg1 = "M81.9,25.1c11.5-7.8,17.6-8,31.7-8.3c14.1-0.3,33.8,1.1,56.4,1.1s41.8-1.4,54.7-1.1c12.8,0.3,32.2-0.2,37.6,4.6  c13.4,12,9.5,34.1-4.1,38.9c-7.8,2.8-21.7,5.9-36.1,4S187.7,64,172.7,62c-15-2-39.7,1.4-52.1,1.9c-12.4,0.6-23.9,3.1-35.4-1  S70.4,32.9,81.9,25.1z"
        const svg2 = "M45.6,21c27.3,19.5,27.3,19.5,49.1,4.8c25.4-17,34.1,33,80.7-3.7c24.3-19.1,57.7,16,79.5,5.9c26.2-12.1,38-6.3,46.6,0  s18.3,16.7,18.3,16.7s-26.4-12.2-47.3-1.1c-38,20.1-54.9-25.3-99.8,10.9c-24.8,20.1-45.8-23.9-74-0.4c-29,24.1-59.2-4.8-66.2-10.8  C18.4,31.1,27.3,8,45.6,21z"
        const svg3 = "M40.9,24.2c20-12,29.3-12.4,49.1,4.8c32.4,28.3,41.6-32.2,80.7-3.7c39.4,28.7,52.2-4.5,72.1,4.5  c22.3,10.1,18.6,19.1,35,22.9s44.5,1.6,44.5,1.6s-49.7,20-70,3.3c-33.1-27.3-51.9,23.7-84.3,0c-37.7-27.7-49.4,22.7-79.2,0  c-23.4-17.9-23.1-12.6-40.6,0C21.9,76.7,11.5,41.9,40.9,24.2z"
        gsap.to(path, {
          keyframes: [
          {
            duration: 0.25,
            morphSVG: svg1
          }, {
            duration: 0.3,
            morphSVG: svg2
          }, {
            duration: 0.3,
            morphSVG: svg3
          }, {
            duration: 0.3,
            morphSVG: svg2
          }, {
            duration: 0.3,
            morphSVG: svg3
          }, {
            duration: 0.3,
            morphSVG: svg1
          }, {
            duration: 0.25,
            morphSVG: "M106,25c0-3.9,3.1-7,7-7c3.9,0,30.7,0,57,0s53.1,0,57,0s7,3.1,7,7v30c0,3.9-3.1,7-7,7s-30.7,0-57,0s-53.1,0-57,0 c-3.9,0-7-3.1-7-7V25z"
          }],
          onComplete: () => {
            gsap.to(path, {
              duration: 0.3,
              fill: 'rgb(243 93 93)'
            })
            gsap.to(button, {
              keyframes: [{
                scale: 0.9,
                duration: 0.1
              }, {
                scale: 1,
                duration: 0.1
              }]
            })
            gsap.to(txt2, {
              stagger: {
                amount: 0.15
              },
              keyframes: [
                {
                  scale: 0,
                  translateX: '0px',
                  duration: 0
                }, {
                  scale: 0.2,
                  translateX: '-20px',
                  duration: 0.15 
                }, {
                  scale: 1,
                  opacity: 1,
                  translateX: '0px',
                  duration: 0.2 
                }
              ],
              onComplete: () => {
                gsap.to(txt2[2], {
                  translateX: '41px',
                  translateY: '27px',
                  rotateZ: (360+68)+'deg',
                  duration: 0.6,
                  delay: 0.45
                })
                gsap.to(path, {
                  delay: 0.3,
                  keyframes: [
                    {
                      duration: 0,
                      morphSVG: "M106,25c0-3.9,3.1-7,7-7s30.7,0,57,0c2.1,0,11.3,0,11.3,0h1h0.9h0.9h0.7h1h1h1h1.1l1.3,0l1.2,0c0,0,34.2,0,35.6,0  c3.9,0,7,3.1,7,7l0,30c0,3.9-3.1,7-7,7c-3.2,0-43.7,0-43.7,0s-8.8,0-13.3,0c-26.3,0-53.1,0-57,0s-7-3.1-7-7V25z"
                    }, {
                      duration: 0.1,
                      morphSVG: "M106,25c0-3.9,3.1-7,7-7s30.7,0,57,0c2.1,0,11.3,0,11.3,0l4.5,8.1l-3.7,6.7l-3.9,7.2l4.6,7.4l0.6,11.4l3.8-9.7l-4.3-9.8  l4.7-5.9l4.8-5.5l-0.9-9.9c0,0,34.2,0,35.6,0c3.9,0,7,3.1,7,7l0,30c0,3.9-3.1,7-7,7c-3.2,0-43.7,0-43.7,0s-8.8,0-13.3,0  c-26.3,0-53.1,0-57,0s-7-3.1-7-7V25z"
                    }, {
                      duration: 0.4,
                      delay: 0.1,
                      ease: 'bounce.out',
                      morphSVG: "M106,25c0-3.9,3.1-7,7-7s30.7,0,57,0c2.1,0,11.3,0,11.3,0l4.5,8.1l-3.7,6.7l-3.9,7.2l4.6,7.4l0.6,11.4l3.8-9.7l-1.7-9.6l7-4  l6.7-4.7l-0.9-9.9c0,0,35,14,36.2,14.5c3.6,1.4,5.3,5.5,3.9,9.1l-11.1,27.9c-1.4,3.6-5.5,5.3-9.1,3.9c-3-1.2-34.9-14.3-34.9-14.3  s-8.8,0-13.3,0c-26.3,0-53.1,0-57,0s-7-3.1-7-7V25z"
                    }
                  ],
                  onComplete: () => {
                    setTimeout(reset, 3000);
                  }
                })
              }
            })
          }
        });
      }
    })
  }
}

Less:

.snakeBtn {
  display: inline-block;
  position: relative;
  svg {
    display: block;
    width: 340px;
    height: 80px;
    pointer-events: none;
    transition: fill 0.2s, transform 0.1s;
    transform: translateZ(0);
  }
  .eye {
    width: 16px;
    height: 16px;
    transform: translate(0px, 0px) rotate(0deg);
    position: absolute;
    top: 30%;
    left: 30%;
    opacity: 0;
  }
  .txts, .txts2 {
    position: absolute;
    z-index: 1;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    font-size: 17px;
    color: #fff;
    span {
      display: inline-block;
      vertical-align: top;
      transform-origin: 0% 50%;
      transform: translateZ(0);
    }
    .b {
      font-weight: bold;
      font-size: 18px;
    }
  }
  .txts2 {
    span {
      opacity: 0;
    }
  }
  .hoverBox {
    width: 130px;
    height: 44px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 2;
    cursor: pointer;
  }
}

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值