I'm trying to do something very simple on a progress-bar script but my non-existent knowledges of javascript are making me struggle a little bit.
What I have looks like this :
if (progress == 100) {
var element = document.getElementById("avatar-progress");
element.classList.add("finish");
setTimeout(function () {
$('#avatar-progress').circleProgress(
'value',
0
);
}, 250);
}
if ($('#avatar-progress').circleProgress('value') == 0) {
var element = document.getElementById("avatar-progress");
element.removeAttribute("class");
}
$('#avatar-progress').circleProgress({
value: 0,
size: 156,
fill: { color: "#60bcff" },
emptyFill: "#ffffff",
thickness: 2,
});
Basically what happens with this progress-bar is this :
when the uploading hits 100% : a class is added to #avatar-progress for a cool pulse effect, then, after the requiered delay for the pulse animation, the value is set back to 0, making the progress-bar disapear, and is ready for the next upload.
However, for the script's sake, I have to wait the value has returned to 0 before deleting the class (if I don't the pulse animation won't load again).
And this is what I failed to do, I don't know how to write it, as you see in my code I tried some stuffs :
if ($('#avatar-progress').circleProgress('value') == 0) {
var element = document.getElementById("avatar-progress");
element.removeAttribute("class");
}
but my condition doesn't work. I'm looking for some help to write this line the right way. Thank you for your suggestions
You can actually do both things inside your setTimeout function:
if (progress === 100) {
var element = document.getElementById('avatar-progress');
element.classList.add('finish');
setTimeout(function () {
$('#avatar-progress').circleProgress('value', 0);
element.classList.remove('finish');
}, 250);
}
N.B. don't forget to use triple equals (===) in JavaScript, to ensure your comparison is correct.
Related
I'm developing a game engine in HTML5. Characters are div elements using an animated sprite for background. As sprite animation have fluid parameters and must be set by code, they can't be predefined in a static CSS definition, thus I use element.animate to set sprite animations to a given row at a given speed knowing my scales and frame counts.
// Applies the given frame and animation to the sprite
// Frame is an angle, clockwise direction: 0 = up, 1 = right, 2 = down, 3 = left
set_animation(frame, duration) {
const scale_x = this.settings.sprite.scale_x * this.settings.sprite.frames_x;
const pos_y = this.settings.sprite.scale_y * -frame;
// Cancel the existing animation
if(this.data_actors_self.anim) {
this.data_actors_self.anim.cancel();
this.data_actors_self.anim = null;
}
// Play the animation for this row or show the first frame if static
if(duration > 0) {
this.data_actors_self.anim = this.element.animate([
{
backgroundPosition: px([0, pos_y])
}, {
backgroundPosition: px([scale_x, pos_y])
}
], {
duration: duration * 1000,
direction: "normal",
easing: "steps(" + this.settings.sprite.frames_x + ")",
iterations: Infinity
});
this.data_actors_self.anim.play();
} else {
this.element.style.backgroundPosition = px([0, pos_y]);
}
}
Obviously that's a snippet from an actor class function: this.element is the div, this.settings is an object with parameters to be used who's names should make sense in this context, the px() function is a simple converter to turn arrays into pixel strings for HTML (eg: [0, 0] to "0px 0px").
The issue I'm having: While I can always run this function to set a new animation, I want the ability to change the speed of the animation without resetting it. It doesn't need to be a smooth transition, for all I care the new speed can be applied at the next iteration... I only want to avoid a visual snap or any kind of reset upon applying the change. Once an animation is set, I have no idea how to access and update its duration parameter. Does anyone have any suggestions?
When using console.log on this.data.anim I'm rightfully told it's an animation object. I tried using JSON.stringify to get more information but nothing relevant is printed. this.data.anim.duration returns undefined so the setting must be stored under some other property. Even if I know that property, I'd like to be sure web browsers will agree with me changing it like this.data.anim.options.duration = new_duration.
You can wait for the end of an iteration before changing the animation duration if that is what is required.
This snippet only sets an event listener for animationiteration event when you click the button to increase the speed.
function upthespeed() {
const div = document.querySelector('div');
div.addEventListener('animationiteration', function() {
div.style.animationDuration = '1s';
});
document.querySelector('button').style.display = 'none';
}
div {
width: 10vmin;
height: 10vmin;
background-color: magenta;
animation: move 10s linear infinite;
}
#keyframes move {
0% {
transform: translateX(50vw);
}
50% {
transform: translateX(0);
}
100% {
transform: translateX(50vw);
}
}
<div></div>
<button onclick="upthespeed()">Click me to increase the speed at the end of the next iteration (you may have to wait!)</button>
The value for the animation duration isn't in the Animation object itself but in the CSS animation-duration property for the Element: so this.data_actors_self.style.animationDuration = new_duration will do the job. It will however restart the animation if it is being played, but if I understand correctly that isn't a problem for you.
Edit: To change the animation's duration without restarting it, all you have to do is set the value of anim.startTime to what it was before. For example:
const startTime = anim.startTime;
this.data_actors_self.style.animationDuration = new_duration
anim.startTime = startTime;
I would like to add category icons to a Wordpress page, each icon animated with snap.svg.
I added the div and inside an svg in the loop that prints the page (index.php). All divs are appearing with the right size of the svg, but blank.
The svg has a class that is targeted by the js file.
The js file is loaded and works fine by itself, but the animation appears only in the first div of that class, printed on each other as many times it is counted by the loop (how many posts there are on the actual page from that category).
I added "each()" and the beginning of the js, but is not allocating the animations on their proper places. I also tried to add double "each()" for the svg location and adding the snap object to svg too, but that was not working either.
I tried to add unique id to each svg with the post-id, but i could not pass the id from inside the loop to the js file. I went through many possible solutions I found here and else, but none were adaptable, because my php and js is too poor.
If you know how should I solve this, please answer me. Thank you!
// This is the js code (a little trimmed, because the path is long with many randoms, but everything else is there):
jQuery(document).ready(function(){
jQuery(".d-icon").each(function() {
var dicon = Snap(".d-icon");
var dfirepath = dicon.path("M250 377 C"+ ......+ z").attr({ id: "dfirepath", class: "dfire", fill: "none", });
function animpath(){ dfirepath.animate({ 'd':"M250 377 C"+(Math.floor(Math.random() * 20 + 271))+ .....+ z" }, 200, mina.linear);};
function setIntervalX(callback, delay, repetitions, complete) { var x = 0; var intervalID = window.setInterval(function () { callback(); if (++x === repetitions) { window.clearInterval(intervalID); complete();} }, delay); }
var dman = dicon.path("m136 ..... 0z").attr({ id: "dman", class:"dman", fill: "#222", transform: "r70", });
var dslip = dicon.path("m307 ..... 0z").attr({ id: "dslip", class:"dslip", fill: "#196ff1", transform:"s0 0"});
var dani1 = function() { dslip.animate({ transform: "s1 1"}, 500, dani2); }
var dani2 = function() { dman.animate({ transform: 'r0 ' + dman.getBBox().cx + ' ' + dman.getBBox(0).cy, opacity:"1" }, 500, dani3 ); }
var dani3 = function() { dslip.animate({ transform: "s0 0"}, 300); dman.animate({ transform: "s0 0"}, 300, dani4); }
var dani4 = function() { dfirepath.animate({fill: "#d62a2a"}, 30, dani5); }
var dani5 = function() { setIntervalX(animpath, 200, 10, dani6); }
var dani6 = function() { dfirepath.animate({fill: "#fff"}, 30); dman.animate({ transform: "s1 1"}, 100); }
dani1(); }); });
I guess your error is here:
var dicon = Snap(".d-icon");
You are passing a query selector to the Snap constructor, this means Snap always tries to get the first DOM element with that class, hence why you're getting the animations at the wrong place.
You can either correct that in two ways:
Declare width and height inside the constructor, for example var dicon = Snap(800, 600);
Since you are using jQuery you can access to the current element inside .each() with the $(this) keyword. Since you are using jQuery instead of the dollar you could use jQuery(this).
Please keep in mind this is a jQuery object and probably Snap will require a DOM object. In jQuery you can access the dom object by appending a [0] after the this keyword. If var dicon = Snap( jQuery(this) ); does not work you can try with var dicon = Snap( jQuery(this)[0] );
Additionally, you have several .attr({id : '...', in your code. I assume you are trying to associate to the paths an ID which are not unique. These should be relatively safe since they sit inside a SVG element and I don't see you are using those ID for future selection.
But if you have to select those at a later time I would suggest to append to these a numerical value so you wont have colliding ID names.
I have a series of blocks that fall, and when they get to a certain point a grey block (placeholder for dust) appears; it goes away if the block bounces up past a certain point, then reappears when the block falls down again, getting smaller each time impact occurs.
Currently, however, since a class for the grey block is what is being affected, any falling block affects the overall size of the "dust" block despite falling and hitting at different times. I would like to keep track of each dust block separately, so each one changes depending on when the color block it is associated with impacts on the ground.
I've tried using ids - id='smoke"+i+"', $("#smoke"+i).height(40+smokeGlobal); - instead of a single class, but it appears doing that only allows the first block to animate. (I assumed a "for" loop is necessary there, but adding that in seemed to cancel the dust block altogether.)
As always, help would be appreciated. Thanks.
http://jsfiddle.net/pjhoL899/1/
JS:
var links = ["#portfolio", "#hamumu", "#beep", "#jk"];
var linkGlobal;
smokeGlobal = 0;
//home page functions
$(document).ready(function homePage() {
//check collisions
//initial animation
for (var i=0; i<links.length; i++) {
//animate links
$(links[i]).animate({
top: '0'
}, {
duration: 2000*(Math.random()*(2)+1),
easing: 'easeOutBounce',
step: function(y) {
if (y >= -2) {
smokeGlobal -= 2;
linkGlobal = $(this);
linkGlobal.html("<div class='smoke' id='smoke"+i+"'></div>");
$(".smoke").height(40+smokeGlobal);
$(".smoke").fadeTo("fast", 0);
}
}, complete: setTimeout(function() {
linkGlobal.html("");
}, 2000)
});
}
})
Edit, updated
Substituted now for y and $(tween.elem) for $(this) at step function. step function argument tween is one of objects within jquery .animate() ; having several properties , including currently animated element (elem) ; easing ; now ; options - which also has queue property (see .queue()).
For each element within the set links , the currently animated property could be retrieved utilizing tween object , along with the elements' animated position between the start and end of the animated property.
At original post , y could actually be utilized as now argument ; and $(this) for linkGlobal . The tween object should give access to the animated element at the specific portion of the animation required ; for adjustment of the element , animation , callback.
To keep track of each dust block separately , can access that element through .fadeTo() callback , or utilize done callback to adjust effects ; done fires before complete . done also has promise which could be utilized to adjust next animated element . There may be several potential options available to adjust features utilizing .animate().
Try (this pattern)
var links = ["#portfolio", "#hamumu", "#beep", "#jk"];
var linkGlobal;
smokeGlobal = 0;
//home page functions
$(document).ready(function homePage() {
//check collisions
//initial animation
for (var i=0; i<links.length; i++) {
//animate links
$(links[i]).animate({
top: '0'
}, {
duration: 2000*(Math.random()*(2)+1),
easing: 'easeOutBounce',
step: function(now, tween) {
console.log(now, tween);
// substituted `now` for `y`
if (now >= -2) {
smokeGlobal -= 2;
// substituted `$(tween.elem)` for `$(this)`
linkGlobal = $(tween.elem);
linkGlobal.html("<div class='smoke' id='smoke"+i+"'></div>");
// adjust "dust" colors ,
// from checking `tween.elem` `backgroundColor` , `now`
var color = linkGlobal.css("background-color");
$(".smoke").eq(0)
.css("background-color"
, color === "yellow" && now < -1.05 ? "purple" : "orange");
$(".smoke").eq(1)
.css("background-color"
, color === "purple" && now < -0.00025 ? "orange" : "pink");
$(".smoke").eq(2)
.css("background-color"
, color === "pink" && now >= -0.0075 ? "purple" : "yellow");
$(".smoke").eq(3)
.css("background-color"
, color === "orange" && now < -0.003 ? "pink" : "purple");
// alert(now);
$(".smoke").height(40+smokeGlobal);
$(".smoke").fadeTo("fast", 0);
}
}, complete: setTimeout(function() {
linkGlobal.html("");
}, 2000)
});
}
});
jsfiddle http://jsfiddle.net/guest271314/pjhoL899/10/
I'm playing around with pure JavaScript, so I created a small fade in/out object, to adjust images opacity onmouseover and onmouseout. Fading works fine when the mouseover and mouseout actions are precise:
Start moving the cursor from the white background
Hover over an image
Hover back over the white background
The problem is, as soon as I start to move the mouse "naturally" from one image to another, the fading (or rather the script itself) freezes.
I'm not sure whether it's a animation-speed problem, or there's something I'm missing in the implementation.
If someone has the time to take a look, I would appreciate a peer check, so I can crack the issue and learn new stuff.
Here's a fiddle: http://jsfiddle.net/6bd3xepe/
Thanks!
As I see it, you have one INTERVAL for you FADER, you need one for each IMG.
My jsfiddle fixes this. I added an ALT-attribute to each IMG with "dome" content, so as to circumvent the jsfiddle working on non-cat-images .. ignore that part - commented out below.
There are some fundamental things wrong with the design - keeping track of objects & references is key. Usage of "this" & "that" aren't helping in the current implementation (see comments to OP). Also, on another note, the usage of "toFixed(2)" is not really required IMHO and you can shorten "o = o + 0.1" to "o += 0.1".
JS:
var fader = {
target: document.getElementsByTagName('img'),
interval: [],
speed: 25,
default_opacity: 1,
init: function() {
this.bindEvents();
},
// Get element's opacity and increase it up to 1
fadeIn: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fI: "+idx+" "+element_opacity);
this.default_opacity = element_opacity.toFixed(2);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) < 1) {
element_opacity = element_opacity + 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
}
}, that.speed);
},
// Get current opacity and decrease it back to the default one
fadeOut: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fO: "+idx+" "+element_opacity);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) > that.default_opacity) {
element_opacity = element_opacity - 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
element.removeAttribute('style');
}
}, that.speed);
},
// Get opacity of an element using computed styles
getOpacity: function(element) {
var styles = window.getComputedStyle(element),
opacity = parseFloat(styles.getPropertyValue('opacity'));
return opacity;
},
bindEvents: function() {
var that = this, count = 0;
for (var i in this.target) {
// the whole "dome" is just a fsfiddle hack - otherwise it sees 7 images instead of 4!
//if( this.target[i].alt == "dome" ){
console.log("COUNT: "+count);
this.target[i].setAttribute('data-idx',count);
this.target[i].onmouseover = function() {
that.fadeIn(this);
}
this.target[i].onmouseout = function() {
that.fadeOut(this);
}
count++;
//}
}
}
};
fader.init();
I have a #ball that when clicked uses jquery animate to move down 210px using this code:
$('#ball').click(function() {
$(this).animate({
top: '+=210px'
}, 500);
setTimeout(crack, 400);
});
currently Im using Timeout to trigger the next function which is "crack".
Instead I want to track the movement of #ball and when its css top = 210px I want to trigger the function crack(), how can I do this?
I saw in a somewhat similar post that the Step function might be what I'm looking for, but I am not sure how to approach that solution based on the info provided at http://api.jquery.com/animate/
Look at Demo: http://jsfiddle.net/EnigmaMaster/hbvev/4/
I am not sure why you want to use a tracker if you know that the ball will reach the box in 210px.
If you want to get rid of setTimeout, then use the .animate callback function which will be called when the ball reaches the box.
$('#ball').click(function() {
$(this).animate({
top: '+=210px'
}, 500, crack); //<== crack will be called after ball animation
});
DEMO
Incase if you want to call crack when the ball touches the box and still continue the movement of box then you can execute it 2 steps like below,
$('#ball').click(function() {
$(this).animate({
top: '+=180px'
}, 400, function() {
crack();
$(this).animate({
top: '+=30px'
}, 100);
});
});
Also check this version for fun in slow motion http://jsfiddle.net/skram/hbvev/8/
If you truly want to do something based on the position of the ball, then yes, step is probably the best way to go:
$('#ball').click(function() {
$(this).animate({
top: '+=210px'
}, {
duration: 500,
step: function() {
if($(this).offset().top > 208) {
crack();
}
}
});
});
Demo: http://jsfiddle.net/qJjnN/1/
Now, there are a couple of caveats:
There will be a possible performance hit.
The position at each step will not necessarily be a whole number, and the object will not exist at every pixel between the start and stop location.
step is not called on the final position, so you cannot actually check for 210 if it is the final location.
Taking those into mind, you will not be able to check for the exact position of 210px. Instead, you will want to watch when it passes a certain position and only trigger crack at that point and not every point after:
$('#ball').click(function() {
var cracked = false;
$(this).animate({
top: '+=210px'
}, {
duration: 500,
step: function() {
if($(this).offset().top > 208 && !cracked) {
cracked = true;
crack();
}
}
});
});
Demo: http://jsfiddle.net/qJjnN/2/
The step function also has parameters now and fx that can be used to see the current value of the css being animated. step is called for each step of each css attribute being animated. So, you have to be careful using those, because you need to look at fx to see what attribute value you are looking at (if you are animating more than one, i.e. top and left).
$('#ball').click(function() {
var cracked = false;
$(this).animate({
top: '+=210px'
}, {
duration: 500,
step: function(now, fx) {
if(fx.prop != 'top') {
return;
}
if(now > 208 && !cracked) {
cracked = true;
crack();
}
}
});
});