Mittwoch, 10. März 2010

Scroll while mousedown

Nachdem ich jQuery und jQuery UI kennen und schätzen gelernt habe, wollte ich gleich mal probieren, wie ich eigene Scroll-Elemente erstellen könnte. Denn mal ehrlich — die Betriebssystem-Scrollbalken passen einfach nie zum Website-Design
Das mit dem scrollen war dank jQuery und scrollTo-Plugin schnell erledigt: Button-Element mit Event-Callback mousedown ausstatten und schon wird gerollt. Genau einmal! Hmm...
Also muss eine Art while(mousedown) her, nur leider gibt es das nicht so einfach. Dann fand ich aber den Forumsbeitrag von itsallkizza auf CodingForums, der aber ein kleines Problem aufweist, das wir im Folgenden beheben werden.
Das soll getan werden: Der Inhalt eines div-Containers soll über zwei weitere Elemente nach oben bzw. unten gescrollt werden. Diese Elemente können beliebige Bilder oder andere DOM-Objekte sein (um den Download der Demodateien klein zu halten, verwende ich hier HTML-Buttons).
Solange auf den Button gedrückt wird, soll die Scrollfunktion laufen, bei mouseup soll sie wieder aufhören. Das Problem: Die jQuery-Funktion mousemove ist sehr CPU-hungrig, die im Forum gefundene Lösung hat hingegen einen unschönen Seiteneffekt.

Hier mal die Grundidee:

function scrolly( whichdirection ) {
    var dir = '0';
    if (whichdirection == 'up') {   
        dir = '-=10'; // pixels
    }
    else{   // the opposite of 'up' is 'down' - always ;O)
        dir = '+=10'; // px
    }
     // now call scrollTo for the desired target
    $("#scrollcontent").scrollTo(dir,{ axis:"y"});
}

function beginAction(d)    {
    scrolly(d);
    action_timeout = setInterval(function(){scrolly(d)},70);
}

function endAction()    {
    if (typeof(action_timeout) != "undefined") clearTimeout(action_timeout);
}

$(document).ready( function() {
        $("#down").mousedown(function(){beginAction("down")});
$("#down").mouseup(function(){endAction()});
$("#up").mousedown(function(){beginAction("up")});
$("'#down").mouseup(function(){endAction()});
});

Der Trick besteht also darin, eine Timerfunktion setInterval anzustoßen, die periodisch scrollt. Und das tut sie solange, bis ein mouseup das Verhalten wieder stoppt, indem der Timer mit clearTimeout gelöscht wird. Der unschöne Seiteneffekt: Wird die Maustaste ausserhalb des Schaltelements losgelassen, dann scrollt die Funktion bis in alle Ewigkeit (doomsday, ihr wißt schon...). Nicht gut...
Die Lösung ist allerdings so simpel wie kurz und erspart sogar einen eventhandler! Wir setzen das Target für den mouseup-Handler auf window und schon geht's:
$(document).ready( function() {
$("#down").mousedown(function(){beginAction("down")});
$("#up").mousedown(function(){beginAction("up")});
$(window).mouseup(function(){endAction()});
});
Es ist wie beim Autofahren - bremsen heißt bremsen, egal, ob ich gerade vorwärts oder rückwärts fahre. Hier bedeutet es, die endAction nur einmal zuweisen zu müssen und nicht für jeden Button.
Gut? Dann optimieren wir noch schnell die if-Abfrage im scrolly und sind hier fertig:
function scrolly( whichdirection ) {
    var dir = '0';
    // shorter...
    dir = (whichdirection == 'up')? '-=10' : '+=10';
    // now call scrollTo for the desired target
    $("#scrollcontent").scrollTo(dir,{ axis:"y"});
}
Hier gibt es die Quellen und eine Demo und hier eine Weiterentwicklung mit Beschleunigung/Abbremsen.
Eine dritte Version [ Demo | blog ] zeigt, wie man absatzweise scrollen kann.

Hier geht's zum scrollTo-Plugin

Keine Kommentare: