Tidslinie (Sequencer)

De foregående slideshows er udmærkede. De vil imidlertid til visse formål have den mangel, at alle hændelser indtræffer i en ganske bestemt takt. Vi kan variere takten, men vi kan ikke have en "uregelmæssig" takt.

På denne side introducerer jeg et tidsliniescript, der gør det muligt for os have fuld kontrol over de tidspunkter, forskellige hændelser indtræffer. Samtidig får vi fuld kontrol over, hvad det er der skal på det givne tidspunkt. Det kan altså bruges til andet og mere en udskiftning af billeder.

Tidsliniescript – eksempel 1

Et sådant script hedder på engelsk en sequencer. Det grundlæggende script, der demonstreres i Eksempel 1, er beskrevet i Jeff Rules bog "Dynamic HTML", Addison-Wesley 1999. Dette script, som det anvendes i eksempel 1, gengives herunder:

<script type="text/javascript">
var totalTime, currTime;  // Tidsvariable
var Time, Seq;            // Arrays
var speed = 100;  // Timer interval
var timer;                // timer ID
 
function startSeq(SeqNumber) {
  Time[SeqNumber] = 0;
  SeqController(SeqNumber);
}
function stopSeq(SeqNumber){
   Time[SeqNumber] = Seq[SeqNumber].length;
}
function SeqController(SeqNumber) {
    if (Time[SeqNumber] <= 
    -->  Seq[SeqNumber].length - 1) {
      Time[SeqNumber]++;
        if (Seq[SeqNumber][Time[SeqNumber]] 
        -->  != null){
          eval(Seq[SeqNumber][Time[SeqNumber]]);
        }
        timer=setTimeout('SeqController(' + 
        -->  SeqNumber + ')', speed);
    }
}

function init() {

  Time = new Array();
  Seq = new Array();

Seq[0] = new Array();
Seq[0][20] = 'visPix(2);';
Seq[0][40] = 'visPix(3);';
Seq[0][60] = 'visPix(4);';
Seq[0][80] = 'visPix(5);';
Seq[0][100] = 'visPix(6);';
Seq[0][120] = 'visPix(7);';
Seq[0][140] = 'visPix(8);';
Seq[0][160] = 'start();';

startSeq(0);
}
</script>

Der hører naturligvis mere til eksempel 1 end lige dette script, blandt andet skal jeg jo have indlæst nogle billeder og definere mine billedvisningsfunktioner. Men lad os lige få tidslinien på plads først.

Tidsliniescriptet

I dette script har jeg med grønt markeret de dele, der skal ændres, alt efter scriptets anvendelse. Det scriptet gør, er, at det med mellemrum udløser nogle funktioner. Disse funktioner kan jo være hvadsomhelst, men som det nok fremgår af eksemplet, drejer det sig her om visning af nogle billeder.

Funktionen init() udløser scriptet, der efter 20 * 100 millisekunders forløb udløser funktionen visPix(2), efter yderligere 20 * 100 millisekunders forløb udløser funktionen visPix(3) osv, indtil der er vist i alt 8 billeder plus startbilledet, der ikke indgår i tidslinien. Det er tallene 20, 40, 60 osv der bestemmer, hvornår de forskellige billeder vises. Hvor lang tid tallet 20 rent faktisk er, bestemmes af den variable speed, som her er sat til 100. Det synes klart, at vi dermed har to værktøjer i hænde til tidsstyringen – dels den samlede tid, ved ændring af speed, og dels hvor længe hvert enkelt billede skal vises, ved ændringen af Seq[0][Time].

Det er selvfølgelig den sidste mulighed, der er det interessante ved dette script, kombineret med de muligheder, der er for at udløse en hvilkensomhelst funktion på ethvert ønsket tidspunkt – eller for den sags skyld, udløse flere funktioner på samme tidspunkt. Som rosinen i pølseenden vil jeg nævne, hvis dette ikke er tilstrækkeligt, at du kan oprette flere Seq-arrays.

Hvad skal være på plads?

Jeg vil arbejde videre med disse muligheder i denne artikel, men først vil jeg lige have det grundlæggende helt på plads.

Eksempel 1 såvel som de øvrige eksempler drejer sig primært om billedvisning, så derfor indlæser jeg en serie billeder i et array (som beskrevet på websiden Automatisk fyldning af arrays):

