# Animation synchron zu Scrollposition



## demrks (24. Juni 2013)

Hallo,
zuerst einmal die Tatsache, dass ich nicht sonderlich gut in Mathematik bin und ich wahrscheinlich deshalb dieses Problem bisher noch nicht gelöst habe. Wahrscheinlich kennen viele von euch Webseiten, wo sich ein Element (sei es ein Bild) synchron zur Scrollposition bewegt. Ein Beispiel ist zum Beispiel dieses jQuery Plugin, das quasi schon genau das kann, was ich suche, allerdings eben nur fast: http://prinzhorn.github.io/skrollr/

Wenn man hier nach unten scrollt, animieren sich die Elemente auf unterschiedlichste Art und Weise. Gibt auch noch andere Beispiele, wie diese iPhone App: http://nizoapp.com. Soweit der Grundgedanke, nun zu dem Problem: Mit dem "Skrollr" Plugin lassen sich zwar Seiten wie die Nizo Seite relativ einfach umsetzen, allerdings funktioniert Skrollr mit data-Attributen, was natürlich super einfach ist, allerdings dadurch auch wieder relativ "statisch" und in meinem speziellen Fall nicht zu brauchen (zu meinem Fall gleich noch mehr). Ich bräuchte also eine JS Funktion, die in meinem Kopf ungefähr so funktioniert:



```
var
 $window = $(window),
 $el = $(".element"),
 scrollTop;

$(window).on("scroll", function() {
 scrollTop = $window.scrollTop();
 
 $el.css({
 "translateX" : linearMovement(-300, 0, 500, 200)
 });
});

function linearMovement(startValue, endValue, startingPoint, time) {
 return ...;
}
```

Nur zur Erklärung die Werte, die ich mir frei ausgedacht habe:
1. Parameter: Den Startwert, von der die Position aus anfängt, in dem Fall fängt die Bewegung bei -300px an.
2. Parameter: Der Endwert zu der animiert wird. Das Element bewegt sich also von -300 zu 0
3. Parameter: Die Skrollposition ab der die Animation anfangen soll, in dem Fall fängt die Animation an, sobald man 500px nach unten gescrollt hat.
4. Parameter: Die Zeit (in px) bis die Animation beendet ist. Also bei 700px (500px + 200px) bleibt das Element an der Endposition stehen.

Ich bin mir nicht ganz sicher, ob so etwas möglich ist, da allerdings Skrollr genau so etwas macht, denke ich, dass es mit einer mathematischen Berechnung möglich sein sollte, die allerdings bisher jenseits meiner mathematischen Fähigkeiten liegt. Ich habe also gesucht, nichts wirklich gefunden, außer ein Tutorial, dass erklärt, wie man so eine Seite, wie die Nizo App umsetzt (ohne plugin): http://pehaa.com/2011/08/intriguing-animate-on-scroll-effect-jquery-tutorial/

Klang ganz gut, funktioniert auch, allerdings sieht die Funktion, die die Bewegung berechnet, so aus:


```
function move(p0, p1, s) {
 return Math.min((-p0 + p1) / s_max * s + p0, p1);
}
```

Man kann quasi nur den Startwert und den Endwert angeben und einen Parameter "s", der in etwa vergleichbar mit der Distanz ist, aber eben nicht ganz. Eher irgendeine "Magic-number". Außerdem kann man nicht angeben, ab wann die Animation starten soll. Die Frage ist nun, ob eine Funktion wie die oben angedachte "linearMovement" überhaupt möglich ist (vielleicht muss man nur die "move" Funktion irgendwie erweitern) und ob irgendjemand in der Lage ist so eine Funktion zu schreiben?

Mein eigentliches Ziel, bei der ich diese Funktion bräuchte (geht evt. auch anders, aber mit einer solchen Funktion hätte ich etwas, das man immer wieder verwenden kann) ist nur eines von vielen, weil ich eine solche Funktion sicherlich schon öfter gebraucht hätte und deshalb  immer irgendeine Notlösung machen musste:

Lässt sich ganz kurz beschreiben, ist nichts super spezielles, aber es sollte so funktionieren wie: http://nzopera.com/2013/don-giovanni

