homepage Welcome to WebmasterWorld Guest from 54.166.66.204
register, free tools, login, search, pro membership, help, library, announcements, recent posts, open posts,
Become a Pro Member

Visit PubCon.com
Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
Forum Library, Charter, Moderator: open

JavaScript and AJAX Forum

    
help needed with slideshow
slideshow, animation
jerry95




msg:4296610
 3:09 am on Apr 13, 2011 (gmt 0)

Hello everyone, I'd like that if you may answer if it is even possible to create a slideshow that animates images position without using jquery. I'm trying to understand the use of setInterval cause it's very interesting how people animate things inside the client window. But I haven't found any tutorial on how to animate a slideshow so I made my very first attempt at something. But I seriously doubt that it will work given the fact that I'm a rookie. Would you please suggest what may be wrong with the code that follows and how to achieve an animation of a series of images that change their position, and, if a certain amount is reached, they animate back into position. Thank you very much in advance for your patience.
The code I wrote is this:

//Javascript

if(document.images) {
img1= document.getElementById ('image1');
img2= document.getElementById ('image2');//etc.
var o = {}
o.content=document.getElementById('containerdiv');
o.width = o.offsetwidth;
count=0}
function slide (speed, imgnumber) {
o.style.left+=10;
count+=10; setInterval ('slide()', speed);
if (o.count >= imgnumber.offsetwidth) {if (o.style.left !=o.width) {clearInterval ('slide()');
return count=0;}
}
else {clearInterval ('slide()'); o.style.left -= o.width;}
}

//and call the function from an anchor on the image or elsewhere using <a href=javascript:slide(speed,this).

 

Skier88




msg:4297004
 5:54 pm on Apr 13, 2011 (gmt 0)

I'm sorry, but I need to say this: anything that is possible with jQuery is possible without it. jQuery just makes coding easier.

As for your problem, I'm not 100% sure what you want. You are calling it a slideshow, but saying the slides will progress via links. So I'm going to assume that you want an automatically progressing slideshow that also lets users jump to a different slide by clicking a link, even though your current code does not indicate this.

I'm not sure why you test for document.images, or why you declare
o the way you do. As for grabbing the images, I would suggest creating a more flexible method, but if you are set on using scrolling as a transition you don't even need to access them, just the container.

For an animated slideshow you need two intervals, one which progresses the slides and another which animates the progression. You function addresses the latter. Additionally, clearInterval is used with the return value of setInterval. So to clear the slide() interval you would do something like this:

var interval=setInterval('slide()',15);
[...]
clearInterval(interval);


While that works, putting the first parameter in quotes is considered bad practice. It forces the browser to compile the string at runtime, which can take 100 times as long as directly feeding it the function, like this:

var interval=setInterval(slide,15);


Additionally, element.style.left will return a full distance value, like "100px", so if, for example, o was set to 100 pixels from the left, writing "o.style.left+=10" would try to set o.style.left to "100px10". Since it is costly to constantly extract the number you are better off using your own variable, count. So at every update, write something like:

count+=10;
o.style.left=count+'px';


Putting all that together with a bit of structural improvement, I've written an example for you to work with. This example uses 8 240x180 images [imgur.com].

HTML
<div id='slideshow'><div>
<img src='img1.jpg'/>
<img src='img2.jpg'/>
<img src='img3.jpg'/>
<img src='img4.jpg'/>
<img src='img5.jpg'/>
<img src='img6.jpg'/>
<img src='img7.jpg'/>
<img src='img8.jpg'/>
</div></div>
<a href='javascript:goTo(0); void 0;'>slide 0</a>
<a href='javascript:goTo(1); void 0;'>slide 1</a>
<a href='javascript:goTo(2); void 0;'>slide 2</a>
<a href='javascript:goTo(3); void 0;'>slide 3</a>
<a href='javascript:goTo(4); void 0;'>slide 4</a>
<a href='javascript:goTo(5); void 0;'>slide 5</a>
<a href='javascript:goTo(6); void 0;'>slide 6</a>
<a href='javascript:goTo(7); void 0;'>slide 7</a>


CSS
#slideshow {width:240px; height:180px; overflow:hidden;}
#slideshow div {width:10000px; position:relative;}
#slideshow img {float:left;}


Javascript
//set speed variables
var animSpeed=1000;
var tickSpeed=3000;

//These values are hard-coded only as an example
var w=240,len=8;

//global variables
var container=document.getElementById('slideshow').childNodes[0],
animate,slideshow,slide=0,x=0;