var antal=8;
var images = new Array()
for(i = 1; i < antal + 1; i++){
  images[i] = new Image();
  images[i].src = "pix" + i + ".jpg";
}

De billeder, der indlæses her, hedder allesammen noget med pix1.jpg, pixb.jpg osv. Filnavnene er for så vidt ligegyldige, fordi jeg nu har et array af billeder, jeg kan anvende i en billedvisningsfunktion:

function visPix(nr) {
  document.images.pix1.src = images[nr].src;
}

– og det er så dét. Mere skal der egentlig ikke til.

Bemærk det med rødt markerede, der kendetegner den korrekte måde at tilgå et billede på en webside på.

Tidligere nøjedes vi med
document.pix1.src = images[nr].src;
– hvor pix1 var billedets name. I moderne JavaScript benytter vi os af billedets id og den såkaldte DOM dot-syntaks, der her udvælger billedet med id="pix1" fra websidens image array, en tabel over alle billeedr på websiden.

Start og stop

Jeg vil nu gerne forsyne mit billedvisningsprogram med et minimum af brugerkontrol, så derfor har jeg også i Eksempel 1 indbygget en start og stop-funktion. Som du ser er både start og stop knyttet til det samme link. Det kræver en smule behændighed at ordne det sådan, men jeg har i tidligere artikler beskrevet, hvordan du ved at spore en variabels tilstand kan få den samme funktion til at udføre to forskellige ting. Scriptet ser i dette tilfælde således ud:

var running=0;

function start(){
  if (running==1){
    stopSeq(0);
    visPix(1);
    nytIndhold('stopknap','Start det!')
    running=0;
    return;
  }
  if (running==0){
    nytIndhold('stopknap','Stop det!')
    init()
    running=1;
  }
}

Når siden indlæses sættes den variable running til 0. Funktionen start() vil, når den udløses, tjekke tilstanden af denne variabel. Når den er nul, falder scriptet igennem til den anden betingelse, der er opfyldt, således at scriptet udfører følgende:

  1. Udskifter tekstindholdet af stopknappen (Om denne funktion, se websiden Ændring af et elements indhold),
  2. udløser init()-funktionen, der sætter tidslinien i gang; og
  3. sætter værdien af den variable running til 1

Klikkes endnu en gang på stopknappen, har den variable running værdien 1, og derfor udføres den første del af funktionen start():

  1. Funktionen stopSeq(0), der stopper timeren, udløses,
  2. billede nr 1 vises,
  3. Stopknappens tekstindhold ændres,
  4. Den variable running sættes til 0, og
  5. scriptet stoppes ved hjælp af kommandoen return.

Du kan kopiere scriptet i sin helhed ved at bruge browserens "Vis kilde"-funktion på Eksempel 1.

Brugerkontrol – eksempel 2

Bortset fra at hun kan starte og stoppe billedvisningen har brugeren ingen kontrol over afspilningen, så mit næste skridt i denne udviklingsproces er at give hende muligheden for at

  1. Variere hastigheden, og
  2. Kigge på billederne enkeltvis

Det ser du illustreret i eksempel 2.

Du ser nu to elementer, der ikke har været der før – nemlig en talrække, hvor hvert tal svarer til et af de ni billeder, samt et panel, der kun er synligt, når showet kører, og hvor brugeren kan vælge mellem forskellige hastigheder. Tager vi det sidste først, er HTML-koden til dette panel som følger:

<div id="speedpanel">
  Hastighed: 
  <a id="speed1" href="#" 
  onclick="setSpeed(1);return false;">1 sek</a> 
  <a id="speed2" href="#" 
  onclick="setSpeed(2);return false;">2 sek</a>  
  <a id="speed3" href="#" 
  onclick="setSpeed(3);return false;">3 sek</a>
  <a id="speed4" href="#" 
  onclick="setSpeed(5);return false;">5 sek</a> 
  <a id="speed5" href="#" 
  onclick="setSpeed(10);return false;">10 sek</a>
</div>

Til hver hastighed er der knyttet dels en id, og dels et funktionskald til funktionen setSpeed(faktor). Denne funktion ser således ud:

function setSpeed(faktor) {
  speed = faktor * 100;
  speedpanelColor(faktor)
}

