web-dev-qa-db-fra.com

Qu'est-ce qu'une fonction d'assouplissement?

Qu'entend-on par fonction d'accélération dans le contexte de l'animation. Il semble que le dojo, le jquery, le silverlight, le flex et d'autres systèmes d'interface utilisateur aient la notion de fonction d'assouplissement. Je n'ai pas pu trouver une bonne explication des fonctions d'assouplissement? Quelqu'un peut-il expliquer le concept d'assouplissement des fonctions, ou en indiquer une bonne explication, je suis intéressé par le concept et non par les détails spécifiques d'un cadre?

L'assouplissement est-il strictement utilisé pour la localisation ou est-il général et peut-il être appliqué à n'importe quelle propriété d'un objet?

46
ams

Une fonction d'accélération est généralement une fonction qui décrit la valeur d'une propriété étant donné un pourcentage d'exhaustivité. Différents cadres utilisent des variations légèrement différentes, mais le concept est facile à saisir une fois que vous avez l'idée, mais il est probablement préférable de regarder quelques exemples.

Voyons d'abord l'interface que toutes nos fonctions d'accélération respecteront.

Nos fonctions d'accélération prendront plusieurs arguments:

  • percentComplete: (0.0 à 1.0).
  • elaspedTime: nombre de millisecondes pendant lesquelles l'animation a été exécutée
  • startValue: la valeur à partir de laquelle (ou la valeur lorsque le pourcentage achevé est 0%)
  • endValue: la valeur à laquelle se terminer (ou la valeur lorsque le pourcentage achevé est de 100%)
  • totalDuration: la durée totale souhaitée de l'animation en millisecondes

Et renverra un nombre qui représente la valeur à laquelle la propriété doit être définie.

Remarque: il s'agit de la même signature que jQuery utilise pour ses fonctions d'accélération, que j'emprunterai pour des exemples.

Le plus simple à comprendre est une facilité linéaire:

var linear = function(percent,elapsed,start,end,total) {
    return start+(end-start)*percent;
}

Et maintenant, pour mettre cela à profit:

Disons que nous avions une animation qui allait durer 1000 millisecondes et devait commencer à 0 et se terminer à 50. Le passage de ces valeurs dans notre fonction d'accélération devrait nous dire quelle devrait être la valeur réelle:

linear(0, 0, 0,50, 1000)        // 0
linear(0.25, 250, 0, 50, 1000)  // 12.5
linear(0.5, 500, 0, 50, 1000)   // 25
linear(0.75, 750, 0, 50, 1000)  // 37.5
linear(1.0, 1000, 0, 50, 1000)  // 50

C'est une interpolation assez simple (sans jeu de mots). Il s'agit d'une simple interpolation linéaire. Si vous deviez représenter graphiquement la valeur en fonction du temps, ce serait une ligne droite:

Linear ease

Jetons un œil à une fonction d'accélération un peu plus compliquée, une facilité quadratique dans:

var easeInQuad = function (x, t, b, c, d) {
    return c*(t/=d)*t + b;
}

Et regardons les mêmes résultats, en utilisant les mêmes entrées que précédemment:

easeInQuad(0, 0, 0, 50, 1000)      // 0
easeInQuad(0.25, 250, 0, 50, 1000) // 3.125
easeInQuad(0.5, 500, 0, 50, 1000)  // 12.5
easeInQuad(0.75, 750, 0, 50, 1000) // 28.125
easeInQuad(1, 1000, 0, 50, 1000)   // 50

Notez que les valeurs sont très différentes de notre facilité linéaire. Il commence très lentement, puis accélère jusqu'à son point d'arrivée. À la fin de 50% de l'animation, il n'a atteint qu'une valeur de 12,5, ce qui correspond au quart de la distance réelle entre les valeurs start et end que nous avons spécifiées.

Si nous devions représenter graphiquement cette fonction, cela ressemblerait à ceci:

Quad-Ease-In

Jetons maintenant un coup d'œil à une facilité de base:

var easeOutQuad = function (x, t, b, c, d) {
    return -c *(t/=d)*(t-2) + b;
};

Cela fait essentiellement la courbe d'accélération "opposée" d'une facilité d'entrée. Il démarre rapidement puis décélère jusqu'à sa valeur finale:

Ease out

Et puis il y a des fonctions qui facilitent l'entrée et la sortie:

var easeInOutQuad = function (x, t, b, c, d) {
    if ((t/=d/2) < 1) return c/2*t*t + b;
    return -c/2 * ((--t)*(t-2) - 1) + b;
};

EaseInOut

Cette fonction commencera lentement et finira lentement, atteignant sa vitesse maximale au milieu.

