Start multiple animations simultaneously in Framer Motion - javascript

I'm new to Framer Motion (and animations in general) and am trying to replicate the below GSAP effect
https://codepen.io/cameronknight/pen/pogQKwR
I can achieve the reveal effect and scale effect individually, but when applying both animations,
only one of the animation triggers, and the remainder of the second animation only triggers after the first has completed.
I've tried altering the delay, duration etc. but to no avail, and was wondering if there was any alternative method to trigger both animations at the same time (or with very minimal delay).
The code can be found below:
I have a motion image element, and a motion div element on top styled to be the exact same dimensions on top of the image element. An X transformation is applied to the motion div element to achieve the reveal effect. I then tried to apply a scale effect to the motion image element, and this is where the problem occurs.
function App() {
const overlayVariant = {
start: {
x: "0%",
opacity: 1,
},
end: {
x: "100%",
transition: {
delay: 0.5,
duration: 1,
ease: "easeInOut"
}
}
}
const imageVariant = {
start: {scale: 1.1},
end: {
scale: 1,
transition: {
delay: 0.5,
duration: 1,
ease: "easeOut"
}
}
}
return (
<main className="app-container">
<section className="app-contents">
<div className="app-image">
<motion.div
className="image-overlay"
variants={overlayVariant}
initial="start"
animate="end"
>
</motion.div>
<motion.img
src={image}
variants={imageVariant}
whileHover="hover"
transition={{duration: 0.6, ease: [0.43, 0.13, 0.23, 0.96]}}
initial="start"
animate="end"
/>
</div>
</section>
</main>
);
}
Thank you advance, and I would appreciate any help I could get.

Related

Animating child elements in ScrollTrigger GSAP horizontal scroll

I have an svg which forms the basis of my horizontal scroller.
Within this svg, I have added the class .animate to the elements which I want to fade in up as the item comes into view. The .animate class for reference has been added to all the text items in the svg.
Currently, only the .animate elements that are in view initially fade in up. When I scroll down to continue the scroller, the other elements are static. They're not fading in or translating up or down in any way?
TL;DR, here is what I'm trying to achieve:
When the scroller pins in place, and the user continued to scroll down, start fading away .horizontalScroller__intro.
Once .horizontalScroller__intro has faded away, start the horizontal scroll for .horizontalScroller__items
Any elements with the class of .animate in my scroller will fade in up to its original position.
Note: I understand SO rules and preferences to post code here. But, my demo's contain a length SVG, which I cannot post here as it exceeds SO's character limit.
Here is a demo of my latest approach
From the scrollTrigger docs, containerAnimation is what helps achieve animations on horizontal scrollers, and is what I've tried to achieve.
However, in my demo above, I have the following issues:
.horizontalScroller__intro doesn't show initially, when it should, and should fade out on scroll.
The horizontal scroller doesn't work anymore
The .animate elements that are in view, do not fade in up
If I use timeline (see below snippet), then the intro fade out and scroller works. But, doesn't animate in the child elements, which is where I need containerAnimation
$(function() {
let container = document.querySelector(".horizontalScroller__items");
let tl = gsap.timeline({
scrollTrigger: {
trigger: ".horizontalScroller",
pin: ".horizontalScroller",
anticipatePin: 1,
scrub: true,
invalidateOnRefresh: true,
refreshPriority: 1,
end: '+=4000px',
markers: true,
}
});
tl.to('.horizontalScroller__intro', {
opacity: 0,
})
tl.to(container, {
x: () => -(container.scrollWidth - document.documentElement.clientWidth) + "px",
ease: "none",
})
});
I'm struggling to find a way in which I can make the intro fade in, the scroller scroll horizontally, and the .animate elements to fade in, or fade in up.
Edit:
#onkar ruikar, see notes based on your sandbox below:
When you scroll down and the comes into view, I want the initial .animate elements to scroll up into view (currently, once the text fade away, and then the horizontal scroller starts working, only then does the .animate that are suppose to be in view, fade in up
After the initial .animate elements have loaded, the next .animate elements that are part of the scroller, they do not fade in up. They're static. As each .animate element comes into view, then it should fade in up (I think it's currently triggering once, for all the elements).
See visual here:
In the above gif, you can see the first two text blocks are hidden, as soon as they're in view, I want them to fade up. Then the 3rd and 4th text blocks are static, when they should fade up as the user scrolls to that section.
You need to use onUpdate method on the scroll trigger.
onUpdate: self => console.log("progress", self.progress)
Based on the self.progress set opacity, x position etc.
Full demo on codesandbox. Click on "Open Sandbox" button on bottom right to see the code.
if ("scrollRestoration" in history) {
history.scrollRestoration = "manual";
}
$(function() {
let container = document.querySelector(".horizontalScroller__items");
let elements = gsap.utils.toArray(
document.querySelectorAll(".animate")
);
let intro = document.querySelector(".horizontalScroller__intro");
let svg = document.querySelector("svg");
let animDone = false;
window.scrollPercent = -1;
var scrollTween = gsap.to(container, {
ease: "none",
scrollTrigger: {
trigger: ".horizontalScroller",
pin: ".horizontalScroller",
anticipatePin: 1,
scrub: true,
invalidateOnRefresh: true,
refreshPriority: 1,
end: "+=600%",
markers: true,
onEnter: (self) => {
moveAnimate();
},
onLeaveBack: (self) => {
resetAnimate();
},
onUpdate: (self) => {
let p = self.progress;
if (p <= 0.25) {
let op = 1 - p / 0.23;
intro.style.opacity = op;
animDone = false;
}
if (p > 0.23) {
moveAnimate();
// we do not want to shift the svg by 100% to left
// want to shift it only by 100% - browser width
let scrollPercent =
(1 - window.innerWidth / svg.scrollWidth) * 100;
let shift = ((p - 0.22) * scrollPercent) / 0.78;
gsap.to(svg, {
xPercent: -shift
});
}
}
}
});
function resetAnimate() {
gsap.set(".animate", {
y: 150,
opacity: 0
});
}
resetAnimate();
function moveAnimate() {
for (let e of elements) {
if (ScrollTrigger.isInViewport(e, 0.4, true))
gsap.to(e, {
y: 0,
opacity: 1,
duration: 2
});
}
}
});
You need to set opacity 0 on .animate elements in CSS. And use end: '+=400%' instead of 4000px. Relative dimensions can be used in position based calculations easily.

