推荐一个强大的前端动效插件:GSAP。
也许你见过tweenMax、TweenLite等插件,而GSAP就是整合了这些插件之后的一个完整版动效平台。
他的强大之处在于:非常完善。你能想到的所有前端动画,几乎都能实现。包括svg动画、canvas动画。
但缺点是部分plugins是收费的,所以商用的话就要小心点了。
下面是我用他的MorphSVGPlugin模块做的一个小按钮。
最终实现效果视频可以看我Dou音,DY号:G_console
SVG形状可以用adobe illustrator编辑。
代码里的gsap和MorphSVGPlugin需要自行安装,可以看下官方文档,代码里放了链接。
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;
}
}