mirror of
https://github.com/Xevion/grain.git
synced 2025-12-06 01:15:10 -06:00
Noise & Gradient Generation
This commit is contained in:
124
src/App.tsx
Normal file
124
src/App.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import Chance from "chance";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { useEventListener, useWindowSize } from "usehooks-ts";
|
||||
import { useState } from "react";
|
||||
import { getEdgePoint } from "@/utils";
|
||||
|
||||
const chance = Chance();
|
||||
const colors = [
|
||||
"#ed625d",
|
||||
"#42b6c6",
|
||||
"#f79f88",
|
||||
"#446ba6",
|
||||
"#4b95f0",
|
||||
"#d16ba5",
|
||||
];
|
||||
|
||||
const generateBackground = () => {
|
||||
return chance.pickset(colors, 5).map((color) => {
|
||||
const [x, y] = getEdgePoint(chance.integer({ min: 0, max: 100 }), 100, 100);
|
||||
return `radial-gradient(farthest-corner at ${x}% ${y}%, ${chance.pickone(
|
||||
colors
|
||||
)}, ${chance.pickone(colors)}, transparent 100%)`;
|
||||
});
|
||||
};
|
||||
|
||||
function App() {
|
||||
const { width, height } = useWindowSize();
|
||||
const [background, setBackground] = useState(generateBackground());
|
||||
|
||||
useEventListener("keydown", (e) => {
|
||||
if (e.code != "Enter") return;
|
||||
e.preventDefault();
|
||||
|
||||
setBackground(generateBackground());
|
||||
});
|
||||
|
||||
const ratio = 0.3;
|
||||
const svgWidth = Math.ceil((width ?? 1920) * ratio);
|
||||
const svgHeight = Math.ceil((height ?? 1080) * ratio);
|
||||
console.log({ svgWidth, svgHeight });
|
||||
|
||||
const noise = (
|
||||
<svg
|
||||
viewBox={`0 0 ${svgWidth} ${svgHeight}`}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<filter id="noiseFilter">
|
||||
<feTurbulence
|
||||
type="fractalNoise"
|
||||
baseFrequency="2"
|
||||
numOctaves="2"
|
||||
stitchTiles="stitch"
|
||||
/>
|
||||
</filter>
|
||||
<g opacity={0.9}>
|
||||
<rect width="100%" height="100%" filter="url(#noiseFilter)" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const svg = `data:image/svg+xml;base64,${window.btoa(
|
||||
ReactDOMServer.renderToString(noise)
|
||||
)}`;
|
||||
|
||||
const appStyle = {
|
||||
background: [`url("${svg}")`, ...background].join(", "),
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={appStyle} className="gradient">
|
||||
<div className="font-inter min-w-[100vw] min-h-[100vh] bg-zinc-800/50 bg-blend-overlay">
|
||||
<div className="grid grid-cols-12 w-full">
|
||||
<div className="col-span-1" />
|
||||
<div className="flex col-span-10 md:col-span-8 lg:col-span-6 offset-1 bg-white w-full min-h-[100vh] text-zinc-800">
|
||||
<div className="m-3 p-5 border-y-[1px] border-zinc-100">
|
||||
<div className="mb-3">
|
||||
<h2 className="text-4xl tracking-wide font-semibold drop-shadow-xl">
|
||||
Ryan Walters
|
||||
</h2>
|
||||
<span className="py-1 text-zinc-500">
|
||||
Published on {new Date().toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
|
||||
consequat lacus sit amet erat efficitur elementum. Fusce
|
||||
hendrerit bibendum ipsum, sit amet volutpat augue egestas sed.
|
||||
Curabitur ut blandit felis. Suspendisse euismod, orci quis
|
||||
consectetur pretium, libero eros lobortis lacus, vel interdum
|
||||
ex sem sed sapien. Aliquam erat volutpat. Curabitur tempus
|
||||
faucibus lobortis. Nulla sodales ipsum sit amet ligula
|
||||
elementum faucibus. Donec aliquam enim a arcu gravida tempus.
|
||||
Etiam tempus lectus et mauris feugiat, vel imperdiet quam
|
||||
mattis. Suspendisse urna enim, cursus quis nibh et, egestas
|
||||
ultrices tortor. Quisque imperdiet elit molestie lorem
|
||||
placerat posuere. Duis in dolor non elit tempor mattis. Proin
|
||||
vehicula facilisis nibh.
|
||||
</p>
|
||||
<p>
|
||||
Maecenas rhoncus, erat auctor scelerisque condimentum, nibh
|
||||
magna tristique felis, vitae condimentum mi est quis dui.
|
||||
Fusce non imperdiet massa. Sed varius ultrices odio non
|
||||
faucibus. Pellentesque habitant morbi tristique senectus et
|
||||
netus et malesuada fames ac turpis egestas. Pellentesque
|
||||
semper pulvinar vehicula. Fusce vel convallis eros. Duis
|
||||
cursus feugiat quam, quis sodales enim malesuada non.
|
||||
Vestibulum ante ipsum primis in faucibus orci luctus et
|
||||
ultrices posuere cubilia curae; Maecenas in efficitur elit,
|
||||
sed suscipit magna. Duis finibus blandit leo vitae mollis. In
|
||||
et est pulvinar, condimentum est sed, scelerisque nisl. Nunc
|
||||
eleifend eros aliquet libero.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
35
src/index.scss
Normal file
35
src/index.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Raleway:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap");
|
||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap");
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
body {
|
||||
min-width: 100vw;
|
||||
min-height: 100vh;
|
||||
|
||||
background-color: #242424;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
filter: contrast(150%) brightness(90%);
|
||||
background-blend-mode: overlay;
|
||||
}
|
||||
10
src/main.tsx
Normal file
10
src/main.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App'
|
||||
import './index.scss'
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
14
src/utils.ts
Normal file
14
src/utils.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export const getEdgePoint = (
|
||||
n: number,
|
||||
width: number,
|
||||
height: number
|
||||
): [number, number] => {
|
||||
const full = 2 * width + 2 * height;
|
||||
if (n > full) n %= full;
|
||||
if (n < 0) n = full - n;
|
||||
|
||||
if (n < width) return [n, 0];
|
||||
if (n < width + height) return [width, n - width];
|
||||
if (n < 2 * width + height) return [n - (2 * width + height), height];
|
||||
return [0, height - (n - (2 * width + height))];
|
||||
};
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user