applying multi animations in one component using Framer motion variants

I am new by Framer motion, what I am trying to do is imitating wheel motion by moving image while it is rotating
I don't know how to make this work
I have tried something like this but it doesn't work
const imageRuning :Variants = {
hidden:{
x:"-100vw",
opacity:0
},
visible:{
x:0,
opacity:1,
transitionDuration:"3s"
},
rotation:{
rotate:[180,0],
transition:{
repeat:Infinity,
type:"tween",
ease:"linear"
}
}
}
const HomePage =()=> {
return (
<div className={style.animationContainer}>
<motion.img
className={style.animatedImage}
variants={imageRuning}
initial="hidden"
animate={["visible","rotation"]}
width={100} height={100} src="/static/me.jpg" >
</motion.img>
</div>
)
any help please ,
It looks like you are trying to animate two properties (x and rotate) with different transition values.
You can only animate to one variant at a time, so if you want them to happen at the same time, you'll need to combine those into a single variant. Luckily, you can specify unique transition values for any animating property by listing it within the transition object.
Like this:
visible: {
x: 0,
opacity: 1,
rotate: 180, // rotate and x animate in the same variant
transition: {
duration: 3, // default transition duration (applies to x and opacity)
rotate: {
// unique transition for rotate property only
repeat: Infinity,
type: "tween",
ease: "linear"
}
}
}
The way you have it set up, the object will continue rotating even after the x animation finishes (repeat: Infinity). Is that what you want? You can look into Animation Controls if you want more control.

Anime Js and opacity

I wanted to use animate to translate and fade element but the opacity property doesn't affect the animation.
I've got so far :
let timeline1 = anime.timeline();
timeline1.add({
targets: '#operation .letter',
translateY: [0,40],
opacity: [0, 1],
easing: "linear",
duration: 300,
delay: (el, i) => 30 * i
})
Translation and other properties work nicely but it seems that it doesn't change the opacity as a css property but as an attribute.
In the HTML, once rendered, the targets shows:
<span class="letter" opacity="1" style="transform: translateY(40px);">p</span>
It seems to get opacity as an attribute not like a css property
I tried to opacity: 1, and opacity:"1" both didn't change the animation
Thanks in advance,
I set the "opacity" property to 0 in the .css and configured the script like this:
var timeline = anime.timeline();
timeline.add({
targets: '#home-intro',
opacity: 1,
duration: 4000,
}
);
Everything works fine, with and without timeline.

How can I remove a class *after* a GSAP timeline animation finishes?

I've created a simple GSAP timeline animation to slide a fullscreen div over the entire viewport.
Because the page that it's going to be sliding in over has scrollable content on it, I've set the visibility to hidden, and pushed it off of the page. When I click the selected nav link, the class of "active" is added and the animation fires.
For some reason, when I reverse it, it removes the class immediately, even if I add something like this:
$(closeAbout).on('click', "#close", function () {
timeline.reversed() ? timeline.play() : timeline.reverse();
$("#teambio").removeClass('active');
});
I've included all of the timeline code below, in case that's helpful to anyone:
var closeAbout = $('#close');
var openAbout = $('#about');
var main = $('main');
var timeline = new TimelineMax({
paused: true,
reversed: true
});
timeline
.to( "main", 0.3, {
opacity: 0,
}, 0)
.to(".about", 1, {
y: "0%",
ease: Power4.easeInOut
})
.to(".blurb", 1, {
y: "0%",
opacity: 1,
ease: Power4.easeInOut,
delay: 0.5
}, 0)
.to("#close", 0.5, {
opacity: 1,
ease: Power4.easeInOut,
delay: 0.8,
}, 0);
$(openAbout).on('click', "#about", function () {
$("#teambio").addClass('active');
timeline.reversed() ? timeline.play() : timeline.reverse();
});
$(closeAbout).on('click', "#close", function () {
timeline.reversed() ? timeline.play() : timeline.reverse();
$("#teambio").removeClass('active');
});
All I want is for the class to be removed AFTER the timeline finishes, and for some reason nothing that I've tried is working.
Also, I know that I could probably structure and name all of this better than it appears here, but I'm both new to GSAP and also just trying to get this to work.
Any help and patience would be greatly appreciated.
With something like this:
timeline.reversed() ? timeline.play() : timeline.reverse();
$("#teambio").removeClass('active');
The .play() or .reverse() function will run and start animating. Once that function returns, the next line will run, removing the class. You wouldn't want the next line to wait for the animation to complete because then all of your JavaScript would be waiting for the animation to complete!
What you should do instead is make use of GSAP's onComplete callback:
var timeline = new TimelineMax({
paused: true,
reversed: true,
onComplete: function() {
$("#teambio").removeClass('active');
}
});
GSAP will fire that function every time the tween completes.
By the way, we recommend that you upgrade to GSAP 3 doing so is easy! The new formatting is quite nice once you're used to it.

JavaScript rotation is wrong after translating

I'm using Velocity.js for animation.
I'm first translating and rotating an object. After the animation is complete I need to rotate the object by an additional 360 degrees.
The problem is that during the second animation the rotation axis is off. Instead of rotating around the center the object rotates around its original point.
$.Velocity( obj, "stop" );
$.Velocity( obj,
{translateX: pos, rotateZ: rotation + 'deg'},
{duration: 1000, complete: function() {
$.Velocity( obj, {rotateZ: "360deg"}, {duration: 1000} ); }
});
What may be the problem?
UPDATE
Codepen that demonstrates the issue: http://codepen.io/anon/pen/MYZaaj
This is because Velocity currently does not parse initial transform values. From the docs:
Note that Velocity's performance optimizations have the byproduct of ignoring outside changes to transform values (including initial values as defined in your stylesheets, but this is remedied via Forcefeeding). (You can manually set transform values within Velocity by using the Hook function.)
This will be addressed in future version but currently follow the advice below to use forcefeeding to fix it.
Sorry, I don't have enough points to comment, but hook (the other answer is right). Just add $.Velocity.hook(circle, "translateY", "0");
var circle = $(".circle");
circle.velocity({
translateX: 500,
rotateZ: 360
}, {
duration: 2500
}).delay(500).velocity({
rotateZ: -360 * 2,
translateY: 200
}, {
duration: 2500
});
$.Velocity.hook(circle, "translateY", "0");
circle.delay(500)
.velocity({
translateY: 0,
rotateZ: -360 * 5
}, {
duration: 2500
});
.circle {
background: url("https://dl.dropboxusercontent.com/u/16997159/square.png");
width: 128px;
height: 128px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.min.js"></script>
<div class="circle">
</div>

Categories

Resources