260102

const { animate } = anime;

const w = 800;
const h = 800;
const SIZE = 50;
const GAP = 6;
const GRIDNUM = 3;
const ANIMATION_SPEED = 180;
const DELAY_STEP = 0.09;
const SCALE_FACTOR = 0.05;
const MIN_SCALE = 0.2;

let palette = [];
let gridSystems = [];

class GridSystem {
  constructor(offsetX = 0, offsetY = 0, offsetZ = 0, colorOffset = 0) {
    this.offsetX = offsetX;
    this.offsetY = offsetY;
    this.offsetZ = offsetZ;
    this.colorOffset = colorOffset;
    this.rectArray = [];
    this.rectCurrentOffsets = [];
    this.bgCurrentOffsets = [];
    this.targetOffsetX = 0;
    this.targetOffsetY = 0;
    this.targetOffsetZ = 0;
    this.lastTimeSlot = -1;
    this.rectAnimeValue = { count: 0 };
    this.bgAnimeValue = { count: 0 };
    this.init();
  }

  init() {
    let rectCenterOffset = (SIZE - GAP) / 2;
    let offsetX = -(SIZE + GAP) - rectCenterOffset;
    let offsetY = -(SIZE + GAP) - (SIZE - GAP) - rectCenterOffset;

    for (let y = 0; y < GRIDNUM; y++) {
      for (let x = 0; x < GRIDNUM; x++) {
        this.rectArray.push(
          createVector(
            offsetX + x * (SIZE + GAP),
            offsetY + y * (SIZE + GAP),
            0
          )
        );
      }
    }

    let gridOffset = this.getGridOffset();
    this.targetOffsetX = gridOffset;
    this.targetOffsetY = gridOffset;
    this.targetOffsetZ = gridOffset;

    for (let i = 0; i < this.rectArray.length; i++) {
      this.rectCurrentOffsets.push({
        x: gridOffset,
        y: gridOffset,
        z: gridOffset,
      });
      this.bgCurrentOffsets.push({
        x: gridOffset,
        y: gridOffset,
        z: gridOffset,
      });
    }

    animate(this.rectAnimeValue, {
      count: 1,
      duration: 2000,
      ease: "inBounce",
      loop: true,
      loopDelay: 1000,
      alternate: false,
    });

    animate(this.bgAnimeValue, {
      count: 1,
      duration: 2000,
      ease: "inOutExpo",
      loop: true,
      loopDelay: 1000,
      alternate: false,
    });
  }

  getGridOffset() {
    let smallGridSize = GRIDNUM * SIZE + (GRIDNUM - 1) * GAP;
    let gridSpacing = smallGridSize + GAP * 4;
    return (-(GRIDNUM - 1) * gridSpacing) / 2;
  }

  getBigGridSpacing() {
    let smallGridSize = GRIDNUM * SIZE + (GRIDNUM - 1) * GAP;
    return smallGridSize + GAP * 4;
  }

  updateTarget() {
    let timeSlot = floor(frameCount / ANIMATION_SPEED);
    if (timeSlot !== this.lastTimeSlot) {
      let randomIndex = floor(random(GRIDNUM * GRIDNUM * GRIDNUM));
      let bigGridX = randomIndex % GRIDNUM;
      let bigGridY = floor((randomIndex / GRIDNUM) % GRIDNUM);
      let bigGridZ = floor(randomIndex / (GRIDNUM * GRIDNUM));
      let gridSpacing = this.getBigGridSpacing();
      let gridOffset = this.getGridOffset();
      this.targetOffsetX = gridOffset + bigGridX * gridSpacing;
      this.targetOffsetY = gridOffset + bigGridY * gridSpacing;
      this.targetOffsetZ = gridOffset + bigGridZ * gridSpacing;
      this.lastTimeSlot = timeSlot;
    }
  }

  calculateSize(prevX, prevY, prevZ, currentX, currentY, currentZ, baseSize) {
    let moveX = currentX - prevX;
    let moveY = currentY - prevY;
    let moveZ = currentZ - prevZ;
    let moveSpeed = sqrt(moveX * moveX + moveY * moveY + moveZ * moveZ);
    let moveDirX = moveSpeed > 0 ? moveX / moveSpeed : 0;
    let moveDirY = moveSpeed > 0 ? moveY / moveSpeed : 0;
    let moveDirZ = moveSpeed > 0 ? moveZ / moveSpeed : 0;
    let scaleFactor = 1.0 + moveSpeed * SCALE_FACTOR;
    let widthRatio =
      1.0 -
      abs(moveDirX) * (scaleFactor - 1.0) +
      (abs(moveDirY) + abs(moveDirZ)) * (scaleFactor - 1.0);
    let heightRatio =
      1.0 -
      abs(moveDirY) * (scaleFactor - 1.0) +
      (abs(moveDirX) + abs(moveDirZ)) * (scaleFactor - 1.0);
    let width = baseSize * widthRatio;
    let height = baseSize * heightRatio;
    return {
      width: max(baseSize * MIN_SCALE, width),
      height: max(baseSize * MIN_SCALE, height),
    };
  }

