Javascript GSAP:同时管理多个时间线和时间线
首先,我是GSAP的新手,所以请容忍我。我尽力描述我正在努力解决的问题 为了制作SVG文件的动画,我开始深入研究GSAP。我为SVG文件中的不同元素创建了几个tween。 一切都进行得很好,并按预期制作了动画 因为我在SVG中有很多元素需要设置动画,所以我开始添加TimeLineSite以更好地控制整个过程 此时,我的脚本文件如下所示: 首先,我声明了我要设置动画的所有元素:Javascript GSAP:同时管理多个时间线和时间线,javascript,animation,svg,gsap,Javascript,Animation,Svg,Gsap,首先,我是GSAP的新手,所以请容忍我。我尽力描述我正在努力解决的问题 为了制作SVG文件的动画,我开始深入研究GSAP。我为SVG文件中的不同元素创建了几个tween。 一切都进行得很好,并按预期制作了动画 因为我在SVG中有很多元素需要设置动画,所以我开始添加TimeLineSite以更好地控制整个过程 此时,我的脚本文件如下所示: 首先,我声明了我要设置动画的所有元素: this.phone = document.querySelector('#gsap-phone-svg'); this
this.phone = document.querySelector('#gsap-phone-svg');
this.body = document.querySelectorAll('.gsap-phone-body');
this.body.shadow = this.phone.querySelectorAll('.gsap-phone-body-shadow');
this.body.mask = this.phone.querySelectorAll('.gsap-phone-body-mask');
this.layer = this.phone.querySelectorAll('.gsap-phone-layer');
this.screen = this.phone.querySelectorAll('.gsap-phone-screen');
this.screen.clipPath = this.phone.querySelectorAll('.gsap-phone-screen-clipPath');
.
.
.
// many more following here
然后我创建了一个对象来保存我的孩子:
const tweens = {};
// creating tweens
tweens.body = TweenMax.to(this.body, this.animDur/2,{
y: this.maxExpand/6*-1,
ease: this.ease
});
.
.
.
// many more following here
最后,我将我所有的tweens添加到一个临时数组中,然后我将其传递到一个新的TimeLineSite()中,如下所示:
const tl = new TimelineLite();
tl.add(tweensArray, 0, "start", 0.05);
到目前为止似乎是合乎逻辑的,我想。。。现在是关键。你可能已经注意到或没有,我喜欢超过20个元素左右的动画。这就是为什么为每个元素单独添加一个tween变得非常混乱的原因。另外,我希望整个主要的时间表是重复的。这里的问题是,我希望我所有的孩子在“in-animation”和“out-animation”上都有不同的放松,我也不希望在“out-animation”上有错台
所有这些小问题让我想到了另一个解决方案来管理我的孩子和时间线的创建
我想到的最方便的解决方案是将有关我的动画元素和时间线的所有信息存储在一个对象中:
const animation = {
settings : {
duration: 1.5,
expansion: 1,
easeIn: Elastic.easeOut.config(1, 0.5),
easeOut: Power2.easeInOut
},
timelines : {
main : {
delay : 0,
paused : true,
align : 'start',
stagger : 0.05,
},
test : {
delay: 0,
paused : true,
align : 'start',
stagger : 0.5
}
},
items : {
phone : {
id : '#gsap-phone-svg',
start : { },
end : { },
timeline : 'test',
},
body : {
class : '.gsap-phone-body',
start : {
y : 0,
},
end : {
y : -21,
},
timeline : 'test',
},
layer : {
class : '.gsap-phone-layer',
start : {
y : 0,
},
end : {
y : -62.5,
},
timeline : 'main',
},
radar : {
class : '.gsap-phone-radar',
start : {
y : 0,
},
end : {
y : -25,
},
timeline : 'main',
},
radarBase : {
class : '.gsap-phone-radar-base',
start: {
y : 0,
},
end : {
y: -16,
},
timeline : 'test',
},
ringOne : {
class : '.gsap-phone-radar-ring-1',
start : {
y : 0,
},
end : {
y: -25,
},
timeline : 'test',
},
ringTwo : {
class : '.gsap-phone-radar-ring-2',
start : {
y : 0,
},
end : {
y: -41,
},
timeline : 'main',
},
ringThree : {
class : '.gsap-phone-radar-ring-3',
start : {
y : 0,
},
end : {
y: -62.5,
},
timeline : 'main',
},
cancel : {
class : '.gsap-phone-cancel',
start : {
y : 0,
},
end : {
y: -50,
},
timeline : 'main',
},
submit : {
class : '.gsap-phone-submit',
start : {
y : 0,
},
end : {
y: -100,
},
timeline : 'main',
}
}
};
然后我编写了这个“createTweens”方法来返回GSAP Tweens
/* create tweens */
function createTweens(anim){
const el = anim.items;
const settings = anim.settings;
const duration = settings.duration;
const easeIn = settings.easeIn;
const easeOut = settings.easeOut;
const tweensIn = [];
const tweensOut = [];
let tempTween = null;
for (const key in el){
const curEl = el[key];
const selector = curEl.class || el[key].id;
const startPoint = curEl.start || '';
const endPoint = curEl.end || '';
const timeline = curEl.timeline || '';
const nodes = document.querySelectorAll(selector);
nodes.forEach(object => {
tweensIn.push(getTween(object, endPoint, duration, easeIn, `${timeline}-in`));
tweensOut.push(getTween(object, startPoint, duration, easeOut, `${timeline}-out`));
});
}
function getTween(tw, twValues, twDur, twEase, tl){
const vars = twValues;
vars.paused = false;
vars.ease = twEase;
tempTween = TweenMax.to(tw, twDur/2, vars);
tempTween.data = {
timelineName : tl
};
return tempTween;
}
return tweensIn.concat(tweensOut);
}
和另一个返回时间线的函数:
/* create timelines */
function createTimelines(anim, tweens){
const el = anim.timelines;
const timelines = {};
// timelines.mainIn = new TimelineLite();
// timelines.mainOut = new TimelineLite();
const tweensForTimelines = {};
for(const key in el){
const delay = el[key].delay;
const paused = el[key].paused;
const align = el[key].align;
const stagger = el[key].stagger;
const vars = {};
vars.paused = paused;
timelines[`${key}-in`] = new TimelineLite(vars);
timelines[`${key}-in`].delay = delay;
timelines[`${key}-in`].align = align;
timelines[`${key}-in`].stagger = stagger;
timelines[`${key}-out`] = new TimelineLite(vars);
timelines[`${key}-out`].delay = delay;
timelines[`${key}-out`].align = align;
timelines[`${key}-out`].stagger = stagger;
tweensForTimelines[`${key}-in`] = [];
tweensForTimelines[`${key}-out`] = [];
}
if(Object.keys(tweensForTimelines).length !== 0){
for(let i = 0; i < tweens.length; i++){
const curTween = tweens[i];
const tlTarget = curTween.data.timelineName;
tweensForTimelines[tlTarget].push(curTween);
}
}
for(const key in timelines){
try{
timelines[key].add(tweensForTimelines[key], timelines[key].delay, timelines[key].align, timelines[key].stagger);
console.log(TweenMax.getTweensOf(timelines[key]));
timelines[key].data = tweensForTimelines[key];
} catch(e){
}
}
return timelines;
}
到目前为止,这确实有效。但是,如果我尝试将“主输入”时间线添加到新的时间线,它将不再工作
const anotherTimeline = new TimelineLite();
anotherTimeline.add(timelines['main-in']);
anotherTimeline.play();
为了调试这个,我尝试了
TweenMax.getTweensOf(anotherTimeline);
但所有这些返回的都是一个空数组。然后,我为我的“主输入”时间线记录了相同的内容:
console.log(TweenMax.getTweensOf(timelines['main-in']));
同时返回一个空数组,这让我非常困惑,因为即使这个时间线看起来是空的,它也会在以下位置播放我的“动画中”:
我真的被困在这里,非常感谢更高级的用户或任何对此有想法的人的帮助。我希望你们能跟上我。。。如果没有,请查看提供的代码笔
更新:
提前谢谢 我没有时间解析你所有的代码并设计一个完整的替换,但这确实让我觉得有点过度设计,但我也意识到我的大脑可能会有不同的工作方式,这是一个风格选择(不好也不坏) 我发现最直观、可读和灵活的方法是将动画分解成块,放入函数中,每个函数都会吐出一个TimeLineite/Max或TweenLite/Max,可以嵌套在主时间轴中(如果您选择) 有点像:
function phoneIntro() {
var tl = new TimelineLite();
tl.to(...)
.to(...);
return tl;
}
function flipPhone() {
var tl = new TimelineLite();
tl.to(...);
return tl;
}
var master = new TimelineMax({repeat:-1});
master.add(phoneIntro(), 0)
.add(flipPhone(), "-=1"); //overlap by 1 second
...
当然,如果你有很多元素,你正在做同样类型的动画,这种模块化的方法也是非常有用的,因为你可以输入任何你需要的变量,函数为你做的工作和吐回动画
function buildStep(element, duration, x, y) {
return TweenMax.to(element, duration, {x:x, y:y, rotation:30});
}
希望当您创建一些模块化的函数来满足您的任何需求时,事情会变得多么灵活。整个方法也可以使编辑动画的速度更快(并尝试计时等),因为在代码中找到自己的位置非常简单。“我想让简介长2秒…”只需找到phoneIntro()函数并调整里面的内容。完成。由于您将事物与主TimelineMax中的相对计时联系在一起,因此您对第一个模块化块所做的更改会自动推后后续事物的计时,并且它会很好地通过。20个不同的延迟都没有问题
另外,TweenLite.getTweensOf(另一个Timeline)返回空数组的原因是该方法查找该对象的tweens。就像,从字面上说,如果你在时间线本身(也许是它的进度)之间来回移动,它会返回这个时间线。听起来好像你假设它在某个时间线实例中得到了tweens(它没有)。但是,如果这就是您所追求的,那么它就像另一个时间表一样简单。getChildren()
我希望这至少有一点帮助。欢迎在GSAP专业人士社区的论坛上发布问题。这是一个非常好的学习场所,即使你从未发帖:)
快乐的吐温 我没有时间解析你所有的代码并设计一个完整的替换,但这确实让我觉得有点过度设计,但我也意识到我的大脑可能会有不同的工作方式,这是一个风格选择(不好也不坏) 我发现最直观、可读和灵活的方法是将动画分解成块,放入函数中,每个函数都会吐出一个TimeLineite/Max或TweenLite/Max,可以嵌套在主时间轴中(如果您选择) 有点像:
function phoneIntro() {
var tl = new TimelineLite();
tl.to(...)
.to(...);
return tl;
}
function flipPhone() {
var tl = new TimelineLite();
tl.to(...);
return tl;
}
var master = new TimelineMax({repeat:-1});
master.add(phoneIntro(), 0)
.add(flipPhone(), "-=1"); //overlap by 1 second
...
当然,如果你有很多元素,你正在做同样类型的动画,这种模块化的方法也是非常有用的,因为你可以输入任何你需要的变量,函数为你做的工作和吐回动画
function buildStep(element, duration, x, y) {
return TweenMax.to(element, duration, {x:x, y:y, rotation:30});
}
希望当您创建一些模块化的函数来满足您的任何需求时,事情会变得多么灵活。整个方法也可以使编辑动画的速度更快(并尝试计时等),因为在代码中找到自己的位置非常简单。“我想让简介长2秒…”只需找到phoneIntro()函数并调整里面的内容。完成。由于您将事物与主TimelineMax中的相对计时联系在一起,因此您对第一个模块化块所做的更改会自动推后后续事物的计时,并且它会很好地通过。20个不同的延迟都没有问题
还有,为什么