SOITZ

Easing Functions

Published on

easing 함수는 애니메이션 또는 상태의 변화가 시간에 따라 어떻게 진행되는지를 정의하는 수학적 함수입니다. 이는 주로 웹 개발, 게임 개발, 그래픽 디자인 등에서 시각적인 요소의 움직임을 부드럽고 자연스럽게 만들기 위해 사용됩니다. easing 함수는 단순히 선형(linear) 움직임보다는 더 복잡하고 현실적인 동작을 재현합니다.

항상 애니메이션 라이브러리를 통해서만 사용하던 easing 함수에 대해서 한 번 정리해보고 싶어서 찾아보았습니다. 아래 GitHub에 있는 코드를 참고했습니다.

https://github.com/ai/easings.net/blob/master/src/easings/easingsFunctions.ts

자주 사용하는 함수는 따로 선언되어 있습니다.

const pow = Math.pow;
const sqrt = Math.sqrt;
const sin = Math.sin;
const cos = Math.cos;
const PI = Math.PI;
const c1 = 1.70158;
const c2 = c1 * 1.525;
const c3 = c1 + 1;
const c4 = (2 * PI) / 3;
const c5 = (2 * PI) / 4.5;

선형 (Linear)

선형 easing은 변환되지 않은 비율을 반환합니다. 애니메이션은 균일한 속도로 진행됩니다.

const linear = (t) => t;

Quad (Quadratic)

Quadratic easing은 제곱을 사용하여 애니메이션의 속도를 조절합니다. t^2의 형태로 나타나며, 애니메이션은 부드러운 시작과 끝을 가지되, 중간에는 상대적으로 빠른 속도로 진행됩니다. 이는 기본적이면서도 널리 사용되는 easing 형태로, 약간의 가속이 필요할 때 유용합니다.

const easeInQuad = (t) => t * t;
const easeOutQuad = (t) => t * (2 - t);
const easeInOutQuad = (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t);

Cubic

Cubic easing은 세제곱을 사용하여 속도를 조절합니다. t^3의 형태로 표현되며, Quad보다 더 빠른 가속을 제공합니다. 시작과 끝에서 더 부드럽게, 중간에는 더욱 가파르게 속도가 변화합니다. 이 방식은 더욱 동적인 움직임이 요구될 때 적합합니다.

const easeInCubic = (t) => t * t * t;
const easeOutCubic = (t) => --t * t * t + 1;
const easeInOutCubic = (t) =>
  t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;

Quart (Quartic)

Quartic easing은 네제곱을 사용하여 애니메이션의 속도를 조절합니다. t^4의 형태로 나타나며, Cubic에 비해 더욱 강한 가속과 감속을 제공합니다. 이 유형의 easing은 매우 부드러운 시작과 끝, 그리고 중간에 매우 급격한 속도 변화를 원할 때 사용됩니다.

const easeInQuart = (t) => t * t * t * t;
const easeOutQuart = (t) => 1 - --t * t * t * t;
const easeInOutQuart = (t) =>
  t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;

Quint (Quintic)

Quintic easing은 다섯 제곱을 사용하여 속도를 조절합니다. t^5의 형태로, 시작과 종료 시 매우 부드러운 움직임과 함께, 가장 강력한 가속을 경험할 수 있습니다. 이는 Quart보다 더욱 강조된 가속과 감속이 특징으로, 매우 동적이고 강력한 애니메이션 효과를 원할 때 적합합니다.

const easeInQuint = (t) => t * t * t * t * t;
const easeOutQuint = (t) => 1 + --t * t * t * t * t;
const easeInOutQuint = (t) =>
  t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;

Sine (Sinusoidal)

Sinusoidal easing은 사인 곡선의 특성을 이용하여 애니메이션의 속도를 조절합니다. sin(t)의 형태로, 자연스러운 움직임을 생성하기 위해 사용됩니다. Sine easing은 시작과 종료가 매우 부드럽고, 전체적으로 부드러운 속도 변화를 나타내는 것이 특징입니다. 이 유형은 자연스러운 움직임을 시뮬레이션하거나, 부드러운 전환 효과가 필요할 때 유용합니다.

const easeInSine = (t) => 1 - cos((t * PI) / 2);
const easeOutSine = (t) => sin((t * PI) / 2);
const easeInOutSine = (t) => -(cos(PI * t) - 1) / 2;

Expo (Exponential)