  drawRect(i, pos, delayedRectAnimation) {
    let prevX = this.rectCurrentOffsets[i].x;
    let prevY = this.rectCurrentOffsets[i].y;
    let prevZ = this.rectCurrentOffsets[i].z;

    this.rectCurrentOffsets[i].x = lerp(
      this.rectCurrentOffsets[i].x,
      this.targetOffsetX,
      delayedRectAnimation
    );
    this.rectCurrentOffsets[i].y = lerp(
      this.rectCurrentOffsets[i].y,
      this.targetOffsetY,
      delayedRectAnimation
    );
    this.rectCurrentOffsets[i].z = lerp(
      this.rectCurrentOffsets[i].z,
      this.targetOffsetZ,
      delayedRectAnimation
    );

    let baseSize = SIZE - GAP;
    let size = this.calculateSize(
      prevX,
      prevY,
      prevZ,
      this.rectCurrentOffsets[i].x,
      this.rectCurrentOffsets[i].y,
      this.rectCurrentOffsets[i].z,
      baseSize
    );

    push();
    translate(pos.x + this.offsetX, pos.y + this.offsetY, pos.z + this.offsetZ);
    translate(
      this.rectCurrentOffsets[i].x,
      this.rectCurrentOffsets[i].y,
      this.rectCurrentOffsets[i].z
    );
    let colorIndex = (this.colorOffset + i) % palette.length;
    fill(palette[colorIndex % 4]);
    let rectX = -(size.width - baseSize) / 2;
    let rectY = SIZE - GAP - (size.height - baseSize) / 2;
    rect(rectX, rectY, size.width, size.height);
    pop();
  }

  drawBackground(i, pos) {
    push();
    translate(pos.x + this.offsetX, pos.y + this.offsetY, pos.z + this.offsetZ);
    push();
    noFill();
    stroke(palette[5]);
    translate(
      this.bgCurrentOffsets[i].x,
      this.bgCurrentOffsets[i].y,
      this.bgCurrentOffsets[i].z
    );
    rect(-5, SIZE - GAP - 5, SIZE - GAP + 10, SIZE - GAP + 10);
    pop();
    pop();
  }

  update() {
    this.updateTarget();
  }

  draw() {
    push();

    for (let i = 0; i < this.rectArray.length; i++) {
      let pos = this.rectArray[i];
      let delay = i * DELAY_STEP;
      let delayedRectAnimation = max(
        0,
        min(1, this.rectAnimeValue.count - delay)
      );
      let delayedBgAnimation = max(0, min(1, this.bgAnimeValue.count - delay));

      this.bgCurrentOffsets[i].x = lerp(
        this.bgCurrentOffsets[i].x,
        this.targetOffsetX,
        delayedBgAnimation
      );
      this.bgCurrentOffsets[i].y = lerp(
        this.bgCurrentOffsets[i].y,
        this.targetOffsetY,
        delayedBgAnimation
      );
      this.bgCurrentOffsets[i].z = lerp(
        this.bgCurrentOffsets[i].z,
        this.targetOffsetZ,
        delayedBgAnimation
      );

      this.drawBackground(i, pos);
      this.drawRect(i, pos, delayedRectAnimation);
    }

    pop();
  }
}

function setup() {
  createCanvas(w, h, WEBGL);
  palette = colorArray[1].colors;
  background(palette[4]);
  stroke(palette[6]);
  strokeWeight(4);

  gridSystems.push(new GridSystem(0, 0, 0, 0));
  gridSystems.push(new GridSystem(0, 0, 0, 2));
  gridSystems.push(new GridSystem(0, 0, 0, 4));
}

function draw() {
  orbitControl();
  background(palette[4]);

  for (let gridSystem of gridSystems) {
    gridSystem.update();
    gridSystem.draw();
  }
}

const colorArray = [
  {
    id: 0,
    colors: ["#253276", "#dfdad3", "#ffffff", "#000000"],
  },
  {
    id: 1,
    colors: [
      "#9dbdba",
      "#f8b042",
      "#e47763",
      "#253276",
      "#dfdad3",
      "#ffffff",
      "#000000",
    ],
  },
];
const { animate } = anime;