//moves the slideshow to slide # i
function goTo(i) {
// if the animation is already running (due to
// a user's click) we don't want to run two both
// animations at once
clearInterval(animate);

// if the user changes the slide they should get
// as much time on that slide as if the script
// changed it
clearInterval(slideshow);

// position to move from
var _x=x;

// animation is based on time so it doesn't take too
// long on slow computers
var start=new Date().getTime();

// we don't need to name the function, but we do need
// to store the return value so it can be cancelled
animate=setInterval(function() {
// calculate p=progress
var p=(new Date().getTime()-start)/animSpeed;

// if progress>=100%, stop animating
if(p>=1) {
p=1;
clearInterval(animate);
}

// optional easing function
p=1-Math.pow(1-p,2);

//calculate new position
x=_x+p*(i*w-_x);

//apply position to slideshow
container.style.right=(x|0)+'px';
},15);

// restart the automatic progression
slideshow=setInterval(function() {
if(++slide==len)
slide=0;
goTo(slide);
},tickSpeed);
}
goTo(0);

jerry95




msg:4297338
 3:27 am on Apr 14, 2011 (gmt 0)

Thanks a lot, skier for the time and your offer to help me, but I hate to tell you, it doesn't work. I've seen it in Opera, Firefox and IE8 and it doesn't do its work. The layout is perfect, I only changed the image size to 340 x 255 px, however. I must tell you I don't understand entirely what's going on, so I thought I'd write a new function. I would appreciate if you read the comments I made on both functions, yours and mine, and be kind to answer me.

My function:


//I eliminated almost all the timing and I don't call the //function twice, but still I don't think it will work, //what may be missing?
var animateSpeed =1000;
var tickSpeed = 8000;
var w = 340; len = 8;
var container=document.getElementById('slideshow').childNodes[0];
slide = 0, x = 0;
count = 0;
function goTo (slide)
{animate = setInterval (function {var_x = x;
x = slide*w-x; container.style.right = (x|0) + "px";
}, 16); count++; if (count == len) {clearInterval(animate);}
};
goTo(0);


Lastly, yours, why could it be not working? I checked the source in Dreamweaver CS5 and there are no syntax errors, also I followed your layout to the letter, I only changed the width and height of both divs, added a wrapper div to prevent changing the width property of the body, and made minor css changes to the links:

//Javascript
/* I understand that you call the internal clock of the computer only to have a shorter measure of the progress, which would equal 1 when the difference of time generates a number equal to the hundred percent, I also reckon this is an arbitrary measure of yours.
Not that I mind, though. However I don't understand what's going on: two functions are called simultaneously, and the second one, slideshow, is called only to measure how many times you have called it, from there the ++len? I think the slideshow function works as an end to the whole progression, but can't figure why. Obviously if I put the last line into the braces, the computer alerted me I was out of memory, because I'm calling the function over and over, isn't it? */


var animSpeed=1000;

var tickSpeed=8000;

//desired time to lapse

var w=340; len=8 //8 images 340 px wide

//global vars

var container=document.getElementById('slideshow').childNodes[0], animate,slideshow, slide=0, x=0;



//move to the desired slide

//from here the link can do its work, why the void value? (:

function goTo(i) {



//clear the inner interval, we don't want two animations running at once

clearInterval(animate);

//As you said the function must take the same time if the user had called it, I understand otherwise you'd stop one calling to the function and not both.

clearInterval(slideshow);

//initial pos

var _x = x;

var start=new Date().getTime();

//It isn't necessary to name the function but to store it so the interval can be cancelled

animate = setInterval(function() {

//p = progress

var p = (new Date().getTime()-start)/animateSpeed;

//si es 100% detener la animaci髇

if (p>=1) {

p=1; //safer to return this value to use less resources?

clearInterval(animate);}

//optional easing function

p=1-Math.pow(1-p, 2);

//new position

x = _x+p(i*w-_x); /*ere is where the div is animated, simply moving it n times (the number of slides multiplied by their width) to the right. But how does the interval work, simply doing it so quickly that you can't tell it's computing?*/

//Am I animating the div or the images, I figure out it's the div.

//Apply the position

container.style.right=(x|0)+"px";

}, 15); //shorter perceived interval in miliseconds



//What happens here with the whole of the multitude?

slideshow=setInterval(function() {

if (++slide==len)

slide=0;

goTo(slide);

}, tickSpeed);

}