Det er den variable speed, vi manipulerer med her. Den var i det første eksempel 50, men for at slippe lidt billigere omkring hovedregningen er den i dette og de følgende eksempler sat til 100. Du skal hele tiden huske på, at det faktiske interval mellem to billeder er speed-variablen ganget med differencen mellem to på hinanden følgende instanser af Seq[0][Time]. Er speed-variablen 100 og differencen 20, er intervallet 2000 milisekunder = 2 sekunder.

Nu vil jeg gerne have markeret den aktuelle hastighed, så derfor har jeg lavet en funktion, der hedder speedpanelColor(faktor), hvor jeg også bruger den parameter, jeg fik fra setSpeed(faktor) funktionskaldet. Denne funktion ser således ud:

function speedpanelColor(faktor){
  for (i=1;i<6;i++){
    setFgColor('speed'+ i,'#000')
    setBgColor('speed' + i,'#efefef')
  }
  switch(faktor){
    case 1: setFgColor('speed1','#c30')
    break;
    case 2: setFgColor('speed2','#c30')
    break;
    case 3: setFgColor('speed3','#c30')
    break;
    case 5: setFgColor('speed4','#c30')
    break;
    case 10: setFgColor('speed5','#c30')
    break;
  }
}

For det første nulstiller jeg forgrundsfarve og baggrundsfarve på alle hastighedsangivelserne; når det er sket, kan jeg sætte farven på den aktuelle hastighed, der jo er givet ved parameteren faktor. Denne er et tal, men der er ikke tale om en talrække. Derfor må jeg bruge switch-funktionen til at bestemme, hvad der skal ske i hvert enkelt tilfælde.

DHTML funktioner

Eksemplerne anvender forskellige DHTML-funktioner, jeg for nemheds skyld definerer en gang for alle i begyndelsen af mine scripts:

function setBgColor(objekt, color) {
document.getElementById(objekt).style.backgroundColor 
  = color;
}
function setFgColor(objekt,color) {
document.getElementById(objekt).style.color
  = color;
}
function collapseObjekt(objekt) {
document.getElementById(objekt).style.display 
  = "none";
}
function expandObjekt(objekt) {
document.getElementById(objekt).style.display 
  = "block";
}
function skjulObjekt(objekt) {
document.getElementById(objekt).style.visibility 
  = "hidden";
}
function visObjekt(objekt) {
document.getElementById(objekt).style.visibility 
= "visible";
}
function nytIndhold(objekt,text){
document.getElementById(objekt).firstChild.nodeValue
  =text;
return;
}

Betydningen af de forskellige funktioner skulle gerne gives ved funktionens navn.

Vis aktuelt billede

I eksempel 2 ser du endvidere et panel med en dobbeltfunktion: Dels viser det, når slideshowet kører, hvilket billede, der aktuelt kan ses; og dels kan det, når slideshowet ikke kører, anvendes til at vise et vilkårligt billede ved at klikke på billedets nummer.

HTML-koden til panelet ser således ud:

<div id="nrpanel">
  Viser/vis billede nr:
  <a id="pixnr1" href="#" 
  onclick="pixNr(1);return false;">1</a>
  <a id="pixnr2" href="#" 
  onclick="pixNr(2);return false;">2</a>
  <a id="pixnr3" href="#" 
  onclick="pixNr(3);return false;">3</a>
  ... osv
</div>

Jeg ønsker, at hvert billede skal vises, når der klikkes på det tal, der svarer til billedet. Men jeg kan ikke bruge min visPix(nr)-funktion, for der går kuk i timeplanen, hvis jeg bruger denne funktion, mens showet kører. Derfor introducerer jeg en ny funktion, pixNr(nr), der har følgende udseende:

function pixNr(nr) {
  if (running==1) {
    alert("Stop venligst autovisning, \nfør du viser enkeltbilleder.")
  }
  else 
    visPix(nr)
}

Her undersøges først tilstanden af den variable running. Hvis den er 1, kører showet, og brugeren anmodes via en advarselsboks om at stoppe showet, før der kan vises enkelte billeder. Hvis running er 0, falder pixNr(nr)-funktionen igennem til den "rigtige" visPix(nr)-funktion, der så udføres, således at det rigtige billede vises.

Panelets anden funktionalitet er at vise i en talrække, hvilket billede der aktuelt vises, når showet kører. Det klares ved at føje et par funktionskald til visPix(nr)-funktionen:

