Jerky/flickering scroll

Could really do with some help on this if possible.

I’m trying to create a scrolling menu. Example here

http://www.pixel-shack.com/critter/ScrollMenu.html

I don’t think there’s an issue with the javascript necessarily. I’ve tried alternatively using jquery’s animate function with scrollLeft and I still get the flickering.

I’m thinking it’s more to do with the HTML/CSS. For instance I’m wondering if using a table instead of a floated list for the menu might be a step in the right direction.

If anyone here could give me some pointers it really would be much appreciated.

Here’s the code. Note it’s far from complete, I’m just focused on the scrolling at this time.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>Gallery</title>
<style type="text/css">
* { /* default styles */
  margin: 0px; 
  padding: 0px; 
  outline: none; 
  list-style: none; 
  border: none;
  font-family: verdana, arial, sans-serif;
  color: #aaa;
  text-decoration: none; }

.centre { margin-left: auto; margin-right: auto; text-align: center; }

h1 { color: #ccc; margin: 1% 0; font: normal 1.2em/1em verdana, arial, sans-serif; }

p { text-align: left; margin: 2% 5%; font: normal 0.85em/1.4em verdana, arial, sans-serif; }

body { background: #151515; }

div#wrapper { width: 1024px; }

div#gallery { 
  background: black; 
  width: 800px; 
  overflow: hidden; } /* clear float */

div#gallery #mainImage { /* Hi-res Gallery Image Container*/
  position: relative; 
  height: 520px; 
  background: black }

div#gallery #mainImage img { /* Hi-res Gallery Image NEED TO CHANGE POSITIONING NOT WORKING IN OPERA */
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  margin: auto; }
  
div#gallery #mainImage #loadImage { opacity: 0.5; margin: 0; top: 495px; left: 60px; } /* waiting gif */

#scrollNav, #scrollNav li, #scrollNav li ul { float:left; }
#scrollNav a { 
  display: block; 
  opacity: 0.3; filter: alpha(opacity=30);
  -webkit-transition: opacity 0.5s linear; }

#scrollNav a:hover { 
  opacity: 1; filter: alpha(opacity=100);
  -webkit-transition: opacity 0.25s linear; }

#scrollWin {
  width: 690px;
  height: 190px; /* 17px for scrollbar */ 
  overflow: hidden; }

#scrollWin ul {
  width: 2484px; /* 18 images = 138 x 18 */
  background: black url("ASSETS/Thumbs/bgCrop.png") center left; }

#scrollWin ul li a{
  width: 120px; height: 160px;
  background: red;
  margin: 15px 9px; }
</style>

<!--<script type="text/javascript" src="../Jquery/jquery-1.3.2.min.js"></script>-->
</head>

<body>
<div id="wrapper" class="centre">
 <div id="gallery" class="centre" > <!-- Note look at the main image area -->
  <div id="mainImage">
  <h1>Tyrannosaurus Rex</h1>
  </div>
  <p id="description">A <strong>Tyrannosaurus</strong> emerges from the forest, startling a small herd of Edmontosaurus, but the large carnivore is not too interested. A small early mammal looks up, invisible to the <em>huge Therapod</em>. Remains of these Late Cretaceous animals are found In the <em>Hell Creek and Morrison</em> formations in America.</p>
  <ul id="scrollNav"> <!-- scroll navigation -->
	<li><a id="previous" class="scrollButton" href="#" ><img src = "ASSETS/Thumbs/Previous.png" alt="previous 5 images" /></a></li>
	<li id="scrollWin"> <!-- a container with overflow settings -->
		<ul id="thumbs">
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/AnkylosaurusEdmonto.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Asteroid.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Diplodocus.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/EarlyMidJurassic.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Edmontosaurus.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/EdmontosaurusDeath.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/HeronPiranha.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/JurassicCretaceous01.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/JurassicCretaceous02.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/LateCretaceous.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Mammoths.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Migration.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Protoceratops.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Triceratops.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Tyrannosaurus.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Protoceratops.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Triceratops.jpg" alt = "" />--></a></li>
			<li><a href = "#"><!--<img src = "ASSETS/Thumbs/Tyrannosaurus.jpg" alt = "" />--></a></li>
		</ul>
	</li>
	<li><a id="next" class="scrollButton" href="#" ><img src = "ASSETS/Thumbs/Next.png" alt="next 5 images" /></a></li>
  </ul>
 </div>