Exponential easing은 지수 함수를 사용하여 매우 느린 시작 후 급격한 가속 또는 급격한 감속 후 매우 느린 종료를 표현합니다. 이 유형의 easing은 애니메이션의 효과가 시간이 지남에 따라 지수적으로 증가하거나 감소함을 의미합니다. 예를 들어, easeInExpo는 애니메이션이 매우 천천히 시작되어 끝으로 갈수록 급격히 속도가 증가하는 효과를 만듭니다.

const easeInExpo = (t) => (t === 0 ? 0 : pow(2, 10 * (t - 1)));
const easeOutExpo = (t) => (t === 1 ? 1 : 1 - pow(2, -10 * t));
const easeInOutExpo = (t) =>
  t === 0
    ? 0
    : t === 1
      ? 1
      : t < 0.5
        ? pow(2, 20 * t - 10) / 2
        : (2 - pow(2, -20 * t + 10)) / 2;

Circ (Circular)

Circular easing은 원형 경로를 따라 움직이는 것처럼 보이게 하는 easing 방식입니다. 시작과 끝에서 매끄럽게 속도가 변화하며, 특히 easeInCirc와 easeOutCirc는 각각 원의 내부와 외부 경로를 따라 움직이는 것처럼 보이게 합니다. 이 유형은 애니메이션에 부드러운 시작과 끝을 제공하면서도 중간에는 상대적으로 급격한 속도 변화를 가져옵니다.