function visPix(nr) {
  doTrans('pix1',23,1);
  document.images.pix1.src = images[nr].src;
  setColors();
  setFgColor('pixnr'+nr,'#fff');
  setBgColor('pixnr'+nr,'#666');
}

For det første nulstilles alle farver ved hjælp af en funktion til dette:

function setColors(){
  for (i=1;i<antal+1;i++){
    setFgColor('pixnr'+ i,'#000')
    setBgColor('pixnr' + i,'#efefef')
  }
}

Dernæst sættes forgrundsfarve og baggrundsfarve på det tal, der repræsenterer det aktuelle billede. Og dermed har jeg opnået det jeg ville med dette panel.

Starttilstand

Der er et par frynser mere på dette eksempel:

  • Når jeg nu har valgt, at billede nr. 1 skal vises, når siden skal indlæses, vil det være fiksest, hvis det også var markeret i nummerpanelet som det aktuelle billede
  • Panelet, hvor brugeren kan vælge hastighed, er kun aktuelt, når showet kører, og jeg ønsker derfor at skjule det, når showet ikke kører.

Begge dele opnås ved hjælp af en sidste ny funktion, initpage(), der udløses onload:

function initpage(){
  visPix(1);
  skjulObjekt('speedpanel');
}
onload=initpage

Bemærk den sidste linie her, der nøje svarer til, at du normalt vil udforme din body-markør som følger:

<body onload="initpage()">

Og til allersidst, for så vidt angår eksempel 2, skal vi lige rette start/stop-funktionen til, så de nødvendige funktioner også udføres af denne:

function start(){
  if (running==1){
    stopSeq(0);
    visPix(1);
    skjulObjekt('speedpanel');
    nytIndhold('stopknap','Start det!')
    running=0;
    return;
  }
  if (running==0){
    visPix(1);
    nytIndhold('stopknap','Stop det!')
    visObjekt('speedpanel')
    setSpeed(2)
    init()
    running=1;
  }
}

Du kan kopierer Javascript'et i dets helhed ved at bruge vis kilde-funktion i din browser, når du ser på eksempel 2.

Flere billeder på samme tid – eksempel 3

Indtil nu har jeg dækket stort set det samme stof som i de foregående artikler om JavaScript slideshows, men på grundlag af et andet tidsstyringsscript. Strukturen af dette tidsliniescript gør det muligt at køre flere sideløbende hændelsesforløb.

Den nemmeste måde at gøre det på er illustreret i eksempel 3.

Som du ser, vises to billeder side om side og udskiftes uafhængigt at hinanden. Scriptet til dette eksempel ser noget mere indviklet ud, men grundlæggende er der ikke de store forskelle i forhold til de foregående eksempler.

Selve tidsliniescriptet er uforandret, bortset fra vort Seq-array, der er forsynet med nogle flere funktionskald:

Seq[0] = new Array();
Seq[0][20] = 'visPix(2);';
Seq[0][25] = 'visbPix(2);';
Seq[0][40] = 'visbPix(3);';
Seq[0][50] = 'visPix(3);';
Seq[0][60] = 'visPix(4);visbPix(4)';
Seq[0][80] = 'visPix(5);';
Seq[0][90] = 'visPix(6);';
Seq[0][100] = 'visbPix(5);';
Seq[0][120] = 'visPix(7);';
Seq[0][130] = 'visbPix(6);';
Seq[0][140] = 'visPix(8);';
Seq[0][150] = 'visbPix(7);';
Seq[0][160] = 'visPix(9);';

Læg mærke til de lidt forskellige udformninger: Der er to slags funktionskald, visPix(nr) og visbPix(nr). Det ene funktionskald udløser en visning af billedet til venstre, det andet af billedet til højre. De to forskellige funktioner er vist herunder. Du skal også lige notere dig, at det er helt problemløst efter 6 sekunders forløb at udskifte 2 billeder på samme tid.

function visPix(nr) {
  document.images.pix1.src = a_images[nr].src;
  setColors();
  setFgColor('pixnr'+nr,'#fff');
  setBgColor('pixnr'+nr,'#666');
}
function visbPix(nr) {
  document.images.pix2.src = b_images[nr].src;
}