goTo(0); //Why is it necessary to call the function from here, where am I

//calling the function, here or above? I reckon this is the function that calls when the page loads, the rest is called from the links, isn't it?




Skier88




msg:4297780
 6:52 pm on Apr 14, 2011 (gmt 0)

I tested my code in the latest version of each of the big 5 on Windows XP (so latest IE is 8 not 9), so I know it can work. I suspect the problem came from resizing and repositioning the entire slideshow, as the CSS is a little tricky and some of it must be hardcoded into the script. It would be helpful if you would tell me exactly how it is failing to work - is it doing nothing? going to the wrong position? etc.

The function goTo goes to a specified slide. If you want to automatically progress slides then you have to do it outside of that function.

At the end of your function you have:
if (count == len) {
clearInterval(animate);
}
I'm not sure why you are pairing that condition with the clearInterval statement. The condition determines if you are on the last slide; to wrap around you would put "count=0;" in the brackets. The statement stops the current animation. This should be done inside the function called by setInterval, and surrounded by a condition to check if the animation is done. I had a variable "p", so I could know that if p>=1 then I should stop the animation. If you just want to increment x then your test could be "if(x>=slide*w)", since slide*w yields the x position you are moving towards.

_x is an artifact of my time-based function; it is not necessary in yours.

Also, I'm not sure how you're trying to calculate the current x position. slide*w give you the x position you are moving towards and x is the current position; if you want to move forward 10 pixels every step just write "x += 10".

Lastly, there are a few syntax errors, namely in the global variable declaration. Nothing that would keep the code from running, but it's good to always declare new variables with "var". Also, a comma separates variables when declaring multiple variables at the same time. ie, "var a=0; var b;" is equivalent to "var a=0,b;".

I know that's a lot of comments, but your code is actually a lot closer to working than it was the first time around. The structure at least is much better. Anyway, I put all that together in a revised version of your function - this should work as long as the user doesn't click a link while an animation is occurring.

var w = 340;
var container=document.getElementById('slideshow').childNodes[0];
var count = 0, x = 0;
function goTo (slide) {
var animate = setInterval (function {
x+=10; // if you want to be able to go backwards you need to change this
if(x>=slide*w) {
clearInterval(animate);
}
container.style.right = (x|0) + "px";
}, 16);
}
// this is where you would increment "count"
// (I do it in the "slideshow" interval)


As for my code ...

Using the clock for animation is a fairly common method, although not the simplest. There are two reasons I use the internal clock; the first is to deal with slower computers. Imagine a computer that can only render this animation at 15 frames per second. Using the clock method the animation will still take 1 second to complete, but be choppier. Using the increment method and the values in my example, the animation will take over 6 seconds to complete and be equally choppy. Using the clock makes the script feel more responsive on slower computers. Secondly, using the clock makes it easier to determine the overall progress, which makes using easing functions simpler.

The slideshow function doesn't measure anything, it increments the current slide and resets it to 0 if it gets too high. This is the statement that says "after 8 seconds show the next slide". The lack of clarity is my fault; I like to write compact code, while what I should be doing, now anyways, is writing clear code. This does exactly the same thing as the current slideshow function:
slideshow=setInterval(function() {
slide=slide+1;
if (slide==len) //if we're past the last slide
slide=0;
goTo(slide);
}, tickSpeed);

You're right about the last line. The progression of slides is called in the above code as well as from the links; the last goTo(0) is called only to start the slideshow when the page loads. Really all it has to do is call the code directly above, but by calling goTo(0) I can avoid re-writing code, and the 0 makes the animation invisible since we're already at 0.

The explanatory comments are correct. I suppose in this case "void 0;" isn't necessary; I put it there out of habit. I'm not sure on the specifics, but basically some functions called via a url can cause the entire page to be cleared, so it's safer to make sure you're always returning null.

p=1; //safer to return this value to use less resources?

My concern isn't resources but rounding. Imagine that the interval only manages to execute every .3 seconds. It will continue at .3s, .6s, and .9s, then stop at 1.2s. However, if you don't set it to 1 after stopping the animation then the final position displayed will be 120% of the difference you want. While errors that large are rare, it is easy to be off by a pixel or two.

x = _x+p(i*w-_x); /*ere is where the div is animated, simply moving it n times (the number of slides multiplied by their width) to the right. But how does the interval work, simply doing it so quickly that you can't tell it's computing?*/

