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
Related
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 am building a portfolio page using nuxt.js and Green Sock(GSAP) animation for on-load, menu, and page transitions. It is more difficult to accommodate GSAP than using CSS based transitions but I feel the results are worth it.
I am not using webpack and nuxt correctly to make everything more modular and concise. I need some help to better export variables and functions, use Vue.js reactive properties better, and take advantage of what's already available instead of working against it.
My default.vue file has a method that manages the menu:
showMenu() {
if (process.browser) {
var tl = new TimelineMax();
var slide = document.querySelector("#slide-menu");
var pl = document.querySelector("body");
var nl = document.querySelectorAll(".nav-link");
if (this.$store.state.menuIsActive === false) {
tl
.to(slide, 0.5, {
y: "100%",
ease: this.$store.state.animParams.ease
})
.staggerTo(
nl,
this.$store.state.animParams.dur3,
{
y: "-10px",
autoAlpha: 1,
ease: this.$store.state.animParams.ease
},
this.$store.state.animParams.dur2
);
} else if (this.$store.state.menuIsActive === true) {
console.log("true");
tl
.staggerTo(
nl,
this.$store.state.animParams.dur3,
{
y: "10px",
autoAlpha: 0,
ease: this.$store.state.animParams.ease
},
this.$store.state.animParams.dur2
)
.to(slide, 0.5, {
y: "-100%",
ease: this.$store.state.animParams.ease
});
}
this.$store.commit("toggleMenuState");
}
}
I have a menu.js middleware that closes the menu when open. I would prefer to use the same variable but redeclare it instead:
import TweenMax from 'gsap'
export default function ({ store }) {
if (store.state.menuIsActive === !false) {
if (process.browser) {
var tl = new TimelineMax()
var slide = document.querySelector('#slide-menu')
var nl = document.querySelectorAll('.nav-link')
if (store.state.menuIsActive === true) {
console.log('initmenu')
tl
.staggerTo(
nl,
store.state.animParams.dur3,
{ y: '10px', autoAlpha: 0, ease: store.state.animParams.ease },
store.state.animParams.dur2
)
.to(slide, 0.5, { y: '-100%', ease: store.state.animParams.ease })
store.commit('setMenuState', false)
}
}
}
}
I duplicated the page transition because I couldn't get it working correctly in my nuxt.config.js. I would like to keep all of my transitions there or in a separate plugins file maybe but am not sure the best way to do so.
From my root index.vue page file:
import TweenMax from "gsap";
export default {
transition: {
name: "page",
mode: "out-in",
css: false,
enter: function(el, done) {
console.log("animenter contact");
if (process.browser) {
var tl = new TimelineMax();
var tg = document.querySelectorAll(".el");
var st = document.querySelector(".overlay-grid img");
var box = document.querySelectorAll(".box");
tl.fromTo(st, 0.3, { x: -100 }, { x: 0, autoAlpha: 1 });
tl.staggerTo(tg, 0.3, { autoAlpha: 1 }, 0.1);
tl.staggerTo(box, 0.3, { autoAlpha: 1, onComplete: done }, 0.1);
}
},
leave: function(el, done) {
console.log("leaving contact...");
if (process.browser) {
var tl = new TimelineMax();
var tg = document.querySelectorAll(".el");
var st = document.querySelector(".overlay-grid img");
var pl = document.querySelector("body");
var box = document.querySelectorAll(".box");
tl.fromTo(st, 0.3, { x: 0 }, { x: -100, autoAlpha: 0 });
tl.staggerTo(tg, 0.3, { autoAlpha: 0 }, 0.1);
tl.staggerTo(box, 0.3, { autoAlpha: 0, onComplete: done }, 0.1);
}
}
}
And finally, in nuxt.config.js:
router: {
middleware: 'menu'
},
build: {
vendor: [
'gsap',
....
] /*
}
I also have some variables set in the store to manage state, and some transition durations so I don't have to edit them in as many places to see how something looks. It is becoming a lot of work to preview ideas or small changes and I wasn't able to find an in-depth article that tackles this problem.
Any help with tips and tricks for Nuxt, Vue, GSAP, Webpack, and Javascript in general would be greatly appreciated.
I am using the Velocity UI library for text animation. I have registered two effects (In & Out, thus it handles the display property), and they are running in a sequence. How can I loop the sequence? I tried to trigger it as an option at the last in the sequence, but it did not work (as the running of the sequence is not a function).
I went through the answers here on Stack Overflow and on Github, but I could not figure out an answer. Please give me a suggestion what would be a good way to put it on an infinite loop. Thank you!
$.Velocity.RegisterUI("mythingIn", {
calls: [
[{ color: "#fff", opacity: 1 }, 0.5],
[{ color: "#ffac00" }, 0.5]
]
});
$.Velocity.RegisterUI("mythingOut", {
calls: [
[{ opacity: 0 }]
]
});
var a1wordflow1 = [
{ e: $('.a1w-1-1'), p: ("mythingIn"), o: { stagger: 70, duration: Math.random()*6000+14000 } },
{ e: $('.a1w-1-1'), p: ("mythingOut"), o: { stagger: 70, duration: 800 } },
{ e: $('.a1w-1-2'), p: ("mythingIn"), o: { stagger: 70, duration: Math.random()*6000+14000 } },
{ e: $('.a1w-1-2'), p: ("mythingOut"), o: { stagger: 70, duration: 800 } },
];
$.Velocity.RunSequence(a1wordflow1);
All you need to do is to calculate the total time of your sequence in advance, then call your sequence inside a setInterval call.
$.Velocity.RegisterUI("mythingIn", {
// ...
});
$.Velocity.RegisterUI("mythingOut", {
// ...
});
const rand = Math.random()*6000+14000;
var a1wordflow1 = [
{ e: $('.a1w-1-1'), p: ("mythingIn"), o: { stagger: 70, duration: rand } } ,
{ e: $('.a1w-1-1'), p: ("mythingOut"), o: { stagger: 70, duration: 800 } },
{ e: $('.a1w-1-2'), p: ("mythingIn"), o: { stagger: 70, duration: rand } },
{ e: $('.a1w-1-2'), p: ("mythingOut"), o: { stagger: 70, duration: 800 } },
];
let totalTime = 2 * rand + 1600 + maybeSomeOtherTime;
const interval = setInterval(() => {
$.Velocity.RunSequence(a1wordflow1);
}, totalTime);
If you some time later decide to stop the loop, you need to add the following to your code to stop the loop :
clearInterval(interval);
I'm trying to build clock using jQuery Knob.
My clock is working (http://jsfiddle.net/Misiu/9Whck/1/), but right now I'm trying to add some extras to it.
At beginning I want to have all knobs set to 0, then using animate I want to animate their value to current time and then start normal timer update.
My code looks like this (demo here: http://jsfiddle.net/Misiu/cvQED/81/):
$.when(
$('.h').animate({
value: h
}, {
duration: 1000,
easing: 'swing',
progress: function () {
$(this).val(Math.round(this.value)).trigger('change');
}
}),
$('.m').animate({
value: m
}, {
duration: 1000,
easing: 'swing',
progress: function () {
$(this).val(Math.round(this.value)).trigger('change');
}
}),
$('.s').animate({
value: s
}, {
duration: 1000,
easing: 'swing',
progress: function () {
$(this).val(Math.round(this.value)).trigger('change');
}
})).then(function () {
myDelay();
});
function myDelay() {
var d = new Date(),
s = d.getSeconds(),
m = d.getMinutes(),
h = d.getHours();
$('.h').val(h).trigger("change");
$('.m').val(m).trigger("change");
$('.s').val(s).trigger("change");
setTimeout(myDelay, 1000)
}
In when I must call animate for every knob separately, but I would like to use data-targetValue and to have only one animate inside when.
Can this be done?
if you want to use data-targetValue you need to change your js like this
$('.h').data('targetValue', h);//$('.h').attr('targetValue', h);
$('.m').data('targetValue', m);
$('.s').data('targetValue', s);
//...
$.when(
$('.knob').each(function(){//each .knob
$(this).animate({//animate to data targetValue
value: $(this).data('targetValue')
}, {
duration: 1000,
easing: 'swing',
progress: function () {
$(this).val(Math.round(this.value)).trigger('change')
}
});
})
).then(function () {
myDelay();
});
http://jsfiddle.net/cvQED/83/
or without .each
$.when(
$('.knob').animate({
value: 100
}, {
duration: 1000,
easing: 'swing',
progress: function () {
$(this).val(Math.round(this.value/100*$(this).data('targetValue'))).trigger('change')
}
})
).then(function () {
myDelay();
});
http://jsfiddle.net/cvQED/84/