JS/GSAP solution for infinite animation - javascript

I am trying to create a infinite star rain animation, all stars are SVG's.
I tried this to create the animation:
(function($) {
TweenMax.set(".astar", {
x:function(i) {
return i * 50;
}
});
TweenMax.to(".astar", 5, {
ease: Linear.easeNone,
x: "+=500", //move each box 500px to right
modifiers: {
x: function(x) {
return x % 500; //force x value to be between 0 and 500 using modulus
}
},
repeat: -1
});
})(jQuery);
The repeat process is not smooth as you can see on this Codepen:
https://codepen.io/daniellwdb/pen/NXogoB
Is there any JS or GSAP solution to make the animation smooth so that it will look like stars keep spawning from the left and move to the right?

With your current setup, I think the easiest way to pull this off would be to duplicate your starfield so that the beginning of your next loop is identical to the end of your first one. Let's say this is your starfield SVG:
|...o.|
|o....|
|..o..|
Your new "duplicated" starfield would essentially be:
|...o.|...o.|
|o....|o....|
|..o..|..o..|
So when you move that duplicated image from left to right 100%, what you see in the last "frame" is identical to what it will return to when it loops.
Here's a fiddle that shows this concept in action: https://jsfiddle.net/yarp4oLs/5/
I have two identical starfield images that are 200x200 (so 400x200 when side-by-side) and they are displayed in a "viewport" container that is 200x200. Then I just slide them to the left 200px and repeat. Instant stars!

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.

Snap.svg hover/unhover bug (for longer unhover)

