Loop animation with svg.js - javascript

I have a very simple animation with svg.js that I would like to run on a loop as long as the page is open. I haven't been able to find any thing while looking through either the github, documentation, or stack overflow pages. A working version of the animation without looping can be found here. The important js is:
//create the svg element and the array of circles
var draw = SVG('canvas').size(300, 50);
var circ = [];
for (var i = 0; i < 5; i++) {
//draw the circles
circ[i] = draw.circle(12.5).attr({
fill: '#fff'
}).cx(i * 37.5 + 12.5).cy(20);
//first fade the circles out, then fade them back in with a callback
circ[i].animate(1000, '<>', 1000 + 100 * i).attr({
opacity: 0
}).after(function () {
this.animate(1000, '<>', 250).attr({
opacity: 1
});
});
}
I know this would be pretty easy to do without a js library, but I'm looking at this as just a first step into using svg.js. Later I plan on using it for much more robust animations. Thanks for any advice or pointers.

From version 0.38 of svg.js on the loop() method is built in:
https://github.com/wout/svg.js#loop
I'm also planning on creating a reverse() method in one of the upcoming releases. Right now the loop() method restarts the animation from the beginning.

I'm not sure if its possible just with svg.js attributes, as its not clear from svg.js if its creating typical svg animation elements or not. Whatever, it could be done with a loop though. So...
function anim( obj,i ) {
obj.animate(1000, '<>', 1000 + 100 * i).attr({
opacity: 0
}).after(function () {
obj.animate(1000, '<>', 250).attr({
opacity: 1
});
});
};
function startAnims() {
for( var i = 0; i< 5; i++ ) {
anim( circ[i],i );
}
setTimeout( startAnims, 5000 ); // Or possibly setInterval may be better
};
jsfiddle here http://jsfiddle.net/8bMBZ/7/ As its not clear if its adding elements each time behind the scenes (you may want to store the animation and just start that if so). There are other libs that tie in differently to SVG if you need like Raphael, snap, d3, Pablo.js that you could try as alternatives if you need to look at animation from a slightly different way.

I used after to call a function that start the animation recursively. This way I was able to achieve both infinite looping and reversing. Of course you can count to avoid infinite looping but the general idea is as follows:
//custom animation function whose context is the element animated
function myCustomAnimation(pos, morph, from, to) {
var currentVal = morph(from, to); //do morphing and your custom math
this.attr({ 'some prop': currentVal });
}
var animationStart = 0; //just extra values for my custom animation function
var animationEnd = 1; //animation values start at 0 and ends at 1
line.attr({ 'stroke-width': 2, stroke: 'red' });
animateMeRepeatedly.apply(line);
function animateMeRepeatedly()
{
this.animate(1500)
.during(function (pos, morph) {
myCustomAnimation.apply(this, [pos, morph, animationStart, animationEnd]);
})
.after(function () {
this.animate(1500).during(function (pos, morph) {
myCustomAnimation.apply(this, [pos, morph, animationEnd, animationStart]);
}).after(animateMeRepeatedly);
});
}

Related

Adding delay to function during drawing objects