</div>
<script type="text/javascript">
// Just testing...................
var Anim = (function (){
	var _i = 0, _len, _duration = 0, _piHalf = Math.PI/2;
	
	return {
		iD : function (id){return document.getElementById(id)},
		el : null, x0 : 0, x1 : 0, dx : 0,
		timing : null,
		timer : null,
		
		easeInOut : function(){ // compiles an array of distance percentages 0 - 1;
			var eIO = [], val; var output = "";
			for (var currentTime = 0; currentTime < _duration; currentTime+=10){
				eIO.push(((Math.sin(((currentTime/_duration)*2-1)*_piHalf)+1)/2).toFixed(6));
			}
			_len = eIO.length-1;
			return eIO;
		},
				
		go: function(){
			var o = Anim;
			var progress = o.timing[_i+=1];
			o.el.scrollLeft = parseInt(progress * o.dx) + o.x0; // percentage * delta + offset
			if (!(_i < _len)) { clearTimeout(o.timer); o.timer = null; return; }
		},
		
		scroll: function(elem, from, to, fn, duration) {
			var o = Anim;
			if (o.timer) { clearTimeout(o.timer); o.timer = null; } // reset timer
			o.el = o.iD(elem); // get Element by Id
			o.x0 = from; o.x1 = to; o.dx = to - from;
			_duration = duration;
			o.timing = o[fn](); // compile transition
			_len = o.timing.length-1; // set array length
			_i = 0; // reset counter;
			o.timer = setInterval(o.go, 10);
			return false;
		}
	}
})();
Anim.iD("next").onclick = function(){Anim.scroll("scrollWin",0,690,"easeInOut",1000)};
Anim.iD("previous").onclick = function(){Anim.scroll("scrollWin",690,0,"easeInOut",1000)};
//$('#scrollWin').animate({scrollLeft : 690},2000);
</script>

</body>
</html>

RLM

A tabular version with the thumb images. Hasn’t solved the issue.

http://www.pixel-shack.com/critter/ScrollMenuTable.html

RLM

Would be lovely to get some advice or tips on this. I’ve got other questions that I’m not sure about e.g. how best to go about pre-loading the hi-res images and how best to set it up if javascript is switched off. Is there an alternative to just bringing up a new page when you click on the thumb?

Just posting an update, because I think it’s an improvement. http://www.pixel-shack.com/critter/ScrollMenu.html

With javascript switched off the nav buttons are hidden and the scroll-bar is usable instead. Also using a few webkit transitions for safari and chrome.

Scrolling still seems to stutter from time to time in firefox though.


