Add random shape backgrounds
This commit is contained in:
parent
59c848acad
commit
4cf69f2070
|
@ -1,5 +1,5 @@
|
|||
import { useWindowDimension } from "hooks/useWindowDimension";
|
||||
import React, { CSSProperties, useEffect, useState } from "react";
|
||||
import React, { CSSProperties, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { Image } from "./Image";
|
||||
|
||||
|
@ -12,13 +12,21 @@ interface Props {
|
|||
export function ShapesBackground({ getConfig }: Props) {
|
||||
const [config, setConfig] = useState<ShapesConfig>({});
|
||||
const { width, height } = useWindowDimension();
|
||||
const shapesContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
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]);
|
||||
|
||||
return (
|
||||
<div className={styles.shapesContainer}>
|
||||
<div className={styles.shapesContainer} ref={shapesContainerRef}>
|
||||
{Object.entries(config).map(([type, instances]) =>
|
||||
instances.map((attributes, idx) => (
|
||||
<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 =
|
||||
| "asterisk"
|
||||
| "circle"
|
||||
|
@ -43,24 +61,196 @@ export type ShapeType =
|
|||
| "triangle"
|
||||
| "triangleBig"
|
||||
| "waves"
|
||||
| "waveBig";
|
||||
| "wavesBig";
|
||||
|
||||
export type ShapesConfig = {
|
||||
[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 (
|
||||
<Image
|
||||
src={`/images/shapes/${props.type}.svg`}
|
||||
className={styles.shape}
|
||||
style={props.style}
|
||||
/>
|
||||
(minAngle + Math.floor(Math.random() * (maxAngle - minAngle + 1))) *
|
||||
direction
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
export const mobileShapesConfig = {
|
||||
dots: [
|
||||
|
|
|
@ -12,6 +12,7 @@ import { Link } from "@/components/Link";
|
|||
import { Navbar } from "@/components/Navbar";
|
||||
import { Pre } from "@/components/Pre";
|
||||
import {
|
||||
defaultGetShapesConfig,
|
||||
GetShapesConfig,
|
||||
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 */}
|
||||
<div className={styles.contentContainer}>
|
||||
<ShapesBackground
|
||||
getConfig={Component.getShapesConfig ?? undefined}
|
||||
getConfig={Component.getShapesConfig ?? defaultGetShapesConfig}
|
||||
/>
|
||||
<Layout>
|
||||
<Component {...pageProps} />
|
||||
|
|
Loading…
Reference in New Issue