i placed setInterval into the code but this obviously delaying all the lasers by 1 second.
I want it to be working in the following sequence:
- at start laser1 and laser2 are fired.
- 1 second break and fire another set of lasers etc.
Also if someone could teach me how to move all block of code by four spaces on forum, that would be amazing, as none of the ways i found in google solve this ridiculous problem.
Code shortcut:
let laser1;
let lasers1 = [];
let laser2;
let lasers2 = [];
function createLaser() {
laser1 = new Laser(bossOne.x, bossOne.y + bossOne.sizeY, 10, 50, 5);
lasers1.push(laser1);
laser2 = new Laser(bossOne.x + bossOne.sizeX - 10, bossOne.y +
bossOne.sizeY, 10, 50, 5);
lasers2.push(laser2);
}
function draw() {
requestAnimationFrame(draw);
setInterval(createLaser, 1000);
for (i = 0; i < lasers1.length; i++) {
lasers1[i].show();
lasers1[i].move();
}
for (i = 0; i < lasers2.length; i++) {
lasers2[i].show();
lasers2[i].move();
}
}
requestAnimationFrame(draw);
Remote host for full code if needed:
https://stacho163.000webhostapp.com/
lasers are red
I think i should work with booleans, but can't handle to set it there.
Got a similar topic with key activation, but i lost contact with the one who proposed a solution on the basis of booleans (as it didn't work well) so i took the easiest part first without involving the keys.
Any tips are appreciated :)
Try changing setInterval to setTimeout, as it's already looping recursively - just add a delay to it. Also move the requestAnimationFrame call to the bottom of draw, not the top:
function draw()
setTimeout(createLasers, 1000);
//Loops
requestAnimationFrame(draw);
}

Javascript using a For Loop with setTimeout (or another delay)

I apparently don't fully understand how the setTimeout function works in Javascript:
function move() {
var a;
for(a = 0; a < 101; a++){
setTimeout(function(){
block.style.marginTop = (750 - a) + 'px');
}, 1000);
}
for(a = 0; a < 101; a++){
setTimeout(function(){
block.style.marginTop = (650 + a) + 'px');
}, 1000);
}
}
I have tried writing this out in many different ways, but the For Loops always execute instantly every single time. How can I make a For Loop wait for 1 second between each value of 'a'? So, when a = 0, the code executes and then waits for 1 second before running when a = 1, etc. until the first For Loop is finished, then the second For Loop executes in the same way.
Also, is there a more efficient way of doing this than using setTimeout? Like a way of just writing
sleep(1000);
or something like that. This whole setTimeout feature seems very overly complicated if it is the only way of producing delays in javascript. I tried this once but it didn't work at all in any way
await sleep(1000);
Any help with Timeouts and delays in Javascript, especially within a loop, would be greatly appreciated!
You may find value in the answer I posted here. That will explain setTimeout in loops a little more.
Separately, you may want to explain what you are trying to accomplish. It looks like you are either
trying to move an element one pixel per second
trying to move an element ~100 pixels after one second
For the first option I would use CSS Transitions instead. You'll have a lot more flexibility over how the element moves and you only need to dictate the direction and distance.
For the second option, you could toss the loop and keep the stuff inside, setting the new marginTop to the full value after some timeout.
setTimeout(function(){
block.style.marginTop = (750 - a) + 'px');
}, 1000);
this part of your code use 'a' variable after 1000 miliseconds. in this time 'a' is 100, because your loop not stoped for run setTimeout function, and it happened because javascript is asyncronous.
one solution for solving this problem in js is using recursive functions. if is not necessary to use for loop, you can use this code:
var a = 0;
function my_loop(a) {
if (a < 101) {
setTimeout(function() {
block.style.marginTop = (750 - a) + 'px');
my_loop(a);
}, 100);
a++;
}
}
my_loop(a);
but if you want do your question's job, i seriously recommened you to use CSS.
As #squint mentioned, you can use setInterval for your task.
Here's an example:
// create an element
const width = 10;
const el = document.createElement('div');
el.setAttribute('id', 'main');
document.body.appendChild(el);
el.style.width = width + 'px';
// question-relevant code starts here
const a = [...Array(101).keys()]; // fancy way to create [0, 1, 2, 3, ...]
const it = a[Symbol.iterator](); // for convenience
const int = setInterval(() => {
const { value, done } = it.next(); // next iteration
if (done) { clearInterval(int); return }; // finished?
el.style.width = width + value + 'px'; // adjust width
}, 10);
#main {
height: 100px;
width: 10px;
background: green;
}

KineticJS: Play tween after finish other tween

