blob: 60c6c0a7bcdaddedb0a8d614e47bcd06297ea7cf [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import * as p5 from "p5";
import {documentReady} from "./utils.js";
const BLOCK_COUNT_X = 7;
const BLOCK_COUNT_Y = 7;
const MAX_BLOCK_SIZE = 30;
const ANIM_SPEED = 0.1;
const ANIM_AMPLITUDE = 40;
const DRAW_SAFE_AREA = false;
const randomBetween = (from, to) => from + Math.random() * (to - from);
const randomInt = (from, to) => randomBetween(from, to) | 0;
class Polygon {
constructor(pos, vectors) {
this.pos = pos;
this.vectors = vectors;
}
draw(sketch) {
sketch.push();
sketch.noStroke();
sketch.fill(128, 128, 128);
sketch.translate(this.pos.x, this.pos.y);
sketch.beginShape();
for (let i = 0; i < this.vectors.length; i += 1) {
const v = this.vectors[i];
sketch.vertex(v.x, v.y);
}
sketch.endShape(p5.CLOSE);
sketch.pop();
}
isVectorInside(vector) {
const vs = this.vectors;
const x = vector.x - this.pos.x,
y = vector.y - this.pos.y;
let inside = false;
for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) {
const intersect =
vs[i].y > y !== vs[j].y > y &&
x < (vs[j].x - vs[i].x) * (y - vs[i].y) / (vs[j].y - vs[i].y) + vs[i].x;
if (intersect) inside = !inside;
}
return inside;
}
calcArea() {
let total = 0;
for (let i = 0, l = this.vectors.length; i < l; i++) {
const addX = this.vectors[i].x;
const addY = this.vectors[i === this.vectors.length - 1 ? 0 : i + 1].y;
const subX = this.vectors[i === this.vectors.length - 1 ? 0 : i + 1].x;
const subY = this.vectors[i].y;
total += addX * addY * 0.5;
total -= subX * subY * 0.5;
}
return Math.abs(total);
}
}
class Box {
constructor(pos, size, color, scale, direction, rotate) {
this.pos = pos;
this.size = size;
this.color = color;
this.scale = scale;
this.rotate = rotate;
this.direction = direction;
this.shift = null;
this.animFrame = Math.random() * 100;
}
calc(sketch) {
if (!this.shift) {
this.shift = sketch.createVector(0, 0);
}
this.animFrame += ANIM_SPEED;
this.rotate = this.animFrame / 50 * sketch.PI;
this.shift.x =
Math.cos(this.animFrame / 10) *
Math.sin(this.animFrame / 10) *
ANIM_AMPLITUDE;
this.shift.y = Math.sin(this.animFrame / 10) * ANIM_AMPLITUDE;
}
draw(sketch) {
sketch.push();
sketch.noStroke();
sketch.fill(this.color);
sketch.translate(this.pos.x, this.pos.y);
sketch.scale(this.scale);
sketch.translate(this.shift.x, this.shift.y);
sketch.rotate(this.rotate * this.direction);
sketch.rect(-this.size / 2, -this.size / 2, this.size, this.size, 5);
sketch.pop();
}
calcArea() {
return this.size * this.size * this.scale;
}
}
function createBoxes(sketch, vw, vh, clearArea, colors) {
const boxes = [];
const gridWidth = vw / BLOCK_COUNT_Y;
const gridHeight = vh / BLOCK_COUNT_X;
for (let x = 0; x <= BLOCK_COUNT_X; x++) {
for (let y = 0; y <= BLOCK_COUNT_Y; y++) {
const pos = sketch.createVector(
randomBetween(gridWidth * x, gridWidth * (x + 1)),
randomBetween(gridHeight * y, gridHeight * (y + 1))
);
if (clearArea.isVectorInside(pos)) {
continue;
}
const box = new Box(
pos,
randomBetween(MAX_BLOCK_SIZE / 2, MAX_BLOCK_SIZE),
colors[randomInt(0, colors.length)],
randomBetween(0.5, 1),
randomBetween(0, 1) > 0.5 ? 1 : -1,
Math.random() * 2 * sketch.PI
);
boxes.push(box);
}
}
return boxes;
}
function createLogoArea(sketch, canvas, title, subtitle, button) {
const canvasRect = canvas.getBoundingClientRect();
const titleRect = title.getBoundingClientRect();
const subtitleRect = subtitle.getBoundingClientRect();
const buttonRect = button.getBoundingClientRect();
const vectors = [
sketch.createVector(titleRect.x, titleRect.y),
sketch.createVector(titleRect.x + titleRect.width, titleRect.y),
// sketch.createVector(titleRect.x + titleRect.width, titleRect.y + titleRect.height),
sketch.createVector(subtitleRect.x + subtitleRect.width, subtitleRect.y),
sketch.createVector(subtitleRect.x + subtitleRect.width, subtitleRect.y + subtitleRect.height),
sketch.createVector(buttonRect.x + buttonRect.width, buttonRect.y + buttonRect.height),
sketch.createVector(buttonRect.x, buttonRect.y + buttonRect.height),
sketch.createVector(subtitleRect.x, subtitleRect.y + subtitleRect.height),
sketch.createVector(subtitleRect.x, subtitleRect.y),
];
for (const vector of vectors) {
// The canvas does not start from the beginning of the document.
vector.x = vector.x - canvasRect.x;
vector.y = vector.y - canvasRect.y;
}
// Add a small margin for the entire stroke
const minX = Math.min(...vectors.map((d) => d.x));
const maxX = Math.max(...vectors.map((d) => d.x));
const minY = Math.min(...vectors.map((d) => d.y));
const maxY = Math.max(...vectors.map((d) => d.y));
const centerX = minX + (maxX - minX) / 2;
const centerY = minY + (maxY - minY) / 2;
for (const vector of vectors) {
vector.x -= centerX;
vector.x *= 1.2;
vector.x += centerX;
vector.y -= centerY;
vector.y *= 1.2;
vector.y += centerY;
}
return new Polygon(sketch.createVector(0, 0), vectors);
}
export function initHeaderAnimation() {
const canvas = document.querySelector("#header-canvas");
const title = document.querySelector("#header-title");
const subtitle = document.querySelector("#header-lead");
const button = document.querySelector("#header-button");
documentReady(function() {
new p5((sketch) => {
let colors = [];
let boxes = [];
let logoArea;
sketch.setup = () => {
const positionInfo = canvas.getBoundingClientRect();
const vw = positionInfo.width; // viewport width
const vh = positionInfo.height; // viewport height
colors = [
sketch.color("#017cee"),
sketch.color("#00ad46"),
sketch.color("#0cb6ff"),
sketch.color("#ff7557"),
sketch.color("#e43921"),
sketch.color("#11e1ee"),
sketch.color("#04d659"),
sketch.color("#00c7d4"),
sketch.color("#cbcbcb")
];
logoArea = createLogoArea(sketch, canvas, title, subtitle, button);
boxes = createBoxes(sketch, vw, vh, logoArea, colors);
sketch.createCanvas(vw, vh);
};
sketch.draw = () => {
sketch.background(255, 255, 255);
if (DRAW_SAFE_AREA) {
logoArea.draw(sketch);
}
boxes.forEach((box) => {
box.calc(sketch);
box.draw(sketch);
});
};
sketch.windowResized = () => {
const positionInfo = canvas.getBoundingClientRect();
const vw = positionInfo.width; // viewport width
const vh = positionInfo.height; // viewport height
logoArea = createLogoArea(sketch, canvas, title, subtitle, button);
boxes = createBoxes(sketch, vw, vh, logoArea, colors);
sketch.resizeCanvas(vw, vh);
};
}, document.querySelector("#header-canvas"));
});
}