$(document).ready(function(){
$('#space').css({
'-webkit-transform': 'scale(2,3)',
});
$('#space').css({
'-webkit-transform': 'skew(30deg,20deg)',
});
});
CSS
#space{transition:duration:20s;}
Using the above Jquery, I want the scale property to run for the first 20 seconds and then the skew property for the next 20 seconds but here it only does skew.I was thinking to provide a delay of 20 seconds for the next statement but is there any other simple way to do it? Thanks
You cannot use .delay() for CSS properties. Instead, you can try using the setInterval() function to progressively add transform to your element based on a predefined set of transform you want. I have made a fiddle here - http://jsfiddle.net/teddyrised/5AqCm/
This answer is made under the assumption that you want to, eventually, both scale and skew the element in it's final state.
Let me explain my code a little:
$(document).ready(function () {
var $spce = $("#space"),
trsfm = [], // Declare empty array for transforms
delay = 1000, // Set delay in ms
count = 0; // Set iteration count
// Declare a stepwise array where you want the transform to occur
trsfm = ['scale(2,3)', 'skew(30deg,20deg)'];
var timer = window.setInterval(function () {
if(count < trsfm.length) {
// Increase count by 1
count += 1;
// Stepwise addition of transforms
var trsfmStep = trsfm.slice(0, count).join(' ');
$spce.css({
'-moz-transform': trsfmStep,
'-o-transform': trsfmStep,
'-webkit-transform': trsfmStep,
'transform': trsfmStep
});
// Log in the console, just for fun
console.log(trsfmStep);
} else {
// If you have iterated through all the transforms, clear interval
window.clearInterval(timer);
console.log('Timer cleared.');
}
}, delay);
});
I have defined the delay, 1000ms (of course you can change that), and also used an array to store all the transforms you want to apply. The transforms are applied in a step-wise fashion from left to right, starting from scale and then to skew.
A timer is set, and the count is started. Every time an interval is reached, the script checks if you have iterated through the transform array. If not, it will apply stepwise addition of the transform, by joining the items in the array from the start, but stopping at whatever step you are at (using the .slice()) method :)
Related
My program: https://jsfiddle.net/60ucsfrb/
I am trying to copy the format of my change size function which manages to loop itself and continuously change the size of div with the help of intervals from an animate function. However, when I try to do the same format for my change opacity for the div, it does not work. Instead it only works once, making the opacity 0.9 from 1 and being done. I'm going to try a for loop instead, but I don't think I'm supposed to since the animate function is supposed to already loop it for me, but I'm not sure.
This is the change size function that WORKS and changes the div from 150 to 50.
function shrinkSize(elem){
var currentWidth = parseInt(getTheStyle(elem.id,'width'))-2;
elem.style.width=currentWidth+"px";
if(parseInt(elem.style.width)==50){
clearInterval(sizeTimer);
sizeTimer = setInterval(function(){growSize(elem);},10);
}
}
function growSize(elem){
var currentWidth = parseInt(getTheStyle(elem.id,'width'))+2;
elem.style.width=currentWidth+"px";
if(parseInt(elem.style.width)==150){
clearInterval(sizeTimer);
sizeTimer = setInterval(function(){shrinkSize(elem);},10);
}
}
This is the animate function that creates intervals for loops.
var moveTimer;
var sizeTimer;
var opacityTimer;
var mover = document.getElementById("movingElem");
function Animate(elem){
//only clears most recent interval, cant clear anything before that
clearInterval(moveTimer);
clearInterval(sizeTimer);
clearInterval(opacityTimer);
moveTimer = setInterval(function(){MoveRight(elem,'wrapper');}, 10);//a function that'll call another function after certain amount of time, speed of animation, once every ten milisecond
sizeTimer = setInterval(function(){shrinkSize(elem);}, 10);
opacityTimer = setInterval(function(){decreaseOpacity(elem);}, 10);
}
And this is my NOT WORKING change opacity function that I'm trying to follow the same format as change size function.
function increaseOpacity(elem){
var currentOpacity = parseInt(getTheStyle(elem.id,'opacity'))+0.1;
elem.style.opacity= parseInt(currentOpacity);
console.log(currentOpacity);
if(parseInt(elem.style.opacity)==1){
clearInterval(opacityTimer);
opacityTimer = setInterval(function(){decreaseOpacity(elem);},10);
}
}
function decreaseOpacity(elem){
var currentOpacity = parseInt(getTheStyle(elem.id,'opacity'))-0.1;
elem.style.opacity=currentOpacity;
if(parseInt(elem.style.opacity)==0){
clearInterval(opacityTimer);
opacityTimer = setInterval(function(){increaseOpacity(elem);},10);
}
}
I know it has to do with this opacity line not matching up with this pixel in change size, but I don't know how to get it working.
elem.style.opacity= parseInt(currentOpacity);
elem.style.width=currentWidth+"px";
I think the problem is that you use parseInt which parses the number to a rounded integer (in your case after the first shrink, it parses the opacity from '0.9' to 0). In the opacity case you are dealing with decimals, so you have to use parseFloat instead.
function increaseOpacity(elem){
var currentOpacity = parseFloat(getTheStyle(elem.id,'opacity'))+0.1;
elem.style.opacity= parseFloat(currentOpacity);
console.log(currentOpacity);
if(parseFloat(elem.style.opacity)==1){
clearInterval(opacityTimer);
opacityTimer = setInterval(function(){decreaseOpacity(elem);},10);
}
}
function decreaseOpacity(elem){
var currentOpacity = parseFloat(getTheStyle(elem.id,'opacity'))-0.1;
elem.style.opacity=currentOpacity;
I have a class test whose background-color I want to flip between lime and green faster and faster.
For that, I'm using a for loop variable and passing it to a function containing a setTimeout(), but it's not working.
(This is not a duplicate question. The said "original" is about a simple setTimeout() whereas this question is about a setTimeout() within a for loop. I understand that the answers on that question might indirectly answer mine, but the questions themselves aren't the same)
$(document).ready(function() {
for (var i = 0; i < 20; i++) {
delay(i);
$(".test").css('background-color', 'lime');
}
});
function delay(i) {
setTimeout(function() {
$(".test").css('background-color', 'green');
}, 1000 - 50 * i);
}
.test {
width: 300px;
height: 300px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test"></div>
Try this way:
for(var i=0;i<20;i++)
{
delay(i);
}
function delay(i) {
setTimeout(function() {
if (i%2 == 0) {
$(".test").css('background-color', 'green');
} else {
$(".test").css('background-color', 'lime');
}
}, 1000 - 50 * i);
}
The problem is the loop executes faster than the timeout. setTimeout function basically says execute the given function after a certain time. The for loop you created there will continue without waiting for the code inside the setTimeout function to be executed, In other words your code producing 20 functions that will be executed in the future.
There are many way to produce the functionality you need.
To keep it simple and solve it you should create two functions instead:
$(document).ready(function() {
for (var i = 0; i < 20; i++) {
delay_lime(i);
delay_green(i+1);
}
});
function delay_green(i) {
setTimeout(function() {
$(".test").css('background-color', 'green');
}, 1000 - 50 * i);
}
function delay_lime(i) {
setTimeout(function() {
$(".test").css('background-color', 'lime');
}, 1000 - 50 * i);
}
try this: here is a example example
$(document).ready(function() {
delay();
var start = 0;
delay(start);
function delay(start) {
setTimeout(function() {
if(start == 0 ){
$(".test").css('background-color', 'green');
start = 1;
}else{
$(".test").css('background-color', 'red');
start = 0;
}
delay(start);
}, 100);
}
});
If you want to use a for loop, you should turn its containing function into an async function and await promises that resolve at the desired time:
const delay = (i) => new Promise(resolve => {
setTimeout(resolve, 1000 - 50 * i);
});
function changeToGreen() {
$(".test").css('background-color', 'green');
}
function changeToLime() {
$(".test").css('background-color', 'lime');
}
(async () => {
for (var i = 0; i < 20; i++) {
await delay(i);
changeToLime();
await delay(i);
changeToGreen();
}
})();
Your loop doesn't wait for any of the timeouts to occur, it runs through and queues up the events which will fire at the relevant intervals.
However, whilst doing so it sets the background color to lime a number of times.
After the loop has finished, the queued intervals start firing, and they set the background color to green a number of times.
But the colours do not alternate as the code execution is not in the order you expect.
Also, the multiple calls to setInterval queue the events to be fired after the specified delay. The code does not wait for the allotted time and then fire the next one. So your could of 1000 - 50 * i actually queues the latest event first, and so on until it queues the event that will actually fire first. Does that make sense? It will be more intuitive for you to set these in the order that they will fire. You could achieve the reducing delay by incrementing the timeout by a variable which reduces, e.g.
time = 1000;
delay = 1000;
setTimeout (blah, time);
time += delay;
delay -= 50;
setTimeout (blah, time);
// etc.
You could achieve an alternating effect by setting alternate intervals to be green and lime. For that a simple toggle variable would help.
color = 1;
color = 1 - color; // toggles between 0 and 1
useColor = ["lime", "green"][color];
I shan't rewrite your entire program for you, but I can assist more if you have specific questions. The best way to learn is to do.
There is a slight misunderstanding about the way timeouts work in the example code. Timeouts are asynchronous, meaning that they execute out of the normal order of execution. As a result, the lime green is shown immediately, and then at various times later the background is repeatedly changed to green; although, the only time the change is noticed is the first time as changing from green to green has no effect.
setTimeout creates a task, JavaScript in a browser is single threaded and will execute tasks through a task scheduler.
Using 1000 - 50 * i from 0 to 19 in the approach shown in the question will result in timeouts being scheduled for execution. First at 1000, then at 950, etc. However, they are all scheduled at the exact same time. So there is no difference scheduling them in forward or reverse order as the only relevant metric used is the time. Essentially the result is that every 50 milliseconds, the background color is set to green in this example.
Unfortunately, tasks that get executed in the browser are not executed exactly on time, and using this will aim at 50 milliseconds per call, but due to Operating System scheduling and depending on the system in use the result could be wildly different.
This could have been done with an interval just as easily, where the interval used was 50 milliseconds (although it would still suffer from the aforementioned OS issue). That said, there is no acceleration being used there. A better approach here, since we are dealing with animation (the colors flashing) would be to instead use requestAnimationFrame MDN.
requestAnimationFrame will attempt to run your code at 60 frames per second, or roughly 16.6 milliseconds per frame (1000 milliseconds / 60 frames).
Given that the goal was acceleration, a rate could be put in place to ramp the flashing.
// Cache the constructed jQuery object for element with class "test"
var testCache = $('.test');
// Create a set of colors to use in the flashing
var colors = ['lime','green'];
// Use a variable for a switch between the two colors
var colorSwitch = 0;
// Keep track of how many times the color has flashed
var i = 0;
// Used for tracking the start of an animation sequence
var start;
// In order to facilitate acceleration, use a function for
// determining the time between flashes,
// used an offset x^2 line at (20,16) with a 2x width
// y = 1/2(x-19)^2 - 19x + 16
var ft = t => 0.5*(t-19)*(t-19) - (t-19) + 16;
// This function will be called every 16.6 milliseconds
// by requestAnimationFrame, the timestamp is automatically injected
(function flashAccel(timestamp){
// Loop control to ensure only 20 flashes occur
if(i >= 20) return;
// Track the start of the timing for the animation sequence
start = start || timestamp;
// This is the milliseconds since the last sequence was updated
var elapsed = timestamp - start;
// Check to see if enough time has elapsed based on the acceleration
// function's value and the current value, if it has then update the view
if( elapsed > ft(i) ){
// Swaps between 0 and 1
colorSwitch = 1 - colorSwitch;
// Selects 0 or 1 indexed color
var color = colors[colorSwitch];
testCache.css('background-color',color);
// Update metrics
i++;
start = timestamp;
}
// Request the function to be called again in roughly 16.6 milliseconds
window.requestAnimationFrame(flashAccel);
})()
.test {
width: 300px;
height: 300px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test"></div>
I am using jquery to make a simple shooting game from the left side of screen to the right. When the bullet is shot it will travel forever to the right. How can i remove it when it reaches a certain coordinate at the end of the page?
This makes the bullet move...
var flyingBullet = function(){
$(".bullet").each(function() {
var oldLeft = $(this).offset().left;
$(this).css("left", oldLeft + 10 + "px");
});
}
setInterval(flyingBullet, 200);
jsBin demo
(Use jQuery's .remove() method once the bullet is at leftPosition === worldWidth).
(I would do that all in Canvas, for better performance results, visuals and repainting, but here you go: )
I find a really bad idea to loop constantly uncached $() class selector elements.
Create among other variables like...
var world = {$el:$("body"), width: $('body').width()};
var $player = $('#player');
var mousePosY = 0;
...an Array that will hold all the created Bullets
var allBullets = [];
to keep track of all the active bullets and to remove them.
Than create a Bullet Object with the bullet properties (with inner handlers for the position, color, shape, speed etc etc...) and append that Object to our Array of bullets and insert it into the DOM:
function Bullet() {
var obj = {
$el : $('<div/>',{'class':'bullet'}),
css : {top: 100, left:0}, // Start at left 0
speed : 10,
move : function() { // This method will be used inside the animation loop!
obj.css.left += obj.speed; // Advance Bullet
if(obj.css.left > world.width){ // Notice this!!!!
var i = allBullets.indexOf(obj); // !! Use of indexOf (xBr. support).
allBullets.splice(i, 1);
obj.$el.remove();
}else{
return obj.$el.css( obj.css );
}
}
};
allBullets.push( obj );
$('body').prepend( obj.$el );
return obj;
}
To remove the bullets (as you can see in the code above) simply:
var i = allBullets.indexOf(obj); // Find the index of that object inside Array
allBullets.splice(i, 1); // Remove it from Array
obj.$el.remove(); // Remove it from the DOM
Note that indexOf() is introduced in ES5 and not present in old browsers. You might want to use a Polyfill or replace it with an object that will collect Bullets ID... (I'll not cover that topic in this answer).
Once the player clicks to "Fire" you simply create a new instance of that Bullet like:
$("body").on('click', function(){
new Bullet(); // Create a new Bullet with all the properties, move etc.
});
and it will be immediately collected into the allBullets Array.
To move the bullets you pass the the window.requestAnimationFrame :
function moveBullets(){
if(allBullets.length>0) // Only if we have bullets in Array!
for(var i=0; i<allBullets.length; i++) allBullets[i].move(); // Move it using
// his own method.
}
I don't see how's that bullet supposed to fly nicely? 20 by 20px every 200ms?
Also for animation purposes I would go with window.requestAnimationFrame instead of setInterval, to allow the browser dictate the repainting tempo.
// ::: FRAME RATE
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
}());
// ::: ENGINE
(function engine() {
// ----- Insert here functions you want to loop in framerate:
moveBullets();
// -----
requestAnimFrame(engine); // request a new frame
})();
If you really want to use DOM element, I would also suggest to use enhanced CSS3 transitions and move the element with translate3d(x, y, z). You'll not have to move the element every tick, but the browser will move it for you! You can pre-calculate it trajectory in order to keep track of the element position in relation to the animation framerate and an internal clock. (I'll also not cover this examples in my answer.)
The above was for guidance, take a look at the result
in this DEMO (with random bullets speed)
I've written a little function to loop through a sprite sheet. Within the function I have a settimeout which cycles through the frames. Upon the last frame, the timeout is cleared, the counter is set to zero - and the animation begins again.
This works fine with one animation, but when I try and call many animations - they all start but fail to loop, apart from designSprite which loops quite happily. I call the designSprite anim last....
So I'm guessing the problem is due to variables being overwritten when I call a new intance of the function - setTimeOut referencing the new variables?? I've confused myself. I've had a stab at trying to fix it, but keep failing.
Thanks,
Rob
// Arrays to hold our sprite coordinates.
var animationSprite=[{"X":"-2","Y":"-2"},........ etc etc ];
var mediaSprite=[{"X":"-2","Y":"-2"},........ etc etc ];
var filmSprite=[{"X":"-2","Y":"-2"},........ etc etc ];
var designSprite=[{"X":"-2","Y":"-2"},........ etc etc ];
// call the loopAnim function, passing in the sprite array, and id of the div
loopAnim(animationSprite ,'#animationFrame')
loopAnim(mediaSprite ,'#mediaFrame')
loopAnim(filmSprite ,'#filmFrame')
loopAnim(designSprite ,'#designFrame')
function loopAnim(sprite , frameID) {
var totalFrames = sprite.length; // count how many 'frames' are in our sprites array.
var count = 0; // set up a basic counter to count which frame we're on.
var theLoop = function(){
// Move the background position of our frame by reading the X & Y co-ordinates from the sprites array.
$(frameID).css("background-position" , sprite[count].X + "px " + sprite[count].Y + "px");
count++; // increment the frame by 1 on each loop
// if count is LESS than total number of frames, set a timeout to keep running the "theLoop" function
if (count < totalFrames){
setAnim = setTimeout(theLoop, 60);
// if count is greater than the total number of frames - clear our timeout. Reset the counter back to zero, and then run our loop function again
} else {
clearTimeout(setAnim);
count = 0;
theLoop();
}
}
theLoop();
}
setAnim looks like it wasn't declared, meaning it's a global variable. This means all your calls to loopAnim are using and overwriting the same timer ID reference.
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);
});
}