<script type="text/javascript">
var Anim = (function (){
	var _piHalf = -(Math.PI/2);
	
	return {
		iD : function (id){ this.el = document.getElementById(id); return this; },
		
		style : function(prop, set){ this.el.style[prop] = set; return this; },
		
		setClass : function(name){ this.el.className = name; return this; },
		
		addEvent: function (type, handler){
		    if (this.el.addEventListener){
		        this.el.addEventListener(type, handler, false);
			} else if (this.el.attachEvent) {
			    this.el.attachEvent('on'+type, handler);
			} else { this.el['on'+type] = handler; }
			return this;
		},
		
		prevDefault : function(e){
			if(e.returnValue){ e.returnValue = false;
			} else if(e.preventDefault){ e.preventDefault(); }
			return Anim;
		},
		
		easeInOut : function(){ // Compiles an array of distance percentages 0 - 1;
			var o = Anim, eIO = [], val, dur = o.dur, currTime = dur;
			while (currTime>-10) { 
				eIO.push(((Math.sin(((currTime/dur)*2-1)*_piHalf)+1)/2).toFixed(6));
				currTime-=10; }
			return eIO;
		},
				
		goScrollX: function(prop){ // Intend to make this more generic so that it can handle fades etc.
			var o = Anim;
			var progress = o.timing[o.i+=1];
			o.el.scrollLeft = parseInt(progress * o.dx) + o.x0; // percentage * delta + offset
			if (!(o.i < o.len)) { clearTimeout(o.timer); o.timer = null; return; }
		},
		
		scrollX: function(offset, easing, duration, cbFn) { //cbFn will be a callback
			var o = Anim;
			if (o.timer) { return this; } // wait
			o.x0 = o.el.scrollLeft; // current x coordinate
			o.width = o.el.scrollWidth; // overall width
			o.dx = offset;
			switch (true) {
			  case o.x0+offset < 0 : o.x1 = 0; break;
			  case o.x0+offset > o.width : o.x1 = o.width; break;
			  default : o.x1 = o.x0+offset; }
			o.dur = duration;
			o.timing = o[easing](); // compile transition
			o.len = o.timing.length-1; // set array length
			o.i = 0; // reset counter;
			o.timer = setInterval(o.goScrollX, 10);
			return o;
		}
	}
})();

Anim.iD("scrollWin").style("overflow","hidden"); // hide scrollbar
Anim.iD("next").setClass("scrollButton").addEvent("click", function(e){ 
   Anim.prevDefault(e).iD("scrollWin").scrollX(790,"easeInOut",400);}); //scrollX now takes an offset instead
Anim.iD("previous").setClass("scrollButton").addEvent("click", function(e){ 
   Anim.prevDefault(e).iD("scrollWin").scrollX(-790,"easeInOut",400);});
</script>

RLM

A fellow sitepoint member was kind enough to give me a few suggestions, so albeit this maybe a bit naive to some of you I thought I’d share an update.

The usual link http://www.pixel-shack.com/critter/ScrollMenu.html

This was a useful link he pointed me to regarding threading and timers.
http://ejohn.org/blog/how-javascript-timers-work/

Albeit I didn’t come away with a deep understanding of javascript threading, it was enough to give me the idea that maybe I should be swapping out setInterval for setTimeout. Which is what I’ve done.

It certainly seems to have helped.

Regarding the ease in-out equation I found this link was the easiest to interpret.
http://ersinbasaran.blogspot.com/2009/08/using-different-ease-functions-in.html

There’s also an excellent an comprehensive tutorial here
http://robertpenner.com/easing/

The main script. I’m not sure on the pattern/coding. It maybe restrictive if I want to have multiple events firing off like fades and scrolls, but that’s another issue.

