Mocking CSS key frame animation in K-Frame (VR) - animation

I want to create an animation on a single property (eg: scale) of a single entity (eg: a-circle) which would run one after another, using K-Frame. Is this possible?
For example, this would be a simple case:
<a-scene>
<a-circle
position="0 1.25 -5"
color="#F55"
radius="1"
scale=".1 .1 1"
animation__scale="property: scale; dir: normal; dur: 500; easing: easeInSine; loop: true; to: 1 1 1"
>
</a-circle>
</a-scene>
I want to, say, scale from .1 .1 1 to .5 .5 1 for the first 500ms, then stay at .5 .5 1 for another 500ms, and go back to .1 .1 1 in the next 200ms, and then loop through this entire process over and over infinitely.
Is this even possible?

You can daisy-chain the animations within a component checking which animation ended and starting the next one. In Your case that would be
animation1 ended -> wait 500ms and start animation2 -> animation2 ended -> wait 200 ms and start animation1.
You can achieve this, by simply listening for the animation__[ID]-complete event, and emit another one.
If You just chained them, You could just map multiple listeners on the start event list.
However You want to give them some custom wait periods, which can be included within the mapped functions, or done manually:
el.addEventListener('animation__scale1-complete', function() {
setTimeout(function(){
el.emit('secondAnimation');
},500);
})
el.addEventListener('animation__scale2-complete', function() {
setTimeout(function(){
el.emit('firstAnimation');
},200);
})
working fiddle here: https://jsfiddle.net/gftruj/2qoz8b75/2/ Please note, that i trigger the first animation also on the loaded event in case it fires before its loaded.

Related

How to fake a computation elapsed time and message within transition?

I have a D3 JS code that does a transition between two states of a barchart. I now need some extra elapsed time in the middle of the transition i.e. after the old state is removed and before the new state appears that adds an extra delay and shows something like "Computing something, please wait ...".
This example could be used as reference. What I need is an extra pause in between the transition that shows a message to the user.
For a fixed delay, you can use use delay. See https://github.com/d3/d3-transition#transition_delay
For a dynamic delay, you can use the 'end' event, coupled with another transition (from https://stackoverflow.com/a/10692220/6184972), like:
d3.select('#myid').transition().style('opacity', '0').on('end', function () {
console.log('computing...');
setTimeout(function () {
console.log('Done!');
d3.select('#myid').transition().style('opacity', 1);
}, Math.random() * 2000 + 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="myid">Hi World</div>

Multiple overlapping animations on the same element in svg.js

I want to start a animation on an element while a previous animation is still active. However, calling animate() on the element queues the new animation at the end of the current animation.
For example, consider an animation where an element is being moved to a new position. Now, I also want to make it fade out when it reaches a certain position. The following queues the “once” animation at the end of the move, rather than at 80%.
rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
rect.animate(200).opacity(0);
});
How do I make the element start fading out when it reaches 80% of the move? The API seems to be designed for chaining animations rather simultaneous overlapping animations.
What you are trying to do is a bit more complicated. Unforrtunately its not possible to "hack" into the current animation and add a new animation on the fly.
However what you can do is adding a new property which should be animated:
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.opacity(0);
});
As you will notice that has its own problems because the oopacity immediately jumps to 80%. So this is not an approach which works for you.
Next try: Use the during method:
var morh = SVG.morph(
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.during(function(pos, morphFn, easedPos) {
pos = (pos - 0.8) / 0.2
this.target().opacity(pos)
}
});
We just calculate the opacity ourselves

SVG performance drop when animating 2 or more elements, RaphaelJS

I am testing SVG performance using RaphaelJS library.
My code works, can be found here: JSFiddle
When you type in textbox "1" and press "add", a rectangle will be generated on screen and 4 animations will loop on it- moving right, down, left, up (also rotating, scaling and changing colour).
Performance seems to be ok. But add another element on stage and the performance gets knocked down to minimum in 3-4 seconds. Checked in Chrome timeline, the thing that is getting stacked up is "Animation Frame Fired - > Install Timer".
Perhaps I am doing loop incorrectly? Altough next animation starts when the previous ends, through callback function. Or is it Raphael itself? Should I try doing this with SVG and SMIL? (Raphael uses VML)
Any ideas?
------------------------------------UPDATE--------------------------------------
With RaphaelJS I did bad animation loop hooks, see answer below.
But another problem that does occur - add 1 element 10 times and you can see how animations get distorted, not finishing their full cycle, or add 10 elements 1 time and after few seconds you can see delayed animations on some of the elements.
I made SMIL version JSFiddle (no Raphael here), animations do not lag, delay, but they get syncronized. Can anyone explain why? And how to make those animations NOT sync, but unique?
I think the problem is you are recursively calling animations on a set.
So at the end of each animation, each element in the set calls an animation for the set again, so it spirals and grinds to a halt. You can get around this, by using 'this' instead of the set 'rectangles'.
//define 4 animations
var move_up = Raphael.animation({fill: "green", transform: "t0,0r360s1,1"}, 400, function(){ this.attr({"transform" : "t0,0"}); this.animate(move_right); });
var move_left = Raphael.animation({fill: "yellow", transform: "t0,100r270s0.5,0.5"}, 400, function(){ this.animate(move_up); });
var move_down = Raphael.animation({fill: "red", transform: "t100,100r180s1,1"}, 400, function(){ this.animate(move_left); });
var move_right = Raphael.animation({fill: "blue", transform: "t100,0r90s1.5,1.5"}, 400, function(){this.animate(move_down); });
jsfiddle