This code is for a single frame; what it does is, given an initial value _x, destination value i*w, and progress value p, calculate what the current x position should be. Also, this could be why my example isn't working for you: you changed "p*(i*w-_x)" to "p(i*w-_x)" - javascript requires the multiplication sign.

Am I animating the div or the images, I figure out it's the div.

You're changing the offset of the inner div. The images are lined up side by side, filmstip style, inside the inner div. By moving the div you move all the images. Also, .right sets the offset from the right side. So increasing it moves the div to the left, not right.

What happens here with the whole of the multitude?

I can't understand what you're asking. See above for an explanation of the code after that comment.

jerry95




msg:4303196
 4:35 am on Apr 24, 2011 (gmt 0)

Hi Skier and thanks again for writing. So, I figure out now that the function goTo is called by the links only to move to the slide in question. Then the slideshow function is used to automatically progress the slideshow, forgive me for the repetition and lack of clear formulation here. If the number of slides is complete, the function calls the previous goTo function to "rewind" the slideshow. Forgive me for writing that much in the previous post but I wanted to get it straight. Also, the phrase about what happened to the "multitude" was pitifully ambiguous, but I thought that if I was figuring out right what I wrote about two functions going on simultaneusly, one measuring the other, etc., it would be understandable in the context of what I was thinking. Anyway, I tried correcting the multiplication operator in the code actually moving the position at the part: x=_x+p*(i*w-_x) where I have to wonder how do you get the actual value of p with the easing function, but nevermind, I have to go through my reference of the Math.pow function, and then understand how you're substracting the value of _x which is actually x at the start and so and so.
Well now to the point, I've tried that and also tried with the function you kinly revised for me. But It's not working with neither of them. It doesn't work in the sense that it doesn't move in any direction and it doesn't move either when I click the links. I'll post the markup code that I'm using, which I changed many times and I even made the mistake previously of using classes instead of id's, but the markup now is very similar to what you suggested and also the CSS.
The HTML is:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- saved from url=(0014)about:internet -->
<html>
<head>

<meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
<title>Decoration page</title>
<link rel="stylesheet" href="slideshow.css" type="text/css">
<script src="scripts/diapositivas.js" type="text/javascript"></script>
</head>
<body style="margin-left: 8px;">

<div id="slideshow">
<div>
<img src="activos/portfoliodeco1.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco2.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco3.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco4.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco5.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco6.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco7.jpg" alt="dise帽o de decoraci贸n">
<img src="activos/portfoliodeco8.jpg" alt="dise帽o de decoraci贸n"></div>
</div>

<div class="go">
<a href="javascript:goTo(0); void 0;">diapositiva 0</a>
<a href="javascript:goTo(1); void 0;">diapositiva 1</a>
<a href="javascript:goTo(2); void 0;">diapositiva 2</a>
<a href="javascript:goTo(3); void 0;">diapositiva 3</a>
<a href="javascript:goTo(4); void 0;">diapositiva 4</a>
<a href="javascript:goTo(5); void 0;">diapositiva 5</a>
<a href="javascript:goTo(6); void 0;">diapositiva 6</a>
<a href="javascript:goTo(7); void 0;">diapositiva 7</a>
</div>

</body></html>


And the CSS is:

/*This is only a reset stylesheet*/
* { margin: 0; padding: 0; text-decoration: none; font-size: 1em; outline: none; }
code, kbd, samp, pre, tt, var, textarea, input, select, isindex, listing, xmp, plaintext { font: inherit; font-size: 1em; white-space: normal; }
dfn, i, cite, var, address, em { font-style: normal; }
th, b, strong, h1, h2, h3, h4, h5, h6 { font-weight: normal; }
a, img, a img, iframe, form, fieldset, abbr, acronym, object, applet, table { border: none; }
table { border-collapse: collapse; border-spacing: 0; }
caption, th, td, center { text-align: left; vertical-align: top; }
body {line-height: 1;}
q { quotes: "" ""; }
ul, ol, dir, menu { list-style: none; }
sub, sup { vertical-align: baseline; }
a { color: inherit; }
hr { display: none; } /* we don't need a visual hr in layout */
font { color: inherit !important; font: inherit !important; color: inherit !important; } /* disables some nasty font attributes in standard browsers */
blink { text-decoration: none; }
nobr { white-space: normal; }