var Anim = (function (){
	var _piHalf = -(Math.PI/2);	// _piHalf is used in our easeInOut equation.
	var _step = 10; 			// duration used for setTimeout and calculations
	var _i = 0;					// counter
	var _tLen = 0; 				// timing array length
	
	return {
		
		// simple shortcut for getElementById. Result is stored in this.el
		iD : function (id){ this.el = document.getElementById(id); return this; },
		
		// simple set style method. works with the current element in this.el
		style : function(prop, set){ this.el.style[prop] = set; return this; },
		
		// simple set class method. works with the current element in this.el
		setClass : function(name){ this.el.className = name; return this; },
		
		// cross browser event handler
		addEvent: function (type, handler){
			var o = this;
		    if (o.el.addEventListener){ o.el.addEventListener(type, handler, false); } 
			else if (o.el.attachEvent) { o.el.attachEvent('on'+type, handler); } 
			else { o.el['on'+type] = handler; }
			return o;
		},
		
		prevDefault : function(e){
			if(e.returnValue){ e.returnValue = false; } 
			else if(e.preventDefault){ e.preventDefault(); }
			return Anim;
		},
		
		// Compiles an array of percentages to be used by scrollX;
		// Working with radians, our sine curve formula is the sine of -pi/2 to pi/2
		// The following link provides a useful reference
		// http://ersinbasaran.blogspot.com/2009/08/using-different-ease-functions-in.html
		
		sineInOut : function(){
			var o = this, dur = o.dur, time = dur, percent, tArr = o.timing = [];
			while (time>-_step) {
				percent = ((Math.sin(((time/dur)*2-1)*_piHalf)+1)/2).toFixed(6);
				tArr.push(percent); time-=_step; 
			}
			return tArr.length;
		},
		
		goScrollX: function(){
			var o = Anim; // this
			// scrollLeft = parseInt( percentage * delta ) + offset
			o.el.scrollLeft = parseInt(o.timing[_i+=1] * o.deltaX)+ o.x0;
			// if the end of the timing array is reached cleartimers and return or invoke callback.
			if (_i == _tLen) { clearTimeout(o.tr); o.timer = null; return (o.Fn)? o.Fn() : o; }
			o.timer = setTimeout(o.goScrollX, _step);
		},
		
		scrollX: function(offset, easing, duration, cbFn) {
			var o = Anim;
			if (o.timer) { return o; } 		// if mid scroll return
			o.x0 = o.el.scrollLeft; 		// current x coordinate of scroll
			o.width = o.el.scrollWidth; 	// total width of our scroll content
			o.deltaX = offset; 				// amount to scroll by
			o.Fn = cbFn || null; 			// a reference to our call back function.
			o.dur = duration;
			_tLen = o[easing]()-1; 			// compile array and get the returned length
			_i = 0; 						// reset counter;
			o.goScrollX();
			return o;
		}
	}
})();

Setting up the events onload.


Anim.iD("scrollWin").style("overflow","hidden"); 	// hide scrollbar

// --------------------------- scroll left
Anim.iD("next").setClass("visible"). 			// make the next navigation button visible
	addEvent("click", function(e){ 
		Anim.prevDefault(e).
			// scrollX( offset to scroll by, timing function, duration, optional callback )
			iD("scrollWin").scrollX(790,"sineInOut",400,
				// A callback function to switch on and off previous and next buttons
				function(){ 
					var o = Anim, el = o.el;
					o.iD("previous").setClass("visible");
					// A simple condition to check whether we've reached the end of the line.
					if (el.scrollLeft+o.deltaX >= o.width) { o.iD("next").setClass("hidden"); }					
				}
			)
		}
	);
// --------------------------- scroll right
Anim.iD("previous").
	addEvent("click", function(e){ 
		Anim.prevDefault(e).
			iD("scrollWin").scrollX(-790,"sineInOut",400, 
				function(){
					var o = Anim, el = o.el;
					o.iD("next").setClass("visible");
					if (el.scrollLeft<= 0) { o.iD("previous").setClass("hidden"); }
				}
			)
		}
	);

RLM

I can see the jQuery inspiration in how you’ve designed your script. :slight_smile: The scrolling happens without any flickering or jerkiness.

With the way you’ve designed your code, you will have issues adding fading stuff at the same time as the scrolling, unless you incorporate the fading as an option for the animation. This is what jQuery does. But I don’t see the need for any fading as the scrolling seems to work fine. I’d imagine any fading would probably be for the swapping of the main image.

Cheers for the comments Raffles appreciated.

As mentioned I’ve have webkit handling some of the fades for google and safari. That’ll do a better job of it than I will.

As for further modifications though, I’ve got a bit of reading up to do tonight I think.:slight_smile:

RLM

