Interaction Design WikiProgramming

p5.js Easing

Easing functions are a type of function that takes a numeric input between 0 and 1. That number runs through the specified function and returns another number between 0 and 1. This special property helps us make any computation we want while remaining within specific bounds.

The purpose of an easing function is to get non-linear values from linear value inputs. When used for animation, easing can be used to create more natural movements and for creating physics like effects such as bouncing. Have a look at the following diagram to see what happens if different values are being used:

Simple easing

let x = 1;
let y = 1;
let easing = 0.05;
let currentPos;
let targetPos;

function setup() {
  createCanvas(720, 400);
  noStroke();
}

function draw() {
  background(237, 34, 93);

  let targetX = mouseX;
  let dx = targetX - x;
  x += dx  * easing;     //easing in the X axis

  let targetY = mouseY;
  let dy = targetY - y;
  y += dy * easing;   //easing in the Y axis

  ellipse(x, y, 66, 66);
  
  currentPos=createVector(round(x),round(y));
  targetPos=createVector(mouseX,mouseY);

}

function mousePressed() {
  if(targetPos.sub(currentPos) > [2,0,0] ) {
    for(let i = mouseX; i<width; i++){
      x = mouseX + i ;
    }
   }  
}

Easing.js

The code below uses a bell curve to generate the animation. The advantage of using this function is that the size of the points return to there original state once the transformation is over. There is however a boundless number of functions that can be used for animation, many of which have been documented by Robert Penner for several programming languages.

let arrayWidth = 20;
let arrayHeight = 20;
let dist_x = 20;
let dist_y = 20;
let shapeWidth = 10;
let shapeHeight = 10;
let duration = 120;
let scaleTimer = 0;
let amplitude = 2; 
let wavelength = 30;
let xOffset;
let yOffset;
var reactorPosition;
 
function setup() {
  createCanvas(800, 700);
  xOffset = floor(width/2-(dist_x*arrayWidth/2));
  yOffset = floor(height/2-(dist_y*arrayHeight/2));
  reactorPosition = createVector(0, 0);
  noStroke();
  fill(0);
}
 
function draw() {
  background(255);
  scaleTimer+=3;
  push();
  translate(xOffset, yOffset);
  for (let i = 0; i<arrayWidth; i++ ) {
    for (let j = 0; j<arrayHeight; j++ ) {
      rectMode(CENTER);
      var myPos =  createVector(i*dist_x, j*dist_y);
      var reactorDistance = dist(reactorPosition.x, reactorPosition.y, myPos.x, myPos.y);
      var myStartTime = int(scaleTimer-reactorDistance);
      var scaler =  bellCurve(myStartTime, shapeWidth, amplitude, wavelength);
      ellipse(myPos.x, myPos.y, scaler, scaler);
    }
  }
  pop();
}
 
function mouse_X() {
  return (mouseX - xOffset); //correct positions matrix translations
}
 
function mouse_Y() {
  return (mouseY - xOffset); //correct positions matrix translations
}

function mouseClicked() {
  scaleTimer = -100;
  reactorPosition.x = mouse_X();
  reactorPosition.y = mouse_Y();
}
 
function bellCurve(t, a, b, c) {
// see https://en.wikipedia.org/wiki/Gaussian_function
  // t = time
  // a = start value
  // b = amplitude
  // c = wavelength 
  var scaler = 1+(c/sqrt((c*c)+(t*t)))*b; //bell curve
  return scaler*a;
}
 


Exercise

What other wave functions could be used to generate patterns in this way, and how could it be made more responsive? Change the following code so the movement is influenced by the user using different easing method.

var gridWidth = 20;
var gridHeight = 20;
var shapeWidth = 20;
var shapeHeight = 20;
var dist_x = 20;
var dist_y = 20;
var duration = 40;
var duration2 = 50;
var scaleTimer = 0;
var amplitude = 2;
var xOffset;
var yOffset;

 
function setup() {
 createCanvas(800, 800);
 xOffset = floor(width/2-(dist_x*gridWidth/2));
 yOffset = floor(height/2-(dist_y*gridHeight/2));
 reactorPosition = createVector(0, 0);

}
 
function draw() {
 background(0);
 //fill(10);
 stroke(0);
 scaleTimer++;
 push(); //saves current position of the coordinate system
 translate(xOffset, yOffset);//translate grid to center
 for (var i = 0; i<gridWidth; i++ ) {
   for (var j = 0; j<gridHeight; j++ ) {
 
   let reactorDistance = dist(reactorPosition.x,   reactorPosition.y, i*dist_x, j*dist_y);
   let myStartTime = int(scaleTimer-reactorDistance);
   let angle = radians(myStartTime)*1.5;
   let _scale = sin(angle)*amplitude;
 
   ellipse(i*dist_x, j*dist_y, shapeWidth*_scale,     shapeHeight*_scale);
   
   }
 }
 pop();
}
 
function mouse_X() {
 return (mouseX - xOffset); //correct positions matrix translations
}
 
function mouse_Y() {
 return (mouseY - xOffset); //correct positions matrix translations
}

function mouseClicked() {
 scaleTimer = 0;
 reactorPosition.x = mouse_X();
 reactorPosition.y = mouse_Y();
}