const easeInCirc = (t) => 1 - sqrt(1 - t * t);
const easeOutCirc = (t) => sqrt(1 - --t * t);
const easeInOutCirc = (t) =>
  t < 0.5
    ? (1 - sqrt(1 - 2 * t * (2 * t))) / 2
    : (sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;

Back

Back easing은 애니메이션이 시작점이나 종료점에서 약간 후퇴한 후, 목표 지점으로 빠르게 전진하거나 부드럽게 정착하는 효과를 만듭니다. 이는 마치 애니메이션이 뒤로 잠깐 후퇴하는 듯한 인상을 주며, 이러한 효과는 애니메이션에 독특한 '밀어내기' 느낌을 추가합니다.

const easeInBack = (t) => c3 * t * t * t - c1 * t * t;
const easeOutBack = (t) => 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2);
const easeInOutBack = (t) =>
  t < 0.5
    ? (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
    : (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2;

Elastic

Elastic easing은 고무줄이 늘어났다가 돌아오는 것처럼, 애니메이션이 목표 지점을 넘어서 잠깐 확장되었다가 다시 돌아오는 효과를 만듭니다. 이 유형의 easing은 시작과 끝에서 '바운스' 효과를 나타내며, 마치 물체가 탄성을 가진 것처럼 늘어나고 줄어드는 모션을 보여줍니다.

const easeInElastic = (t) =>
  t === 0 ? 0 : t === 1 ? 1 : -pow(2, 10 * t - 10) * sin((t * 10 - 10.75) * c4);
const easeOutElastic = (t) =>
  t === 0 ? 0 : t === 1 ? 1 : pow(2, -10 * t) * sin((t * 10 - 0.75) * c4) + 1;
const easeInOutElastic = (t) =>
  t === 0
    ? 0
    : t === 1
      ? 1
      : t < 0.5
        ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
        : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 +
          1;

Bounce

Bounce easing은 물체가 떨어져서 바닥에 닿았을 때 여러 번 튀어오르는 것과 유사한 효과를 애니메이션에 추가합니다. 이 easing 유형은 물체가 마지막 목적지에 도달하기 전에 여러 번 바운스(튕겨오름)하는 모습을 통해 생동감과 재미를 더합니다. easeOutBounce는 목표 지점에 도착하여 멈추기 전에 몇 번의 반동을 포함하는 효과를 제공합니다.

const bounceOut = function (x) {
  const n1 = 7.5625;
  const d1 = 2.75;

  if (t < 1 / d1) {
    return n1 * t * t;
  } else if (t < 2 / d1) {
    return n1 * (t -= 1.5 / d1) * t + 0.75;
  } else if (t < 2.5 / d1) {
    return n1 * (t -= 2.25 / d1) * t + 0.9375;
  } else {
    return n1 * (t -= 2.625 / d1) * t + 0.984375;
  }
};

const easeInBounce = (t) => 1 - bounceOut(1 - t),
const easeOutBounce = bounceOut,
const easeInOutBounce = (t) =>
	t < 0.5
	? (1 - easeOutBounce(1 - 2 * t)) / 2
	: (1 + easeOutBounce(2 * t - 1)) / 2,

전체 코드

아래는 전체 코드 입니다. 이 함수들은 추후에 requestAnimationFrame을 활용하는 Motion 클래스에 사용할 예정입니다.

/* 
  https://github.com/ai/easings.net/blob/master/src/easings/easingsFunctions.ts
*/
const bounceOut = function (x) {
  const n1 = 7.5625;
  const d1 = 2.75;

  if (t < 1 / d1) {
    return n1 * t * t;
  } else if (t < 2 / d1) {
    return n1 * (t -= 1.5 / d1) * t + 0.75;
  } else if (t < 2.5 / d1) {
    return n1 * (t -= 2.25 / d1) * t + 0.9375;
  } else {
    return n1 * (t -= 2.625 / d1) * t + 0.984375;
  }
};

const pow = Math.pow;
const sqrt = Math.sqrt;
const sin = Math.sin;
const cos = Math.cos;
const PI = Math.PI;
const c1 = 1.70158;
const c2 = c1 * 1.525;
const c3 = c1 + 1;
const c4 = (2 * PI) / 3;
const c5 = (2 * PI) / 4.5;

const easingFunctions = {
  // 선형 easing은 변환되지 않은 비율을 반환합니다. 애니메이션은 균일한 속도로 진행됩니다.
  linear: (t) => t,

  /*
    Quad (Quadratic)
    Quadratic easing은 제곱을 사용하여 애니메이션의 속도를 조절합니다. 
    t^2의 형태로 나타나며, 애니메이션은 부드러운 시작과 끝을 가지되, 중간에는 상대적으로 빠른 속도로 진행됩니다. 
    이는 기본적이면서도 널리 사용되는 easing 형태로, 약간의 가속이 필요할 때 유용합니다.
  */
  easeInQuad: (t) => t * t,
  easeOutQuad: (t) => t * (2 - t),
  easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),

  /*
    Cubic
    Cubic easing은 세제곱을 사용하여 속도를 조절합니다. 
    t^3의 형태로 표현되며, Quad보다 더 빠른 가속을 제공합니다. 
    시작과 끝에서 더 부드럽게, 중간에는 더욱 가파르게 속도가 변화합니다. 
    이 방식은 더욱 동적인 움직임이 요구될 때 적합합니다.
  */
  easeInCubic: (t) => t * t * t,
  easeOutCubic: (t) => --t * t * t + 1,
  easeInOutCubic: (t) =>
    t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,

  /*
    Quart (Quartic)
    Quartic easing은 네제곱을 사용하여 애니메이션의 속도를 조절합니다. 
    t^4의 형태로 나타나며, Cubic에 비해 더욱 강한 가속과 감속을 제공합니다. 
    이 유형의 easing은 매우 부드러운 시작과 끝, 그리고 중간에 매우 급격한 속도 변화를 원할 때 사용됩니다.
  */
  easeInQuart: (t) => t * t * t * t,
  easeOutQuart: (t) => 1 - --t * t * t * t,
  easeInOutQuart: (t) =>
    t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t,

  /*
    Quint (Quintic)
    Quintic easing은 다섯 제곱을 사용하여 속도를 조절합니다. 
    t^5의 형태로, 시작과 종료 시 매우 부드러운 움직임과 함께, 가장 강력한 가속을 경험할 수 있습니다. 
    이는 Quart보다 더욱 강조된 가속과 감속이 특징으로, 매우 동적이고 강력한 애니메이션 효과를 원할 때 적합합니다.
  */
  easeInQuint: (t) => t * t * t * t * t,
  easeOutQuint: (t) => 1 + --t * t * t * t * t,
  easeInOutQuint: (t) =>
    t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t,

  /*
    Sine (Sinusoidal)
    Sinusoidal easing은 사인 곡선의 특성을 이용하여 애니메이션의 속도를 조절합니다. 
    sin(t)의 형태로, 자연스러운 움직임을 생성하기 위해 사용됩니다. 
    Sine easing은 시작과 종료가 매우 부드럽고, 전체적으로 부드러운 속도 변화를 나타내는 것이 특징입니다. 
    이 유형은 자연스러운 움직임을 시뮬레이션하거나, 부드러운 전환 효과가 필요할 때 유용합니다.
  */
  easeInSine: (t) => 1 - cos((t * PI) / 2),
  easeOutSine: (t) => sin((t * PI) / 2),
  easeInOutSine: (t) => -(cos(PI * t) - 1) / 2,

  /*
    Expo (Exponential)
    Exponential easing은 지수 함수를 사용하여 매우 느린 시작 후 급격한 가속 또는 급격한 감속 후 매우 느린 종료를 표현합니다. 
    이 유형의 easing은 애니메이션의 효과가 시간이 지남에 따라 지수적으로 증가하거나 감소함을 의미합니다. 
    예를 들어, easeInExpo는 애니메이션이 매우 천천히 시작되어 끝으로 갈수록 급격히 속도가 증가하는 효과를 만듭니다.
  */
  easeInExpo: (t) => (t === 0 ? 0 : pow(2, 10 * (t - 1))),
  easeOutExpo: (t) => (t === 1 ? 1 : 1 - pow(2, -10 * t)),
  easeInOutExpo: (t) =>
    t === 0
      ? 0
      : t === 1
        ? 1
        : t < 0.5
          ? pow(2, 20 * t - 10) / 2
          : (2 - pow(2, -20 * t + 10)) / 2,

  /*
    Circ (Circular)
    Circular easing은 원형 경로를 따라 움직이는 것처럼 보이게 하는 easing 방식입니다. 
    시작과 끝에서 매끄럽게 속도가 변화하며, 특히 easeInCirc와 easeOutCirc는 각각 원의 내부와 외부 경로를 따라 움직이는 것처럼 보이게 합니다. 
    이 유형은 애니메이션에 부드러운 시작과 끝을 제공하면서도 중간에는 상대적으로 급격한 속도 변화를 가져옵니다.
  */
  easeInCirc: (t) => 1 - sqrt(1 - t * t),
  easeOutCirc: (t) => sqrt(1 - --t * t),
  easeInOutCirc: (t) =>
    t < 0.5
      ? (1 - sqrt(1 - 2 * t * (2 * t))) / 2
      : (sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2,

  /*
    Back
    Back easing은 애니메이션이 시작점이나 종료점에서 약간 후퇴한 후, 목표 지점으로 빠르게 전진하거나 부드럽게 정착하는 효과를 만듭니다. 
    이는 마치 애니메이션이 뒤로 잠깐 후퇴하는 듯한 인상을 주며, 이러한 효과는 애니메이션에 독특한 '밀어내기' 느낌을 추가합니다.
  */
  easeInBack: (t) => c3 * t * t * t - c1 * t * t,
  easeOutBack: (t) => 1 + c3 * pow(t - 1, 3) + c1 * pow(t - 1, 2),
  easeInOutBack: (t) =>
    t < 0.5
      ? (pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
      : (pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2,

  /*
    Elastic
    Elastic easing은 고무줄이 늘어났다가 돌아오는 것처럼, 애니메이션이 목표 지점을 넘어서 잠깐 확장되었다가 다시 돌아오는 효과를 만듭니다. 
    이 유형의 easing은 시작과 끝에서 '바운스' 효과를 나타내며, 마치 물체가 탄성을 가진 것처럼 늘어나고 줄어드는 모션을 보여줍니다.
  */
  easeInElastic: (t) =>
    t === 0
      ? 0
      : t === 1
        ? 1
        : -pow(2, 10 * t - 10) * sin((t * 10 - 10.75) * c4),
  easeOutElastic: (t) =>
    t === 0 ? 0 : t === 1 ? 1 : pow(2, -10 * t) * sin((t * 10 - 0.75) * c4) + 1,
  easeInOutElastic: (t) =>
    t === 0
      ? 0
      : t === 1
        ? 1
        : t < 0.5
          ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
          : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 +
            1,

  /*
    Bounce
    Bounce easing은 물체가 떨어져서 바닥에 닿았을 때 여러 번 튀어오르는 것과 유사한 효과를 애니메이션에 추가합니다. 
    이 easing 유형은 물체가 마지막 목적지에 도달하기 전에 여러 번 바운스(튕겨오름)하는 모습을 통해 생동감과 재미를 더합니다.
    easeOutBounce는 목표 지점에 도착하여 멈추기 전에 몇 번의 반동을 포함하는 효과를 제공합니다.
  */
  easeInBounce: (t) => 1 - bounceOut(1 - t),
  easeOutBounce: bounceOut,
  easeInOutBounce: (t) =>
    t < 0.5
      ? (1 - easeOutBounce(1 - 2 * t)) / 2
      : (1 + easeOutBounce(2 * t - 1)) / 2,
};