No tenía muy claro cómo titular esta entrada, dado que su título original es AngularJS: Scroll Animations. Como en ocasiones anteriores se trata de una «traducción-no-literal»/explicación de un artículo tutorial en inglés.
En fin, ¿a qué nos referimos con esto de animaciones con scroll? Seguro que habéis visto más de una página que las tiene. Se trata de animaciones que se activan conforme vas haciendo scroll en la página, al alcanzar una determinada posición. La web Let’s Free Congress es presentada con ejemplo de esto en el texto original.

La idea es que la animación comience cuando el usuario llegue a la parte de la pantalla, para lo cual nos serviremos de la directiva scrollPosition, que se nos presenta así en el ejemplo original
.directive('scrollPosition', ['$window', '$timeout', '$parse', function($window, $timeout, $parse) {
return function(scope, element, attrs) {
var windowEl = angular.element($window)[0];
var directionMap = {
"up": 1,
"down": -1,
"left": 1,
"right": -1
};
// Recuperamos el elemento con el scroll
scope.element = angular.element(element)[0];
//Almacenamos los elementos que escuchan a este evento
windowEl._elementsList = $window._elementsList || [];
windowEl._elementsList.push({element: scope.element, scope: scope, attrs: attrs});
var element, direction, index, model, scrollAnimationFunction, tmpYOffset = 0, tmpXOffset = 0;
var userViewportOffset = 200;
function triggerScrollFunctions() {
for (var i = windowEl._elementsList.length - 1; i >= 0; i--) {
element = windowEl._elementsList[i].element;
if(!element.firedAnimation) {
directionY = tmpYOffset - windowEl.pageYOffset > 0 ? "up" : "down";
directionX = tmpXOffset - windowEl.pageXOffset > 0 ? "left" : "right";
tmpXOffset = windowEl.pageXOffset;
tmpYOffset = windowEl.pageYOffset;
if(element.offsetTop - userViewportOffset < windowEl.pageYOffset && element.offsetHeight > (windowEl.pageYOffset - element.offsetTop)) {
model = $parse(windowEl._elementsList[i].attrs.scrollAnimation)
scrollAnimationFunction = model(windowEl._elementsList[i].scope)
windowEl._elementsList[i].scope.$apply(function() {
element.firedAnimation = scrollAnimationFunction(directionMap[directionX]);
})
if(element.firedAnimation) {
windowEl._elementsList.splice(i, 1);
}
}
} else {
index = windowEl._elementsList.indexOf(element); //TODO: Add indexOf polyfill for IE9
if(index > 0) windowEl._elementsList.splice(index, 1);
}
};
};
windowEl.onscroll = triggerScrollFunctions;
};
}]);
Tal directiva se ha utilizado en este ejemplo. En ella podéis ver como la barra de la pantalla del móvil progresa según bajáis la barra.
En fin, la cosa es explicar ahora como usar esto. Como en el ejemplo original, iremos desgranando poco a poco el código, despedazándolo para ver cómo funciona.
Lo principal es añadir la directiva al elemento que queremos animar:
.row.show-for-large-up
.teaser(scroll-position, scroll-animation='fireupApplicationDesignAnimation')
.row
.large-6.columns.left-align.margin-top
h1(data-i18n="_CareerDesign_APPLICATIONDESIGNTITLE")
Esto le dice a AngularJS que cuando el usuario llegue al área determinada del DOM debe dispararse la animación. En vuestro controlador deberíais tener algo así
$scope.fireupApplicationDesignAnimation = function(scrollDirection) {
scrollDirection > 0 ? reduceAmount() : aumentAmount(); // We want to increase on scrollDown
setOffsetForImage();
};
La directiva envía al controlador la dirección del scroll, lo que permite mostrar animaciones basadas en él. En ocasiones querrás ejecutar la animación sólo una vez en lugar de que ocurra cada vez que el scroll esté a su altura. Puedes lograrlo devolviendo un valor true y chequeándolo antes de lanzar la animación, como en este ejemplo:
$scope.fireupMarketingDesignAnimation = function() {
if(!firedMarketingAnimation) {
window.animations.marketingAnimation.init();
firedMarketingAnimation = true;
return firedMarketingAnimation;
}
}
Como podéis ver en el ejemplo la función que lanza la animación está dentro de un if que hará que sólo se ejecute una vez.
En fin, resumiendo ¿Cómo funciona la directiva?. El proceso es el siguiente:
- Crea un array de elementos que requieren un eventListener del tipo onScroll. Puedes tener varios almacenados, y si devuelves un true para una sola ejecución serán eliminados para reducir el uso de memoria.
- Añade un eventListener onScroll que comprueba en qué parte de la página está el usuario.
- Recorre tus elementos vinculados con la directiva comprobando si se han activado las animaciones.
- Si no lo han hecho y el usuario se posiciona junto a ellas con el scroll entonces se activan. Esto obliga a hacer mucho uso de $parse, así que léete bien su documentación para entenderlo al 100%.
- Si la animación llamada a través de scope devuelve true, la extrae del array, como dijimos en el primer punto.
La conclusión:
La directiva es útil cuando necesitas lanzar varias animaciones en un periodo de tiempo específico. También te permitirá definir un comportamiento específico según la posición del scroll o hasta para realizar cambios en propiedades CSS3 relacionándolas con la posición de la página.
En este ejemplo en Codepen, creado por el autor del artículo original, podéis ver cómo también funciona en horizontal. Si movéis el scroll lentamente veréis como el balón lo sigue. Eso sí, probad con Chrome porque a mi en Firefox en lugar de una pelota se me ve una especie de mojón raro.
En fin, finalmente recomendar el blog de jjperezaguinaga Surviving by Coding, donde podréis encontrar mucha info sobre AngularJS y otros temas de desarrollo web, en inglés.