Add random shape backgrounds
This commit is contained in:
parent
59c848acad
commit
4cf69f2070
|
@ -1,5 +1,5 @@
|
||||||
import { useWindowDimension } from "hooks/useWindowDimension";
|
import { useWindowDimension } from "hooks/useWindowDimension";
|
||||||
import React, { CSSProperties, useEffect, useState } from "react";
|
import React, { CSSProperties, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Image } from "./Image";
|
import { Image } from "./Image";
|
||||||
|
|
||||||
|
@ -12,13 +12,21 @@ interface Props {
|
||||||
export function ShapesBackground({ getConfig }: Props) {
|
export function ShapesBackground({ getConfig }: Props) {
|
||||||
const [config, setConfig] = useState<ShapesConfig>({});
|
const [config, setConfig] = useState<ShapesConfig>({});
|
||||||
const { width, height } = useWindowDimension();
|
const { width, height } = useWindowDimension();
|
||||||
|
const shapesContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setConfig(getConfig?.() ?? {});
|
const width = shapesContainerRef.current?.clientWidth;
|
||||||
|
const height = shapesContainerRef.current?.clientHeight;
|
||||||
|
|
||||||
|
if (width == null || height == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfig(getConfig?.(width, height) ?? {});
|
||||||
}, [getConfig, width, height]);
|
}, [getConfig, width, height]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.shapesContainer}>
|
<div className={styles.shapesContainer} ref={shapesContainerRef}>
|
||||||
{Object.entries(config).map(([type, instances]) =>
|
{Object.entries(config).map(([type, instances]) =>
|
||||||
instances.map((attributes, idx) => (
|
instances.map((attributes, idx) => (
|
||||||
<Shape
|
<Shape
|
||||||
|
@ -32,6 +40,16 @@ export function ShapesBackground({ getConfig }: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Shape(props: { type: ShapeType; style: CSSProperties }) {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={`/images/shapes/${props.type}.svg`}
|
||||||
|
className={styles.shape}
|
||||||
|
style={props.style}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export type ShapeType =
|
export type ShapeType =
|
||||||
| "asterisk"
|
| "asterisk"
|
||||||
| "circle"
|
| "circle"
|
||||||
|
@ -43,24 +61,196 @@ export type ShapeType =
|
||||||
| "triangle"
|
| "triangle"
|
||||||
| "triangleBig"
|
| "triangleBig"
|
||||||
| "waves"
|
| "waves"
|
||||||
| "waveBig";
|
| "wavesBig";
|
||||||
|
|
||||||
export type ShapesConfig = {
|
export type ShapesConfig = {
|
||||||
[key in ShapeType]?: CSSProperties[];
|
[key in ShapeType]?: CSSProperties[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetShapesConfig = () => ShapesConfig;
|
export type GetShapesConfig = (width: number, height: number) => ShapesConfig;
|
||||||
|
|
||||||
function Shape(props: { type: ShapeType; style: CSSProperties }) {
|
type ShapeSize = {
|
||||||
|
[key in ShapeType]: {
|
||||||
|
size: "big" | "small";
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
// 0 <= minAngle, maxAngle <= 180
|
||||||
|
minAngle?: number;
|
||||||
|
maxAngle?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const shapeTypes: ShapeType[] = [
|
||||||
|
"asterisk",
|
||||||
|
"circle",
|
||||||
|
"cross",
|
||||||
|
"dots",
|
||||||
|
"hash",
|
||||||
|
"plus",
|
||||||
|
"ring",
|
||||||
|
"triangle",
|
||||||
|
"triangleBig",
|
||||||
|
"waves",
|
||||||
|
"wavesBig",
|
||||||
|
];
|
||||||
|
|
||||||
|
const shapesSizes: ShapeSize = {
|
||||||
|
asterisk: {
|
||||||
|
size: "big",
|
||||||
|
width: 168,
|
||||||
|
height: 168,
|
||||||
|
},
|
||||||
|
circle: {
|
||||||
|
size: "big",
|
||||||
|
width: 132,
|
||||||
|
height: 132,
|
||||||
|
},
|
||||||
|
cross: {
|
||||||
|
size: "big",
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
},
|
||||||
|
dots: {
|
||||||
|
size: "big",
|
||||||
|
width: 232,
|
||||||
|
height: 250,
|
||||||
|
},
|
||||||
|
hash: {
|
||||||
|
size: "small",
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
},
|
||||||
|
plus: {
|
||||||
|
size: "small",
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
ring: {
|
||||||
|
size: "small",
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
},
|
||||||
|
triangle: {
|
||||||
|
size: "small",
|
||||||
|
width: 68,
|
||||||
|
height: 68,
|
||||||
|
minAngle: 15,
|
||||||
|
maxAngle: 26,
|
||||||
|
},
|
||||||
|
triangleBig: {
|
||||||
|
size: "big",
|
||||||
|
width: 138,
|
||||||
|
height: 138,
|
||||||
|
minAngle: 15,
|
||||||
|
maxAngle: 26,
|
||||||
|
},
|
||||||
|
waves: {
|
||||||
|
size: "small",
|
||||||
|
width: 102,
|
||||||
|
height: 50,
|
||||||
|
},
|
||||||
|
wavesBig: {
|
||||||
|
size: "big",
|
||||||
|
width: 252,
|
||||||
|
height: 132,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const shapesBySize = {
|
||||||
|
big: Object.entries(shapesSizes)
|
||||||
|
.map((shape) => shape[0])
|
||||||
|
.filter(
|
||||||
|
(shape) => shapesSizes[shape as ShapeType]["size"] == "big"
|
||||||
|
) as ShapeType[],
|
||||||
|
small: Object.entries(shapesSizes)
|
||||||
|
.map((shape) => shape[0])
|
||||||
|
.filter(
|
||||||
|
(shape) => shapesSizes[shape as ShapeType]["size"] == "small"
|
||||||
|
) as ShapeType[],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used to generate random shapes in the margins
|
||||||
|
export const defaultGetShapesConfig = ((pageWidth, pageHeight) => {
|
||||||
|
if (window.innerWidth <= 768) {
|
||||||
|
return mobileShapesConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultConfig: ShapesConfig = {};
|
||||||
|
shapeTypes.forEach((shape) => (defaultConfig[shape] = []));
|
||||||
|
|
||||||
|
const gap = 20;
|
||||||
|
const boxWidth = Math.max(300, (pageWidth - 800 - 2 * gap) / 2);
|
||||||
|
const boxHeight = 400;
|
||||||
|
|
||||||
|
for (let y = 0; y + boxHeight <= pageHeight; y += gap + boxHeight) {
|
||||||
|
for (let x = 0; x <= 1; ++x) {
|
||||||
|
if (Math.random() < 0.3) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = y == 0 || y + 2 * boxHeight > pageHeight ? "big" : "small";
|
||||||
|
const shape: ShapeType = getRandomShape(size);
|
||||||
|
const verticalOffset = getVerticalOffset(boxHeight, shape);
|
||||||
|
const horizontalOffset = getHorizontalOffset(boxWidth, shape);
|
||||||
|
const shapeWidth = shapesSizes[shape]["width"];
|
||||||
|
const shapeHeight = shapesSizes[shape]["height"];
|
||||||
|
const angle = getRandomAngle(shape);
|
||||||
|
const colour = getRandomColour();
|
||||||
|
const opacity = getRandomOpacity(colour);
|
||||||
|
|
||||||
|
defaultConfig[shape]?.push({
|
||||||
|
top: `${((y + verticalOffset) / 16).toFixed(5)}rem`,
|
||||||
|
left: x == 0 ? `${(horizontalOffset / 16).toFixed(5)}rem` : "unset",
|
||||||
|
right: x == 1 ? `${(horizontalOffset / 16).toFixed(5)}rem` : "unset",
|
||||||
|
width: `${(shapeWidth / 16).toFixed(5)}rem`,
|
||||||
|
height: `${(shapeHeight / 16).toFixed(5)}rem`,
|
||||||
|
transform: `rotate(${angle}deg)`,
|
||||||
|
filter: `var(--${colour})`,
|
||||||
|
opacity: `${opacity}%`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultConfig;
|
||||||
|
}) as GetShapesConfig;
|
||||||
|
|
||||||
|
function getRandomShape(size: "big" | "small"): ShapeType {
|
||||||
|
const idx = Math.floor(Math.random() * shapesBySize[size].length);
|
||||||
|
return shapesBySize[size][idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVerticalOffset(boxHeight: number, shape: ShapeType): number {
|
||||||
|
const padding = shapesSizes[shape]["height"];
|
||||||
|
return Math.floor(Math.random() * (boxHeight - padding));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getHorizontalOffset(boxWidth: number, shape: ShapeType): number {
|
||||||
|
const padding = shapesSizes[shape]["width"];
|
||||||
|
return Math.floor(Math.random() * (boxWidth - padding));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomAngle(shape: ShapeType): number {
|
||||||
|
const minAngle = shapesSizes[shape]["minAngle"] ?? 0;
|
||||||
|
const maxAngle = shapesSizes[shape]["maxAngle"] ?? 0;
|
||||||
|
const direction = Math.random() < 0.5 ? 1 : -1;
|
||||||
return (
|
return (
|
||||||
<Image
|
(minAngle + Math.floor(Math.random() * (maxAngle - minAngle + 1))) *
|
||||||
src={`/images/shapes/${props.type}.svg`}
|
direction
|
||||||
className={styles.shape}
|
|
||||||
style={props.style}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRandomColour(): "blue" | "teal" {
|
||||||
|
return Math.random() < 0.7 ? "blue" : "teal";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomOpacity(colour: "blue" | "teal"): number {
|
||||||
|
if (colour == "blue") {
|
||||||
|
return Math.random() < 0.8 ? 20 : 25;
|
||||||
|
} else {
|
||||||
|
return Math.random() < 0.8 ? 25 : 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Used for most mobile pages
|
// Used for most mobile pages
|
||||||
export const mobileShapesConfig = {
|
export const mobileShapesConfig = {
|
||||||
dots: [
|
dots: [
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { Link } from "@/components/Link";
|
||||||
import { Navbar } from "@/components/Navbar";
|
import { Navbar } from "@/components/Navbar";
|
||||||
import { Pre } from "@/components/Pre";
|
import { Pre } from "@/components/Pre";
|
||||||
import {
|
import {
|
||||||
|
defaultGetShapesConfig,
|
||||||
GetShapesConfig,
|
GetShapesConfig,
|
||||||
ShapesBackground,
|
ShapesBackground,
|
||||||
} from "@/components/ShapesBackground";
|
} from "@/components/ShapesBackground";
|
||||||
|
@ -41,7 +42,7 @@ export default function App({ Component, pageProps }: AppProps): JSX.Element {
|
||||||
{/* Wrapping content with a div to allow for a display: block parent */}
|
{/* Wrapping content with a div to allow for a display: block parent */}
|
||||||
<div className={styles.contentContainer}>
|
<div className={styles.contentContainer}>
|
||||||
<ShapesBackground
|
<ShapesBackground
|
||||||
getConfig={Component.getShapesConfig ?? undefined}
|
getConfig={Component.getShapesConfig ?? defaultGetShapesConfig}
|
||||||
/>
|
/>
|
||||||
<Layout>
|
<Layout>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
|
|
Loading…
Reference in New Issue