I just need animate something, i need create few tween for feew difrent object, example bubbles. So i just want animate bubble per bubble, i mean start animate bubble number two when animation bubble numer one was end. I write somethink like that:
var tweens = [];
for(var i =0; i < bubbleTab.length; i++)
{
var tween = new Kinetic.Tween({
node: bubbleTab[i],
x: invisibleBubbles[i].getX(),
y: invisibleBubbles[i].getY(),
easing: Kinetic.Easings.BounceEaseOut,
onFinish: function(){
tweens[i+1].play();
},
duration: 2
});
tweens.push(tween);
}
or i just replace onFinish: function() to playNextTween(i) and write method
function playNextTween(i)
{
tweens[i].play();
}
But still isn't working. I don't know what i can do. I try animate tween in loop but in loop all tween execute in the same time.
Any ideas? I know about GSAP and his TweenTimeline but with KineticJS no one ease from GSAP working
Instead of using
tweens[i+1].play();
Use a variable counter to iterate through your tween array. For example:
var rects = layer.get('Rect');
var rectCount = rects.length;
var tweens = [];
var tweenCounter = 1;
for (var i = 0; i < rectCount; i++) {
var tween = new Kinetic.Tween({
node: rects[i],
duration: 1,
y: 150,
onFinish: function() {
if (tweenCounter !== rectCount) { //Prevent an undefined tween from being played at the end
tweens[tweenCounter].play();
tweenCounter++;
}
}
});
tweens.push(tween);
}
JSFIDDLE
Ok thanks to #projeqht i know how do this with KineticJS but i working on it, and i have better solution with GASP. Cause with GASP i can overlap animation and much more ;) so here is the code:
//Dodawanie animacje kolejnego bombla do timeline
function addAnim(i){
tl.to(bubbleTab[i], 0.5, {kinetic:{scaleX:1, scaleY:1}}, "-=0.45");//-0.45 mean overlap
}
//Dodawanie animacji do timeline
for(var i = 0; i < bubbleTab.length; i++)
{
layer.add(bubbleTab[i])
addAnim(i);
}
tl.play()
It's simple doesn't it? But you must remeber to add kineticJS plugin :D i fo

Set background color in a smooth way

I've got the following JS code:
// utility function to convert r,g,b to html color
function RGB2HTML(red, green, blue) {
var decColor =0x1000000+ blue + 0x100 * green + 0x10000 *red ;
return '#'+decColor.toString(16).substr(1);
}
// recursive utility function to animate color
// elNames an array of Ids (these are mainly TDs)
// curCnt is current animation step
// totSteps is total steps
function pulseBGMany(elNames, curCnt, totSteps) {
for(var i=0; i < elNames.length; ++i) {
var curEl = document.getElementById(elNames[i]);
var curColor = RGB2HTML(255, 255*(curCnt/totSteps), 255*(curCnt/totSteps));
curEl.style.backgroundColor = curColor;
}
if(curCnt < totSteps) {
setTimeout( function(){ pulseBGMany(elNames, curCnt+1, totSteps); }, 40);
}
}
// eventually in another piece of code, it all gets triggered
...
// schedule ui update here!
// use a closure
(function(names){ setTimeout( function(){ pulseBGMany(names, 0, 25); }, 40)})(diffRes);
...
The code above works, but unfortunately the animation is very chopped and I'm not able to see a smooth gradient from red to white; it seems like all major browsers are losing frames (tested on Firefox and Chromium on Ubuntu).
The array of TDs varies from 1 to even 80 elements, but the effect is always the same.
What am I doing wrong?
As requested, JSFiddle link: http://jsfiddle.net/PsvCP/2/ (You have to set No wrap in body)
Thanks,
Ema
Try doubling the amount of steps, 25fps is somewhat choppy.
Doubling the steps should put you at 50fps which should be fine.
Also make the elmntlist a array of dom elements and not a array of element id's,
Dom lookups are really slow and probaply causing most of your problems.
Think I've gotten around it. Apparently the setTimeout API is particularly slow in both Chrom(ium) and Firefox. Scheduling all the gradient function calls in advance is much more efficient for current browsers and does the trick:
// non-recursive utility function to animate color
function pulseBGMany(elNames, curCnt, totSteps) {
var curColor = RGB2HTML(255, 255*(curCnt/totSteps), 255*(curCnt/totSteps));
for(var i=0; i < elNames.length; ++i) {
elNames[i].style.backgroundColor = curColor;
}
}
...
// schedule ui update here!
// use a closure
var numFade = 15;
for(var i=0; i < numFade; ++i) {
(function(names, iter, tot){ setTimeout( function(){ pulseBGMany(names, iter+1, numFade); }, 50*iter)})(diffRes, i, numFade);
}
...
and as expected this works a lot faster.