const w = 800;
const h = 800;
const SIZE = 50;
const GAP = 6;
const GRIDNUM = 3;
const ANIMATION_SPEED = 180;
const DELAY_STEP = 0.09;
const SCALE_FACTOR = 0.05;
const MIN_SCALE = 0.2;

let palette = [];
let gridSystems = [];

class GridSystem {
  constructor(offsetX = 0, offsetY = 0, offsetZ = 0, colorOffset = 0) {
    this.offsetX = offsetX;
    this.offsetY = offsetY;
    this.offsetZ = offsetZ;
    this.colorOffset = colorOffset;
    this.rectArray = [];
    this.rectCurrentOffsets = [];
    this.bgCurrentOffsets = [];
    this.targetOffsetX = 0;
    this.targetOffsetY = 0;
    this.targetOffsetZ = 0;
    this.lastTimeSlot = -1;
    this.rectAnimeValue = { count: 0 };
    this.bgAnimeValue = { count: 0 };
    this.init();
  }

  init() {
    let rectCenterOffset = (SIZE - GAP) / 2;
    let offsetX = -(SIZE + GAP) - rectCenterOffset;
    let offsetY = -(SIZE + GAP) - (SIZE - GAP) - rectCenterOffset;

    for (let y = 0; y < GRIDNUM; y++) {
      for (let x = 0; x < GRIDNUM; x++) {
        this.rectArray.push(
          createVector(
            offsetX + x * (SIZE + GAP),
            offsetY + y * (SIZE + GAP),
            0
          )
        );
      }
    }

    let gridOffset = this.getGridOffset();
    this.targetOffsetX = gridOffset;
    this.targetOffsetY = gridOffset;
    this.targetOffsetZ = gridOffset;

    for (let i = 0; i < this.rectArray.length; i++) {
      this.rectCurrentOffsets.push({
        x: gridOffset,
        y: gridOffset,
        z: gridOffset,
      });
      this.bgCurrentOffsets.push({
        x: gridOffset,
        y: gridOffset,
        z: gridOffset,
      });
    }

    animate(this.rectAnimeValue, {
      count: 1,
      duration: 2000,
      ease: "inBounce",
      loop: true,
      loopDelay: 1000,
      alternate: false,
    });

    animate(this.bgAnimeValue, {
      count: 1,
      duration: 2000,
      ease: "inOutExpo",
      loop: true,
      loopDelay: 1000,
      alternate: false,
    });
  }

  getGridOffset() {
    let smallGridSize = GRIDNUM * SIZE + (GRIDNUM - 1) * GAP;
    let gridSpacing = smallGridSize + GAP * 4;
    return (-(GRIDNUM - 1) * gridSpacing) / 2;
  }

  getBigGridSpacing() {
    let smallGridSize = GRIDNUM * SIZE + (GRIDNUM - 1) * GAP;
    return smallGridSize + GAP * 4;
  }

  updateTarget() {
    let timeSlot = floor(frameCount / ANIMATION_SPEED);
    if (timeSlot !== this.lastTimeSlot) {
      let randomIndex = floor(random(GRIDNUM * GRIDNUM * GRIDNUM));
      let bigGridX = randomIndex % GRIDNUM;
      let bigGridY = floor((randomIndex / GRIDNUM) % GRIDNUM);
      let bigGridZ = floor(randomIndex / (GRIDNUM * GRIDNUM));
      let gridSpacing = this.getBigGridSpacing();
      let gridOffset = this.getGridOffset();
      this.targetOffsetX = gridOffset + bigGridX * gridSpacing;
      this.targetOffsetY = gridOffset + bigGridY * gridSpacing;
      this.targetOffsetZ = gridOffset + bigGridZ * gridSpacing;
      this.lastTimeSlot = timeSlot;
    }
  }

  calculateSize(prevX, prevY, prevZ, currentX, currentY, currentZ, baseSize) {
    let moveX = currentX - prevX;
    let moveY = currentY - prevY;
    let moveZ = currentZ - prevZ;
    let moveSpeed = sqrt(moveX * moveX + moveY * moveY + moveZ * moveZ);
    let moveDirX = moveSpeed > 0 ? moveX / moveSpeed : 0;
    let moveDirY = moveSpeed > 0 ? moveY / moveSpeed : 0;
    let moveDirZ = moveSpeed > 0 ? moveZ / moveSpeed : 0;
    let scaleFactor = 1.0 + moveSpeed * SCALE_FACTOR;
    let widthRatio =
      1.0 -
      abs(moveDirX) * (scaleFactor - 1.0) +
      (abs(moveDirY) + abs(moveDirZ)) * (scaleFactor - 1.0);
    let heightRatio =
      1.0 -
      abs(moveDirY) * (scaleFactor - 1.0) +
      (abs(moveDirX) + abs(moveDirZ)) * (scaleFactor - 1.0);
    let width = baseSize * widthRatio;
    let height = baseSize * heightRatio;
    return {
      width: max(baseSize * MIN_SCALE, width),
      height: max(baseSize * MIN_SCALE, height),
    };
  }