Sobald man ein bisschen nach unten scrollt, passiert erstmal nichts, außer dass die Navigation schonmal relativ zur Scrollposition zusammengeschoben wird (das ginge mit Skrollr allerdings auch problemlos). Sobald man allerdings weiter nach unten scrollt, verschwindet die Navigation erstmal komplett - auch noch alles relativ einfach machbar. Die Navigation wird erst dann wieder sichtbar, wenn man nach oben scrollt. Das Besondere ist allerdings, dass sich diese Navigation nicht einfach per jQuery "animate" oder CSS Transitions wieder reinbewegt, sondern wieder relativ zur Scrollposition (Einfach einige Zeit nach unten scrollen und dann wieder langsam nach oben, um es zu sehen). Und genau das ist mir ein Rätsel. Mit einer Animation wie oben angedacht, könnte ich so etwas auch umsetzen, aber leider fehlt mir dieses Stück noch.

Ich hoffe ihr könnt mir helfen, vielen Dank schonmal und Grüße


----------



## tombe (25. Juni 2013)

So und jetzt erkläre bitte nochmal etwas kürzer was du genau haben willst.

Geht es dir darum wie sich auf der nzopera-Seite das obigige Menü "zusammenschiebt" und erst dann nach oben verschwindet?


----------



## demrks (25. Juni 2013)

Sorry für den langen Beitrag, ich versuche es nochmal kurz 
Die neuseeländische Seite war nur ein Beispiel für was ich die Funktion brauche. Ich suche also kurz gesagt nur die oben beschriebene Funktion, die ich "linearMovement" genant habe. Diese Funktion sollte anhand der Scrollposition Werte von einem zu einem anderen festgelegten Wert innerhalb einer bestimmten Zeit (in px) ausgeben (siehe erstes Codebeispiel oben). War das etwas verständlicher oder immer noch nicht ganz?

Danke


----------



## tombe (25. Juni 2013)

Ok, jetzt habe ich es wohl verstanden. Nur die Verwendung der Parameter ist noch unklar.

*Startwert:* Muss doch eigentlich nicht definiert werden. Das ist die Position des entsprechenen Elements.

*Endwert:* Müssten eigentlich 2 Angaben sein, horizontaler und vertikaler Wert. Außer natürlich es soll nur in eine Richtung bewegt werden.

*Scrollposition:* Soll das ein Pixel-Wert sein oder prozentual zur Fenstergröße?

*Zeit:* Wenn ich unter Punkt 2 den Endwert/die Endwerte definiere, wozu dann diese Angabe?


----------



## demrks (25. Juni 2013)

Zu den einzelnen Parametern:
*Startwert*: Du hast recht, dass man hier auch die Position des Elementes nehmen könnte. Nur habe ich vor, dass die Position nicht immer der Anfangswert ist, sondern auch mal der Endwert im CSS. Das hat den Grund, dass die Webseite und die Elemente bei abgeschaltetem JS o.ä. dann an der Endposition stehen und nicht z.B außerhalb vom Bildschirm, wenn die "reinfliegen" sollen. Ich könnte dann später auch beispielsweise noch einbauen, dass der Wert optional ist und bei Nichtsetzen die Position benutzt wird.
*Endwert*: Um die Benutzung der Funktion flexibel zu lassen, habe ich mir gedacht, dass die Funktion unabhängig von CSS Eigenschaften funktioniert und "nur" Werte ausgibt. Will ich also sowohl translate x als auch y animieren, habe ich mir das einfach so vorgestellt:


```
$element.css({
	"translateX" : linearMovement(-300, 0, 500, 300),
	"translateY" : linearMovement(-500, 0, 500, 300)
});
```

