847 lines
22 KiB
JavaScript
847 lines
22 KiB
JavaScript
const Colors = (() => {
|
|
// main
|
|
const main = {
|
|
primary: "#ff3388",
|
|
secondary: "#33bbff"
|
|
};
|
|
|
|
// original
|
|
const original = {
|
|
primary: "#ff3fa6",
|
|
secondary: "#f2ff7f"
|
|
};
|
|
|
|
// pg
|
|
const pg = {
|
|
primary: "#FF3388",
|
|
secondary: "#33FFAA"
|
|
};
|
|
|
|
// goldBlue
|
|
const goldBlue = {
|
|
primary: "#465CFA",
|
|
secondary: "#FAE446"
|
|
};
|
|
|
|
// pink
|
|
const pink = {
|
|
primary: "#DC1A93",
|
|
secondary: "#E841A9"
|
|
};
|
|
|
|
let color = main;
|
|
const rand = Math.floor(Math.random() * 1000);
|
|
const colorVariantToRange = new Map([
|
|
[original, [0, 5]],
|
|
[pg, [5, 8]],
|
|
[goldBlue, [8, 80]],
|
|
[pink, [80, 82]]
|
|
]);
|
|
for (let [variant, range] of colorVariantToRange) {
|
|
if (rand >= range[0] && rand < range[1]) color = variant;
|
|
}
|
|
|
|
return {
|
|
bg: "#000000",
|
|
loadingColor: "#424242",
|
|
...color
|
|
};
|
|
})();
|
|
|
|
class Point2D {
|
|
constructor(x = 0, y = 0) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
mag() {
|
|
return Math.sqrt(this.x * this.x + this.y * this.y);
|
|
}
|
|
|
|
scale(s) {
|
|
return new Point2D(this.x * s, this.y * s);
|
|
}
|
|
|
|
unit() {
|
|
return this.scale(1 / this.mag());
|
|
}
|
|
|
|
add(p2) {
|
|
return new Point2D(this.x + p2.x, this.y + p2.y);
|
|
}
|
|
|
|
sub(p2) {
|
|
return new Point2D(this.x - p2.x, this.y - p2.y);
|
|
}
|
|
|
|
norm() {
|
|
return new Point2D(-this.y, this.x);
|
|
}
|
|
|
|
dot(p2) {
|
|
return this.x * p2.x + this.y * p2.y;
|
|
}
|
|
}
|
|
|
|
class ContainerBox {
|
|
constructor(ptsIn, flippy = 0) {
|
|
let indices = [0, 1, 2, 3];
|
|
switch (flippy) {
|
|
case 1:
|
|
indices = [1, 2, 3, 0];
|
|
break;
|
|
case 2:
|
|
indices = [2, 3, 0, 1];
|
|
break;
|
|
case 3:
|
|
indices = [3, 0, 1, 2];
|
|
break;
|
|
default:
|
|
indices = [0, 1, 2, 3];
|
|
}
|
|
this.points = indices.map(i => ptsIn[i]);
|
|
this.xAx = this.points[3].sub(this.points[0]);
|
|
this.xAxUnit = this.xAx.unit();
|
|
this.wid = this.xAx.mag();
|
|
this.yAx = this.points[1].sub(this.points[0]);
|
|
this.yAxUnit = this.yAx.unit();
|
|
this.hei = this.yAx.mag();
|
|
this.up = this.getUp();
|
|
this.r = this.getRotation();
|
|
}
|
|
|
|
makeRandomPoint(padding = 1) {
|
|
const xs = Math.random() * (this.wid - padding * 2);
|
|
const ys = Math.random() * (this.hei - padding * 2);
|
|
return this.xAxUnit
|
|
.scale(xs)
|
|
.add(this.yAxUnit.scale(ys))
|
|
.add(this.points[0]);
|
|
}
|
|
|
|
makeRandomPointAtBottom(padding = 1) {
|
|
const xs = Math.random() * (this.wid * 1.1);
|
|
const ys = 0.99 * this.hei + padding;
|
|
return this.xAxUnit
|
|
.scale(xs)
|
|
.add(this.yAxUnit.scale(ys))
|
|
.add(this.points[0]);
|
|
}
|
|
|
|
getUp() {
|
|
return this.points[0].sub(this.points[1]).unit();
|
|
}
|
|
|
|
aboveBox(p) {
|
|
const i = 3;
|
|
const j = 0;
|
|
const vNorm = this.points[j]
|
|
.sub(this.points[i])
|
|
.unit()
|
|
.norm();
|
|
const x = p.sub(this.points[i]);
|
|
const y = x.dot(vNorm);
|
|
return y > 0;
|
|
}
|
|
|
|
getRotation() {
|
|
return Math.atan2(this.xAx.y, this.xAx.x);
|
|
}
|
|
}
|
|
|
|
function darken(hexColor, scalar = 0.5, opacity) {
|
|
const col = {
|
|
r: parseInt(hexColor.substring(1, 3), 16),
|
|
g: parseInt(hexColor.substring(3, 5), 16),
|
|
b: parseInt(hexColor.substring(5), 16),
|
|
a: opacity !== undefined ? opacity : 1
|
|
};
|
|
return `rgba(${col.r * scalar}, ${col.g * scalar}, ${col.b * scalar}, ${
|
|
col.a
|
|
})`;
|
|
}
|
|
|
|
function randomColor(minV, opacity) {
|
|
const r = Math.random() * (255 - minV) + minV;
|
|
const g = Math.random() * (255 - minV) + minV;
|
|
const b = Math.random() * (255 - minV) + minV;
|
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
}
|
|
|
|
function svgPolyElementToPointsList(polyElement) {
|
|
const rv = [];
|
|
const list = polyElement
|
|
.getAttribute("points")
|
|
.split(" ")
|
|
.map(Number);
|
|
for (let i = 0; i < list.length; i += 2) {
|
|
rv.push(new Point2D(list[i], list[i + 1]));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
function createSvgElement(type, attributes) {
|
|
const svgUrl = "http://www.w3.org/2000/svg";
|
|
const element = document.createElementNS(svgUrl, type);
|
|
Object.keys(attributes).forEach(k =>
|
|
element.setAttributeNS(null, k, attributes[k])
|
|
);
|
|
return element;
|
|
}
|
|
|
|
function watchSize() {
|
|
const svg = document.querySelector("#woman");
|
|
|
|
function setOptimalSize() {
|
|
const W = window.innerWidth;
|
|
const H = window.innerHeight;
|
|
// original viewBox = 0 0 1189.68 1549
|
|
// console.log(`WH: ${Math.round(W*100/H)}`);
|
|
if (W < H) {
|
|
const topLeft = new Point2D(83, 50);
|
|
const size = new Point2D(418, 630).sub(topLeft);
|
|
const heightAdjust = H / W;
|
|
size.y = heightAdjust * size.x;
|
|
const midY = size.y / 2;
|
|
topLeft.y = (630 - 50) / 1.5 - midY;
|
|
|
|
svg.style.width = "100vw";
|
|
svg.style.height = "auto";
|
|
svg.setAttribute(
|
|
"viewBox",
|
|
`${topLeft.x} ${topLeft.y} ${size.x} ${size.y}`
|
|
);
|
|
} else {
|
|
const topLeft = new Point2D(0, 146);
|
|
const size = new Point2D(505, 629).sub(topLeft);
|
|
const widthAdjust = W / H;
|
|
size.x = widthAdjust * size.y;
|
|
const midX = size.x / 2;
|
|
topLeft.x = 505 / 2 - midX;
|
|
svg.style.width = "auto";
|
|
svg.style.height = "100vh";
|
|
svg.setAttribute(
|
|
"viewBox",
|
|
`${topLeft.x} ${topLeft.y} ${size.x} ${size.y}`
|
|
);
|
|
}
|
|
}
|
|
|
|
setOptimalSize();
|
|
window.onresize = setOptimalSize;
|
|
}
|
|
|
|
watchSize();
|
|
|
|
const PATTERN_MAPS = new Map();
|
|
const SK_INC = 2;
|
|
|
|
async function chessPattern(w, h, { color = red, size = 8 }) {
|
|
const SK = window.devicePixelRatio * 1;
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const W = Math.ceil(w * SK);
|
|
const H = Math.ceil(h * SK);
|
|
canvas.width = Math.ceil(W);
|
|
canvas.height = Math.ceil(H * 2);
|
|
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(0, 0, W, H * 2);
|
|
|
|
const sqSize = size * 5 * SK;
|
|
|
|
for (let i = 0; i < Math.round(W / sqSize); i++) {
|
|
const cols =
|
|
i % 2 === 0 ? [color, darken(color, 0.9)] : [darken(color, 0.9), color];
|
|
for (let j = 0; j < Math.floor(H / sqSize) + 1; j++) {
|
|
const x = sqSize * i;
|
|
const y = sqSize * j;
|
|
const width = sqSize;
|
|
const height = sqSize;
|
|
ctx.fillStyle = cols[j % 2];
|
|
ctx.fillRect(x, y, width, height);
|
|
ctx.fillRect(x, y + H, width, height);
|
|
}
|
|
}
|
|
|
|
return {
|
|
canvas
|
|
};
|
|
}
|
|
|
|
function chunkUpRects(rects, chunkWidth, spacing, H, variation) {
|
|
const out = [];
|
|
for (let i = 0; i < rects.length; i++) {
|
|
const rect = rects[i];
|
|
let m = rect.x;
|
|
const endX = rect.x + rect.width;
|
|
while (m < endX) {
|
|
let distLeft = endX - m;
|
|
const randScalar = 1 + (Math.random() - 0.5) * variation;
|
|
const len = randScalar * chunkWidth;
|
|
const word = {
|
|
x: m,
|
|
y: rect.y,
|
|
width: len
|
|
};
|
|
out.push(word);
|
|
out.push({ ...word, y: word.y + H });
|
|
|
|
m = m + word.width + spacing;
|
|
distLeft = endX - m;
|
|
|
|
if (distLeft < chunkWidth) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
async function glowTextPattern(
|
|
w,
|
|
h,
|
|
{ N = 300, color = "red", size = 4, seed }
|
|
) {
|
|
const SK = window.devicePixelRatio + SK_INC;
|
|
const height = 10 * SK;
|
|
const chunkWidth = 30 * SK;
|
|
const gap = 2 * SK;
|
|
const totalSize = height + gap;
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const W = Math.ceil(w * SK);
|
|
const H = Math.ceil(h * SK);
|
|
canvas.width = Math.ceil(W);
|
|
canvas.height = Math.ceil(H * 2);
|
|
|
|
const bgColor = darken(color, 0.6);
|
|
|
|
ctx.fillStyle = bgColor;
|
|
ctx.fillRect(0, 0, W, H * 2);
|
|
|
|
const key = `textPattern-${seed || ""}`;
|
|
const radius = height / 2;
|
|
|
|
let rects = [];
|
|
if (seed && PATTERN_MAPS.has(key)) {
|
|
rects = PATTERN_MAPS.get(key);
|
|
} else {
|
|
for (let j = 0; j < Math.floor(H / totalSize); j++) {
|
|
const x =
|
|
Math.random() < 0.8 ? Math.floor(Math.random() * 4) * 10 * SK : 0;
|
|
const y = totalSize * j;
|
|
const width = W - (x + radius * 2);
|
|
rects.push({ x, y, width });
|
|
if (Math.random() < 0.14) j += 1;
|
|
}
|
|
rects = chunkUpRects(rects, chunkWidth, radius / 2, H, 0.8);
|
|
if (seed) {
|
|
PATTERN_MAPS.set(key, rects);
|
|
}
|
|
}
|
|
|
|
const glowFillPerc = 0.05;
|
|
|
|
ctx.fillStyle = color;
|
|
for (let i = 0; i < rects.length; i++) {
|
|
const { x, y, width } = rects[i];
|
|
|
|
const c1 = new Point2D(x + radius, y + radius);
|
|
const radialGrad1 = ctx.createRadialGradient(
|
|
c1.x,
|
|
c1.y,
|
|
0,
|
|
c1.x,
|
|
c1.y,
|
|
radius
|
|
);
|
|
radialGrad1.addColorStop(0, color);
|
|
radialGrad1.addColorStop(glowFillPerc / 2, color);
|
|
radialGrad1.addColorStop(1, darken(color, 1, 0));
|
|
ctx.fillStyle = radialGrad1;
|
|
ctx.fillRect(x, y, radius * 2, radius * 2);
|
|
ctx.fillStyle = bgColor;
|
|
ctx.fillRect(x + radius, y, radius, radius * 2);
|
|
|
|
const c2 = new Point2D(x + width - radius, y + radius);
|
|
const radialGrad2 = ctx.createRadialGradient(
|
|
c2.x,
|
|
c2.y,
|
|
0,
|
|
c2.x,
|
|
c2.y,
|
|
radius
|
|
);
|
|
radialGrad2.addColorStop(0, color);
|
|
radialGrad2.addColorStop(glowFillPerc / 2, color);
|
|
radialGrad2.addColorStop(1, darken(color, 1, 0));
|
|
ctx.fillStyle = radialGrad2;
|
|
ctx.fillRect(x + width - radius * 2, y, radius * 2, radius * 2);
|
|
ctx.fillStyle = bgColor;
|
|
ctx.fillRect(x + width - radius * 2, y, radius, radius * 2);
|
|
|
|
const linearGradient = ctx.createLinearGradient(0, y, 0, y + height);
|
|
linearGradient.addColorStop(0, darken(color, 1, 0));
|
|
linearGradient.addColorStop(0.5 - glowFillPerc, color);
|
|
linearGradient.addColorStop(0.5 + glowFillPerc, color);
|
|
linearGradient.addColorStop(1, darken(color, 1, 0));
|
|
|
|
ctx.fillStyle = linearGradient;
|
|
ctx.fillRect(x + radius, y, width - radius * 2, height);
|
|
ctx.fillStyle = "white";
|
|
ctx.fillRect(x + radius, y + height / 2 - 1, width - radius * 2, 1);
|
|
}
|
|
|
|
return {
|
|
canvas
|
|
};
|
|
}
|
|
|
|
async function textPattern(w, h, { N = 300, color = "red", size = 4, seed }) {
|
|
const SK = window.devicePixelRatio * 1; // todo(luke): draw fast v then slow v
|
|
const height = 7 * SK;
|
|
const gap = 2 * SK;
|
|
const totalSize = height + gap;
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const W = Math.ceil(w * SK);
|
|
const H = Math.ceil(h * SK);
|
|
canvas.width = Math.ceil(W);
|
|
canvas.height = Math.ceil(H * 2);
|
|
|
|
ctx.fillStyle = darken(color, 0.8);
|
|
ctx.fillRect(0, 0, W, H * 2);
|
|
|
|
const key = `textPattern-${seed || ""}`;
|
|
|
|
let rects = [];
|
|
if (seed && PATTERN_MAPS.has(key)) {
|
|
rects = PATTERN_MAPS.get(key);
|
|
} else {
|
|
for (let j = 0; j < Math.floor(H / totalSize) + 1; j++) {
|
|
const x =
|
|
Math.random() < 0.8 ? Math.floor(Math.random() * 4) * 10 * SK : 0;
|
|
const y = totalSize * j;
|
|
const width = W - x;
|
|
rects.push({ x, y, width });
|
|
if (Math.random() < 0.4) j += 2;
|
|
}
|
|
if (seed) {
|
|
PATTERN_MAPS.set(key, rects);
|
|
}
|
|
}
|
|
|
|
ctx.fillStyle = color;
|
|
for (let i = 0; i < rects.length; i++) {
|
|
const { x, y, width } = rects[i];
|
|
ctx.fillRect(x, y, width, height);
|
|
ctx.fillRect(x, y + H, width, height);
|
|
}
|
|
|
|
return {
|
|
canvas
|
|
};
|
|
}
|
|
|
|
async function layeredBubbles(w, h, { N = 300, color = "red", size = 4 }) {
|
|
const SK = window.devicePixelRatio + SK_INC;
|
|
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const W = Math.ceil(w * SK);
|
|
const H = Math.ceil(h * SK);
|
|
canvas.width = Math.ceil(W);
|
|
canvas.height = Math.ceil(H * 2);
|
|
|
|
ctx.fillStyle = color;
|
|
ctx.fillRect(0, 0, W, H * 2);
|
|
|
|
for (let i = 0; i < N * 6; i++) {
|
|
const r = (size * SK) / 1;
|
|
const cy = Math.random() * (H + r);
|
|
const cx = Math.random() * (W + r);
|
|
const fill = darken(color, Math.random() * 0.4 + 1);
|
|
ctx.fillStyle = fill;
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy + H, r, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
}
|
|
return {
|
|
canvas
|
|
};
|
|
}
|
|
|
|
async function coloredBubbles(
|
|
w,
|
|
h,
|
|
{ N = 300, color = "#ff0000", size = 4, secondaryColor = "#00ff00" }
|
|
) {
|
|
const SK = window.devicePixelRatio * 1;
|
|
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
const W = Math.ceil(w * SK);
|
|
const H = Math.ceil(h * SK);
|
|
canvas.width = Math.ceil(W);
|
|
canvas.height = Math.ceil(H * 2);
|
|
|
|
ctx.fillStyle = "black";
|
|
ctx.fillRect(0, 0, W, H * 2);
|
|
|
|
for (let i = 0; i < N * 6; i++) {
|
|
const r = size * SK;
|
|
const cy = Math.random() * (H + r);
|
|
const cx = Math.random() * (W + r);
|
|
let fill = Math.random() < 0.8 ? color : secondaryColor;
|
|
fill = darken(fill, Math.random() * 0.4 + 1, Math.random() * 0.2 + 0.1);
|
|
ctx.fillStyle = fill;
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
ctx.arc(cx, cy + H, r, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
}
|
|
return {
|
|
canvas
|
|
};
|
|
}
|
|
|
|
const svg = document.querySelector("#woman");
|
|
const svgUrl = "http://www.w3.org/2000/svg";
|
|
|
|
//color socials
|
|
function colorSocials() {
|
|
try {
|
|
const color = Colors.secondary;
|
|
document.querySelector("#twitter-icon").style.stroke = color;
|
|
document.querySelector("#insta-rect").style.stroke = color;
|
|
document.querySelector("#insta-circ").style.stroke = color;
|
|
document.querySelector("#insta-circ-2").style.stroke = color;
|
|
[1, 2, 3].forEach(
|
|
i => (document.querySelector(`#code-pen-${i}`).style.stroke = color)
|
|
);
|
|
} catch (e) {}
|
|
}
|
|
colorSocials();
|
|
|
|
async function createAnimationObjects({ shapeId, boxId, flippy = 0, options }) {
|
|
const boundsRect = document.querySelector(`#${boxId}`);
|
|
if (!boundsRect) {
|
|
console.log("cant find", boxId);
|
|
return () => {};
|
|
}
|
|
// grab bounds points
|
|
const boundsPoints = svgPolyElementToPointsList(boundsRect);
|
|
const containerBox = new ContainerBox(boundsPoints, flippy);
|
|
const vector = containerBox.getUp();
|
|
boundsRect.style.opacity = "0";
|
|
const angle = Math.atan2(vector.y, vector.x) * 57.3;
|
|
|
|
const patternHolder = createSvgElement("g", {});
|
|
const canvasImgElement = createSvgElement("image", {
|
|
width: containerBox.wid,
|
|
height: containerBox.hei * 2,
|
|
href: ""
|
|
});
|
|
patternHolder.appendChild(canvasImgElement);
|
|
|
|
// insert this group right after
|
|
const mainPath = document.querySelector(`#${shapeId}`);
|
|
mainPath.style.opacity = 0;
|
|
const g = createSvgElement("g", {});
|
|
mainPath.parentNode.insertBefore(g, mainPath.nextElementSibling);
|
|
|
|
const clipId = `mange-` + Math.floor(Math.random() * 200000);
|
|
const clipPath = createSvgElement("clipPath", {});
|
|
clipPath.id = clipId;
|
|
clipPath.innerHTML = `<path d=${mainPath.getAttribute("d")}></path>`;
|
|
g.appendChild(clipPath);
|
|
g.setAttribute("clip-path", `url(#${clipId})`);
|
|
if (mainPath.getAttribute("transform"))
|
|
g.setAttribute("transform", mainPath.getAttribute("transform"));
|
|
g.appendChild(patternHolder);
|
|
|
|
const topLeftCorner = containerBox.points[0];
|
|
patternHolder.style.transform = `translate(${topLeftCorner.x}px, ${
|
|
topLeftCorner.y
|
|
}px) rotateZ(${angle + 90}deg) translate(0px, 0px)`;
|
|
|
|
return {
|
|
step: (offset = 0) => {
|
|
const Y = -offset % containerBox.hei;
|
|
patternHolder.style.transform = `translate(${topLeftCorner.x}px, ${
|
|
topLeftCorner.y
|
|
}px) rotateZ(${angle + 90}deg) translate(0px, ${Y}px)`;
|
|
},
|
|
imgElement: canvasImgElement,
|
|
options,
|
|
patternSpec: {
|
|
width: containerBox.wid,
|
|
height: containerBox.hei
|
|
},
|
|
originalShape: mainPath
|
|
};
|
|
}
|
|
|
|
const pathsToGo = [
|
|
{
|
|
shapeId: "pink-face",
|
|
boxId: "pink-face-box",
|
|
options: {
|
|
N: 800 * 0.7,
|
|
color: Colors.primary,
|
|
secondaryColor: Colors.secondary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "highlighted-yellow-top",
|
|
boxId: "shirt-box",
|
|
options: {
|
|
N: 1200 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8,
|
|
seed: "shirt"
|
|
}
|
|
},
|
|
{
|
|
shapeId: "highlighted-pink-top",
|
|
boxId: "shirt-box",
|
|
options: {
|
|
N: 1200 * 0.7,
|
|
color: Colors.primary,
|
|
secondaryColor: Colors.secondary,
|
|
size: 8,
|
|
seed: "shirt"
|
|
}
|
|
},
|
|
{
|
|
shapeId: "right-ear-section",
|
|
boxId: "right-ear-section-box",
|
|
options: {
|
|
N: 400 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "face-shadow",
|
|
boxId: "face-shadow-box",
|
|
options: {
|
|
N: 400 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "bottom-right-pink-hair",
|
|
boxId: "bottom-right-pink-hair-box",
|
|
options: {
|
|
N: 200 * 0.7,
|
|
color: Colors.primary,
|
|
secondaryColor: Colors.secondary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "bottom-right-yellow-hair",
|
|
boxId: "bottom-right-yellow-hair-box",
|
|
options: {
|
|
N: 200 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "right-center-hair-line",
|
|
boxId: "right-center-hair-line-box",
|
|
options: {
|
|
N: 50 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
},
|
|
flippy: 3
|
|
},
|
|
{
|
|
shapeId: "pink-top-hair-line",
|
|
boxId: "pink-top-haiir-line-box",
|
|
options: {
|
|
N: 20 * 0.7,
|
|
color: Colors.primary,
|
|
secondaryColor: Colors.secondary,
|
|
size: 8
|
|
},
|
|
flippy: 3
|
|
},
|
|
{
|
|
shapeId: "medium-left-pink",
|
|
boxId: "medium-left-pink-box",
|
|
options: {
|
|
N: 20 * 0.7,
|
|
color: Colors.primary,
|
|
secondaryColor: Colors.secondary,
|
|
size: 8
|
|
},
|
|
flippy: 1
|
|
},
|
|
{
|
|
shapeId: "bottom-left-yellow",
|
|
boxId: "bottom-left-yellow-box",
|
|
options: {
|
|
N: 20 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
}
|
|
},
|
|
{
|
|
shapeId: "safari-clip",
|
|
boxId: "safari-box",
|
|
options: {
|
|
N: 13 * 0.7,
|
|
color: Colors.secondary,
|
|
secondaryColor: Colors.primary,
|
|
size: 8
|
|
}
|
|
}
|
|
];
|
|
|
|
const items = [
|
|
{ id: "dark-top", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "highlighted-yellow-top", fill: Colors.secondary },
|
|
{ id: "highlighted-pink-top", fill: Colors.primary },
|
|
{ id: "left-arm", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "arm-crease", stroke: Colors.secondary },
|
|
{ id: "left-sleeve-outline", stroke: Colors.secondary },
|
|
{ id: "left-sleeve-outline", stroke: Colors.secondary },
|
|
{ id: "black-hair-bg", fill: Colors.primary },
|
|
{ id: "black-hair", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "bottom-right-yellow-hair", fill: Colors.secondary },
|
|
{ id: "bottom-right-pink-hair", fill: Colors.primary },
|
|
{ id: "right-center-hair-line", fill: Colors.secondary },
|
|
{ id: "pink-top-hair-line", fill: Colors.primary },
|
|
{ id: "left-hair-line-top", stroke: Colors.secondary },
|
|
{ id: "medium-left-pink", fill: Colors.primary },
|
|
{ id: "bottom-left-yellow", fill: Colors.secondary },
|
|
{ id: "pink-face", fill: Colors.primary },
|
|
{ id: "face-shadow", fill: Colors.secondary },
|
|
{ id: "right-ear-section", fill: Colors.secondary },
|
|
{ id: "left-hand", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "right-arm", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "back", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "front", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "phone-p1", fill: Colors.bg, stroke: Colors.secondary },
|
|
{ id: "phone-p2", fill: Colors.bg, stroke: Colors.secondary }
|
|
];
|
|
|
|
pathsToGo.forEach(i => (document.getElementById(i.boxId).style.opacity = 0));
|
|
function updateColors(isLoading = false) {
|
|
items.forEach(item => {
|
|
const e = document.getElementById(item.id);
|
|
if (!e) {
|
|
console.log("could not find", item.id);
|
|
return;
|
|
}
|
|
if (item.stroke)
|
|
e.style.stroke = isLoading ? Colors.loadingColor : item.stroke;
|
|
if (item.fill) e.style.fill = isLoading ? "black" : item.fill;
|
|
});
|
|
}
|
|
updateColors(true);
|
|
svg.style.animationName = "loading";
|
|
var togglePattern = () => {};
|
|
|
|
async function start() {
|
|
const animationObjects = await Promise.all(
|
|
pathsToGo.map(p => createAnimationObjects(p))
|
|
);
|
|
let isChanging = false;
|
|
function redraw(offset) {
|
|
animationObjects.forEach(x => x.step(offset));
|
|
}
|
|
|
|
function setLoading() {
|
|
updateColors(true);
|
|
svg.style.animationName = "loading";
|
|
for (let obj of animationObjects) {
|
|
obj.imgElement.style.opacity = 0;
|
|
obj.originalShape.style.opacity = 1;
|
|
}
|
|
}
|
|
|
|
async function changePatterns(patternFn) {
|
|
if (isChanging) return false;
|
|
isChanging = true;
|
|
updateColors(true);
|
|
svg.style.animationName = "loading";
|
|
setLoading();
|
|
await new Promise(r => setTimeout(r, 50));
|
|
const animationObjectToPattern = new Map();
|
|
for (let obj of animationObjects) {
|
|
const pattern = await patternFn(
|
|
obj.patternSpec.width,
|
|
obj.patternSpec.height,
|
|
obj.options
|
|
);
|
|
animationObjectToPattern.set(obj, pattern.canvas.toDataURL());
|
|
}
|
|
for ([obj, pattern] of animationObjectToPattern) {
|
|
obj.imgElement.setAttribute("href", pattern);
|
|
}
|
|
await new Promise(r => setTimeout(r, 50));
|
|
animationObjects.forEach(obj => {
|
|
obj.imgElement.style.opacity = 1;
|
|
obj.originalShape.style.opacity = 0;
|
|
});
|
|
svg.style.animationName = "none";
|
|
svg.style.opacity = 1;
|
|
updateColors(false);
|
|
|
|
isChanging = false;
|
|
return true;
|
|
}
|
|
changePatterns(layeredBubbles);
|
|
let isLayeredBubbles = true;
|
|
togglePattern = async () => {
|
|
if (isLayeredBubbles) {
|
|
const changed = await changePatterns(glowTextPattern);
|
|
if (changed) isLayeredBubbles = false;
|
|
} else {
|
|
const changed = await changePatterns(layeredBubbles);
|
|
if (changed) isLayeredBubbles = true;
|
|
}
|
|
};
|
|
|
|
let offset = 0;
|
|
const useScroll = window.location.search.includes("scroll");
|
|
if (useScroll) {
|
|
addEventListener("scroll", () => {
|
|
offset = window.scrollY;
|
|
});
|
|
document.body.style.height = "77000px";
|
|
}
|
|
|
|
let lastRedraw = 0;
|
|
let lastDelta = 0;
|
|
function step(delta) {
|
|
const timePassed = delta - lastDelta;
|
|
const scalar = timePassed / 16;
|
|
if (lastRedraw !== offset) redraw(offset * 0.1);
|
|
lastRedraw = offset;
|
|
if (!useScroll) offset += 4 * scalar;
|
|
lastDelta = delta;
|
|
requestAnimationFrame(step);
|
|
}
|
|
requestAnimationFrame(step);
|
|
}
|
|
setTimeout(start, 20); |