Easing circular
Imaginemos una pelota que sigue al ratón, no en línea recta sino en un movimiento circular. Esto es fácil. Primero iniciamos el canvas y declaramos algunas variables necesarias:
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
var cw = canvas.width = 300,
cx = cw / 2;
var ch = canvas.height = 300,
cy = ch / 2;
ctx.fillStyle = "#6ab150";
ctx.strokeStyle = "#ccc";
var rad = (Math.PI / 180);// un grado sexagesimal en radianes
var R = 100; // el radio de la trayectoria circular
var centro = { // el centro de la trayectoria circular
x: cx,
y: cy
};
var m = {// inicia el ratón
x: cx,
y: cy,
a: 0
};
La función dibujarTrayectoria()
hace exactamente esto dibuja la trayectoria a seguir por la pelota.
function dibujarTrayectoria() {
ctx.beginPath();
ctx.arc(cx, cy, R, 0, 2 * Math.PI);
ctx.stroke();
}
La función Pelota()
es un constructor que crea una nueva pelota.
El nuevo objeto tiene dos métodos: un método para dibujar()
, y otro método para actualizar()
la pelota en función de la posición del ratón ( m
).
También necesitamos una manera de detectar la posición del ratón:
function oMousePos(canvas, evt) {
//detecta la posición del ratón y devuelve un objeto con las coordenadas de este
var ClientRect = canvas.getBoundingClientRect();
return {
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
A continuación instanciamos un nueva pelota, dibujamos la trayectoria y dibujamos la pelota.
var pelota = new Pelota();
dibujarTrayectoria();
pelota.dibujar();
Aquí empieza la parte realmente interesante: un evento mousemove
detecta la posición del ratón ( m
) cuando este pasa por encima del canvas y calcula el ángulo correspondiente a la posición del ratón ( m.a
) relativo al centro de la trayectoria ( centro
).
// detecta la posición del ratón, cuando el ratón pasa por encima del canvas (on "mousemove",):
canvas.addEventListener("mousemove", function(evt) {
m = oMousePos(canvas, evt);
var dx = (m.x - centro.x);
var dy = (m.y - centro.y);
m.a = Math.atan2(dy, dx);
console.log(m.a);
}, false);
Finalmente la función Animacion()
( una función recursiva que se llama a si misma ) limpia el canvas con cleatRect()
( borra última fotograma ), actualiza las coordenadas de la pelota, y dibuja de nuevo la trayectoria y la pelota.
function Animacion() {
requestId = window.requestAnimationFrame(Animacion);
ctx.clearRect(0, 0, cw, ch);
pelota.actualizar();
dibujarTrayectoria()
pelota.dibujar();
}
requestId = window.requestAnimationFrame(Animacion);
Veamos el código
See the Pen easing circular ES #0 by Gabi (@enxaneta) on CodePen.
Easing circular
Para utilizar el easing ( var easing = .1;
) en este caso calculamos la velocidad angular de esta manera:
1. Primero calculamos el ángulo ( m.a
) correspondiente a la posición del ratón ( m
), relativo al centro de la trayectoria ( c
)
var dx = (m.x - c.x); var dy = (m.y - c.y); m.a = Math.atan2(dy, dx);
2. Calculamos la velocidad del objeto ( o
) que queremos mover, la pelota en este caso.
o.va = (m.a - o.a)*easing;
3. Actualizamos las coordenadas del objeto o ( recuerde: en este caso el objeto es la pelota ):
o.x = cx + R*Math.cos(o.a); o.y = cy + R*Math.sin(o.a);
En cada fotograma volvemos a repetir todo esto, así que podemos ponerlo en una función:
function Interpolar(o,m,c,easing){
// m es el ratón o sea el punto de destino: m = {x:cx,y:cy,a:0};
// o es el objeto que queremos mover, la pelota en este caso o = {x:cx,y:cy,a:0};
// c es el centro de la trayectoria c = {x:cx,y:cy};
var dx = (m.x - c.x);
var dy = (m.y - c.y);
m.a = Math.atan2(dy, dx);
o.va = (m.a - o.a)*easing;
o.a += o.va;
o.x = cx + R*Math.cos(o.a);
o.y = cy + R*Math.sin(o.a);
}
También hay que modificar la función Animación()
:
function Animacion() {
requestId = window.requestAnimationFrame(Animacion);
fotogramas ++;
ctx.clearRect(0,0,cw,ch);
Interpolar(pelota,m,centro,easing);
dibujarTrayectoria ()
pelota.dibujar();
}
Veamos el código:
See the Pen easing circular ES #1 by Gabi (@enxaneta) on CodePen.
Es obvio que ¡¡HAY UN PROBLEMA, Y GORDO!! Cuando el ratón llega a 180° ( Math.PI
) la pelota da la vuelta a toda la trayectoria para llegar a lo que nosotros consideramos 181°. Esto pasa porque para Math.atan2
"el siguiente punto" no es 181° sino -179°.
Lea este articulo acerca de la arcotangente en JavaScript. Math.atan versus Math.atan2.
Para arreglar este problema sencillamente añadimos dos líneas de código a la función Interpolar()
.
if ( o.a < m.a - Math.PI ) {o.a = o.a + 2*Math.PI;} if ( o.a > m.a + Math.PI ) {o.a = o.a - 2*Math.PI;}
function Interpolar(o,m,c,easing){
// m es el ratón o sea el punto de destino: m = {x:cx,y:cy,a:0};
// o es el objeto que queremos mover, la pelota en este caso o = {x:cx,y:cy,a:0};
// c es el centro de la trayectoria c = {x:cx,y:cy};
var dx = (m.x - c.x);
var dy = (m.y - c.y);
m.a = Math.atan2(dy, dx);
// añadimos estas dos líneas de código
if ( o.a < m.a - Math.PI ) {o.a = o.a + 2*Math.PI;}
if ( o.a > m.a + Math.PI ) {o.a = o.a - 2*Math.PI;}
o.va = (m.a - o.a)*easing;
o.a += o.va;
o.x = cx + R*Math.cos(o.a);
o.y = cy + R*Math.sin(o.a);
}
Ahora sí. Veamos de nuevo el código:
See the Pen easing circular ES #2 by Gabi (@enxaneta) on CodePen.