/*comienza hoja de estilo personalizada */
/*Personalized stylesheet begins*/
#slideshow {width:340px; height:275px;overflow:hidden;}
#slideshow img {float:left;}
.descrip {background: #CCCCCC; filter:alpha(opacity=60)
opacity:0.6; width:360px; line-height:150px; height:auto; color: #333333; font-family:Verdana, Arial, Helvetica, sans-serif; font-size:small; z-index:200; position:relative; bottom:10px;
text-align:center; margin:0;}
#slideshow div {width:10000px; position:relative;}
a {text-align:center; line-height:30px;
background:#CC3300;width:200px; padding:5px; text-decoration:none; color:#FFFFFF; font-family:Verdana, Arial, Helvetica, sans-serif; font-size:small}
.footer p {font-size:x-small; text-align:center; font-family:Arial, Helvetica, sans-serif;}



I am putting the script in the head section, and don't know if thats OK, but I wouldn't know if it could be elswhere, and exactly where?

Also, I have to say that though I didn't like it, specially because it breaks my head down to realize where the functions end in Jquery with all the syntax of selectors and so, I have already managed to get it to work with Jquery, doing a little research. I appreciate your help anyway, and would like to get it to work if you don't mind, just to figure out this much simpler way of doing it than with Jquery.

jerry95




msg:4303197
 4:37 am on Apr 24, 2011 (gmt 0)

Sorry Skier I forgot to mention what I think I should do with the js you revised so kindly, and in short I hope the comments explain it, this is what I'm using:


//Javascript
var w=340;
var container=document.getElementById("slideshow").childnodes[0];
var count=0, x=0;
function goTo(slide) { // I guess all I need to do to prevent it from breaking down while its running if the user clicks a link in the meantime is include a clearInterval function to stop both intervals now
// clearInterval(animate); and
//clearInterval(slideshow);
var animate=setInterval(function() {x+=10;
if(x>=slide*w){clearInterval(animate)} container.style.right=(x|0)+"px" }, 16);
} // Here I should increase the count and include a function that progresses automatically, like slideshow();

Skier88




msg:4304935
 6:18 pm on Apr 27, 2011 (gmt 0)

No problem - the more you write, the clearer the problems become. You're correct in your understanding of the script - in fact, there are only a few simple errors left.

Scripts are executed as they are parsed, so by placing this script in the head you force the browser to execute the entire script before it starts displaying the page. The problem with that (besides increased page load time) is that you need to access the DOM ("document.getElementById('container')"), but if the script is in the head the DOM hasn't yet been created when the script runs. There are two solutions: you can delay the execution of the script until your elements load, or you can place the script tag after the content of the page. Out of habit I do the latter, so pages look like this:
<!doctype html>
<html>
<head>
[title, stylesheets, etc]
</head>
<body>
[page content]
<script type='text/javascript' src='script.js'></script>
</body>
</html>
There's a typo at the start of the javascript - you want element.childNodes, not .childnodes. Additionally, the childNodes list includes text objects - .childNodes[0] is the line break you put between "<div id='slideshow'>" and "<div>". You can either delete the line break or use .childNodes[1], whichever you prefer.

After fixing the above your script works for me. However, remember that we were leaving some necessary features for later. At the very least the script needs to be able to move to a slide before or at its current location. The simplest way to do this is to replace the line "x+=10;" with "x+=x<slide*w?10:-10;", and "if(x>=slide*w)" with "if(x==slide*w)". This is not what I would call a robust solution, but it will work fine as long as you always use images with a width divisible by 10.

To keep it from breaking if the user clicks while the slideshow is moving you can uncomment the first line you indicated - the second, "clearInterval(slideshow)", doesn't really apply to your code any more. However, in addition to uncommenting that line you need to make "animate" a global variable, since you're only setting it later in the function. So, add "var animate;" outside the functions, and change "var animate=setInterval([...])" to "animate=setInterval([...])".

As for my code, sorry if the variable names aren't descriptive enough. The line calculating x means "x = start + %progress * length" - you see "-_x" because the length is calculated by subtracting the starting point from the ending point. And Math.pow just raises the first parameter to the power of the second - my equation is equivalent to "p=1-(1-p)*(1-p)". It's a simple quadratic, inverted to be slower at the end instead of the start.

Also, as a side note, if you link to my code from your html file it will work, you just need to change the w and len variables. It will also work if you plug it in to this template (and change those two vars):
<!doctype html>
<html>
<head>
<title>slideshow</title>
<style type='text/css'>
[css]
</style>
</head>
<body>
[html]
<script type='text/javascript'>
[javascript]
</script>
</body>
</html>

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Code, Content, and Presentation / JavaScript and AJAX
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved