Javascript 使用events.js在动画完成时运行函数
我想在摆动效应的末尾调用一个函数 也就是说,在阻尼效果结束时(当抖动停止时),我想执行一个GSAP时间线函数。我假设需要在窗帘的Javascript 使用events.js在动画完成时运行函数,javascript,animation,callback,gsap,curtains.js,Javascript,Animation,Callback,Gsap,Curtains.js,我想在摆动效应的末尾调用一个函数 也就是说,在阻尼效果结束时(当抖动停止时),我想执行一个GSAP时间线函数。我假设需要在窗帘的onReady()中调用这种类型的“onComplete”函数,也许还需要跟踪阻尼效果。我只熟悉GSAP的onComplete函数,但不知道如何在这里实现它。可能是检查应用的Delta.applied是否小于0.001的东西,然后调用该函数 下面是代码片段(没有片段和顶点着色器)。完整工作代码如下: class-Img{ 构造函数(){ 固定窗帘=新窗帘({ 容器:“
onReady()
中调用这种类型的“onComplete”函数,也许还需要跟踪阻尼效果。我只熟悉GSAP的onComplete
函数,但不知道如何在这里实现它。可能是检查应用的Delta.applied是否小于0.001
的东西,然后调用该函数
下面是代码片段(没有片段和顶点着色器)。完整工作代码如下:
class-Img{
构造函数(){
固定窗帘=新窗帘({
容器:“画布”,
手表卷轴:错,
});
常量参数={
顶点着色器,
碎片着色器,
制服:{
时间:{
名称:“uTime”,
类型:“1f”,
值:0,
},
进展:{
名称:“乌普格”,
类型:“1f”,
值:0,
}
}
}
const planeElements=document.getElementsByClassName(“平面”)[0];
this.plane=窗帘.addPlane(planeElements,params);
如果(这架飞机){
这架飞机
.onReady(()=>{
这个。intranim();
})
.onRender(()=>{
this.plane.uniforms.time.value++;
应用的增量+=(增量最大值-应用的增量)*0.05;
deltas.max+=(0-deltas.max)*0.07;
this.plane.uniforms.prog.value=delta.applied
})
}
//错误处理
窗帘.单面镜(功能(){
文件。正文。类列表。添加(“无窗帘”);
});
}
intranim(){
deltas.max=6;
//console.log(“complete”)这可能不是最好的答案,但您可以从中获得一些想法和见解
打开控制台,查看动画完成后只触发一次
//激发一个未完成的事件并聆听它
const event=新事件('onComplete');
类Img{
构造函数(){
//添加了用于封装的实例变量
this.animplete={anim1:false}
//更改了上面的代码
固定窗帘=新窗帘({
容器:“画布”,
手表卷轴:错,
});
常量参数={
顶点着色器,
碎片着色器,
制服:{
时间:{
名称:“uTime”,
类型:“1f”,
值:0,
},
进展:{
名称:“乌普格”,
类型:“1f”,
值:0,
}
}
}
const planeElements=document.getElementsByClassName(“平面”)[0];
this.plane=窗帘.addPlane(planeElements,params);
如果(这架飞机){
这架飞机
.onReady(()=>{
这个。intranim();
document.addEventListener('onComplete',()=>{
//这里有阻尼效应吗
console.log('complete')
})
})
.onRender(()=>{
this.plane.uniforms.time.value++;
应用的增量+=(增量最大值-应用的增量)*0.05;
deltas.max+=(0-deltas.max)*0.07;
this.plane.uniforms.prog.value=delta.applied
if(deltas.applied您可以使用的是一些代数:)
首先,您应该简化delta.max
函数,如下所示:
deltas.max += (0 - deltas.max) * 0.07;
// Simplifies to
deltas.max -= deltas.max * 0.07;
// Rewrite to
deltas.max = deltas.max - deltas.max * 0.07;
// Rewrite to
deltas.max = deltas.max * (1 - 0.07);
// Simplifies to
deltas.max *= 0.93; // Much nicer :)
这实际上非常重要,因为它使我们计算时间变量的最终值和动画的持续时间变得非常容易:
一旦我们有了这些值,我们所要做的就是创建一个线性二者之间,用这个持续时间将我们的时间设置为该值,并在更新中更新max和prog值:
gsap.to(this.plane.uniforms.time, {
value: n,
duration: dur,
ease: "none",
onUpdate: () => {
this.deltas.applied += (this.deltas.max - this.deltas.applied) * 0.05;
this.deltas.max *= 0.93;
this.plane.uniforms.prog.value = this.deltas.applied;
},
onComplete: () => console.log("complete!")
});
动画完成后,您将获得“完成!”
为了确保即使使用刷新率较高的显示器(即使未使用GSAP直接设置动画的显示器),您的窗帘动画也能以适当的速率运行,最好关闭窗帘的自动渲染器
ing,改用GSAP的计时器:
const curtains = new Curtains({ container: "canvas", autoRender: false });
// Use a single rAF for both GSAP and Curtains
function renderScene() {
curtains.render();
}
gsap.ticker.add(renderScene);
您总共得到了。我找到了一个解决方案,可以在阻尼(摆动)结束时调用函数效果,它不使用GSAP,而是使用窗帘onRender方法。由于uTime值无限增大,而UPGR值接近0,通过跟踪窗帘onRender方法中的uTime和UPGR值,我们可以找到一个点(2个阈值)阻尼效应基本上已经完成。不确定这是否是最有效的方法,但似乎有效
.onRender(() => {
if (this.plane.uniforms.prog.value < 0.008 && this.plane.uniforms.time.value > 50) { console.log("complete")}
})
.onRender(()=>{
如果(this.plane.uniforms.prog.value<0.008&&this.plane.uniforms.time.value>50){console.log(“完成”)}
})
多亏了Dre docs re,我能够更好地控制抖动效果的计时,每次都能达到预期的效果。也就是说,在FPS较低的计算机上,整个阻尼效果平稳地发生,在最后调用了一个onComplete函数,在帧速率较高的计算机上也是如此
尽管如前所述,由于我们没有使用GSAP来控制Utime值,因此对效果长度的控制较少。感谢@Zach!但是,在窗帘内部使用“阈值检查”以这种方式渲染,意味着如果我们在完成调用时禁用绘图,阻尼抖动效果永远不会受到影响
通过在加载图像的同时启用绘图,我们可以避免任何不稳定的行为
export default class Img {
constructor() {
this.deltas = {
max: 0,
applied: 0,
};
this.curtain = new Curtains({
container: "canvas",
watchScroll: false,
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
this.params = {
vertexShader,
fragmentShader,
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0,
},
prog: {
name: "uProg",
type: "1f",
value: 0,
},
},
};
this.planeElements = document.getElementsByClassName("plane")[0];
this.curtain.onError(() => document.body.classList.add("no-curtains"));
this.curtain.disableDrawing(); // disable drawing to begin with to prevent erratic timing issues
this.init();
}
init() {
this.plane = new Plane(this.curtain, this.planeElements, this.params);
this.playWobble();
}
loaded() {
return new Promise((resolve) => {
// load image and enable drawing as soon as it's ready
const asyncImgElements = document
.getElementById("async-textures-wrapper")
.getElementsByTagName("img");
// track image loading
let imagesLoaded = 0;
const imagesToLoad = asyncImgElements.length;
// load the images
this.plane.loadImages(asyncImgElements, {
// textures options
// improve texture rendering on small screens with LINEAR_MIPMAP_NEAREST minFilter
minFilter: this.curtain.gl.LINEAR_MIPMAP_NEAREST,
});
this.plane.onLoading(() => {
imagesLoaded++;
if (imagesLoaded === imagesToLoad) {
console.log("loaded");
// everything is ready, we need to render at least one frame
this.curtain.needRender();
// if window has been resized between plane creation and image loading, we need to trigger a resize
this.plane.resize();
// show our plane now
this.plane.visible = true;
this.curtain.enableDrawing();
resolve();
}
});
});
}
playWobble() {
if (this.plane) {
this.plane
.onReady(() => {
this.deltas.max = 7; // 7
})
.onRender(() => {
this.plane.uniforms.time.value++;
this.deltas.applied += (this.deltas.max - this.deltas.applied) * 0.05;
this.deltas.max += (0 - this.deltas.max) * 0.07;
this.plane.uniforms.prog.value = this.deltas.applied;
console.log(this.plane.uniforms.prog.value);
// ---- "on complete" working!! ( even on hard refresh) -----//
if (
this.plane.uniforms.prog.value < 0.001 &&
this.plane.uniforms.time.value > 50
) {
console.log("complete");
this.curtain.disableDrawing();
}
});
}
}
destroy() {
if (this.plane) {
this.curtain.disableDrawing();
this.curtain.dispose();
this.plane.remove();
}
}
}
const img = new Img();
Promise.all([img.loaded()]).then(() => {
console.log("animation started");
});
导出默认类Img{
构造函数(){
此值为0.delta={
最高:0,
适用范围:0,
};
这个窗帘=新窗帘({
容器:“画布”,
手表卷轴:错,
pixelRatio:Math.min(1.5,window.devicePixelRatio),
});
this.params={
顶点着色器,
碎片着色器,
制服:{
.onRender(() => {
if (this.plane.uniforms.prog.value < 0.008 && this.plane.uniforms.time.value > 50) { console.log("complete")}
})
export default class Img {
constructor() {
this.deltas = {
max: 0,
applied: 0,
};
this.curtain = new Curtains({
container: "canvas",
watchScroll: false,
pixelRatio: Math.min(1.5, window.devicePixelRatio),
});
this.params = {
vertexShader,
fragmentShader,
uniforms: {
time: {
name: "uTime",
type: "1f",
value: 0,
},
prog: {
name: "uProg",
type: "1f",
value: 0,
},
},
};
this.planeElements = document.getElementsByClassName("plane")[0];
this.curtain.onError(() => document.body.classList.add("no-curtains"));
this.curtain.disableDrawing(); // disable drawing to begin with to prevent erratic timing issues
this.init();
}
init() {
this.plane = new Plane(this.curtain, this.planeElements, this.params);
this.playWobble();
}
loaded() {
return new Promise((resolve) => {
// load image and enable drawing as soon as it's ready
const asyncImgElements = document
.getElementById("async-textures-wrapper")
.getElementsByTagName("img");
// track image loading
let imagesLoaded = 0;
const imagesToLoad = asyncImgElements.length;
// load the images
this.plane.loadImages(asyncImgElements, {
// textures options
// improve texture rendering on small screens with LINEAR_MIPMAP_NEAREST minFilter
minFilter: this.curtain.gl.LINEAR_MIPMAP_NEAREST,
});
this.plane.onLoading(() => {
imagesLoaded++;
if (imagesLoaded === imagesToLoad) {
console.log("loaded");
// everything is ready, we need to render at least one frame
this.curtain.needRender();
// if window has been resized between plane creation and image loading, we need to trigger a resize
this.plane.resize();
// show our plane now
this.plane.visible = true;
this.curtain.enableDrawing();
resolve();
}
});
});
}
playWobble() {
if (this.plane) {
this.plane
.onReady(() => {
this.deltas.max = 7; // 7
})
.onRender(() => {
this.plane.uniforms.time.value++;
this.deltas.applied += (this.deltas.max - this.deltas.applied) * 0.05;
this.deltas.max += (0 - this.deltas.max) * 0.07;
this.plane.uniforms.prog.value = this.deltas.applied;
console.log(this.plane.uniforms.prog.value);
// ---- "on complete" working!! ( even on hard refresh) -----//
if (
this.plane.uniforms.prog.value < 0.001 &&
this.plane.uniforms.time.value > 50
) {
console.log("complete");
this.curtain.disableDrawing();
}
});
}
}
destroy() {
if (this.plane) {
this.curtain.disableDrawing();
this.curtain.dispose();
this.plane.remove();
}
}
}
const img = new Img();
Promise.all([img.loaded()]).then(() => {
console.log("animation started");
});