If I make a circle and then animate it like this - everything works:
var s = Snap(200,200);
var c = s.circle(100,100,100);
c.hover(function(){
c.animate({fill:"red"}, 500, mina.easeinout);
}, function() {
c.animate({ fill:"black" }, 500, mina.easeinout);
});
It changes the color to red on hover and on "unhover" it goes back to black. But if I make the time in the second function (unhover) 1500 instead of 500 you'll notice that if you hover over the circle, then unhover and hover AGAIN before in less than 1.5 seconds (before the circle's black again) - it will animate to full red and the rapidly go black without animation. Here is the demo:
https://jsfiddle.net/Drasik/5bcu4rc5/1/
Why is this happening and how can I fix it? Thanks!
The animation to black is still running, so if you hover again 500ms after hovering out, you're telling it to turn red in 500ms, then black again in 1000ms (and I think the browser gets confused, hence the suddenness).
To avoid this, you can try to stop the fade to black animation when hovering back in, like this (https://jsfiddle.net/5bcu4rc5/3/):
c.hover(function(){
c.stop(); // stops the black fill from continuing
c.animate({fill:"red"}, 500, mina.easeinout);
}, function() {
c.animate({ fill:"black" }, 1500, mina.easeinout);
});
If alternatively you want to force the full 1500ms to run before the red fill can start, maybe something like this (though there are probably better ways: https://jsfiddle.net/5bcu4rc5/2/):
c.hover(function(){
if (c.className != "unhovering") {
c.animate({fill:"red"}, 500, mina.easeinout);
}
}, function() {
c.className = "unhovering";
c.animate({ fill:"black" }, 1500, mina.easeinout, function() {
c.className = "";
});
});

Vibrating screen on scroll using transform: scale

I would like a zoom out effect for my header, what loads zoomed in, and on scroll it zoom out.
What I do is to increase the size with transform: scale(1.4) and on scroll I calculate a percentage from the scrollTop and header height and I multiply it with 0.4. The problem is that on scroll the screen starts to vibrate, the scale isn't smooth. Do you have any idea what's wrong with my code or can you tell me what's the best practice to achieve this?
jQuery(document).ready(function(){
function zoom_out() {
var page_header_height = jQuery('#page-header-custom').outerHeight();
var scroll_top = jQuery(window).scrollTop();
var zoom_multiplier = 0.4;
var multiplier = (zoom_multiplier*(1-((scroll_top-jQuery('#page-header-custom').offset().top)/page_header_height))) > 1 ? 1 : (zoom_multiplier*(1-((scroll_top-jQuery('#page-header-custom').offset().top)/page_header_height)));
if(multiplier <= 1) {
jQuery('#page-header-inner').stop(true, true).transition({ scale: 1/(1+multiplier), translate: '0, -50%' });
jQuery('#page-header-custom').stop(true, true).transition({
scale: 1+multiplier
});
}
}
zoom_out();
jQuery(window).on('scroll', function(){
zoom_out();
});
});
I created a JSFiddle to see it in action.
I've updated your Fiddle with smooth scaling using window.requestAnimationFrame. The scale animation is vibrating because you're triggering a translation on each scroll event. Think about it like this:
user scrolls
zoom_out() gets triggered and tells an element to transition it's transform properties. Your element is now transitioning at a certain speed: "length" / transitiontime.
More scroll events have passed and are all triggering zoom_out(). The next transition will probably happen at a different speed, resulting in 'vibrating' animation.
First you can get rid of jQuery's transition() method. If you fire the function at 60fps or close to 60fps it will appear to animate smoothly to the human eye, without the need of transitioning or animating.
if(multiplier <= 1) {
//jQuery('#page-header-inner').stop(true, true).transition({ scale: 1/(1+multiplier), translate: '0, -50%' });
//jQuery('#page-header-custom').stop(true, true).transition({ scale: 1+multiplier });
//becomes:
jQuery('#page-header-inner').css({ scale: 1/(1+multiplier), translate: '0, -50%' });
jQuery('#page-header-custom').css({ scale: 1+multiplier });
}
}
Getting the function triggered at ~60fps can be achieved in multiple ways:
Throttle your scroll event to 60fps.
Or use window.requestAnimationFrame like in the updated Fiddle
function zoom_out(){
//calculation code & setting CSS
window.requestAnimationFrame(zoom_out);
}
//trigger it once instead of the scroll event handler
window.requestAnimationFrame(zoom_out);

Bouncer plugins in jquery

I am looking for a jquery plugin that bounces a div element periodically infinite times. I have no such example website to show, but what I want is a div element that bounces to right(say) and bounces back to left. And at the time of bounce I want to change the content. Also bounce should go on. It should not stop or slow down.
I searched in
http://jquery.com/
http://mootools.net/
but found nothing that I wanted. There is bounce that stops after sometime.
Can you please help me?
If I have interpret your description correctly, you want a div that moves left and right continuously and changes content on "arrival". I'm still not sure if you want to toggle content or loop through more content.
A little illustration to clarify:
__________ __________
| | >>>>>>>>>>>>>>>>>> | |
| content1 | | content2 |
|__________| <<<<<<<<<<<<<<<<<< |__________|
|---------------------------------|
content change content change
Now, because the VERY specific request, I highly doubt there's such a plugin available. You just have to be creative yourself! Luckily, I'm a nice guy and save you some work.
See the online demo.
My javascript function:
function startBouncing(selector, content, duration, easing) {
// get the required movement (parent width - element width)
var movement = $(selector).parent().width() - $(selector).width();
var contentIndex = 0; // we want to start with content index 0
// define function that makes element go forth
var goForth = function() {
// start animation and change text
$(selector).animate({
'margin-left': movement
}, duration, easing, goBack).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// define function that makes element go back
var goBack = function() {
// start animation and change text
$(selector).animate({
'margin-left': 0
}, duration, easing, goForth).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// start the sequence
goForth();
}
Which I call using:
var content = [
'content #1',
'content #2',
'content #3'
]; // if you wish to toggle, just use 2 elements
// calling the function
startBouncing('#bouncer', content, 2000, 'linear');
And finally, the HTML:
<div style="background-color: gray; height: 50px; width: 500px;">
<div id="bouncer" style="background-color: #ff0000; height: 50px; width: 50px;">
<p>content</p>
</div>
</div>
It might not look good, but it works. I haven't spend a second in optimizing the code.
edit
I've edited the function so you specify a different duration and easing per side.
function startBouncing(selector, content, duration1, duration2, easing1, easing2) {
// get the required movement (parent width - element width)
var movement = $(selector).parent().width() - $(selector).width();
var contentIndex = 0; // we want to start with content index 0
// define function that makes element go forth
var goForth = function() {
// start animation and change text
$(selector).animate({
'margin-left': movement
}, duration1, easing1, goBack).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// define function that makes element go back
var goBack = function() {
// start animation and change text
$(selector).animate({
'margin-left': 0
}, duration2, easing2, goForth).children('p:first').html(content[contentIndex % content.length]);
contentIndex++; // increment index for next time
};
// start the sequence
goForth();
}
For more advanced easing strings, you should use an plugin that adds more easing strings like this one.
use should combine the animate function using an cool bouncing easing effect (requires jquery ui). i order to make it bounce repeatedly, use the setTimeout function..

Categories

Resources