How can I better manage using GSAP animations in Nuxt.js? - javascript

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.

Related

AnimeJS animation takes config from initial click

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

TypeError:undefined is not an object (evaluating 'this.haderHomepage.animateIntro') on Macos Big sur

and the details of the error after that:
onPreloaderDone - app.js:57191:93
onComplete app.js:57176
anonymous function - app.js:441
anonymous function - app.js:32994
anonymous function - app.js:614:209
anonymous function - app:js:837
anonymous function - app:js:254
g app.js:277
while the same page works fine in chrome, firefox on windows.
I am trying to add the relevant line of the app.js file if it makes any sense.
line 57145 - 57214
new class {
constructor(t) {
var e = $$("#preloader")
, i = t.lottie.loadAnimation({
name: e,
container: e,
renderer: "svg",
loop: !1,
autoplay: !1,
path: "themes/sailor/js/preloader.json"
});
i.addEventListener("data_ready", (r=>{
t.lottie.play(e),
TweenMax.to($$("#preloader"), .1, {
css: {
opacity: "0"
},
onComplete: ()=>{
$$("body").classList.add("loaded"),
this.headerHomepage = t.monoPages.headerHomepage,
t.monoReveals.startObserve(!1),
t.monoReveals.initLinesReveals(),
t.monoRevealsWords.startObserve(!1),
TweenMax.fromTo($$("#transition"), 1.2, {
scaleY: 1,
transformOrigin: "50% 100%",
backgroundColor: "#434A7F"
}, {
scaleY: 0,
backgroundColor: "#191919",
onComplete: ()=>{
this.onPreloaderDone(),
i.destroy()
}
,
ease: Expo.easeOut
})
}
,
delay: 2.6,
ease: Expo.ease
})
}
))
}
onPreloaderDone() { //next line below is 57191
"home" == $$(".mono-container").getAttribute("data-page") ? this.headerHomepage.animateIntro() : (TweenMax.to(".menu.menu--main", .6, {
opacity: 1,
y: 0,
ease: Power2.easeOut
}),
TweenMax.to(".top.top--main", .6, {
opacity: 1,
y: 0,
ease: Power2.easeOut,
onComplete: ()=>{
$$("body").classList.add("preloader-done")
}
}))
}
}
({
lottie: uh,
monoReveals: dh,
monoRevealsWords: fh,
gravThemeUrl: ch,
monoPages: yh
})
}
]);
so while I tried to check for any 404 error or anything else missing, but couldn't find any.
looks like it is a mac specific issue and need some change in the code for it to work on safari macos big sur.
Note: I also observed that in the body tag class "preloader done" is not added. whereas in windows machine where it is working okay this class is being added fine.

IntersectionObserver and transform transition

my first post on Stackoverflow
I'm trying to build a simple scroll effect with intersectionObserver. The idea is when an item is out of the viewport, it is scale to 0, and scales to 1 when is fully visible. I really don't know if it is the good way to build this type of effect... but seems to work correctly, except:
I have some issues with "jumping" effects on the element when scrolling. I guess requestAnimationsFrame should solve it, but I'm unable to implement it and I would need some help to anderstand how it works.
A pen is visible here
Note: beginner JS,
Thank you.
const sectionScaleOption = {
root: null,
rootMargin: '0px',
threshold: [
0.0,
0.05,
0.1,
0.15,
0.2,
0.25,
0.3,
0.35,
0.4,
0.45,
0.5,
0.55,
0.6,
0.65,
0.7,
0.75,
0.8,
0.85,
0.9,
0.95,
1.0,
],
};
observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const ratio = entry.intersectionRatio;
const section = entry.target;
if (ratio > 0) {
section.classList.add('forTest');
console.log(ratio);
section.style.transform = `scale(${ratio})`;
} else {
section.classList.remove('forTest');
console.log('out');
}
});
}, sectionScaleOption);
const sectionToScale = document.querySelector('.section');
observer.observe(sectionToScale);
For a better result a finally used:
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
function buildThresholdList() {
let thresholds = [];
let numSteps = 100;
for (let i=1.0; i<=numSteps; i++) {
let ratio = i/numSteps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
const obsOptnScrollOpct = {
root: null,
rootMargin: '0px',
threshold: buildThresholdList()
};
It is not perfect, and I would be very happy for any other suggestions/advices.

Matter.js — How to prevent mouseConstraint to capture scroll events?

I am trying to enable scrolling into a page that contains a Matter.js canvas. I looked everywhere and the recommendation is to add removeEventListener so the mouse constraint won't hijack the scrolling event. Unfortunately, I am having no success. How can I enable a scroll?
Thank you in advance!
CodeSandbox
Code
import "./styles.css";
import Matter from "matter-js";
//Fetch our canvas
var canvas = document.getElementById("world");
//Setup Matter JS
var engine = Matter.Engine.create();
var world = engine.world;
var render = Matter.Render.create({
canvas: canvas,
engine: engine,
options: {
width: 500,
height: 500,
background: "transparent",
wireframes: false,
showAngleIndicator: false
}
});
//Add a ball
var ball = Matter.Bodies.circle(250, 250, 50, {
density: 0.04,
friction: 0.01,
frictionAir: 0.00001,
restitution: 0.8,
render: {
fillStyle: "#F35e66",
strokeStyle: "black",
lineWidth: 1
}
});
Matter.World.add(world, ball);
//Add a floor
var floor = Matter.Bodies.rectangle(250, 520, 500, 40, {
isStatic: true, //An immovable object
render: {
visible: false
}
});
Matter.World.add(world, floor);
//Make interactive
var mouseConstraint = Matter.MouseConstraint.create(engine, {
//Create Constraint
element: canvas,
constraint: {
render: {
visible: false
},
stiffness: 0.8
}
});
Matter.World.add(world, mouseConstraint);
// Why is this not working?
mouseConstraint.mouse.element.removeEventListener(
"mousewheel",
mouseConstraint.mouse.mousewheel
);
mouseConstraint.mouse.element.removeEventListener(
"DOMMouseScroll",
mouseConstraint.mouse.mousewheel
);
//Start the engine
Matter.Engine.run(engine);
Matter.Render.run(render);
It's a HTML issue. in index.html set the overflow to scroll

How to fix this? GSAP animation for Page transition work perfectly only the first time

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();
},
},
],
});
});

Categories

Resources