介绍
- 支持移动缩放渐变等常用动画
- 支持多个动画时间线控制
- 支持鼠标滚动控制动画
- 支持定格 pin
- 官网: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>
旋转
gsap.to(".green", {
rotation: 360,
duration: 3, //动画整体 延迟
ease: "bounce.out", // 缓冲效果
});
移动
to
// 移动 从那里出发
gsap.to(".green", {
x: 500 ,
y: 200,
rotation: 1360, //旋转度数
duration:3, //动画整体 延迟
repeat:1, //
yoyo:1 // 回弹
});
from
// 从那里过来
gsap.from(".green", {
x: 500 ,
y: 200,
rotation: 1360,
duration:3,
repeat:1,
yoyo:1
});
fromTo
y 从 300 到 0 移动
//从那里到那里
gsap.fromTo(".box",{
y : 300
} ,{
duration: 2,
y: 0,
repeat:-1, // 一直循环
yoyo:true,
});
时间线
根据配置按顺同步执行
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,
});
时间线 参数控制先后
// 时间参数 ,控制动画的执行顺序 相对于上一个时间。
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秒开发
回调事件
//时间线所有动画结束时调用
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")
})
对象控制
可以传入对象,动态获取变化的值,相当于通过返回参数 自己实现视图的逻辑
let obj = { myNum: 10, myColor: "red" };
gsap.to(obj, {
myNum: 200,
myColor: "blue",
onUpdate: () => console.log(obj.myNum, obj.myColor)
});
进阶
滚动库
要实现鼠标滚动配合动画,需要额外引入 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>
ScrollTrigger.create
重要的参数:
- animation:对应的时间线
- markers:是否打开调试模式
- trigger:需要绑定滚动的元素
- scrub:只有打开才会跟鼠标滚动同步
- start :两个参数, 注意 start :top center 第一个参数 top 是指traget的检测边距,第二个参数是指 浏览器的检测边距center,当两者相交后触发。
- end : 两个参数, 对应动画结束的位置,bottom为target元素的底部,center为浏览器的中间为检查线。
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 固定
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后 当触发动画时,内容会一直固定在触发的位置,直到动画结束
特别长的滚动
可以通过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,
},
});
样式开关
gsap.to(".green", {
scrollTrigger: {
trigger: ".green",
start: "top center",
end: "bottom center",
markers: true,
scrub: true,
toggleClass: {
targets: ".green",
className: "active", // 激活时候背景为红色
}
},
duration: 1000, // 这里延迟意义不大,是根据实际滚动的帧来决定时间
});
多个动画时间线
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>
视频2 + 文字
通过事件onEnter,onComplete和onReverseComplete在对应的地方做视频的播放控制
<!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>