How to make TweenMax(gsap) spin to slowly stop like real-world situation - gsap

I put my code in the link below
https://codepen.io/ntubrian/pen/JjvZZQx
var spin = TweenMax.to('#group',2,{
transformOrigin: 'center center',
rotation: 360,
ease: 'Linear.easeNone',
repeat: -1,
paused: true
});
/* Start */
document.getElementById('start').addEventListener('click',function(){
spin.timeScale(10);
TweenMax.from(spin, 1.5, {
timeScale: 0,
ease: 'Power3.easeIn',
onStart: function(){ spin.restart(); }
},0);
});
document.getElementById('start').click();
/* Finish */
document.getElementById('finish').addEventListener('click',function(){
// var remaining = spin.timeScale() * ( spin.duration() - spin.time() ) * 10;
var remaining = 6
TweenMax.to(spin, remaining, {
timeScale: 0,
ease: 'Linear.easeNone',
onComplete: function(){ spin.pause(); }
},0);
TweenMax.to(spin, remaining, {
progress: 1,
ease: 'Power3.easeOut'
},0);
});
The actual spin behavior is that when I click the "finish" button, its remaining spin depends on its progress or say degree and spins less than 1 round.
My expectation is that it can spin more rounds than 1 round unstill stop
I can't find which params to adjust to fulfill my expectations

Related

Nesting tweens with GSAP don't inherit the defaults of the master timline

I want to nest 2 or more tweens in 1 master timeline instead of just sequencing them. If you sequence them you can add defaults to the timeline, like duration or ease and every tween in the sequence would inherit those defaults. If you nest the same tweens with the same defaults, these defaults seem to be ignored. I was wondering if this is indeed the way it should work. I like the idea of nesting but I also find defaults very useful in this case. In the nesting example I would have to add the duration and ease to each tween.
With sequencing: the defaults are inherited
let tl = gsap.timeline({
defaults: {
duration: 0.75,
ease: 'power2.inOut',
yoyoEase: 'sine.out',
},
repeat: -1,
yoyo: true,
});
tl
.fromTo(
'#Robot',
{
y: 2.5,
},
{
y: -5,
}
)
.to(
'#Shadow',
{
scale: 0.75,
},
'<'
);
With nesting: the defaults are ignored
function robotHover() {
var tl = gsap.timeline();
tl.fromTo(
'#Robot',
{
y: 2.5,
},
{
y: -5,
}
);
return tl;
}
function shadowPulse() {
var tl = gsap.timeline();
tl.to('#Shadow', {
scale: 0.75,
});
return tl;
}
let robotMaster = gsap.timeline({
defaults: {
duration: 0.75,
ease: 'power2.inOut',
yoyoEase: 'sine.out',
},
repeat: -1,
yoyo: true,
});
robotMaster.add(robotHover()).add(shadowPulse(), '<');

Animate rotation to the nearest value in React Native

Given, interpolation, that has to rotate a car marker:
this.rotation.interpolate({
inputRange: [0, 360],
outputRange: ['0deg', '360deg'],
extrapolate: 'clamp'
})
GIF below represents the following bearings:
1. 337.38055647641903
2. 335.202973299875
3. 13.03761189748721
4. 13.042704554861551
5. 358.77805501498045
Green line is how I want it to work (rotate to the nearest value). Red line is how it works currently (on GIF).
Is there a way to animate rotation to the nearest value? (in our case from 335 to 13 clockwise). Maybe there's a better way instead of interpolation?
let heading = this.state.heading;
let oldHeading = this.state.oldHeading;
const duration = 200;
if (oldHeading - heading > 180) {
Animated.timing(
this.state.spinAnim,
{
toValue: 360,
duration: duration,
easing: Easing.linear,
useNativeDriver: true
}).start(() => this.state.spinAnim.setValue(0));
} else if (oldHeading - heading < -180) {
Animated.timing(
this.state.spinAnim,
{
toValue: 0,
duration: duration,
easing: Easing.linear,
useNativeDriver: true
}).start(() => this.state.spinAnim.setValue(360));
} else {
Animated.timing(
this.state.spinAnim,
{
toValue: heading,
duration: duration,
easing: Easing.linear,
useNativeDriver: true
}).start();
}
I had the same problem so my solution was to keep every time the exactly previous heading and if the distance is bigger than 180 i use two separate animations. One that animates from x to 0 and then sets value to 360 (or x to 360 and then sets value to 0) and the one that is the "default" and runs every time.
This is equivalent to avoiding rollbacks in CSS rotate transitions.
A way to do this is to recalculate the next angle based on the amplitude of the previous one. You still have to use interpolation, what changes is that the value fed to the animation will be the result of this function (credits), which adds the difference to the previous value and performs some modulo checks to guess the closest value:
function closestEquivalentAngle(from, to) {
var delta = ((((to - from) % 360) + 540) % 360) - 180;
return from + delta;
}
So for example, next time you move forward from 335 deg to 13 deg, the result will be 373, that is the original value plus a difference of 38 degrees. That works backwards as well. Just remember to store the value of the resulting angle somewhere, so you can use it on the next iteration as prevAngle.
Pulling it all together:
const RotateComp = (props) => {
const rotationValue = React.useRef(new Animated.Value(0)).current;
// Animation interpolation
const rotation = rotationValue.interpolate({
inputRange: [-360, 360],
outputRange: ['-360deg', '360deg'],
});
React.useEffect(() => {
// Obtain prevAngle and nextAngle, then...
Animated.timing(
rotationValue,
{
toValue: closestEquivalentAngle(prevAngle, nextAngle),
duration: 200,
useNativeDriver: true,
},
).start();
}, [rotationValue])
return (
// ...
<Animated.View
style={{
...props.style,
transform: [{rotate: rotation}],
}}
>
{props.children}
</Animated.View>
);
}