Il existe un tas d'assouplissements/interpolations que vous pouvez utiliser: linéaire, quadradique, cubique, quart, quint, sinus. Et il existe des fonctions d'assouplissement spécialisées comme Bounce et élastique, qui ont leurs propres fonctions.

Par exemple, une facilité élastique dans:

var easeInElastic = function (x, t, b, c, d) {
    var s=1.70158;var p=0;var a=c;
    if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
    if (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-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},

Elastic ease in

Peut-être que quelqu'un d'autre peut expliquer la partie mathématique réelle derrière l'interpolation, car honnêtement, je ne suis pas un magicien des mathématiques. Mais c'est le principe de base des fonctions d'assouplissement elles-mêmes.

Lorsque vous démarrez une interpolation/animation, le moteur d'animation se souvient des valeurs de début et de fin souhaitées. Puis, à chaque mise à jour, ses chiffres indiquent le temps écoulé. Il appelle la fonction d'accélération fournie avec les valeurs pour déterminer la valeur à laquelle la propriété doit être définie. Tant que toutes les fonctions d'accélération implémentent la même signature, elles peuvent être échangées facilement et le moteur d'animation de base n'a pas à faire de différence. (Ce qui permet une excellente séparation des préoccupations).

Vous remarquerez que j'ai évité de parler explicitement des positions x et y, car l'assouplissement n'a rien à voir spécifiquement avec la position en soi . Une fonction d'accélération définit simplement une transition entre les valeurs de début et de fin. Il peut s'agir de coordonnées x, ou d'une couleur, ou de la transparence d'un objet.

Et en fait, en théorie, vous pourriez appliquer différentes fonctions d'accélération pour interpoler pour différentes propriétés. J'espère que cela aidera à éclairer l'idée de base.

Et voici vraiment exemple sympa (qui utilise une signature légèrement différente, mais qui est le même principe) avec lequel jouer pour avoir une idée de la façon dont l'assouplissement est lié à la position.


Modifier

Voici un peu jsFiddle J'ai jeté ensemble pour démontrer certaines des utilisations de base en javascript. Notez que la propriété top est interpolée à l'aide de bounce, et la propriété left est interpolée à l'aide d'un quad. Utilisez le curseur pour simuler la boucle de rendu.

Étant donné que toutes les fonctions de l'objet easing ont la même signature, vous pouvez les échanger l'une contre l'autre. À l'heure actuelle, la plupart de ces choses sont toutes codées en dur (des choses comme les valeurs de début et de fin, les fonctions d'interpolation utilisées et la durée de l'animation), mais dans un exemple réel d'aide à l'animation, vous voudriez passer dans les propriétés suivantes:

  • La propriété à changer
  • La valeur de départ (ou si elle est laissée undefined alors utilisez sa valeur actuelle)
  • La valeur finale
  • La longueur de l'animation doit être
  • La référence à la fonction d'interpolation que vous souhaitez utiliser.

Le moteur d'animation garderait une trace de ces paramètres pendant la durée de l'animation et pendant chaque cycle de mise à jour, il utiliserait l'argument d'interpolation pour calculer la nouvelle valeur des propriétés.

101
J. Holmes

Une fonction d'accélération est un algorithme qui contrôle la vitesse d'une animation pour donner l'effet souhaité (rebond, zoom avant et lent, etc.).

Découvrez ce que MSDN a à dire à leur sujet pour un peu plus de détails.

11
Justin Niessner

Je voudrais poster ma réponse à cette vieille question même si elle a une réponse acceptée. 2bitkid a fait l'explication nécessaire. Ce que j'ajouterai, c'est l'implémentation pratique de base, parce que je n'ai pas pu en trouver (dont j'ai également posté une question à ce sujet).

Prenez cette simple animation linéaire, par exemple. Je doute qu'il nécessite des explications car le code est explicite. Nous calculons une valeur d'incrément constante qui ne change pas dans le temps et à chaque itération, nous augmentons la position de la boîte. Nous modifions directement la variable de position, puis l'appliquons sur la boîte.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var posIncrement  = distance / iterations;               // change per frame
var position      = startPosition;                       // current position

function move() {
  position += posIncrement;              // increase position
  if (position >= endPosition) {         // check if reached endPosition
    clearInterval(handler);              // if so, stop interval
    box.style.left = endPosition + "px"; // jump to endPosition
    return;                              // exit function
  }
  box.style.left = position + "px";      // move to the new position
}

var handler = setInterval(move, 1000/fps); // run move() every 16~ millisecond
body {
        background: gainsboro;
}
#box {
        width: 100px;
        height: 100px;
        background: white;
        box-shadow: 1px 1px 1px rgba(0,0,0,.2);
        position: absolute;
        left: 0;
}
<div id="box"></div>