Ok, I think I can smell it now. I very rarely got either a stutter, or the animation would simply not happen, or even move but a few pixels and fail to complete the rest. I added some logging.


        goScrollX: function(){
            var o = Anim; // this
            // scrollLeft = parseInt( percentage * delta ) + offset
            //var calc = parseInt(o.timing[_i+=1] * o.deltaX)+ o.x0;
            var calc = parseInt(o.timing[_i+=1] * o.deltaX)+ o.x0;
            o.el.scrollLeft = calc;
            var now = (new Date).getTime();
            data.push( [now, o.el.scrollLeft, calc] );
            // if the end of the timing array is reached cleartimers and return or invoke callback.
            if (_i == _tLen) { clearTimeout(o.tr); o.timer = null; return (o.Fn)? o.Fn() : o; }
            o.timer = setTimeout(o.goScrollX, _step);
        },

I refresh the page and click an arrow to initiate the slide. Usually worked perfect, but very rarely, it would show its ugly side. Inspecting the contents of my data array I found that when the animations fails, the logged values looked like


1266452496667,2370,2375
1266452496689,2370,2393
1266452496711,2370,2421
1266452496733,2370,2460
1266452496755,2370,2508
1266452496777,2370,2563
1266452496799,2370,2625
1266452496821,2370,2690
1266452496843,2370,2758
1266452496865,2370,2826
1266452496887,2370,2892
1266452496909,2370,2955
1266452496931,2370,3011
1266452496953,2370,3061
1266452496975,2370,3101
1266452496997,2370,3132
1266452497019,2370,3151
1266452497041,2370,3159
1266452497063,2370,3156

Btw- I used a step of 22 for the above data. You can see reading o.el.scrollLeft didn’t return the value of calc that I assigned to it on the previous line. WTF? I know I’ve heard that assigning a value to a dom property doesn’t cause the browser to immediately render the change visually, but I thought it would hold the value and render when the painting thread “gets around to it”. The last frame of the animation never got painted when it failed, so I don’t think it was some issue with where it was trying to play catch up.

It happened so rarely that it’s hard for me to tell it was coincidence or not, but it seemed like changing the value of the step variable, and then refreshing the page made the “bug” more likely to surface. I thought maybe it was something to do with the recent tracemonkey improvements, so in about:config I disabled javascript.options.jit.content but it still happened.

*using FF 3.5.7

Very interesting findings crmalibu, thank you very much.

I think that logging method will prove very useful.

So it looks like another process is taking priority over my method with regards accessing the scrollLeft property.

A couple of thoughts. Rather than using onload, I’m placing my onload script before the end body tag. I wonder if that plays a part, when refreshing. I’ll look into that.

Secondly. Maybe I need to add some sort of conditional statement, which checks for something in the Dom before it executes.

It’s almost like you could do with something similar to css i.e. o.el.scrollLeft = calc !important;

I’ll work with your logging method and see what I can find.

Thanks again.

RLM

What is it about 2370?

Coming up with the same results. Step set to 22

1266482002175 - 2263 - 2263
1266482002190 - 2303 - 2303
1266482002206 - 2334 - 2334
1266482002221 - 2353 - 2353
1266482002237 - 2361 - 2361
1266482002268 - 2358 - 2358
1266482002557 - 2363 - 2363
1266482002581 - 2370 - 2381
1266482002612 - 2370 - 2409
1266482002643 - 2370 - 2448
1266482002674 - 2370 - 2496
1266482002706 - 2370 - 2551
1266482002737 - 2370 - 2613
1266482002768 - 2370 - 2678
1266482002800 - 2370 - 2746
1266482002831 - 2370 - 2814
1266482002862 - 2370 - 2880
1266482002893 - 2370 - 2943
1266482002909 - 2370 - 2999
1266482002940 - 2370 - 3049
1266482002956 - 2370 - 3089
1266482002987 - 2370 - 3120
1266482003005 - 2370 - 3139
1266482003034 - 2370 - 3147
log: 1266482003050 - 2370 - 3144 

Edit: oh, I’ve just clicked. It’s reached the end of the scrollbar that’s all. So albeit calc is coming up with values it’s not possible to set them

