这里的html样式都是基于tailwindcss上的样式,但是大概大家都能明白咋回事儿,不懂随时给我留言,欢迎大家互相交流学习,最近时间比较紧,有时间把例子都整到codepen上,大家看着就更方便了,gsap真是个好框架,大家一起学习吧
gsap.registerEffect
html
<h1 class="text-6xl pb-5 opacity-0">开始懂了</h1>
<h2 class="text-2xl opacity-0">快乐是选择</h2>
js
gsap.registerEffect({
name: 'tweenText',
// 集成到timeline上
extendTimeline: true,
// 默认参数
defaults: {
y: -100,
opacity: 0,
colors: ['blue', 'pink', 'green']
},
effect: (targets: any, config: any) => {
let split = new SplitText(targets, {type: 'chars'});
let chars = split.chars;
// 初始设为可视
gsap.set(targets, {opacity: 1});
return gsap.timeline().from(chars, {
opacity: config.opacity,
y: config.y,
stagger: 0.05,
duration: .5,
}).to(chars, {
color: gsap.utils.wrap([...config.colors])
}, 0);
}
});
// 调用方法
gsap.timeline().tweenText('h1').tweenText('h2', {y: 50}, 0);
其他文字的一些效果整理
文字生成器演示2
文字生成器演示3
文字生成器演示4
文字生成器演示5
gsap.registerEffect({
name: 'slideIn',
extendTimeline: true,
defaults: {
y: 0,
x: 0,
duration: 1,
ease: 'power1'
},
effect: (targets: any, config: any) => {
gsap.set(config.parent, {perspective: 300});
let tl: gsap.core.Timeline = gsap.timeline()
.from(targets, {duration: config.duration, x: config.x, y: config.y, transformOrigin: config.transformOrigin, ease: config.ease, stagger: {each: 0.03, ease: 'power1'}})
.from(targets, {duration: config.duration, opacity: 0, ease: 'none', stagger: {each: 0.03, ease: 'power2' }}, 0)
return tl;
}
});
gsap.registerEffect({
name: 'slideOut',
extendTimeline: true,
defaults: {
y: 0,
x: 0,
duration: 0.5,
ease: 'power1.in'
},
effect: (targets: any, config: any) => {
gsap.set(config.parent, {perspective: 300});
let tl: gsap.core.Timeline = gsap.timeline()
.to(targets, {duration: config.duration, x: config.x, y: config.y, transformOrigin: config.transformOrigin, ease: config.ease, stagger: {each: 0.03, ease: 'power1'}})
.to(targets, {duration: config.duration, opacity: 0, ease: 'none', stagger: {each: 0.01, ease: 'power2' }}, 0)
return tl;
}
});
gsap.registerEffect({
name: 'twistIn',
extendTimeline: true,
defaults: {
duration: 1,
rotationX: 0,
rotationY: 0,
transformOrigin: '50% 50%',
ease: 'back',
parent: 'body'
},
effect: (targets: any, config: any) => {
// gsap.set(targets, {transformPerspective: 300});
gsap.set(config.parent, {perspective: 300});
let tl: gsap.core.Timeline = gsap.timeline()
.from(targets, {duration: config.duration, rotationX: config.rotationX, rotationY: config.rotationY, transformOrigin: config.transformOrigin, ease: config.ease, stagger: {each: 0.05}})
.from(targets, {duration: 0.4, opacity: 0, ease: 'none', stagger: {each: 0.05 }}, 0)
return tl;
}
});
gsap.registerEffect({
name: 'twistOut',
extendTimeline: true,
defaults: {
rotationX: 0,
rotationY: 0,
transformOrigin: '50% 50%',
ease: 'power1.in',
parent: 'body'
},
effect: (targets: any, config: any) => {
gsap.set(config.parent, {perspective: 300});
let tl: gsap.core.Timeline = gsap.timeline()
.to(targets, {duration: 0.5, rotationX: config.rotationX, rotationY: config.rotationY, transformOrigin: config.transformOrigin, ease: config.ease, stagger: {each: 0.01}})
.to(targets, {duration: 0.3 , opacity: 0, ease: 'none', stagger: {each: 0.01 }}, '-=0.2')
return tl;
}
});
gsap.registerEffect({
name: 'in',
extendTimeline: true,
defaults: {
duration: 0.5,
fade: 0.5,
y: 0,
x: 0,
rotationX: 0,
rotationY: 0,
scale: 1,
transformOrigin: '50% 50%',
perspective: 400,
ease: 'power1',
each: 0.05,
staggerEase: 'power1.in',
from: 'start'
},
effect: (targets: any, config: any) => {
if (config.rotationX != 0 || config.rotationY != 0) {
let currentNode = targets[0];
gsap.set(currentNode.parentNode, {
perspective: config.perspective
});
}
let tl: gsap.core.Timeline = gsap.timeline()
.from(targets, {
duration: config.duration,
ease: config.ease,
x: config.x,
y: config.y,
scale: config.scale,
transformOrigin: config.transformOrigin,
rotationY: config.rotationY,
rotationX: config.rotationX,
stagger: {
each: config.each,
ease: config.staggerEase,
from: config.from
}
})
.from(targets, {
duration: config.fade,
opacity: 0,
ease: 'none',
stagger: {
each: config.each,
ease: config.staggerEase,
from: config.from
}
}, 0 )
return tl;
}
});
gsap.registerEffect({
name: 'out',
extendTimeline: true,
defaults: {
duration: 0.5,
fade: 0.5,
y: 0,
x: 0,
rotationX: 0,
rotationY: 0,
scale: 1,
transformOrigin: '50% 50%',
perspective: 400,
ease: 'power1',
each: 0.05,
staggerEase: 'power1.in',
from: 'start'
},
effect: (targets: any, config: any) => {
if (config.rotationX != 0 || config.rotationY != 0) {
let currentNode = targets[0];
gsap.set(currentNode.parentNode, {
perspective: config.perspective
});
}
let tl: gsap.core.Timeline = gsap.timeline()
.to(targets, {
duration: config.duration,
ease: config.ease,
x: config.x,
y: config.y,
scale: config.scale,
transformOrigin: config.transformOrigin,
rotationY: config.rotationY,
rotationX: config.rotationX,
stagger: {
each: config.each,
ease: config.staggerEase,
from: config.from
}
})
.to(targets, {
duration: config.fade,
opacity: 0,
ease: 'none',
stagger: {
each: config.each,
ease: config.staggerEase,
from: config.from
}
}, 0 )
return tl;
}
});
gsap.registerEffect({
name: 'jelly',
extendTimeline: true,
defaults: {
stagger: 0.03,
duration: 0.8
},
effect: (target: any, config: any) => {
gsap.set(target, {transformOrigin: '50% 50%'})
let tl:gsap.core.Timeline = gsap.timeline({delay: 0.5})
.from(target, {scale: 0.8, duration: config.duration, stagger: config.stagger, ease: 'elastic'})
.from(target, {opacity: 0, duration: 0.01, stagger: config.stagger}, 0)
return tl;
}
});
gsap.registerEffect({
name: 'burnIn',
extendTimeline: true,
defaults: {
x: 0,
y: 0,
duration: 0.5,
ease: 'none'
},
effect: (targets: any, config: any) => {
gsap.set(targets, {filter: 'blur(0px) brightness(1)'})
let tl: gsap.core.Timeline = gsap.timeline()
.from(targets, {filter: 'blur(20px) brightness(8)', scale: 0.8, rotation: -10, duration: config.duration, ease: config.ease, x: config.x, y: config.y, clearProps: 'filter', stagger: {each: 0.02, ease: 'none'}})
.from(targets, {duration: 0.1, opacity: 0, ease: 'none', stagger: {each: 0.02, ease: 'power2'}}, 0);
return tl;
}
});
gsap.registerEffect({
name: 'burnOut',
extendTimeline: true,
defaults: {
x: 0,
y: 0,
duration: 0.3,
ease: 'none'
},
effect: (targets: any, config: any) => {
gsap.set(targets, {filter: 'blur(0px) brightness(1)'})
let tl: gsap.core.Timeline = gsap.timeline()
.to(targets, {filter: 'blur(20px) brightness(8)', scale: 0.3, rotation: 10, duration: config.duration, ease: config.ease, x: config.x, y: config.y, clearProps: 'filter', stagger: {each: 0.01, ease: 'none'}})
.to(targets, {duration: .2, opacity: 0, ease: 'none', stagger: {each: 0.01, ease: 'power2'}}, 0);
return tl;
}
});
// 参数配置out
gsap.registerEffect({
name: 'randomFade',
extendTimeline: true,
defaults: {
x: 0,
y: 0,
duration: 1,
ease: 'power1',
scale: 1,
direction: 'in'
},
effect: (targets: any, config: any) => {
let tl = gsap.timeline();
if (config.direction === 'in') {
tl.from(targets, {opacity: 0, duration: config.duration, ease: config.ease, x: config.x, y: config.y, scale: config.scale, stagger: {each: 0.01, from: 'random'}})
} else {
tl.to(targets, {opacity: 0, duration: 0.5, ease: config.ease, x: config.x, y: config.y, scale: config.scale, stagger: {each: 0.01, from: 'random'}})
}
return tl;
}
});
3d双面翻转
html
/**
* transform-style: preserve-3d; 3d元素的子元素保留3d空间
* backface-visibility: hidden;设置完上面这句就有效了
*/
<div class="trans w-10 bg-red-800 h-10 relative" style="transform-style: preserve-3d;">
<div class="bg-green-400 absolute inset-0" style="backface-visibility: hidden;"></div>
</div>
js
let animation = gsap.timeline();
gsap.set('.trans', {
transformPerspective: 200 // 设置后才能有3d效果,非常重要
});
animation.to('.trans', {
rotateY: 360,
duration: 1,
stagger: 0.2
});
gsap.utils.distribute()
html
<div class="bg-black flex justify-between items-end h-96">
<div class="bg-green-800 w-2 h-96 bar trans"></div>
<div class="bg-green-800 w-2 h-96 bar trans"></div>
<div class="bg-green-800 w-2 h-96 bar trans"></div>
******
<div class="bg-green-800 w-2 h-96 bar trans"></div>
<div class="bg-green-800 w-2 h-96 bar trans"></div>
<div class="bg-green-800 w-2 h-96 bar trans"></div>
</div>
js
gsap.set('.bar', {
transformOrigin: '50% 100%'
})
gsap.set('.bar', {
scaleY: gsap.utils.distribute({
base: -1,
amount: 2,
ease: 'power4',
// from: 'edges'
})
})
胶囊文字效果
html
<div class="text-center pt-20">
<span class="myEnglish text-2xl">abcdefghijklmnopqrst</span>
</div>
js
let split = new SplitText('.myEnglish', {type: 'chars'});
let chars = split.chars;
let animation = gsap.timeline();
animation.from(chars, {
opacity: 0,
scale:gsap.utils.distribute({
base: 0.2,
amount: 4,
ease: 'power',
from: 'center'
}),
x: gsap.utils.distribute({// [-200,-199, ... 199, 200]
base: -200,
amount: 400,
}),
stagger: {
each: 0.01,
from: 'center'
},
yoyo: true,
repeat: -1
});
3D翻转文字效果
html
<div class="hasText bg-gray-200 h-80 text-2xl relative opacity-0 font-extrabold w-96">
<div class="absolute w-full text-center left-0 top-32">我爱上让我奋不顾身的一个人</div>
<div class="absolute w-full text-center left-0 top-32">我以为这就是我所追求的世界</div>
<div class="absolute w-full text-center left-0 top-32">然而横冲直撞</div>
<div class="absolute w-full text-center left-0 top-32">被误解被骗</div>
<div class="absolute w-full text-center left-0 top-32">是否成人的世界背后总有残缺</div>
</div>
js 连贯翻转无缝隙
const textArray = ['我爱上让我奋不顾身的一个人', '我以为这就是我所追求的世界', '然而横冲直撞', '被误解被骗', '是否成人的世界背后总有残缺'];
onMounted(() => {
let duration = 0.5;
let delay = 2;
let stagger = duration + delay;
let allDelay = (textArray.length - 1) * stagger + delay;
textTransTop(duration);
function textTransTop(duration: number) {
let animation = gsap.timeline();
gsap.set('.hasText', {
// transformPerspective:200,
perspective: 200,
autoAlpha: 1
});
gsap.set('.hasText div', {
transformOrigin: '50% 50% -50'
});
animation.from('.hasText div', {
rotationX: -90,
rotationY: -45,
y: 30,
duration: duration,
opacity: 0,
stagger: {
each: stagger,
repeat: -1,
repeatDelay: allDelay
}
}).to('.hasText div', {
rotationX: 90,
rotationY: 45,
y: -30,
opacity: 0,
duration: duration,
stagger: {
each: stagger,
repeat: -1,
repeatDelay: allDelay
}
}, stagger);
GSDevTools.create({animation})
}
});
stagger交错运动
html
<div class="border-2 bg-gray-50 w-96 h-96 flex justify-around rollBall">
<div class="w-10 h-10 rounded-full bg-emerald-900"></div>
<div class="w-10 h-10 rounded-full bg-red-800"></div>
<div class="w-10 h-10 rounded-full bg-emerald-900"></div>
<div class="w-10 h-10 rounded-full bg-red-800"></div>
<div class="w-10 h-10 rounded-full bg-emerald-900"></div>
<div class="w-10 h-10 rounded-full bg-red-800"></div>
<div class="w-10 h-10 rounded-full bg-emerald-900"></div>
<div class="w-10 h-10 rounded-full bg-red-800"></div>
</div>
js
gsap.to('.rollBall div', {
y: 80,
stagger: {
each: .5,
yoyo: true,
repeat: -1
}
})
labels
html
<div class="bg-black py-10 flex justify-around">
<div class="box b0 red w-20 h-20 bg-red-800">1</div>
<div class="box b1 red w-20 h-20 bg-green-400">2</div>
<div class="box b2 red w-20 h-20 bg-amber-200">3</div>
<div class="box b3 red w-20 h-20 bg-pink-900">4</div>
</div>
<div class="dotNav bg-gray-800 py-10 flex justify-around">
<div v-for="(item, key) in dots" :key="key" class="dot w-5 h-5 cursor-pointer rounded-full bg-fuchsia-500 border-4 border-amber-800" @click="playTlFn(item.name)"></div>
</div>
<div class="nav text-center pt-10">
<button id="prev" class="px-8 py-4 rounded-xl bg-pink-900 mr-5 text-white">prev</button>
<button id="next" class="px-8 py-4 rounded-xl bg-pink-900 mr-5 text-white">next</button>
</div>
js
type BaseObject = {name: string, time: number};
let dots = ref<BaseObject[]>([]);
let playTlFn;
onMounted(() => {
let tl = gsap.timeline({paused: false, repeat: -1, defaults: {rotation: 180, opacity: 0, duration: 0.4}});
tl
.add('b0')
.from('.b0', {
rotation: -180})
.addPause()
.to('.b0', {})
.add('b1')
.from('.b1', {
rotation: -180
})
.addPause()
.to('.b1', {})
.add('b2')
.from('.b2', {
rotation: -180
})
.addPause()
.to('.b2', {})
.add('b3')
.from('.b3', {
rotation: -180
})
.addPause()
.to('.b3', {});
// tl.tweenFromTo('b3', 'b0');
console.log(tl.labels);
const getLabelsArray = (tl: gsap.core.Timeline) => Object.keys(tl.labels).map(v => ({name: v, time: tl.labels[v]})).sort((a, b) => (a.time - b.time));
const labels = getLabelsArray(tl);
dots.value.push(...labels);
console.log(labels);
playTlFn = (name: string) => {
tl.play(name);
}
next.addEventListener('click', () => tl.play());
prev.addEventListener('click', () => tl.reverse());
});
时间线、tweenTo、 tweenFromTo
html
<div class="bg-black py-10 flex justify-around h-80 relative">
<div class="box0 b0 red w-20 h-20 bg-red-800">1</div>
<div class="box1 b1 red w-20 h-20 bg-green-400">2</div>
<div class="box2 b2 red w-20 h-20 bg-amber-200">3</div>
<div class="bot flex items-center justify-center h-20 absolute left-0 w-full bottom-0 text-white">
<div class="botSub0 text-6xl pr-4">
<Icon name="logos:airtable"/>
</div>
<div class="botSub1 text-2xl whitespace-nowrap">asdfasdf</div>
</div>
</div>
js
function midTl(text: string):gsap.core.Timeline {
gsap.set(['.botSub0', '.botSub1'], {
y: 100
});
let tl = gsap.timeline()
.set('.botSub1',{
innerText: text
})
.to(['.botSub0', '.botSub1'], {
y: 0,
yoyo: true,
repeat: 1,
stagger: .2
})
return tl;
}
onMounted(() => {
let redAni = gsap.timeline()
.to('.box0', {y: 100})
.to('.box0', {rotation: 360})
.to('.box0', {y: 0});
let greenAni = gsap.timeline()
.to('.box1', {y: -100})
.to('.box1', {rotation: 360})
.to('.box1', {y: 0});
let amberAni = gsap.timeline()
.to('.box2', {scale: 2})
.to('.box2', {rotation: 360})
.to('.box2', {scale: 1});
let master = gsap.timeline()
// redAni 执行2次
.add(redAni.tweenTo(redAni.duration(), {repeat: 1}))
.add(midTl('什么情况!'))
.add(greenAni)
.add(midTl('想多了!'))
.add(amberAni)
.add(midTl('redAni 再执行1次!'))
// 再次执行要用fromTo
.add(redAni.tweenFromTo(0, redAni.duration()))
});
曲线轮播效果
html
<div class="bg-black flex relative w-72 h-72">
<div class="panel panel1 flex flex-col items-center w-72 h-72 justify-center absolute opacity-0">
<Icon name="logos:webstorm" class="text-6xl icon" />
<h4 class="text-2xl pt-10 text-cyan-500"><span class="text-amber-500 leftText inline-block relative">logos</span><span class="textRight pl-4 inline-block relative">webstorm</span></h4>
</div>
<div class="panel panel2 flex flex-col items-center w-72 h-72 justify-center absolute opacity-0">
<Icon name="logos:intellij-idea" class="text-6xl icon" />
<h4 class="text-2xl pt-10 text-cyan-500"><span class="text-amber-500 leftText inline-block relative">logos</span><span class="textRight inline-block relative pl-4">intellij-idea</span></h4>
</div>
<div class="panel panel3 flex flex-col items-center w-72 h-72 justify-center absolute opacity-0">
<Icon name="logos:clion" class="text-6xl icon" />
<h4 class="text-2xl pt-10 text-cyan-500"><span class="text-amber-500 leftText inline-block relative">logos</span><span class="textRight inline-block relative pl-4">clion</span></h4>
</div>
<div class="panel panel4 flex flex-col items-center w-72 h-72 justify-center absolute opacity-0">
<svg width="126px" height="120px" viewBox="0 0 126 120" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<polygon id="down" fill="#FFFFFF" points="63 0 125.76973 45.6048784 101.793827 119.395122 24.2061733 119.395122 0.230269925 45.6048784"></polygon>
<polygon id="up" fill="#EE4242" points="63.2129032 44.283871 102.697088 72.9708106 87.6154716 119.387254 38.8103348 119.387254 23.7287182 72.9708106"></polygon>
</svg>
<h4 class="panelText text-2xl top-24 text-cyan-500 ts w-full absolute text-center"><span class="text-amber-500 leftText inline-block relative">down</span><span class="textRight inline-block relative"> ####</span></h4>
</div>
</div>
js
function commonAnimate(panel: string) {
let tl:gsap.core.Timeline = gsap.timeline()
.set(panel, {opacity: 1})
.from(`${panel} .icon`, {x: -180})
.from(`${panel} .icon`, {
y: 50,
ease: 'power1.out'
}, '<')
.from(`${panel} .icon`, {
opacity: 0,
duration: .2
}, '<')
.from(`${panel} .leftText`, {
x: -100,
duration: 0.3
},'-=0.3')
.from(`${panel} .textRight`, {
x: 100,
duration: 0.3
},'-=0.3')
.from([`${panel} .leftText`, `${panel} .textRight`], {
opacity: 0,
duration: 0.2
},'-=0.3')
.to(`${panel} .icon`, {x: 180, delay: 1})
.to(`${panel} .icon`, {
y: 50,
ease: 'power1.in'
}, '<')
.to(`${panel} .icon`, {
opacity: 0,
duration: .2
}, '-=0.2')
.to([`${panel} .leftText`, `${panel} .textRight`], {
opacity: 0,
y: 50,
duration: 0.2
},'-=0.3')
return tl;
}
onMounted(() => {
const downAni = gsap.timeline()
.set('.panel4', {
opacity: 1,
})
.from('.panel4 .panelText', {
y: -124,
ease: 'power1.in'
})
.to('#down', {
attr: {
points: '63.2129032 44.283871 102.697088 72.9708106 87.6154716 119.387254 38.8103348 119.387254 23.7287182 72.9708106'
},
ease: 'power1.in',
duration: 0.2
}, '-=0.2');
let master = gsap.timeline({repeat: -1, repeatDelay: 2})
.add(commonAnimate('.panel1'))
.add(commonAnimate('.panel2'), '-=.2')
.add(commonAnimate('.panel3'), '-=.2')
// .add('myLabel')
.add(downAni);
// master.play('myLabel')
// GSDevTools.create({animation: master})
});
keyframes 关键帧
html
<div class="p-20 bg-black flex justify-evenly">
<div class="ball w-10 h-10 rounded-full bg-green-400"></div>
<div class="ball w-10 h-10 rounded-full bg-red-400"></div>
<div class="ball w-10 h-10 rounded-full bg-green-400"></div>
<div class="ball w-10 h-10 rounded-full bg-red-400"></div>
<div class="ball w-10 h-10 rounded-full bg-green-400"></div>
</div>
js
let animation = gsap.timeline({defaults: {duration: .5, ease: 'none', repeat: -1}});
animation.to('.ball', {
keyframes: [
{y: gsap.utils.wrap([100, -100])},
{y: gsap.utils.wrap([-100, 100]), duration: 1},
{y: 0}
]
})