ScrollMagic - set a variable scene

I have a page with multiple similar sections with the class ".imgPanel" that I would like to apply the same animation to each section every time the page scrolls into view.
I don't want to create a new scene for each animation as they are all the same. I know there is a way to create a variable scene (i hope that is the correct terminology), and I am hoping someone can help.
Does anyone know how I would adjust the code below to make that happen. I am sure if I get one example of it with my code below I will be able to understand and use this technique again.
Thanks in advance. Adam.
function scrollMagic() {
if (!Modernizr.touch) {
var controller = new ScrollMagic.Controller({
duration: "200%",
});
var panelAnim = new TimelineMax();
panelAnim
.from($('.imgPanel'), 1.4, {
y: '-50px',
autoAlpha: 0,
ease: Power1.easeOut
}, '-=0.2')
var introScene = new ScrollMagic.Scene({
duration: "100%",
})
.setTween(panelAnim)
.addTo(controller);
}
}
I figured it out after watching Ihatetomatoes video
function scrollMagic() {
if (!Modernizr.touch) {
var controller = new ScrollMagic.Controller({
duration: "100%",
});
$('.imgPanel').each(function(){
var tween = TweenMax.from($(this), 1.4, { y: '-50px', autoAlpha: 0,
ease: Power1.easeOut }, '-=0.2');
var scene = new ScrollMagic.Scene ({
duration: "100%",
offset: -300, // offset trigger position by 100px
triggerElement: this
})
.setTween(tween)
.addTo(controller);
});
}
}

GSAP pause animation in a function

I have a set of buttons that when clicked show a sort of pop up and some simple animations. Each pop up contain the same animations for most of the content. Each pop up also has its own sets of animations so I have done the following to get this to work correctly.
$gridTitles.click(function() {
const tl = new TimelineMax();
const $pop = $(this).next('.grid__pop');
const $chars = $pop.find(".grid__pop-title span");
const $items = $pop.find(".grid__pop-list li");
const func = $(this).data("graphic-function");
tl.set($pop, {
autoAlpha: 0,
display: 'block',
scale: .5
})
.to($pop, 1, {
autoAlpha: 1,
scale: 1,
ease: Power2.easeInOut
})
.staggerFrom($chars, 0.01, {
autoAlpha: 0,
ease: Power2.easeIn
}, 0.1)
.add(graphicAnimation[func])
.staggerFrom($items, 0.8, {
autoAlpha: 0,
rotationX: 90,
ease: Power2.easeOut
}, .8);
return tl;
});
This runs my pop up code and also using the .add function I call another function that runs the specific animation for the pop up based on a data attribute matching the name of a function in an object.
const graphicAnimation = {
graphicServer: function() {
const tl = new TimelineMax();
const $server = $(".graphic-server__server");
const $one = $(".graphic-server__one");
const $two = $(".graphic-server__two");
const $three = $(".graphic-server__three");
return tl.to($server, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($one, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($two, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($three, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
},
// more functions
}
This works great depending on which button is clicked the correct function is called in the object and my animations run. The problem now is that some of these animations are looping and when I close the popup I can't pause them.
Using something like the following I tried to accomplish this
$gridCloses.click(function() {
const tl = new TimelineMax();
const $pop = $(this).parents(".grid__pop");
const func = $(this).parents('.grid__pop').siblings('.grid__title').data("graphic-function");
graphicAnimation[func].pause();
return tl.to($pop, 1, {
autoAlpha: 0,
scale: .5,
display: 'none'
});
});
But calling graphicAnimation[func].pause(); isn't going to work as pause() is a function on the returned timeline from that function. How can I access the current running function and pause / kill it.
So I thought this out and just need to store the timelines in their own object as well
const graphicTimeLines = {
graphicServer : new TimelineMax(),
graphicType: new TimelineMax()
}
so with that in my closing action I can do something like the following to completely reset my loops and have the timeline be available to be called again.
graphicTimeLines[func].pause().progress(0).kill();
graphicTimeLines[func] = new TimelineMax();

VelocityJS: use stagger with custom sequence

I'm trying to apply a stagger option to a custom velocity sequence.
In my example, I'm using the sequence from the documentation to simplify.
The sequence
$.Velocity.Sequences.mySequence = function (element, options) {
var duration = options.duration || 750;
$.Velocity.animate(element,
{
translateY: "-=10px",
}, {
delay: duration * 0.033,
duration: duration,
loop: 3,
easing: "easeInOutSine"
});
};
The call (failing)
$("div").velocity('mySequence', { stagger: 200 });
The call (succeeding)
Stagger is occurring when I use any predefined sequence from the UI Pack.
$("div").velocity('callout.bounce', { stagger: 200 });
Live example: http://codepen.io/griable/pen/ncsmC
Thanks in advance for helping