RLM

Ah that was it. Looks like the position of it doesn’t reset with an f5 reload, I need to hit enter in the address bar.

But I don’t see the need for any fading

You have a point. Incorporating the fade has opened up a whole can of worms.lol

It’s certainly been a decent learning exercise though.

An example. http://www.pixel-shack.com/critter/ScrollMenu6.html . There’s a silly rollover on the main image, purely for testing.

Setting up is pretty simple

Anim8.iD("Edmond").
  addEvent("mouseover", function(){ 
    this.fade(1, "slow", "sineInOut")
      }).addEvent("mouseout", function(){ 
          this.fade(0.3, "slow", "sineInOut")
        })

Probably a bit too much to trawl through, but if on the slim chance someone has got time on their hands and doesn’t mind pointing out any obvious bloopers it would be appreciated.

var Anim8 = (function (){
  
// ----------------- PRIVATE PROPERTIES ------------------------

  var _piHalf = -(Math.PI/2),  		// _piHalf is used in our easeInOut equation.
  _step = 10,       				// time used for setTimeout and calculations
  _ie = (window.attachEvent), 		// Internet Explorer flag.
  _moz = (window.addEventListener), // Mozilla flag.
  _defaultView = document.defaultView || {}; 

// ----------------- Regular Expressions -----------------------

  var _decimal = /(\\d+(?:\\.\\d+)?)/,
  _offsetChk = /^(\\-(?=\\-)|\\+(?=\\+))?([-\\+]?\\d+(?:\\.\\d+)?)/,
  _speeds = /slow|medium|fast/i,
  _notPixels = /^scroll|^offset|opacity/,
  _notStyle = /^scroll|^offset/;

// ------------------- PRIVATE METHODS --------------------------
  
// ------------------------- Bind -------------------------------

  var _bind = function (scope, fn){
    return function(){ fn.apply(scope, arguments); };
  },
  
// ------------------ Instance/Clone Method ---------------------      

  _clone = function(obj){
    var F = function(){}; F.prototype = obj;
    return new F;
  },

// --------------------- Copy Properties ------------------------  
  
  _copyProps = function(from, to){
    for (var prop in from){
	  if (from.hasOwnProperty(prop) && !to[prop]){ to[prop] = from[prop]; }	  
	}
  },

// ------------------------- IsStyle ----------------------------

  _isStyle = function (propName) { return !(_notStyle.test(propName)) },

// ----------------------- Filter Number ------------------------

  _filterNum = function (num) {
    return String(num).replace(_decimal, function (all, num) {
      return parseFloat(num)*100; }); 
  },
  
// ---------------------- Timing Formulas -----------------------

  _easing = {
    sineInOut : function(duration){
      var time = duration, tArr = [];
      while (time>-_step) {
        tArr.push(((Math.sin(((time/duration)*2-1)*_piHalf)+1)/2).
		  toFixed(6)); time-=_step;         
      }
    return tArr;
    }
  },

// ----------------------- Cached Timings ------------------------

  _preSets = (function(){    
    var speed = {};
    for (var formula in _easing){
	  speed[formula] = {
	    slow : _easing[formula](600), 
		medium : _easing[formula](400),
	    fast : _easing[formula](200)
      };
	}
    return speed; 
  })();

// ------------------------ Delta method --------------------------

  var _delta = function (to, x0){
    var to = String(to).match(_offsetChk);
    return (to[1]) 					// true for -- or ++ match
        ? parseFloat(to[2])			// relative position
        : parseFloat(to[2]) - x0;	// absolute position - start point
  };

// --------------------------- ANIM8 METHODS -------------------------------

  return {
    // creates an element instance linked to Anim 8 which the rest of the chain is bound to.
    iD : function (id){ 
	  var o = _clone(Anim8); this.el = o.el = document.getElementById(id); 
	  return o; 
	},
    
    style : function(prop, set){ this.el.style[prop] = set; return this; },
    
    setClass : function(name){ this.el.className = name; return this; },

  // ---------------------- Get Computed Style -----------------------------

    getStyle : function(styleType) {
      var o = this;
      var styleVal = (_ie)
        ? o.el.currentStyle[styleType]
        : _defaultView.getComputedStyle (o.el,null).getPropertyValue(styleType);
      return styleVal;
    },
  // ------------------------ Opacity Method --------------------------------  
	// Gets the current opacity or filter and makes properties to add to anim
    opacity : function() {
	  var o = this, alpha;
	  return propName = (_ie)
		? {
		    prop : "filter",
		    x0: ((alpha = o.getStyle("filter")) == "")
			  ? 100 : parseFloat(alpha.match(_decimal)[1]), // Strips out 'alpha(opacity=' ')'
			prefix : "alpha(opacity=", suffix : ")" 
		}
		: {
		    prop : "opacity",
		    x0: parseFloat(o.getStyle("opacity")),
			prefix : "", suffix : ""
		};				
	},
    
  // ------------------------- Event Handler -------------------------------- 

    addEvent: function (type, handler){
      var o = this;
	  if (_moz){ o.el.addEventListener(type, _bind(o, handler), false); } 
      else if (_ie) { o.el.attachEvent('on'+type, _bind(o, handler)); } 
      else { o.el['on'+type] = _bind(o, handler); }
      return o;
    },
    
    // ------------------- Event Prevent Default ---------------------------- 

    prevDefault : function(e){
	  if (_ie) { e.returnValue = false; }
	  else { e.preventDefault(); }
      return this;
    },
	
    // ------------ Constructor function for the anim method ----------------

    animInit : function(propName, to, time, eaFn, cbFn){
	  var o = this, anim, speed, oP; 
	  var isStyle = _isStyle(propName);
	  var anim = _clone(o); // creates an instance of anim linked to this
	  anim.elStyle = (isStyle) ? o.el.style : o.el; //e.g. el.scrollLeft or el.style.opacity
	  if (propName === "opacity") { 
		_copyProps(o.opacity(), anim); 
		if (_ie) to = _filterNum(to);
	  } else {
		anim.prop = propName;
        anim.x0 = (isStyle) ? o.getStyle(propName) : o.el[propName] ;		    
		anim.prefix = "";
        anim.suffix = (_notPixels.test(propName))? "" : "px";
	  }
	  anim.delta = _delta(to, anim.x0)  ;  
	  anim.timer = null; 				// new timer
	  //if duration is 'slow','medium','fast' use the cached timings array
	  speed = (typeof time === "string") ? time.match(_speeds) : false;
	  anim.timing = (speed) ? _preSets[eaFn][speed] : _easing[eaFn](time);
	  anim.tLen = anim.timing.length-1;
      anim.i = 0; 						// counter;
	  anim.Fn = cbFn || null;  			// callback function
	  return anim;  
    },

	// ---------------------------- Animate --------------------------------
	
    anim: function(){
        var o = this;
	    o.elStyle[o.prop] = o.prefix + (parseFloat(o.timing[o.i+=1] * o.delta)+ o.x0) + o.suffix;
        if (o.i == o.tLen) { clearTimeout(o.timer); o.timer = null; return (o.Fn)? o.Fn() : o; }
        o.timer = setTimeout(function(){o.anim()}, _step);
    },

    // ---------------------------- Scroll ---------------------------------
    scrollX: function(to, time, eaFn, cbFn) {
        var o = this;
        o.anim.call(o.animInit("scrollLeft", to, time, eaFn, cbFn), null);
	    return o;
    },

	// ----------------------------- Fade ----------------------------------

    fade : function(to, time, eaFn, cbFn){
	    var o = this;
	    o.anim.call(o.animInit("opacity", to, time, eaFn, cbFn), null);
	    return o;
    }
  }
})();