Animation via les équations de robert penner

3/5 (11 avis)

Snippet vu 7 960 fois - Téléchargée 17 fois

Contenu du snippet

Cette source est une 'base' pour ceux et celles qui désirent créer leur moteur d'animations en Javascript.
Robert Penner, mathématicien en herbe plus connu dans l'univers du Flash, a mis au point il y a déjà quelques temps une serie d'équations permettant l'animation d'objets sous divers effets. On retrouve ces équations dans plusieurs librairies JS connues (mootools par exemple).

Mon exemple fournis 4 des effets les plus sympa et leur système d'utilisation. Vous trouverez l'ensemble des équations à l'adresse suivante :

http://www.robertpenner.com/easing/

Source / Exemple :


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Effets</title>
<style type="text/css">
	body{ font: 11px/16px Arial, "Trebushet MS", Helvetica; color: #333; }
	h2{ font-weight: bold; font-size: 14px; }
	td.libelle{ width: 180px; }
	td.valeur{ width: 100px; }
	td.inputs, td.select{ width: 100%; }
	#animation{ height: 32px; width: 32px; background: #aaccf6;	margin: 22px; }
</style>
</head>
<body>
<table>
	<tr>
		<td class="libelle">Durée de l'animation (en ms)</td>
		<td class="valeur"><input type="text" id="duration" value="800"/></td>
	</tr>
	<tr>
		<td class="libelle">Nombre de pixels à animer</td>
		<td class="valeur"><input type="text" id="nbPix" value="180"/></td>
	</tr>
	<tr>
		<td class="libelle">Transition</td>
		<td class="valeur">
			<select id="transition">
				<option value="linear" selected="selected">Lineaire</option>
				<option value="elastic">Elastique</option>
				<option value="bounce">Bonds</option>
				<option value="skid">Liquide</option>
			</select>
		</td>
	</tr>
	<tr>
		<td class="libelle">Valeur à modifier</td>
		<td class="valeur">
			<select id="cssRule">
				<option value="height" selected="selected">Hauteur</option>
				<option value="width">Largeur</option>
				<option value="both">Hauteur et largeur</option>
			</select>
		</td>
	</tr>
</table>

<input type="button" value="GO" onclick="animate()"/> <input type="button" value="Reset" onclick="reset()"/>

<div id="animation"></div>

<script type="text/javascript">
	function $(idObj){
		return document.getElementById(idObj);
	};
	
	var transitions = {
		linear: function(t, b, c, d){ return c*t/d + b; },
		elastic: function (t,b,c,d,a,p){
			if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
			if (!a || a < Math.abs(c)) { a=c; var s=p/4; }
			else var s = p/(2*Math.PI) * Math.asin (c/a);
			return (a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b);
		},
		bounce: function(t,b,c,d){
			if((t/=d) < (1/2.75)) {
				return c*(7.5625*t*t) + b;
			}else if (t < (2/2.75)) {
				return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
			}else if (t < (2.5/2.75)) {
				return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
			}else{
				return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
			}
		},
		skid: function(t,b,c,d){
			var s = 1.70158;
			return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
		}
	};
	
	function reset(){
		$('animation').style.height = '32px';
		$('animation').style.width = '32px';
	};
	
	function animate(){
		var target = $('animation');
		var tmr = (1000/80); //assure une moyenne de 80 images par secondes
		var nbSteps = Math.round($('duration').value/tmr);
		var step = Math.abs(32 - $('nbPix').value)/nbSteps;
		var mapping = [];
		//On remplis un tableau contenant toutes les étapes de l'animation
		for(var i=1; i<=nbSteps; i++){
			mapping.push(transitions[$('transition').value](i*tmr, 32, $('nbPix').value - 32, $('duration').value));
		}
		var currentStep = 0;
		var timer = window.setInterval(function(){
			if(mapping[currentStep]){
				if($('cssRule').value=='both'){
					$('animation').style['height'] = mapping[currentStep]+'px';
					$('animation').style['width'] = mapping[currentStep]+'px';
				}else $('animation').style[$('cssRule').value] = mapping[currentStep]+'px';
				currentStep++;
			}else{
				window.clearInterval(timer);
				return;
			}
		}, tmr);
	};

</script>
</body>
</html>

A voir également

Ajouter un commentaire

Commentaires

nickadele
Messages postés
1251
Date d'inscription
mercredi 7 août 2002
Statut
Modérateur
Dernière intervention
10 avril 2013
-
Très bonne source au niveau du résultat et de l'effet !
Vu que l'objet de cette source c'est de montrer les effets de ROBERT PENNER, un + aurait été des commentaires sur la méthode utilisée.

Nickadele
ralecul
Messages postés
111
Date d'inscription
dimanche 23 mars 2003
Statut
Membre
Dernière intervention
1 août 2008
-
Bravo!

Cette source me donne effectivement envie de faire mon moteur d'animation à la mootools.
C'est une très bonne base pour découvrir les effets de Robert Penner.
Cela permettra également à certain de découvrir les singletons en JS.

La version de mootools n'utilise pas exactement les mêmes équations.
En revanche, j'ai pu constaté que tu as scrupuleusement respecté la version AS de Robert Penner ;-)
Ça aurait été sympa aussi de pouvoir choisir entre les modes easeIn, easeOut et easeInOut.
La version de mootools est beaucoup plus simple pour cela par rapport aux équations fournies par Robert Penner.

Commentaires sur le code :
var step = Math.abs(32 - $('nbPix').value)/nbSteps; -> tu ne te sers pas de cette variable.
window.setInterval et window.clearInterval -> pourquoi tu spécifies window ? je suppose que tu n'utilises pas window.alert...
corps de animate() -> j'aurais bien vu une version objet à la place (du genre : new Animate("animation", {duration: 800, type: linear}); )
ralecul
Messages postés
111
Date d'inscription
dimanche 23 mars 2003
Statut
Membre
Dernière intervention
1 août 2008
-
Désolé, je n'ai pas résisté à "prototyper" ta source :

function animate() {
new Animation("animation", {
duration : $('duration').value,
transition : $('transition').value,
amplitude : $('nbPix').value,
mode : $('cssRule').value,
initSize : 32
});
};

function Animation(div, options) {
var _options = options || {};
var _fps = _options.fps || 80;
var _duration = _options.duration || 800;
var _transition = _options.transition || "linear";
var _amplitude = _options.amplitude || 180;
var _mode = _options.mode || "both";
var _initSize = _options.initSize || 42; //because 42 is the answer ;-)
var _div = $(div);

var _interval = 1000/_fps;
var _nbSteps = Math.round(_duration/_interval);

var _mapping = [];
for(var i=1; i<=_nbSteps; ++i)
_mapping.push(transitions[_transition](i*_interval, _initSize, _amplitude - _initSize, _duration));

var currentStep = 0;
var timer = setInterval(iter, _interval);

function iter() {
if (_mapping[currentStep]) {
if (_mode == 'both') {
_div.style.height = _mapping[currentStep]+'px';
_div.style.width = _mapping[currentStep]+'px';
}
else {
_div.style[_mode] = _mapping[currentStep]+'px';
}
currentStep++;
}
else {
clearInterval(timer);
return;
}
}
}

PS : c'est fait à la va-vite, ya peut-être quelques erreurs...
PPS : Il suffit de remplacer la fonction animate par celle-ci et d'ajouter la classe Animation.
XtremDuke
Messages postés
631
Date d'inscription
samedi 28 septembre 2002
Statut
Membre
Dernière intervention
18 mai 2009
3 -
"tu ne te sers pas de cette variable"

>> Effectivement je ne m'en sers pas. Il s'agit en fait d'un simple copie/colle d'un travaille personnel.

"pourquoi tu spécifies window "

>> Alors ça, aucune idée, c'est une mauvaise (ou bonne ???) habitude.

"Il suffit de remplacer la fonction animate par celle-ci et d'ajouter la classe Animation"

>> tu n'as pas ecris de classe mais deux objets.

Pour parler de ta 'classe', il te faut rajouter une gestion des timers. Sinon, tu vas avoir des surprises si tu utilises cette fonction sur des 'mouseover'.

Remarque : Cette base que j'ai fournis et normalement ecrit sous forme de classe ds un projet perso. Voir :
http://joof-api.net/demo.html
lakichemole
Messages postés
253
Date d'inscription
vendredi 13 juin 2003
Statut
Membre
Dernière intervention
18 mai 2009
-
Il manque l'include de prototype dans ton exemple :/

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.