Maintenant, ajoutons l'assouplissement. Nous commençons simple en utilisant linear (sans assouplissement). Il en résultera la même animation ci-dessus, mais l'approche est différente. Cette fois, nous ne modifierons pas directement la variable de position. Ce que nous allons modifier, c'est le temps.

function linear(time, begin, change, duration) {
    return change * (time / duration) + start;
}

Parlons d'abord des paramètres.

  • time: le temps écoulé
  • begin: valeur initiale d'une propriété (largeur, gauche, marge, opacité, etc.)
  • change: déplacement, (valeur finale - valeur initiale)
  • duration: durée totale de l'animation

time et duration sont directement liés. Si vous avez une animation de 2 secondes, vous augmentez time et la passez à la fonction d'accélération linear. La fonction renverra une position qui indique que la boîte doit être à cette position au moment donné.

Disons que je déplace une case de 0 à 100 en 2 secondes. Si je veux obtenir la position de la boîte, disons à 700 millisecondes, j'appellerais la fonction linear de la manière suivante:

linear(0.7, 0, 100, 2);

qui retournerait 35. 700 millisecondes après le début de l'animation, la position de la boîte sera à 35 pixels. Voyons cela en action.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var timeIncrement = duration / iterations;
var position      = 0;
var time          = 0;

function move() {
        time += timeIncrement;
        position = linear(time, startPosition, distance, duration);
        if (position >= endPosition) {
                clearInterval(handler);
                box.style.left = endPosition + "px";
                return;
        }
        box.style.left = position + "px";
}

var handler = setInterval(move, 1000/fps);

function linear(time, begin, change, duration) {
        return change * (time / duration) + begin;
}
body {
        background: gainsboro;
}
#box {
        width: 100px;
        height: 100px;
        background: white;
        box-shadow: 1px 1px 1px rgba(0,0,0,.2);
        position: absolute;
        left: 0;
}
<div id="box"></div>

La partie qui a besoin d'attention dans ce code est:

var timeIncrement = duration / iterations;
var time = 0;

function move() {
    time += timeIncrement;
    position = linear(time, startPosition, distance, duration);
    // ...

Dans la première animation, nous avons directement modifié la variable de position. Nous avions besoin d'une valeur d'incrémentation de position constante. La façon dont nous avons calculé cela est posIncrement = distance / iterations. Avec l'assouplissement, nous ne modifions plus la variable de position, mais la variable de temps. Nous avons donc besoin d'une valeur d'incrément de temps. Nous le calculons de la même manière que nous avons positionné l'incrément, mais cette fois nous divisons duration par iterations. Nous augmentons le temps avec incrément de temps et passons le temps à la fonction d'accélération, et la fonction d'accélération nous renvoie la position suivante que la boîte devrait occuper.

total distance / iterations (frames) = position change per frame
total duration / iterations (frames) = time change per frame

Voici quelques graphiques pour l'œil.

Ease function graph


Et enfin, un exemple d'easyInOutQuad.

JSFiddle

var box = document.getElementById("box");

var fps           = 60;
var duration      = 2;                                   // seconds
var iterations    = fps * duration;                      // 120 frames
var startPosition = 0;                                   // left end of the screen
var endPosition   = window.innerWidth - box.clientWidth; // right end of the screen
var distance      = endPosition - startPosition;         // total distance
var timeIncrement = duration / iterations;
var time          = 0;
var position      = 0;

function move() {
  time += timeIncrement;
  position = easeInOutQuad(time, startPosition, distance, duration);
  if (position >= endPosition) {
    clearInterval(handler);
    box.style.left = endPosition + "px";
    return;
  }
  box.style.left = position + "px";
}

var handler = setInterval(move, 1000 / fps);

function easeInOutQuad(t, b, c, d) {
  if ((t /= d / 2) < 1) {
    return c / 2 * t * t + b;
  } else {
    return -c / 2 * ((--t) * (t - 2) - 1) + b;
  }
}
body {
        background: gainsboro;
}
#box {
        width: 100px;
        height: 100px;
        background: white;
        box-shadow: 1px 1px 1px rgba(0,0,0,.2);
        position: absolute;
        left: 0;
}
<div id="box"></div>
2
akinuri

Il s'agit d'une propriété (taille, forme, emplacement) de transition d'un état à un autre.

Voici quelques petits graphiques soignés décrivant les fonctions d'accélération offertes par jquery ui.

http://jqueryui.com/demos/effect/easing.html

0
Chris Lacasse