In the below famous angular code, the animation works at the first click, but not at the second click or thereafter.
What code is missing that will make this work at every click?
Thanks!
Js
$scope.animate = function(index) {
$scope.list[index].rotate.set(Math.PI * 4, {curve: Easing.inOutElastic, duration: 3000 })
};
HTML
<fa-grid-layout fa-options="myGridLayoutOptions">
<fa-modifier ng-repeat="item in list"
fa-origin="[0.5, 0.5]"
fa-align="[0.5, 0.5]"
fa-rotate-z="item.rotate.get()">
<fa-surface fa-background-color="item.bgColor" fa-click="animate($index)">
{{item.content}}
</fa-surface>
</fa-modifier>
</fa-grid-layout>
Since you already Transitioned to (Math.PI * 4), that is now the current state of the rotation so there is nowhere to Transition to. Transitionables do not accumulate, in other words. Reset the Transitionable back to the original state, then Transition again.
$scope.animate = function(index) {
$scope.list[index].rotate.set(0);
$scope.list[index].rotate.set(Math.PI * 4, {curve: Easing.inOutElastic, duration: 3000 })
};
Related
I have a GSAP animation like this:
window.addEventListener('keydown', ()=>{
GSAP.to(this.box, {
x: this.box.position.x + 4,
duration: 1,
}
});
I want to increment the box's x position by multiples of 4, and my current code above works, but only if the user waits until 1 second has passed before pressing another key. Then it goes 0 -> 4 -> 8 etc.
If they don't wait a second, what happens is something like 0 -> 3.76 -> 6.45. These numbers change depending on how long a user waits before they press another key. This makes sense because I'm effectively cancelling the animation and using the current position (since 1 second hasn't passed yet, it wouldn't get to 4 yet) and then just rerunning the animation with the current position (that wasn't 4).
Essentially, I want to prevent the 'keydown' event listener from triggered until the 1 second animation has been fully completed.
How would I do that?
This should work:
class Test {
constructor(el) {
this.box = el
window.addEventListener('keydown', () => {
if (!this.tl || !this.tl.isActive()) {
this.tl = gsap.to(this.box, {
x: "+=4",
duration: 1,
})
console.log("Moving to:", Math.floor(this.box.getBoundingClientRect().x))
}
});
}
}
const test = new Test(box)
#box {
width: 25px;
height: 25px;
background: blue;
}
<div id="box"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/CSSRulePlugin.min.js"></script>
Probably not the cleanest solution, gsap is a great tool and surely has some better way to handle this scenario, but this is the first brutal approach that came to my mind.
EDIT:
Using isActive() method is already a tiny bit better.
I am using Bodymovin in combination with ScrollMagic and GSAP to animate through series of images as you scroll back and forth. Everything works great, except, when I reach the end it doesn't stay on the final image, but goes white.
Libraries that I load first:
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
Then my code:
var controller = new ScrollMagic.Controller();
var animation = bodymovin.loadAnimation({
container: document.getElementById('hero-anim'),
animationData: animationframes,
renderer: 'svg',
autoplay: false,
});
var tl = new TimelineMax();
tl.to({frame:0}, 1, {
frame: animation.totalFrames-1,
onUpdate:function(){
animation.goToAndStop((Math.round(this.progress() * 60)), true)
},
ease: Linear.easeNone
})
var lottieScene = new ScrollMagic.Scene({
duration: '100%',
offset: 600
})
.setPin("#hero-anim")
.setTween(tl)
.addTo(controller);
Any ideas what could be causing that? Looking at browser's Inspect Element, it basically adds display:block to the image that is supposed to be currently visible and applies display:none to the previous image, but at the end, they all have display:none, and it doesn't stay visible
RESOLVED
As Zach pointed out, I tried Math.floor and at first it didn't help, but afterwards I tried adjusting animation.goToAndStop((Math.floor(this.progress() * 60)), true) to * 59 (one frame less than the total frame count and now it works, perfectly. Math.round with * total frame count or even * total frames - 1 didn't work, strangely.
I'm setting this loop up with three animations, the first one that runs on initial screen load (firstAnimation). Then the next two animations use callbacks to loop between themselves (slideAnimation -> rotateAnimation -> slideAnimation...)
How can I stop the animation whichever animation it's in using POINTERDOWN and resume it on PONTERUP..?
The way I have done it below works only during the firstAnimation, as soon as its in the loop it obviously no longer targets those two animations. So how would I target all three animations using a single .pause() .resume() command?
var slideAnimation = function(){
scene.beginDirectAnimation(box, [xSlide], 0, 2 * frameRate, false, 2, rotateAnimation);
}
var rotateAnimation = function(){
scene.beginDirectAnimation(box, [yRot], 0, 2 * frameRate, false, 2, slideAnimation);
}
var firsAnimation = scene.beginDirectAnimation(box, [xSlide], 0, 2 * frameRate, false, 2, slideAnimation);
scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERDOWN:
light2.intensity =0.95;
firsAnimation.pause();
box.scaling = new BABYLON.Vector3(4, 1, -1)
break;
case BABYLON.PointerEventTypes.POINTERUP:
light2.intensity =0.15;
firsAnimation.restart();
box.scaling = new BABYLON.Vector3(1, 1, 1)
break;
}
});
Or does this need a different approach all together?
Babylon Group Animations could be useful for you - https://doc.babylonjs.com/how_to/group . It is a way to group animations and animatables, normalize then, and control them together.
You can see an example here - https://www.babylonjs-playground.com/#CBGEQX#5
If you want to see 2 animations running at the same time on one animatable (mesh), change line 79 from
animationGroup.addTargetedAnimation(animation2, box2);
to
animationGroup.addTargetedAnimation(animation2, box1);
And press the run button on the top.
Then pressing the pay button will run both animations on the same mesh.
I have the following
$('.left_arrow').hover(function() {
$('.chart').stop().animate({
left: "+=10"
});
},
function() {
$('.chart').stop();
});
And I want to have it when you mouse over the arrow it smoothly moves the .chart to the left, and the right arrow it moves it to the right. I am doing this by applying a - left (-7500px is the max) to move it to the left and a 0 is the farthest it can go right.
The above moves it over 10, but it doesn't keep on moving it. How can I get it so it keeps on moving it. I was using something like
$('.left_arrow').hover(function() {
$('.chart').stop().animate({
left: "-7500px"
}, 20000);
},
function() {
$('.chart').stop();
});
But the problem is if I am say -6500px over it takes 20 seconds to go the rest of the 1000, vs 20 seconds to go the full distance. So the speed is skewed, I want a standard increment.
Basically what you need is a rate function. I had the same issue when I was creating my carousel.
rate = distance/time
So, your rate is 0.375
Now, all you will need to do is find the distance and you can adjust your timing accordingly.
time = distance/0.375
So it should look something like this:
$('.left_arrow').hover(function() {
var distance = /*Get Distance Remaining*/
var sd = 7500;
var time = 20000;
var rate = sd/time;
var time = distance/rate
$('.chart').stop().animate({
left: "-7500px"
}, time);
},
function() {
$('.chart').stop();
});
Obviously it would need some tweaking to get just right. But the concept is there.
For my situation, because I was using a <ul> since it was a carousel this is the way I got distance:
distance = Math.abs($ul.position().left);
Not fully understanding the question, but you can increment/decrement animations like so:
$('.chart').stop().animate({
"left": "+=100px"
}, 250);
Note the += operator.
EDIT: This answer is only partially correct. Animation behavior on hover is not as desired. Trying to solve.
I have two knobs, I would like to control dynamically the value of the first one when moving the second one.
so far I have:
(First Knob)
$('#inf-knob').knobRot({
classes: ['inf-knob'],
frameWidth: 250,
frameHeight: 250,
frameCount: 100,
detent: false,
discreteSteps: false,
stepCount: 0,
minimumValue: 0,
maximumValue: 2,
dragMultiplier: 0.01,
hideInput: true,
});
$('#inf-knob').on('knobdrag', function(e){
//called until mouse button released
var dial = (500 * $('#inf-knob').val()).toFixed(1);
document.getElementById('freqmain').value = dial + " Hz";
document.getElementById('freqmain2').value = dial;
});
(Second Knob Should Change the first one dynamically when moved)
$('#black-knob').on('knobdrag', function(e){
gainNode.gain.value = this.value;
console.log(this.value * 2);
$('#inf-knob').val(this.value * 2);
});
I have also tried using:
document.getElementById('inf-knob').value = this.value;
with no results.
What am I Missing?
Thanks
As the Document of KnobRot states , you should use :
To return the value of the specified knob:
$('myselector').knobRot('get')
To set the value of the specified knob, or knobs (will work with a collection):
$('myselector').knobRot('set', value)
And then you need to trigger KnobRot refresh. So in your case you can use it as :
$('#black-knob').knobRot({
}).on('knobdrag', function(e){
console.log(this.value * 2);
$('#inf-knob').knobRot('set', this.value);
$('#inf-knob').trigger('knobrefresh');
});
And , here is the Working JsFiddle Demo
P.S
The Fiddle demo doesn't show the graphical icon of Knob as i
couldn't figure out all the inclusion required by KnobRot. But , you
can drag hover below the text-field and drag when a expand cursor
shows up.
I have never used such type of plugin.And the plugin documentation
seems to be poor and not frequently updated. Why not use a different
plugin fore-say jQuery-Knob demo
and code base.