font = loadFont("NotoSansJP-SemiBold.ttf");
str_arr = stringToArray(str_arr);
morphing = new MorphingTypography(char1, char2,-1, font, width / 2, height / 2, 800);
class MorphingTypography {
constructor(char1, char2,n, font, centerX, centerY, fontSize) {
this.fontSize = fontSize;
this.points1 = this.font.textToPoints(this.char1, 0, 0, this.fontSize, {
this.points2 = this.font.textToPoints(this.char2, 0, 0, this.fontSize, {
this.bounds1 = font.textBounds(this.char1, 0, 0, this.fontSize);
this.bounds2 = font.textBounds(this.char2, 0, 0, this.fontSize);
this.char1 = str_arr[(this.n+1)%str_arr.length];
this.char2 = str_arr[(this.n+2)%str_arr.length];
this.points1 = this.font.textToPoints(this.char1, 0, 0, this.fontSize, {
this.points2 = this.font.textToPoints(this.char2, 0, 0, this.fontSize, {
this.bounds1 = font.textBounds(this.char1, 0, 0, this.fontSize);
this.bounds2 = font.textBounds(this.char2, 0, 0, this.fontSize);
x: lerp(this.bounds1.x, this.bounds2.x, this.morph),
y: lerp(this.bounds1.y, this.bounds2.y, this.morph),
w: lerp(this.bounds1.w, this.bounds2.w, this.morph),
h: lerp(this.bounds1.h, this.bounds2.h, this.morph)
let offsetX = this.centerX - boundsLerp.w / 2 - boundsLerp.x;
let offsetY = this.centerY - boundsLerp.h / 2 - boundsLerp.y;
let maxPoints = max(this.points1.length, this.points2.length);
for (let i = 0; i < maxPoints; i++) {
t = Easing.easeOutElastic(t);
let pt1 = this.points1[i % this.points1.length];
let pt2 = this.points2[i % this.points2.length];
let x = lerp(pt1.x, pt2.x, t) + offsetX;
let y = lerp(pt1.y, pt2.y, t) + offsetY;
return 1 - Math.cos((x * Math.PI) / 2);
return Math.sin((x * Math.PI) / 2);
static easeInOutSine(x) {
return -(Math.cos(Math.PI * x) - 1) / 2;
return 1 - (1 - x) * (1 - x);
static easeInOutQuad(x) {
return x < 0.5 ? 2 * x * x : 1 - Math.pow(-2 * x + 2, 2) / 2;
return 1 - Math.pow(1 - x, 3);
static easeInOutCubic(x) {
return x < 0.5 ? 4 * x * x * x : 1 - Math.pow(-2 * x + 2, 3) / 2;
return 1 - Math.pow(1 - x, 4);
static easeInOutQuart(x) {
return x < 0.5 ? 8 * x * x * x * x : 1 - Math.pow(-2 * x + 2, 4) / 2;
return x * x * x * x * x;
return 1 - Math.pow(1 - x, 5);
static easeInOutQuint(x) {
return x < 0.5 ? 16 * x * x * x * x * x : 1 - Math.pow(-2 * x + 2, 5) / 2;
return x === 0 ? 0 : Math.pow(2, 10 * x - 10);
return x === 1 ? 1 : 1 - Math.pow(2, -10 * x);
static easeInOutExpo(x) {
Math.pow(2, 20 * x - 10) / 2 :
(2 - Math.pow(2, -20 * x + 10)) / 2;
return 1 - Math.sqrt(1 - Math.pow(x, 2));
return Math.sqrt(1 - Math.pow(x - 1, 2));
static easeInOutCirc(x) {
(1 - Math.sqrt(1 - Math.pow(2 * x, 2))) / 2 :
(Math.sqrt(1 - Math.pow(-2 * x + 2, 2)) + 1) / 2;
return c3 * x * x * x - c1 * x * x;
return 1 + c3 * Math.pow(x - 1, 3) + c1 * Math.pow(x - 1, 2);
static easeInOutBack(x) {
(Math.pow(2 * x, 2) * ((c2 + 1) * 2 * x - c2)) / 2 :
(Math.pow(2 * x - 2, 2) * ((c2 + 1) * (x * 2 - 2) + c2) + 2) / 2;
static easeInElastic(x) {
const c4 = (2 * Math.PI) / 3;
-Math.pow(2, 10 * x - 10) * Math.sin((x * 10 - 10.75) * c4);
static easeOutElastic(x) {
const c4 = (2 * Math.PI) / 3;
Math.pow(2, -10 * x) * Math.sin((x * 10 - 0.75) * c4) + 1;
static easeInOutElastic(x) {
const c5 = (2 * Math.PI) / 4.5;
-(Math.pow(2, 20 * x - 10) * Math.sin((20 * x - 11.125) * c5)) / 2 :
(Math.pow(2, -20 * x + 10) * Math.sin((20 * x - 11.125) * c5)) / 2 + 1;
return 1 - Easing.easeOutBounce(1 - x);
static easeOutBounce(x) {
return n1 * (x -= 1.5 / d1) * x + 0.75;
} else if (x < 2.5 / d1) {
return n1 * (x -= 2.25 / d1) * x + 0.9375;
return n1 * (x -= 2.625 / d1) * x + 0.984375;
static easeInOutBounce(x) {
(1 - Easing.easeOutBounce(1 - 2 * x)) / 2 :
(1 + Easing.easeOutBounce(2 * x - 1)) / 2;
function stringToArray(str) {