Make a 'Stepwise easing' with the various easing functions

The graph of 'Stepwise easing' with the various easing functions.

The effect of the change in speed called 'Easing' makes your animation more attractive.


The upper one has no easing effect. The under one got an easing effect.

Let me show you the 'Stepwise easing' made with various easing functions. And the example code of animation using the 'Stepwise easing'.

👉 この記事は日本語でも読めます。

 

The various easing functions.

What is the 'easing'?

'Easing' is generally the effect of the change in speed. In the programming code, it'll be the function that changes the parameter value from 0.0 to 1.0 with some curve.

For example, the code and the graph of the animation shown before are like these.


// no easing
function noEasing(x) {
  return x;
}

// easing
function easing(x) {
  return x * x * x;
}

The graph of no-easing and easing function return values.

The left one has no easing effect. The right one got an easing effect. The x-axis is the parameter value of the function. And the y-axis is the return value of the function. The graph of the easing function is just a graph of x cubed.

 

How can I make an easing function?

There are various easing functions, and you can see these in the 'Easing Cheat Sheet'.

'Easing Cheat Sheet' from the Robert Penner's Easing Functions
https://easings.net/

You can see the description of the function when you click each graph. And there is a code in the section of 'Math function'. The code is written in TypeScript, and you can use it in 'p5.js' with some change.


// TypeScript
function easeInCubic(x: number): number {
  return x * x * x;
}

// p5.js
function easeInCubic(x) {
  return x * x * x;
}


 







The example 'p5.js' code using the 'easeInCubic' easing.



// Rotating circles.

const w = 720;
const h = w;
const cNum = 3;
const fRate = 30;
const cycle = fRate * 2;

function setup() {
  createCanvas(w, h);
  frameRate(fRate);
}

function draw() {

  const ease = easeInCubic((frameCount % cycle) / cycle)

  translate(w * 0.5, h * 0.5);
  background(240);

  noStroke();
  for (let c = 0; c < cNum; c++) {
    let r = 0.2 * (0.5 + sin(PI * ease));
    let t = TWO_PI * (ease + c / cNum);
    let x = w * r * cos(t);
    let y = h * r * sin(t);
    fill((c * 100) % 255);
    circle(x, y, w * 0.1);
  }

}

// easing function
function easeInCubic(x) {
  return x * x * x;
}

 

Using many functions at once.

I thought that chaining many easing functions might be some new easing movement. So I drew this graph.

The graph of 'Stepwise easing' with the various easing functions.

This graph used five functions. And the result looks like a stairway so I call it 'Stepwise easing'.

These are example animations. Using one 'easeInOutCubic' function.


Chaining three functions 'easeInOutCubic', 'easeOutQuad', 'easeInCubic'.

The graph of 'Stepwise easing' with three easing functions.

Using the 'Stepwise easing' makes various movements than using only one easing function. You may enjoy the new movement every time if you change the sequence of functions randomly.

 







 

The example code of 'Processing' using the 'Stepwise easing'.


I was interested in the rectangles drawn with the 'blendMode(DIFFERENCE)'.

An interesting shape of rectangles drawn with the 'blendMode(DIFFERENCE)'.

The pattern in the middle of the movement that is characteristic with the 'blendMode(DIFFERENCE)' looked good. So I wanted to show the stop motion, and it is suitable for the 'Stepwise easing'.

This code does not display any images on the screen but generates image files in frames directory. You can make an animation with these files.

Please feel free to use this example code under the terms of the GPL.


/**
 * Life Goes Round.
 * an animation using step easing.
 *
 * @author @deconbatch
 * @version 0.1
 * @license GPL Version 3 http://www.gnu.org/licenses/
 * Processing 3.5.3
 * 2022.01.23
 */


void setup() {
  size(720, 720);
  colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  smooth();
  noLoop();
	rectMode(CENTER);

  int   frmRate  = 30;
  int   cycleSec = 3;
  int   frmMax   = frmRate * cycleSec;
  float rectSiz  = min(width, height) * random(0.5, 0.8);
  float phaseR   = random(PI);
  float phaseT   = random(PI);
  float hueOrg   = random(360.0);

  // easing functions
  ArrayList<Ease> easing = new ArrayList<Ease>();
  easing.add(new InOutQuart());
  easing.add(new OutQuart());
  easing.add(new InBack());
  easing.add(new InQuad());
  easing.add(new OutBack());
  int cycleMax = easing.size();
  int easingStart = floor(random(cycleMax));
  
	translate(width * 0.5, height * 0.5);
  noStroke();
  float easePrev  = 0.0;
  float easeRatio = 0.0;
  for (int cycleCnt = 0; cycleCnt < cycleMax; cycleCnt++) {
    for (int frmCnt = 0; frmCnt < frmMax; frmCnt++) {
      // step easing
      float frmRatio = map(frmCnt, 0, frmMax - 1, 0.0, 1.0);
      easeRatio = easePrev + easing.get((easingStart + cycleCnt) % cycleMax).ease(frmRatio) / cycleMax;
      float radii = abs(sin(phaseR + easeRatio * PI)) * rectSiz * 0.3;
      float hueBase = hueOrg + 360.0 * easeRatio;
  
      blendMode(BLEND);
      background(hueBase % 360.0, 30.0, 60.0, 100.0);

      blendMode(DIFFERENCE);
      fill((hueBase + 60.0) % 360.0, 30.0, 80.0, 100.0);
      for (int p = 0; p < 8; p++) {
        int sign = (p % 2 == 0) ? 1 : -1;
        for (float r = 0.2; r < 0.5; r += 0.1) {
          float t = PI * 0.25 * p + sign * (phaseT + TWO_PI * easeRatio) * r * 2.5;
          float x = r * width * cos(t);
          float y = r * height * sin(t);
          rect(x, y, rectSiz, rectSiz, radii);
        }
      }
  
      saveFrame("frames/" + String.format("%02d", cycleCnt) + ".00." + String.format("%04d", frmCnt) + ".png");

    }

    // for stop motion
    for (int i = 0; i < frmRate; i++) {
      saveFrame("frames/" + String.format("%02d", cycleCnt) + ".01." + String.format("%04d", i) + ".png");
    }
    
    easePrev = easeRatio;

  }
  exit();
}


/**
 * Ease : hold easing functions.
 * reference : Robert Penner's Easing Functions (https://easings.net/)
 */
public interface Ease {
  public float ease(float _t);
}

public class InOutQuart implements Ease {
  public float ease(float _t) {
    return (_t < 0.5) ? 8.0 * _t * _t * _t * _t : 1 - pow(-2.0 * _t + 2.0, 4) / 2.0;
  }
}

public class OutQuart implements Ease {
  public float ease(float _t) {
    return 1.0 - pow(1.0 - _t, 4);
  }
}

public class InQuad implements Ease {
  public float ease(float _t) {
    return pow(_t, 2);
  }
}

public class InBack implements Ease {
  public float ease(float _t) {
    float c1 = 1.70158;
    float c3 = c1 + 1;
    return c3 * _t * _t * _t - c1 * _t * _t;
  }
}

public class OutBack implements Ease {
  public float ease(float _t) {
    float c1 = 1.70158;
    float c3 = c1 + 1;
    return 1.0 + c3 * pow(_t - 1.0, 3) + c1 * pow(_t - 1.0, 2);
  }
}

/*
Copyright (C) 2022- deconbatch

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>
*/


 

Next Post Previous Post
No Comment
Add Comment
comment url