So kann es für alles funktionieren, auch wenn man die opacity nur animieren möchte von 0 zu 1 usw.
*Scrollposition:* Ich habe das als Pixelwert gedacht. Also wenn der Parameter 500 ist, dann soll die "Animation" erst anfangen sobald man > 500 px gescrollt hat. Hat man < 500 gescrollt ist das Element am "Startwert".
*Zeit:* Die Zeit gibt an, wie lange die Animation dauert. In dem Fall allerdings nicht in Sekunden, sondern da man eben scrollt in px. Wenn der Parameter also 300 ist, dann animiert sich das Element innerhalb von 300px scrollen zu der Endposition. Umso größer der Wert, desto langsam ist dann auch die Bewegung. In Verbindung mit dem Parameter "Scrollposition", kann man dann genau festlegen nach wie vielen px scrollen die Animation vorbei ist. Ist also "Scrollposition" = 500 und Zeit = 300, dann ist die "Animation" nach 800px an der Endposition.

__

Also das Prinzip vom "Skrollr Plugin" (Link oben) auf eine Javascript Funktion übertragen. Da geht es nämlich mit data Attributen. Will ich da die opacity animieren beginnend bei 300px und beendet bei 800px sieht das bei dem Plugin so aus:


```
<div class="element" data-300="opacity: 0;" data-800="opacity: 1">...</div>
```


----------



## demrks (25. Juni 2013)

_Sorry erstmal für diesen "Doublepost", aber ich weiß nicht, wie ich das sonst anstellen soll, wenn ich selber mich beantworte. Falls das irgendwie falsch sein sollte, bitte sagen, dann lösche ich es und mache es dort hin, wo es hin soll._

Ich bin mathematisch wirklich nicht sonderlich gut und über Schulwissen geht nicht viel mehr. Ich hatte allerdings, warum auch immer irgendwie die Idee, dass der gute alte Dreisatz eventuell als Funktion dienen kann. Hier meine Überlegung: Die Funktion soll ja immer den Wert relativ zur aktuellen Scrollposition ausgeben, also sieht das Ganze doch so aus:

x - "aktuelle Scrollposition"
"Endwert" - "Scrollposition" + "Zeit"

Also angenommen der "Endwert" der Animation ist 300, die "Zeit" 200px und die "Scrollposition" (bei der die Animation anfangen soll) 500, dann sieht das nach meinem Wissen so aus:

x - aktuelle Scrollposition
300 - 700

Die 700 stammen aus der Addition von "Scollposition" + "Zeit", da das ja dem Endwert entspricht. Als Funktion geschrieben dann so:


```
function linearMovement(startValue, endValue, initScroll, time) {
     //Die Variable "scrollTop" kommt von $(window).scrollTOp();
     return (scrollTop * endValue) / (initScroll + time);
}
```

Es gibt allerdings, wie bei meinen Mathekenntnissen abzusehen einen Fehler. Ich berücksichtige erstens den Anfangswert gar nicht und ich berücksichtige auch nicht, dass die Animation erst bei "initScroll" (bzw. wie vorher gennant: "Scrollposition") anfangen soll. Also irgendwie auch nicht die richtige Lösung, aber vielleicht der Ansatz?

*EDIT:* Ist mir neu trotz Abitur, aber eventuell ist das die Lösung, um noch den Anfangswert mit einzubekommen? Erweiteter Dreisatz: http://www.helpster.de/erweiterter-dreisatz-so-gelingt-die-berechnung_78965

Verstehe ich leider gerade nicht, muss ich erst nochmal nachlesen, vielleicht ist das aber auch der komplett falsche Ansatz... Sieht dann aber so aus als Tabelle

startValue  - initScroll
x               - scrollTop (aktuelle Scrollposition)
endValue   - initScroll + time

*EDIT2:* Habe die Berechung gefunden, die so aussieht angeblich laut meinem Verstehen

(scrollTop * initScroll * (initScroll*time))/(startValue* endValue)

Da kommen allerdings komplett komische Werte dabei raus, also dann doch der falsche Ansatz


----------



## tombe (26. Juni 2013)

Angenommen wir haben folgende Parameter:


```
Start...............: 500
Ende................: 700

Scroll..............: 100
Zeit................: 400

Start - Ende........:  ergibt den Wert wie weit sich das Objekt bewegen muss (hier 200) = DiffPos

Zeit - Scroll.......: ergibt den Wert wie weit gescrollt werden muss bis das Ende erreicht ist (hier 300) = DiffScroll

DiffPos / DiffScroll: ergibt den Wert wie weit sich das Objekt bewegt wenn 1 Pixel gescrollt wird = Bewegung
```