How to optimize this js (now CPU is over 40% when the page is opened)

I have this piece of JavaScript on my page and it loads the CPU considerably. Is there any way to optimize the code? ( I'm using jQuery, so jQuery solutions will be fine )
function Particle() {
this.particleContainerWidth = $('#particle-container').width() - 100;
this.particleContainerHeight = $('#particle-container').height() - 100;
this.path = 'img/';
this.images = ['particle1.png', 'particle2.png', 'particle3.png', 'particle4.png'];
// Randomly Pick a Particle Model
this.image = this.images[randomInt(this.images.length)];
this.file = this.path + this.image;
// Create a Particle DOM
this.element = document.createElement('img');
this.speed().newPoint().display().newPoint().fly();
};
// Generate Random Speed
Particle.prototype.speed = function() {
this.duration = (randomInt(10) + 5) * 1100;
return this;
};
// Generate a Random Position
Particle.prototype.newPoint = function() {
this.pointX = randomInt(this.particleContainerWidth);
this.pointY = randomInt(this.particleContainerHeight);
return this;
};
// Display the Particle
Particle.prototype.display = function() {
$(this.element)
.attr('src', this.file)
.css('position', 'absolute')
.css('top', this.pointY)
.css('left', this.pointX);
$('#particle-container').append(this.element);
return this;
};
// Animate Particle Movements
Particle.prototype.fly = function() {
var self = this;
$(this.element).animate({
"top": this.pointY,
"left": this.pointX
}, this.duration, 'linear', function(){
self.speed().newPoint().fly();
});
};
function randomInt(max) {
// Generate a random integer (0 <= randomInt < max)
return Math.floor(Math.random() * max);
}
$(function(){
$('body').append('<div id="particle-container"></div>');
var total = 8;
var particles = [];
for (i = 0; i < total; i++){
particles[i] = new Particle();
}
});
You cannot make JavaScript consume less of your CPU. That is governed by the priority of the executing application in the OS kernel. The best you can hope for is to reduce execution time.
To improve your execution efficiency limit your usage of prototype and stop assigning values to properties. This method of coding has become popular because it is extremely clean and easy to read, but it horribly backwards to execute.
If you are capable of coding using only variables for assignment, if statements for decisions, and for loops for looping your code execution will be far faster. That will require you to write more code, however, and it will not be so pretty.
To improve output performance write all output segments each into an index of an array and use only a single join method when all output is created and a single innerHTML method to output this text to the page. This will reduce output execution by up to 4 times.
Have you thought about implementing this with a <canvas> version? It won't work in IE directly, of course, and off-hand I'm not sure whether it'd be faster or slower. You could also try it with Processing.
This would need a lot of changing and rewriting, but You can create a new easing function for jquery and post it to animate. Then every particle You have would just be once issued with animate() with Your easing function and the function has to be based on those:
random
current time (new Date()) modulo some number
a singleton holding individual directions
ideas:
Assuming You don't want to change Your code You can try setting the particle to fly with some random timeout when first running fly(). It could change the way it's all executed. No idea if it helps or makes it slower though ;)
Second thing is quality. jquery animate does it too smoothly. You can move Your particles instead of animating and just chande the distance to lower and increase speed and use setTimeout to make it move the same pace as now.

Categories