I want call my function once when the statement if (scrollY > 100vh), but on every scroll when I get more than 100vh is calling func. I understand the reason why is that, but don't know how to fix that.
Perhaps it is simple but I can't do that.
I want a simple animation on scrolling.
code :
import anime from 'animejs';
var txt = document.querySelector('.textanimation');
txt.innerHTML = txt.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
window.addEventListener('scroll', () => {
const vh = window.innerHeight;
const scrolled = window.scrollTop;
if (scrolled > vh) {
const scrollTextAnimation = anime.timeline();
scrollTextAnimation
.add({
targets: '.textanimation .letter',
translateY: [-100, 0],
easing: 'easeOutExpo',
duration: 1400,
delay: (el, i) => 30 * i
})
.add({
targets: '.textanimation',
duration: 1000,
easing: 'easeOutExpo',
delay: 1000
});
}
});
just add once option to the event listener to execute it one time.
window.addEventListener('scroll', () => {
const vh = window.innerHeight;
const scrolled = window.scrollTop;
if (scrolled > vh) {
const scrollTextAnimation = anime.timeline();
scrollTextAnimation
.add({
targets: '.textanimation .letter',
translateY: [-100, 0],
easing: 'easeOutExpo',
duration: 1400,
delay: (el, i) => 30 * i
})
.add({
targets: '.textanimation',
duration: 1000,
easing: 'easeOutExpo',
delay: 1000
});
}
}, {once: true});
I just added a bool that change the statement to false when I make a scrollY more than innerHeight/2. "Once" option in addEventListener was not working, don't know why. Is there a better solution how to fix that?
The code below is working:
let myBool = true;
window.addEventListener('scroll', () => {
if (window.scrollY > window.innerHeight / 2 && myBool) {
anime.timeline()
.add({
targets: '.textanimation .letter',
translateY: [-100, 0],
easing: 'easeOutExpo',
duration: 1400,
delay: (el, i) => 30 * i
})
.add({
targets: '.textanimation',
duration: 1000,
easing: 'easeOutExpo',
delay: 1000
});
myBool = false;
}
});
You can remove the event listener after your logic executes in order to stop it from ever firing again. You can do this in two ways:
1) Use the once option as defined in the parameters for addEventListener here. This option automatically removes the event listener after the event happens once. The OP has already stated this isn't working for them (possibly because the scroll needs to reach a certain height before the logic works so it is being removed too early).
2) Call removeEventListener after the logic executes:
import anime from 'animejs';
var txt = document.querySelector('.textanimation');
txt.innerHTML = txt.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
var animateOnce = function() {
const vh = window.innerHeight;
const scrolled = window.scrollTop;
if (scrolled > vh) {
const scrollTextAnimation = anime.timeline();
scrollTextAnimation
.add({
targets: '.textanimation .letter',
translateY: [-100, 0],
easing: 'easeOutExpo',
duration: 1400,
delay: (el, i) => 30 * i
})
.add({
targets: '.textanimation',
duration: 1000,
easing: 'easeOutExpo',
delay: 1000
});
// remove the event listener after the animation happens on the first time
window.removeEventListener('scroll', animateOnce);
}
}
window.addEventListener('scroll', animateOnce);
3) Here are a couple of other solutions in a post on sitepoint.
Related
I implemented a Staggered Grid in React using AnimeJS, with 2 variants, defined as
export const SimpleAnimationConfig: AnimeParams = {
backgroundColor: COLORS[Math.floor(Math.random() * COLORS.length)],
scale: [
{ value: 0.1, easing: "easeOutSine", duration: 500 },
{ value: 1, easing: "easeInOutQuad", duration: 1200 },
],
};
export const CoolerAnimationConfig: AnimeParams = {
scale: [
{ value: 0.1, easing: "easeOutSine", duration: 500 },
{ value: 1, easing: "easeInOutQuad", duration: 1200 },
],
};
(and some other properties defined in CSS)
and using them as
async function onClickTile(index: number) {
const animationConfig = getAnimationConfig(isCooler);
animation.current = anime({
targets: ".tile",
delay: anime.stagger(50, { grid: [columns, rows], from: index }),
...animationConfig,
});
}
where animation is
const animation = useRef<AnimeInstance>();
and getAnimationConfig is
export function getAnimationConfig(isCooler: boolean): AnimeParams {
if (isCooler) {
return CoolerAnimationConfig;
}
return SimpleAnimationConfig;
}
Problem
Let say I clicked on a tile with index 50, it will start a staggered animation from the div with index 50 , Every subsequent animation I trigger from any other div located at any other index will however start from 50.
I console logged index, it shows the right value ,
I console logged the whole object that I am passing inside anime , it shows the correct value for fromIndex in the scope of function delay.
I did resolve the issue with
function onClickTile(index: number) {
animation.current = anime({
targets: ".tile",
delay: anime.stagger(50, { grid: [columns, rows], from: index }),
scale: [
{ value: 0.1, easing: "easeOutSine", duration: 500 },
{ value: 1, easing: "easeInOutQuad", duration: 1200 },
],
...(!isCooler && {
backgroundColor: COLORS[Math.floor(Math.random() * COLORS.length)],
}),
});
}
but still curious what was happening with separate objects, since using separate constants and helpers seems a much cleaner way than this.
DEMO
I'm quite new in Phaser, but I've already done a bunch of ionic/angular app.
One of my object has a method that will be called by the main scene:
powerOff() {
let easing = 'Cubic'
let overallDuration = 500
let visiblePauseDuration = 100
let flashDuration = overallDuration - visiblePauseDuration / 2
this.scene.tweens.timeline({
tweens: [
{
targets: this,
duration: 0,
tint: 0xff0000,
ease: easing,
},
{
targets: this,
duration: flashDuration,
tint: 0xffffff,
ease: easing,
},
{
targets: this,
duration: visiblePauseDuration,
tint: 0xff0000,
ease: easing,
},
{
targets: this,
duration: flashDuration,
tint: 0xffffff,
ease: easing,
},
{
targets: this,
duration: visiblePauseDuration,
tint: 0xff0000,
ease: easing,
onComplete: () => {
this.scene.sound.play(MainScene.POWER_DOWN)
},
},
{
targets: this,
duration: flashDuration,
tint: 0xf54242,
ease: easing,
},
],
})
this.poweredUp = false
}
The thing is: I need to exit this method only when the timeline has completed.
Is there some await/async support? Or at least promises ?
The scene will call this method on a lot of different objects, and I need that they are not done in parallel but sequentially.
Thanks a lot!!!
var timeline = this.scene.tweens.timeline({
/*TWEENS*/
})
//Timeline complete event listener
timeline.addListener("complete", function(){
//Do something when tweens are complete
}, this);
I`m using barba.js with Gsap.
The idea is to have a transition from the home page and the about page with a centered logo animation, very simple. I click on my button, the transition comes in, the logo scale from 0 to 1, and go down fading, great!
The first animation works as it should but when I clicked again the scale factor is now missing, unfortunately.
How can I fix this to make it work? have a look at my code pen project:
https://codepen.io/cat999/project/editor/AEeEdg
Here my JS
function delay(n) {
n = n || 2000;
return new Promise((done) => {
setTimeout(() => {
done();
}, n);
});
}
function pageTransition() {
var tl = gsap.timeline();
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "0%",
ease: "Expo.easeInOut",
});
tl.to("#svg-1", {
duration: 1, opacity: 0, y: 30, stagger: 0.4, delay: 0.2,
});
tl.to(".loading-screen", {
duration: 1,
width: "100%",
left: "100%",
ease: "Expo.easeInOut",
delay: 0.3,
});
tl.set(".loading-screen", { left: "-100%", });
tl.set("#svg-1", { opacity: 1, y: 0 });
}
function contentAnimation() {
var tl = gsap.timeline();
tl.from(".animate-this", { duration: 1, y: 30, opacity: 0, stagger: 0.4, delay: 0.2 });
}
$(function () {
barba.init({
sync: true,
transitions: [
{
async leave(data) {
const done = this.async();
pageTransition();
await delay(2000);
done();
},
async enter(data) {
contentAnimation();
},
async once(data) {
contentAnimation();
},
},
],
});
});
I was wondering if there is an easy way to delay the start of this anime.timeline animation? I have a pre-loader on my site, so I don't want this animation to trigger until the pre-loader disappears, which is after 2000 ms.
<script>
anime.timeline({loop: true})
.add({
targets: '.ml3 .letter',
opacity: [0,1],
easing: "easeInOutQuad",
duration: 2250,
delay: (el, i) => 150 * (i+1)
}).add({
targets: '.ml3',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 10000
});
</script>
I think this is an easy fix, but I'm still new to JS. Thank you!
You can use setTimeout() Method
<script>
setTimeout(function(){
anime.timeline({loop: true})
.add({
targets: '.ml3 .letter',
opacity: [0,1],
easing: "easeInOutQuad",
duration: 2250,
delay: (el, i) => 150 * (i+1)
}).add({
targets: '.ml3',
opacity: 0,
duration: 1000,
easing: "easeOutExpo",
delay: 10000
});
}, 3000);
</script>
This will run your function with a 3 seconds delay.
Have you try to set timeout before animation start?
var animation = function(){
setTimeout(check, 1000);
}
animation();
I want to make an animation where an image should scale and move to the center at the same time. However, the final position is not correct. If I don't have scale, the translation works perfectly. I want to center the image, so (screenW / 2 - IMAGE_RADIUS * SCALE) should be correct.
(1) before: https://ibb.co/efjAW8, (2): after: https://ibb.co/bUL64T
code:
pressedImage = () => {
const deltaX = (screenW / 2 - IMAGE_RADIUS * SCALE) - origX;
const deltaY = (screenH / 2 - IMAGE_RADIUS * SCALE - 30) - origY;
Animated.parallel([
Animated.timing(this.state.scaleAnimatedValue, {
toValue: SCALE,
easing: Easing.ease,
duration: 1000,
}),
Animated.timing(this.state.animTransX,{
toValue: deltaX,
easing: Easing.ease,
duration: 1000,
}),
Animated.timing(this.state.animTransY,{
toValue: deltaY,
easing: Easing.ease,
duration: 1000,
}),
]).start();
}
render() {
...
<ImageView style={
{
transform: [
{
translateX: this.state.animTransX,
},
{
translateY: this.state.animTransY,
},
{
scale: this.state.scaleAnimatedValue,
},
],
}} source={{ uri: gist.author.avatarUrl }} />
}
How can I achieve this? I've tried with matrix transformations and setNativeProps but failed miserably. This should be a simple thing...