Mit der Formel (Position ist die aktuelle Scrollposition):


```
Start + (Position - Scroll) * Bewegung
```
kann die Position des Objekt abhängig davon wie weit gescrollt wurde berechnet werden


```
Position............: 400
Ergebnis............: 700

Position............: 100
Ergebnis............: 500

Position............: 275
Ergebnis............: 617
```


----------



## demrks (26. Juni 2013)

Vielen vielen Dank, auch für die verständliche Erklärung. Ich habe das Ganze jetzt noch etwas umgestellt und in die Funktion gemacht mit einem kleinen Testbeispiel: http://codepen.io/anon/pen/Fwfghd (einfach nach oben scrollen, dann sollte das Rechteck sich "einfaden")

Funktioniert soweit genauso wie gedacht, im Moment als Beispiel mit der "opacity". Ich habe eine wenig rumprobiert und eine Sache geht noch nicht, da müsste die Gleichung ein wenig umgestellt werden:

Wenn der Startwert größer ist als der Endwert, dann gibt die Funktion nur den Endwert aus. Also wenn ich versuche das hier z.B. zu machen

```
console.log(scrollTransition(0, -300, 0, 300));
```

Ich kann mir denken, dass es damit zusammenhängt, dass dann die Subtraktionen falsch sind logischerweise, aber was genau muss ich dann umstellen? Wahrscheinlich mit einer if Abfrage abfragen ob der Startwert kleiner als der Endwert ist und dann eine andere Gleichung benutzen? Oder geht das auch einfacher?

Zweite Frage bezieht sich auf das Easing. Ich könnte dies zwar fest einbauen in die Funktion, aber ich fände es besser, wenn man, wie auch bei jQuery animate den Easing-Namen (http://gsgd.co.uk/sandbox/jquery/easing/) als Parameter übergeben könnte. Geht das mit meiner Funktion oder ist das nicht möglich? Falls es nicht möglich sein sollte, müsste ich eben das Easing fest einbauen.


----------



## tombe (26. Juni 2013)

Also was den negative Wert angeht, so hängt es wohl davon ab was du damit für eine Funktion auslöst. Mit obiger Funktion kannst du es nicht verwenden.

Was "Easing" angeht, da wird es wohl nicht klappen da es zu unterschiedlich ist. Um aber was genaues sagen zu können hilft nur testen.


----------



## demrks (26. Juni 2013)

Es geht nicht direkt um den negativen Wert bei dem Problem. Es würde auch nicht funktionieren, wenn es so aussehen wird

```
console.log(scrollTransition(300, 0, 0, 300));
```

Sobald der Endwert kleiner ist als der Startwert, was ja durchaus vorkommen kann bei Animation dann funktioniert es nicht. Soweit ich testen konnte, funktionieren bei meinem Codebeispiel oben negative Werte problemlos, solange eben Anfangswert < Endwert.

Ich müsste also in der Funktion überprüfen, ob der Wert kleiner ist

*Zu dem Easing:* Auch das probiere ich gerade, allerdings ohne Erfolg, ich weiß nicht, wie ich die Werte an die Easing Funktion übergebe. Ich kann zwar überprüfen, ob das Easing (das als Parameter angegeben wird) existiert:

```
jQuery.easing['swing'] != undefined // swing wird eben durch den parameter ersetzt
```
Aber wie übergebe ich dann die Werte an die Funktion?

```
jQuery.easing[easingParameter](t, y, s, m) // Die Easing Funktionen haben ja vier Parameter, aber wie rufe ich die auf, so geht es nicht wirklich
```

Grüße

*EDIT:* Die Gleichung war anscheinend richtig, es lag so wie ich es jetzt gestestet habe nur an Math.min und Math.max. Muss ich wohl mit if und else arbeiten so wie es zuschaut. Beim Eeasing habe ich leider noch nichts gefunden.


----------