D3 transition end events not firing correctly?

This should be straightforward: I just want to start a D3 transition running when the user clicks on a button.
However, the D3 end event does not seem to be firing correctly on my transitions: I've asked for a transition to rotate(120), but the end event seems to be firing when the rotate attribute is only 45.
This is my code:
var angle = 0;
// handle click event
d3.selectAll('.mycolour').on('click', function() {
d3.select("#loadingicon").remove();
// redraw the icon (excluded here for brevity)...
// setting the transform attribute to 0 explicitly..
group2.attr("transform", "rotate(0,0,0)");
// and start it turning:
angle = 0;
function rotateLoadingIcon() {
angle = angle%360;
angle += 120;
console.log('rotateLoadingIcon', angle, group2.attr("transform"));
d3.select(".icon").transition().ease("linear")
.duration(4000)
.attr("transform", "rotate(" + angle + ",0,0)")
.each("end", function() {
angle += 120;
console.log('innerRotateLoadingIcon', angle, group2.attr("transform"));
group2.transition().ease("linear")
.duration(4000)
.attr("transform", "rotate(" + angle + ",0,0)")
.each("end", function() {
rotateLoadingIcon();
});
});
}
console.log('about to start icon loading');
rotateLoadingIcon();
});
It works the first time the icon is drawn:
rotateLoadingIcon 120 rotate(0,0,0)
innerRotateLoadingIcon 240 rotate(119.99999999999999)
But after second or subsequent clicks, I've seen it produce console output like this:
about to start icon loading
rotateLoadingIcon 120 rotate(0,0,0)
innerRotateLoadingIcon 240 rotate(45.14999999999999)
In practice what this means is that the direction of rotation changes :(
Why has innerRotateLoadingIcon started, when the rotate attribute is only set to 45.1...? Surely given the code, it shouldn't start until rotate reaches 120 - as in the first time the code is run.
I'm wondering if the way I have set up the JavaScript means that two different versions of rotateLoadingIcon could be running at the same time. Is this possible, and can I fix it if so?
Update: Here's a JSFiddle that demonstrates the problem, though I'm now trying setInterval rather than end - click twice and you'll see the rotation change direction, I can't figure out why: http://jsfiddle.net/QtBvJ/
In the version in your jsfiddle, you only need to clear the timeout properly to make it work. The function setTimeout returns a reference that you need to pass to window.clearTimeout, i.e.
var t = null;
...
window.clearTimeout(t);
...
t = setTimeout(...);
Working fiddle here.

jQuery Mousemove: trigger on change of 5px

For a number of technical reasons, I'm implementing my own 'draggable' feature on jQuery, rather than using jQuery UI, and I'm using mousedown and mousemove events to listen for the user trying to drag an element.
It works good so far, I would just like to fire up the mousemove event every 5 px of movement, rather than pixel by pixel. I've tried coding a simple:
$('#element').bind('mousemove', function(e) {
if(e.pageX % 5 == 0) {
// Do something
}
});
However, the movement is not stable every 5 pixels, and sometimes if you move the mouse too fast, it will skip several steps. I think this is because when moving the mouse very fast, jQuery will not trigger the event every pixel.
Do you guys know how to trigger the event every 5 pixels?
Thanks a lot,
Antonio
Your code does not take into account where your drag started. e.pageX will just give you page coordinates, not differences. You need to check for the change in distance moved.
This post is pretty relevant.
Here is the basic code:
$(document).mousemove(function(event) {
var startingTop = 10,
startingLeft = 22,
math = Math.round(Math.sqrt(Math.pow(startingTop - event.clientY, 2) +
Math.pow(startingLeft - event.clientX, 2))) + 'px';
$('span').text('From your starting point(22x10) you moved: ' + math);
});
EDIT: Now I think I understand what the OP is talking about. I used the above code to come up with this fiddle. It tracks your current position in relation to the Top Left of the screen and it checks to see if your difference is > 5 pixles.
New script:
var oldMath = 0;
$(document).mousemove(function(event) {
var startingTop = 10,
startingLeft = 22,
math = Math.round(Math.sqrt(Math.pow(startingTop - event.clientY, 2) +Math.pow(startingLeft - event.clientX, 2))) + 'px';
$('#currentPos').text('you are at :' + math);
if(Math.abs(parseInt(math) - oldMath) > 5){
//you have moved 5 pixles, put your stuff in here
$('#logPos').append('5');
//keep track of your position to compare against next time
oldMath = parseInt(math);
}
});​

Resources