I am using the following code to glide an image across the top layer of a webpage but its a little jittery, giving streaky vertical lines down the image especially when over content with many nested elements. This is the case even when the border is set to zero. Any suggestions for a smoother method for gliding an image with JS/CSS?
border=4;
pps=250; // speed of glide (pixels per second)
skip=2; // e.g. if set to 10 will skip 9 in 10 pixels
refresh=3; // how often looks to see if move needed in milliseconds
elem = document.createElement("img");
elem.id = 'img_id';
elem.style.zIndex="2000";
elem.style.position="fixed";
elem.style.top=0;
elem.style.left=0;
elem.src='http://farm7.static.flickr.com/6095/6301314495_69e6d9eb5c_m.jpg';
elem.style.border=border+'px solid black';
elem.style.cursor='pointer';
document.body.insertBefore(elem,null);
pos_start = -250;
pos_current = pos_start;
pos_finish = 20000;
var timer = new Date().getTime();
move();
function move ()
{
var elapsed = new Date().getTime() - timer;
var pos_new = Math.floor((pos_start+pps*elapsed/1000)/skip)*skip;
if (pos_new != pos_current)
{
if (pos_new>pos_finish)
pos_new=pos_finish;
$("#img_id").css('left', pos_new);
if (pos_new==pos_finish)
return;
pos_current = pos_new;
}
t = setTimeout("move()", refresh);
}
I do not have a solution that I am sure of will prevent the vertical lines from appearing.
I do however have a couple of tips to improve your code so performance increases and you might have a chance that the lines disappear.
Cache the image element outside of your move function:
var image = $("#img_id")[0];
In your code, there is no reason to query the image ID against the DOM every 3 milliseconds. jQuery's selector engine, Sizzle has to a lot of work¹.
Don't use the jQuery CSS function:
image.style.left = pos_new;
Setting a property object is faster than a function call. In the case of the jQuery css function, there are at least two function calls (one to css and one inside css).
Use interval instead of timeout:
setInterval(move, refresh);
I would consider an interval for one-off animations I wanted to be as
smooth as possible
setTimeout or setInterval?
One other option for smoother animation is to use CSS transitions or animations. A great introduction and comparison can be found in CSS Animations and JavaScript by John Resig
Browser support table: http://caniuse.com/#search=transition
A JavaScript library that I find makes CSS animation via JavaScript very easy is morpheus.
¹ Under the hood, this is the code it goes through every 3 milliseconds to find your image:
In a browser that supports querySelectorAll:
Sizzle = function( query, context, extra, seed ) {
context = context || document;
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
if ( !seed && !Sizzle.isXML(context) ) {
// See if we find a selector to speed up
var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
// Speed-up: Sizzle("TAG")
if ( match[1] ) {
return makeArray( context.getElementsByTagName( query ), extra );
// Speed-up: Sizzle(".CLASS")
} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
return makeArray( context.getElementsByClassName( match[2] ), extra );
}
}
if ( context.nodeType === 9 ) {
// Speed-up: Sizzle("body")
// The body element only exists once, optimize finding it
if ( query === "body" && context.body ) {
return makeArray( [ context.body ], extra );
// Speed-up: Sizzle("#ID")
} else if ( match && match[3] ) {
var elem = context.getElementById( match[3] );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if ( elem && elem.parentNode ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id === match[3] ) {
return makeArray( [ elem ], extra );
}
} else {
return makeArray( [], extra );
}
}
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(qsaError) {}
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
var oldContext = context,
old = context.getAttribute( "id" ),
nid = old || id,
hasParent = context.parentNode,
relativeHierarchySelector = /^\s*[+~]/.test( query );
if ( !old ) {
context.setAttribute( "id", nid );
} else {
nid = nid.replace( /'/g, "\\$&" );
}
if ( relativeHierarchySelector && hasParent ) {
context = context.parentNode;
}
try {
if ( !relativeHierarchySelector || hasParent ) {
return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
}
} catch(pseudoError) {
} finally {
if ( !old ) {
oldContext.removeAttribute( "id" );
}
}
}
}
return oldSizzle(query, context, extra, seed);
};
And a browser that doesn't:
var Sizzle = function( selector, context, results, seed ) {
results = results || [];
context = context || document;
var origContext = context;
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
if ( !selector || typeof selector !== "string" ) {
return results;
}
var m, set, checkSet, extra, ret, cur, pop, i,
prune = true,
contextXML = Sizzle.isXML( context ),
parts = [],
soFar = selector;
// Reset the position of the chunker regexp (start from head)
do {
chunker.exec( "" );
m = chunker.exec( soFar );
if ( m ) {
soFar = m[3];
parts.push( m[1] );
if ( m[2] ) {
extra = m[3];
break;
}
}
} while ( m );
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context, seed );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
while ( parts.length ) {
selector = parts.shift();
if ( Expr.relative[ selector ] ) {
selector += parts.shift();
}
set = posProcess( selector, set, seed );
}
}
} else {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ?
Sizzle.filter( ret.expr, ret.set )[0] :
ret.set[0];
}
if ( context ) {
ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
set = ret.expr ?
Sizzle.filter( ret.expr, ret.set ) :
ret.set;
if ( parts.length > 0 ) {
checkSet = makeArray( set );
} else {
prune = false;
}
while ( parts.length ) {
cur = parts.pop();
pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
if ( pop == null ) {
pop = context;
}
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
Sizzle.error( cur || selector );
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
}
}
} else {
makeArray( checkSet, results );
}
if ( extra ) {
Sizzle( extra, origContext, results, seed );
Sizzle.uniqueSort( results );
}
return results;
};
There are lots of minor ways to tweak you code to run slightly smoother... Use a feedback loop to optimize the step size and delay, look for even steps that don't round up or down causing small jumps at regular intervals, etc.
But the secret API you're probably looking for (and which is used by many of the libraries you are avoiding) is requestAnimationFrame. It's currently non-standarized, so each browser has a prefixed implementation (webkitRequestAnimationFrame, mozRequestAnimationFrom, etc.)
Instead of re-explaining how it helps reduce/prevent tearing and vsync issues, I'll point you to the article itself:
http://robert.ocallahan.org/2010/08/mozrequestanimationframe_14.html
I took a shot at this with a few ideas in mind. I could never get the animation to be incredibly un-smooth, nor did I ever experience any vertical lines, so I'm not sure if it's even an improvement. Nevertheless, the function below takes a few key ideas into account that make sense to me:
Keep the element away from the DOM with a container <div> for the animation. DOM involvement in repaints makes it much longer than it should be for a basic overlay animation.
Keep as much fat as possible out of the move function. Seeing as this function will be called a large amount, the less script there is to run, the better. This includes that jQuery call to change the element position.
Only refresh as much as absolutely necessary. I set the refresh interval here to 121 Hz, but that's an absolute top-end for a 60Hz monitor. I might suggest 61 or less, depending on what's needed.
Only set a value in to the element style object if it's needed. The function in the question did do this, but again it's a good thing to keep in mind, because in some engines simply accessing the setter in a style object will force a repaint.
What I wanted to try out was using the image as the background of an element, so you could just script changing the CSS background-position property instead of changing the element position. This would mean loss DOM involvement in the repaints triggered by the animation, if possible.
And the function, for your testing, with a fairly unnecessary closure:
var border = 4;
var pps = 250;
var skip = 2;
var refresh = 1000 / 121; // 2 * refresh rate + 1
var image = new Image();
image.src = 'http://farm7.static.flickr.com/6095/6301314495_69e6d9eb5c_m.jpg';
// Move img (Image()) from x1,y1 to x2,y2
var moveImage = function (img, x1, y1, x2, y2) {
x_min = (x1 > x2) ? x2 : x1;
y_min = (y1 > y2) ? y2 : y1;
x_max = (x1 > x2) ? x1 : x2;
y_max = (y1 > y2) ? y1 : y2;
var div = document.createElement('div');
div.id = 'animationDiv';
div.style.zIndex = '2000';
div.style.position = 'fixed';
div.style.top = y_min;
div.style.left = x_min;
div.style.width = x_max + img.width + 'px';
div.style.height = y_max + img.height + 'px';
div.style.background = 'none';
document.body.insertBefore(div, null);
elem = document.createElement('img');
elem.id = 'img_id';
elem.style.position = 'relative';
elem.style.top = 0;
elem.style.left = 0;
elem.src = img.src;
elem.style.border = border + 'px solid black';
elem.style.cursor = 'pointer';
var theta = Math.atan2((y2 - y1), (x2 - x1));
(function () {
div.insertBefore(elem, null);
var stop = function () {
clearInterval(interval);
elem.style.left = x2 - x1;
elem.style.top = y2 - y1;
};
var startTime = +new Date().getTime();
var xpmsa = pps * Math.cos(theta) / (1000 * skip); // per milli adjusted
var ypmsa = pps * Math.sin(theta) / (1000 * skip);
var interval = setInterval(function () {
var t = +new Date().getTime() - startTime;
var x = (Math.floor(t * xpmsa) * skip);
var y = (Math.floor(t * ypmsa) * skip);
if (parseInt(elem.style.left) === x &&
parseInt(elem.style.top) === y) return;
elem.style.left = x + 'px';
elem.style.top = y + 'px';
if (x > x_max || x < x_min || y > y_max || y < y_min) stop();
}, refresh);
console.log(xpmsa, ypmsa, elem, div, interval);
})();
};
For your circumstance, you should consider the followings to make animation smoother:
The interval between animation steps (your refresh value) should be long enough for browser to process (JavaScript code, rendering). As my experience, it should be 10 to 20 milliseconds.
Why you made the image position multiple of skip? Set skip value as small as possible (1) could make animation smoother.
Avoid causing browsers reflow if possible (reflow vs repaint).
Using appropriate easing method instead of linear (as in your code) could make animation look better (human sight, not technical)
Optimize JavaScript code for each animation step. This is not problem in simple animation as yours, but you can improve something such as: use setInterval instead of setTimeout, cache image object for fast access, use native JS code to change image position
Hope these help.
Your question really seems to be about browser rendering engines and their capabilities. As you have noticed, there are limitations as to how quick a browser can render animation. If you hit this limitation, you'll see jitter or other 'unsmooth' behavior. Sometimes rendering faults, like not cleaning up parts of the animation or scrambled parts.
Back in the olden days, any form of decent animation was virtually impossible. In time, things got better, but I still remember using the tiniest possible images to keep my nice folding/unfolding menu performing smoothly. Of course, these days we've got hardware accelerated browser rendering, so you can do multiple animations at once, and don't need to worry a whole lot about animation being slow.
But I've been redoing some animations I've used, because my iPad (1) seems quite slow rendering some of them. Like scrolling a large div got quite choppy. So basically, I started to tune things down:
Using simple animation instead of complex, and: no combined animation (like scroll and fade)
Reduce number of html-elements inside animated object
Make animated object smaller
Preload as much as possible
Create space for the animated object (if possible, in case sliding or moving means moving a whole lot of other elements)
This did work, after some trial and error. What you've got to keep in mind is that the javascript is just changing the css properties of html-elements. The browser repaints what the JS tells him to. So the more it tells him, the heavier it gets, and the rendering falls behind.
Looking at performance, it breaks down into three components: CPU, GPU and screen updates. Every browser engine works differently, so performance can differ as well. An interesting look at how this works, comes from the people on the IE 10 team, which is more thorough than I could be: http://blogs.msdn.com/b/ie/archive/2011/04/26/understanding-differences-in-hardware-acceleration-through-paintball.aspx
Javascript animations are always somewhat jittery, since timers aren't very precise. You can get a little better peformance by using a few tricks:
Enable hardware acceleration: img { -webkit-transform: translateZ(0) };
Use setInterval, it can result in smoother animation too, although the change is usually unnoticeable
Set your refresh rate to 1000/60 (60pfs) - that's the screen limit, and timers never go below 4ms
IE9+ seems to solve this by coupling ticks with the screen refresh rate, which makes for much smoother animation, but I wouldn't count on other browsers doing that anytime soon. The future is in CSS transitions.
In CSS, you could use this:
img {
-webkit-transition:2s all linear;
-moz-transition:2s all linear;
-ms-transition:2s all linear;
transition:2s all linear;
}
But since your animation duration depends on the target position to achieve a constant speed, you can manipulate the values via JS:
var img = document.createElement('img')
document.body.appendChild(img)
var styles = {
zIndex : '2000'
, position : 'absolute'
, top : '0px'
, left : '0px'
, border : '4px solid black'
, cursor : 'pointer'
}
Object.keys(styles).forEach(function(key){
img.style[key] = styles[key]
})
var prefixes = ['webkit', 'Moz', 'O', 'ms', '']
, speed = 250
, endPosition = 2000
, transition = Math.floor(endPosition/speed)+'s all linear'
prefixes.forEach(function(prefix){
img.style[prefix+(prefix ? 'T' : 't')+'ransition'] = transition
})
img.onload = function(){
img.style.left = endPosition+'px' // starts the animation
}
img.src = 'http://farm7.static.flickr.com/6095/6301314495_69e6d9eb5c_m.jpg'
(left out a few cross-browser code paths for brevity - onload, forEach, Object.keys)
Try taking advantage of css transforms and requestanimationframe feature.
See the TweenLite library:
http://www.greensock.com/v12/
Related
With three.js I need to get the mesh point wich is nearly equal (within a 'fuzz') to a calculated point. This calculated point is not a THREE.Vector2/3, it's a generic object with x y properties.
The function/method is used by a recursive function and a 'real time' process, that's why it has to be optimized.
The simplest expression I've found is the following. Let's say that it's a 2D point, that I provide an array (geometry.vertices), and there is no need to compute the distance :
function IndexOfPoint ( pt, lst ) {
var i = -1;
var test = lst.some( function ( p ) {
i++;
return ( Math.abs( p.x - pt.x ) < 1E-12 ) && ( Math.abs( p.y - pt.y ) < 1E-12 );
});
return test? i: -1;
}
It works, but I have made this quickly and would like to know if there is a best and fastest method ... (please no additional library for that, only plain js)
Thanks in advance
Looks good for me. You could probably skip the increment on the index var i, since the current index is already provided to the callback as the 2nd argument as in Array.prototype.some( function(element, index, array) { ... } ):
function IndexOfPoint ( pt, lst ) {
var i = -1;
lst.some( function ( p, idx ) {
if ( ( Math.abs( p.x - pt.x ) < 1E-12 ) && ( Math.abs( p.y - pt.y ) < 1E-12 ) ) {
i = idx;
return true;
}
else {
return false;
}
});
return i;
}
I am not familiar with three.js, but if the list is sorted and if the grid is uniform, you might be able to obtain the index of the closest vector immediately and simply test that index. For example:
function IndexOfPoint (pt, list) {
// assuming the list of points is sorted going from left to right, top to bottom
var index = Math.floor((pt.x - offsetX) * scaleX) + gridWidth * Math.floor((pt.y - offsetY) * scaleY);
var candidatePoint = list[index];
if (candidatePoint && (Math.abs(pt.x - candidatePoint.x) < 1E-12) && (Math.abs(pt.y - candidatePoint.y) < 1E-12) {
return index;
} else {
return -1;
}
}
I can't get my head around this problem, so i hope that you guys can help me fixing it.
The thing is that I want to check if an div is overlapping an another div when they are dynamically added to the body.
Let's say that I've got an first div with the following information:
X1 = 316, X2 = 440
This is being calculated with the information that an box has an length of 60px and 1px margin around him. Also, the exL stands for the amount of 'boxes' inside the div ( in this test case it's 2, but it will be many more... ) So the code to calculate that is:
var X2 = ( oldX1 + ( kist_length * exL ) + ( 2 * exL))
I've got this code so far:
// X:
oldX1 = ( 316 );
oldX2 = ( oldX1 + ( kist_length * exL ) + ( 2 * exL));
newX1 = ( 99 );
newX2 = ( newX1 + ( kist_length * newLength ) + ( 2 * newLength));
if( (newX1 >= oldX1 || newX1 <= oldX2) || ( newX2 >= oldX2 || newX2 <= oldX2) ){
console.log("X is overlapping...");
}
If i use the code above the program says it's overlapping, but as you can see below, that's not true. the values that the checker is using are ( + kisten stands for + the length ( so the boxes + length of box * times of boxes ):
newX = 99
newX + kisten = 223
oldX = 316
oldX + kisten = 440
I know that it's because the OR statement. But if I use AND statements between the () inside the if, it's not working 100% to. Let's take a look at the following picture:
First I've placed Box1. After that I've placed Box2, that succeeded without the warning about an overlapping. But X is overlapping...( not the Y, but the X is... ). When i place Box3, i do get the warning that X is overlapping...
So my question is rather simple ( but i guess the answer isn't... ), What do I wrong with the check? Which part of the if statement did i do wrong?
I guess there is a typo in your script, since newX2 >= oldX2 || newX2 <= oldX2 is always true :-)
But the condition in order to not have any overlapping is that (I reason on segments since you are dealing axis by axis here) new segment is before the old one :
newX1----------------newX2 oldX1---------------oldX2
or the second one is after first one :
oldX1---------------oldX2 newX1----------------newX2
So the corresponding condition is the following one :
function overlaps() {
var minOldX = Math.min(oldX1, newX1),
maxOldX = Math.max(oldX1, newX1),
minNewX = Math.min(oldX2, newX2),
maxNewX = Math.max(oldX2, newX2);
return (
maxNewX <= minOldX || // <- first case
minNewX >= maxOldX // <- second case
);
}
I'm looking for a function that would get an element from the DOM and determine whether it is in the sight of the user vertically?
I searched for a function that would get an element and check if it is in the current scroll height the user is viewing. I ended up trying a bunch of functions that didn't quite do what I needed and I built my own. Since I didn't find such function I'm now sharing it with you guys in case someone needs it in future! :P This is without using any frameworks or plugins.
function visible(a, t){
// a => element
// t => tolerance, how much pixels can be hidden and still return true
var w_top = window.pageYOffset || document.documentElement.scrollTop,
w_hgh = window.outerHeight,
a_top = 0,
a_hgh = a.offsetHeight;
while(a.tagName.toLowerCase() !== 'body') {
a_top += a.offsetTop;
a = a.offsetParent;
}
var b = (w_top + w_hgh) - (a_top + a_hgh);
if(b > (0 - t) && b < (w_hgh - a_hgh + t)){
return true;
}
return false;
}
An example in use:
var element = document.getElementById('id');
if(!visible(element, 50)){
element.focus();
}
I was searching up how to fade an element with JavaScript earlier and I came across this function (object). I began wondering how does it work?
var fadeEffect=function(){
return{
init:function(id, flag, target){
this.elem = document.getElementById(id);
clearInterval(this.elem.si);
this.target = target ? target : flag ? 100 : 0;
this.flag = flag || -1;
this.alpha = this.elem.style.opacity ? parseFloat(this.elem.style.opacity) * 100 : 0;
this.si = setInterval(function(){fadeEffect.tween()}, 20);
},
tween:function(){
if(this.alpha == this.target){
clearInterval(this.elem.si);
}else{
var value = Math.round(this.alpha + ((this.target - this.alpha) * .05)) + (1 * this.flag);
this.elem.style.opacity = value / 100;
this.elem.style.filter = 'alpha(opacity=' + value + ')';
this.alpha = value
}
}
}
}();
I know that this is self invoking and only returns one object with two methods. My main concern this why does it use the this keyword? I am assuming the 'this' keyword is a placeholder for the object name "fadeEffect". I would understand if 'this' was used to create multiple objects... but why is it used here?
One other thing bothering me is this ternary operator...
this.target = target ? target : flag ? 100 : 0;
How the heck does that work? It's like two ternary operators combined into one which I never thought was possible?
As for your second question. This will probably make it clearer:
this.target = (target ? target : (flag ? 100 : 0));
So yes, a nested ternary operator! Written out in words:
this.target = (is target a truthy value? Then use target. If not, then use the result from the last part -> (is flag a truthy value? Use 100. Otherwise, use 0)).
Think of it as a namespace. the this keyword refers back to the object literal that the self invoking function returns. This means that this.target is accessible in the global namespace (or whatever scope the fadeEffect was defined) as an object property: fadeEffect.target, but it doesn't interfere with other variables that may exist in the outer scope.
The two methods set new properties of the returned object, that's all there is to it. Personally I find this to be, well, bad code... a closure would have been the better choice in this example:
var fadeEffect=function(){
var elem,target,flag,alpha,si;//make private
return{
init:function(id, flag, target){
elem = document.getElementById(id);
clearInterval(elem.si);
target = target ? target : flag ? 100 : 0;
flag = flag || -1;
alpha = elem.style.opacity ? parseFloat(elem.style.opacity) * 100 :0;
si = setInterval(function(){fadeEffect.tween()}, 20);
},
tween:function(){
if(alpha == target){
clearInterval(si);//this.elem.si doesn't add up, init defines it as this.si
}else{
var value = Math.round(alpha + ((target - alpha) * .05))+ (1 * flag);
elem.style.opacity = value / 100;
elem.style.filter = 'alpha(opacity=' + value + ')';
alpha = value
}
}
}
}();
This does the same thing, but other code cannot interfere with the values of the target, or mess up the interval etc... you're right to say that the this keyword isn't required in this case, but I think the person who wrote this was either unfamiliar with JS closures, or at least insecure about how they work. This code effectively simulates a singleton pattern, or at least treats the object literal as an instance of a class. My guess is, the author is familiar with classical OOP, but not with prototypal inheritance. Anyway, the above code is safer, and safer is better IMHO
On the matter of your nested ternary, I've checked the code below using JSLint, and it suggested an even shorter, yet clearer alternative: use the default operator, followed by a ternary:
//JSLint recommends this
target = argTarget || argFlag ? 100 : 0;
//over nested ternary
target = argTarget ? argTarget : argFlag ? 100 : 0;
Anyway, here's the same code, only not using the dangerous this constructs, but using a closure, one of JavaScripts amazingly powerful features BTW, worth taking a closer look at what you can do with them!
var fadeEffect=(function()
{
var elem,target,flag,alpha,si;//make private
//define private 'methods': functions will be available, but only to return object
//tween shouldn't be callable, it's a callback for the interval, which is set in init
function tween()
{
if(alpha === target)
{
clearInterval(si);//this.elem.si doesn't add up, init defines it as this.si
}
else
{
alpha = Math.round(alpha + ((target - alpha) * 0.05))+ (1 * flag);
//don't know why 1*flag is needed here, suggest:
//alpha = Math.round(alpha + ((target - alpha) * 0.05)) + (+flag); +flag coerces to numeric
elem.style.opacity = alpha / 100;
elem.style.filter = 'alpha(opacity=' + alpha + ')';
}
}
return{
init:function(id, argFlag, argTarget)//arguments !== closure scope
{
if (si !== undefined && si !== null)
{
clearInterval(si);
}
elem = document.getElementById(id);
//JSLint recommends this:
target = argTarget || argFlag ? 100 : 0;
//over nested ternary
target = argTarget ? argTarget : argFlag ? 100 : 0;
flag = argFlag || -1;
alpha = elem.style.opacity ? parseFloat(elem.style.opacity) * 100 :0;
si = setInterval(tween, 20);//just a reference to the tween function will do
}
};
})();
fadeEffect.init('someId',1,50);//will set things in motion
fadeEffect.tween();//undefined
console.log(fadeEffect.target);
fadeEffect.target = document.getElementById('someOtherId');//no problem, but won't change the value of var target
This way, the tween method cannot be called but by the interval, and the element on which the object, and its methods/functions are working their magic can never be overridden by external operations, they are inherent to the object. This makes for a safer construction, what's more, you can only really mess up 1 method: override the .init method, and the object is rendered useless, but harmless. Compare that to your code, where you could mess up both methods, but leave the interval standing... that's bad news: the interval would end up looking for a callback function that could very well have been deleted, causing your code to fail miserably:
//asume your code using this.tween();
fadeEffect.init('id',1,123);
delete fadeEffect.tween;
//inside fadeEffect:
setInterval(function(){fadeEffect.tween()}, 20);
//should be written as:
setInterval(fadeEffect.tween,20);
// === setInterval(undefined,20); === :-(
One more explanation for this.target = target ? target : flag ? 100 : 0;:
if(target){
this.target = target;
}
else{
if(flag){
this.target = 100;
} else {
this.target = 0;
}
}
I've been trying to implement a recursive backtracking maze generation algorithm in javascript. These were done after reading a great series of posts on the topic here
While the recursive version of the algorithm was a no brainer, the iterative equivalent has got me stumped.
I thought I understood the concept, but my implementation clearly produces incorrect results. I've been trying to pin down a bug that might be causing it, but I am beginning to believe that my problems are being caused by a failure in logic, but of course I am not seeing where.
My understanding of the iterative algorithm is as follows:
A stack is created holding representations of cell states.
Each representation holds the coordinates of that particular cell, and a list of directions to access adjacent cells.
While the stack isn't empty iterate through the directions on the top of the stack, testing adjacent cells.
If a valid cell is found place it at the top of the stack and continue with that cell.
Here is my recursive implementation ( note: keydown to step forward ): http://jsbin.com/urilan/14
And here is my iterative implementation ( once again, keydown to step forward ): http://jsbin.com/eyosij/2
Thanks for the help.
edit: I apologize if my question wasn't clear. I will try to further explain my problem.
When running the iterative solution various unexpected behaviors occur. First and foremost, the algorithm doesn't exhaust all available options before backtracking. Rather, it appears to be selecting cells at a random when there is one valid cell left. Overall however, the movement doesn't appear to be random.
var dirs = [ 'N', 'W', 'E', 'S' ];
var XD = { 'N': 0, 'S':0, 'E':1, 'W':-1 };
var YD = { 'N':-1, 'S':1, 'E':0, 'W': 0 };
function genMaze(){
var dirtemp = dirs.slice().slice(); //copies 'dirs' so its not overwritten or altered
var path = []; // stores path traveled.
var stack = [[0,0, shuffle(dirtemp)]]; //Stack of instances. Each subarray in 'stacks' represents a cell
//and its current state. That is, its coordinates, and which adjacent cells have been
//checked. Each time it checks an adjacent cell a direction value is popped from
//from the list
while ( stack.length > 0 ) {
var current = stack[stack.length-1]; // With each iteration focus is to be placed on the newest cell.
var x = current[0], y = current[1], d = current[2];
var sLen = stack.length; // For testing whether there is a newer cell in the stack than the current.
path.push([x,y]); // Store current coordinates in the path
while ( d.length > 0 ) {
if( stack.length != sLen ){ break;}// If there is a newer cell in stack, break and then continue with that cell
else {
var cd = d.pop();
var nx = x + XD[ cd ];
var ny = y + YD[ cd ];
if ( nx >= 0 && ny >= 0 && nx < w && ny < h && !cells[nx][ny] ){
dtemp = dirs.slice().slice();
cells[nx][ny] = 1;
stack.push( [ nx, ny, shuffle(dtemp) ] ); //add new cell to the stack with new list of directions.
// from here the code should break from the loop and start again with this latest addition being considered.
}
}
}
if (current[2].length === 0){stack.pop(); } //if all available directions have been tested, remove from stack
}
return path;
}
I hope that helps clear up the question for you. If it is still missing any substance please let me know.
Thanks again.
I'm not very good in javascript, but I try to implement your recursive code to iterative. You need to store For index on stack also. So code look like:
function genMaze(cx,cy) {
var dirtemp = dirs; //copies 'dirs' so its not overwritten
var path = []; // stores path traveled.
var stack = [[cx, cy, shuffle(dirtemp), 0]]; // we also need to store `for` indexer
while (stack.length > 0) {
var current = stack[stack.length - 1]; // With each iteration focus is to be placed on the newest cell.
var x = current[0], y = current[1], d = current[2], i = current[3];
if (i > d.length) {
stack.pop();
continue;
}
stack[stack.length - 1][3] = i + 1; // for next iteration
path.push([x, y]); // Store current coordinates in the path
cells[x][y] = 1;
var cd = d[i];
var nx = x + XD[cd];
var ny = y + YD[cd];
if (nx >= 0 && ny >= 0 && nx < w && ny < h && !cells[nx][ny]) {
dtemp = dirs;
stack.push([nx, ny, shuffle(dtemp), 0]);
}
}
return path;
}
Does this little code could also help ?
/**
Examples
var sum = tco(function(x, y) {
return y > 0 ? sum(x + 1, y - 1) :
y < 0 ? sum(x - 1, y + 1) :
x
})
sum(20, 100000) // => 100020
**/
function tco(f) {
var value, active = false, accumulated = []
return function accumulator() {
accumulated.push(arguments)
if (!active) {
active = true
while (accumulated.length) value = f.apply(this, accumulated.shift())
active = false
return value
}
}
}
Credits, explanations ans more infos are on github https://gist.github.com/1697037
Is has the benefit to not modifying your code, so it could be applied in other situations too. Hope that helps :)