  drawRect(i, pos, delayedRectAnimation) {
    let prevX = this.rectCurrentOffsets[i].x;
    let prevY = this.rectCurrentOffsets[i].y;
    let prevZ = this.rectCurrentOffsets[i].z;

    this.rectCurrentOffsets[i].x = lerp(
      this.rectCurrentOffsets[i].x,
      this.targetOffsetX,
      delayedRectAnimation
    );
    this.rectCurrentOffsets[i].y = lerp(
      this.rectCurrentOffsets[i].y,
      this.targetOffsetY,
      delayedRectAnimation
    );
    this.rectCurrentOffsets[i].z = lerp(
      this.rectCurrentOffsets[i].z,
      this.targetOffsetZ,
      delayedRectAnimation
    );

    let baseSize = SIZE - GAP;
    let size = this.calculateSize(
      prevX,
      prevY,
      prevZ,
      this.rectCurrentOffsets[i].x,
      this.rectCurrentOffsets[i].y,
      this.rectCurrentOffsets[i].z,
      baseSize
    );

    push();
    translate(pos.x + this.offsetX, pos.y + this.offsetY, pos.z + this.offsetZ);
    translate(
      this.rectCurrentOffsets[i].x,
      this.rectCurrentOffsets[i].y,
      this.rectCurrentOffsets[i].z
    );
    let colorIndex = (this.colorOffset + i) % palette.length;
    fill(palette[colorIndex % 4]);
    let rectX = -(size.width - baseSize) / 2;
    let rectY = SIZE - GAP - (size.height - baseSize) / 2;
    rect(rectX, rectY, size.width, size.height);
    pop();
  }

  drawBackground(i, pos) {
    push();
    translate(pos.x + this.offsetX, pos.y + this.offsetY, pos.z + this.offsetZ);
    push();
    noFill();
    stroke(palette[5]);
    translate(
      this.bgCurrentOffsets[i].x,
      this.bgCurrentOffsets[i].y,
      this.bgCurrentOffsets[i].z
    );
    rect(-5, SIZE - GAP - 5, SIZE - GAP + 10, SIZE - GAP + 10);
    pop();
    pop();
  }

  update() {
    this.updateTarget();
  }

  draw() {
    push();

    for (let i = 0; i < this.rectArray.length; i++) {
      let pos = this.rectArray[i];
      let delay = i * DELAY_STEP;
      let delayedRectAnimation = max(
        0,
        min(1, this.rectAnimeValue.count - delay)
      );
      let delayedBgAnimation = max(0, min(1, this.bgAnimeValue.count - delay));

      this.bgCurrentOffsets[i].x = lerp(
        this.bgCurrentOffsets[i].x,
        this.targetOffsetX,
        delayedBgAnimation
      );
      this.bgCurrentOffsets[i].y = lerp(
        this.bgCurrentOffsets[i].y,
        this.targetOffsetY,
        delayedBgAnimation
      );
      this.bgCurrentOffsets[i].z = lerp(
        this.bgCurrentOffsets[i].z,
        this.targetOffsetZ,
        delayedBgAnimation
      );

      this.drawBackground(i, pos);
      this.drawRect(i, pos, delayedRectAnimation);
    }

    pop();
  }
}

function setup() {
  createCanvas(w, h, WEBGL);
  palette = colorArray[1].colors;
  background(palette[4]);
  stroke(palette[6]);
  strokeWeight(4);

  gridSystems.push(new GridSystem(0, 0, 0, 0));
  gridSystems.push(new GridSystem(0, 0, 0, 2));
  gridSystems.push(new GridSystem(0, 0, 0, 4));
}

function draw() {
  orbitControl();
  background(palette[4]);

  for (let gridSystem of gridSystems) {
    gridSystem.update();
    gridSystem.draw();
  }
}

const colorArray = [
  {
    id: 0,
    colors: ["#253276", "#dfdad3", "#ffffff", "#000000"],
  },
  {
    id: 1,
    colors: [
      "#9dbdba",
      "#f8b042",
      "#e47763",
      "#253276",
      "#dfdad3",
      "#ffffff",
      "#000000",
    ],
  },
];

Last Updated:

260102