Det, der gør forskellen, er herover markeret med rødt. Der er tale om to forskellige id'er, nemlig #pix1 og #pix2, og der er også tale om to forskellige billed-arrays: a_images og b_images. For en ordens skyld viser jeg herunder, hvordan du automatisk fylder sådan to forskellige arrays:

var a_antal=9;
var b_antal=7;
function visPix(nr) {
  document.images.pix1.src = a_images[nr].src;
  setColors();
  setFgColor('pixnr'+nr,'#fff');
  setBgColor('pixnr'+nr,'#666');
}
function visbPix(nr) {
  document.images.pix2.src = b_images[nr].src;
}

Der går lidt kuk i visningen af enkeltbilleder såvel som i visningen af, hvilket billednummer, der aktuelt vises. Der er forskellige løsninger på dette problem, lidt afhængigt af, hvad det egentligt er, vi vil vise.

Hvad er historien?

I dette eksempel mere end i noget andet, jeg har skrevet om, står vi overfor noget, der i sig selv er totalt værdiløst, og som kun får værdi, hvis vi via dette slideshow kan fortælle en historie. Der er jo ingen fortælling i nogle løsrevne billeder af nogle landskaber og nogle billeder fra erhvervslivet.

Mit dilemma er her, at de historier, jeg kan fortælle ved hjælp af billeder, enten tilhører privatsfæren, eller også kræver et kæmpe researcharbejde for at finde egnet billedmateriale.

Praktisk eksempel

Men som eksempel kan jeg dog bruge en del af et projekt for min datter Thilde, der er beklædningsdesigner, og som i efteråret 2002 løste en noget kompliceret costumier-opgave for performance-teatret Hotel Proforma: Eksempel 5.

I denne udførelse har jeg valgt at lade visningen af enkeltbilleder bero på en separat funktion, der sørger for visningen af to billeder ved siden af hinanden.

Hvad der yderligere er specielt ved eksempel 4 er løsningen på problemet med den store mængde billeder, der skal overføres, inden showet kan vises, her ca. 230 kilobyte. Det har jeg også skrevet en artikel om: Vis noget andet mens en webside indlæses).

Flere sideløbende tidslinier

Jeg synes, at muligheden for dels at skræddersy de funktioner, tidsliniescriptet udløser, og dels muligheden for at udløse flere funktioner på samme tidspunkt er nok til mig.

Men har du mere højtflyvende ambitioner om multimedieshows, skal du vide, at du uden stort besvær kan afvikle flere parallelle tidslinier med samme script – jf. eksempel 7. Det er selvfølgelig ikke umiddelbart synligt, at det forholder sig som jeg siger. Men herunder ser du de nu to Seq-arrays, jeg har oprettet for at vise de kulørte kasser:

function init() {
  Time = new Array();
  Seq = new Array();
  
Seq[0] = new Array();
Seq[0][10] = 'visPix(1);';
Seq[0][20] = 'visPix(2);';
Seq[0][30] = 'visPix(3);';
Seq[0][40] = 'visPix(4);';
Seq[0][50] = 'visPix(5);';
Seq[0][60] = 'visPix(6);';
Seq[0][70] = 'visPix(7);';
Seq[0][80] = 'visPix(8);';
Seq[0][90] = 'visPix(9);';

Seq[1] = new Array();
Seq[1][35] = 'visPixa(1);';
Seq[1][45] = 'visPixa(2);';
Seq[1][55] = 'visPixa(3);';
Seq[1][65] = 'visPixa(4);';
Seq[1][75] = 'visPixa(5);';
Seq[1][85] = 'visPixa(6);';
Seq[1][95] = 'visPixa(7);';
Seq[1][105] = 'visPixa(8);';
Seq[1][115] = 'visPixa(9);';
Seq[1][125] = 'start();';

startSeq(0);
startSeq(1);
}

Som det fremgår af denne kode, har jeg oprettet et nyt array med navnet Seq[1]. Dette array kører helt uafhængigt af det det første, bl.a. med sin egen timer. Det eneste, der er fælles for de to tidslinier er den variable speed, hvilket jo er nemt fikset, hvis du vil det.

Konklusion

Jeg har i denne artikel introduceret et universalt anvendeligt tidsliniescript og vist en række eksempler på dets anvendelse til billedvisning.

Denne side er senest opdateret: 25. June, 2006