-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* wip * wip * wip * wip * add blur to tube * add first bubble * add box shadow * simplex noise bubble paths * example * add standard http file server * stretchy bubbles * add delay to stagger bubbles * update tube.svg * add perspective divs * add rising lands effect * wip * wip * wip todo: cubes show rise from the bottom * wip * wip * wip * Squashed commit of the following: commit 1c366c4361564b7dba59df55ca73a61ecd6c6c04 Author: EthanThatOneKid <[email protected]> Date: Mon Apr 22 10:28:31 2024 -0700 fix rising cubes animation commit a7a8ae1a39d857b65ea9f95d481b3ef254de839d Author: EthanThatOneKid <[email protected]> Date: Mon Apr 22 09:25:13 2024 -0700 Update index.css commit a52e75db411e289bfc69dd73e240b4e3d9c742b6 Author: EthanThatOneKid <[email protected]> Date: Mon Apr 22 09:20:28 2024 -0700 change text color of rising cubes * stagger rising rotation * add scroll timeline polyfill https://github.com/flackr/scroll-timeline * add favicon * add hljs syntax highlighting copy hljs output from <https://github.com/FartLabs/jsonx_docs/pull/25>'s Lab example. * add page headings * add grid to hero element * add grid to hero element with mobile responsiveness * seo: add description * Update index.html * Update index.html * add fl-icon class * add molecule blob behind hero logo animation * add baby goop character asset i love baby goop * add projects section * delete badges * add remaining recent projects choose more tube colors (with mom ♥) * Update index.html * fix grammar * fix grammar * remove unneeded punctuation
- Loading branch information
1 parent
dbda9fb
commit 77745b6
Showing
28 changed files
with
1,449 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"deno.enable": true, | ||
"deno.unstable": true, | ||
"editor.formatOnSave": true, | ||
"editor.defaultFormatter": "esbenp.prettier-vscode", | ||
"[markdown]": { | ||
"editor.defaultFormatter": "denoland.vscode-deno" | ||
}, | ||
"[jsonc]": { | ||
"editor.defaultFormatter": "denoland.vscode-deno" | ||
}, | ||
"[typescript]": { | ||
"editor.defaultFormatter": "denoland.vscode-deno" | ||
}, | ||
"[typescriptreact]": { | ||
"editor.defaultFormatter": "denoland.vscode-deno" | ||
}, | ||
"files.eol": "\n" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
# fart.tools | ||
|
||
🧪 Official website of FartLabs. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.background-blob { | ||
background-image: url("molecule-blob.svg"); | ||
background-size: cover; | ||
background-size: 100%; | ||
background-repeat: no-repeat; | ||
background-position: 50% 50%; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"lock": false, | ||
"imports": { | ||
"@std/http": "jsr:@std/http@^0.223.0", | ||
"simplex-noise": "npm:simplex-noise@^4.0.1" | ||
}, | ||
"tasks": { | ||
"start": "deno run --allow-net --allow-read main.ts", | ||
"generate": "deno run --allow-write gen.ts" | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
import { createNoise2D } from "simplex-noise"; | ||
|
||
const noise = createNoise2D(); | ||
|
||
const green = "#C3EF3C"; | ||
const purple = "#9D00FF"; | ||
const yellow = "#FDBD01"; | ||
const turquoise = "#16E2F5"; | ||
const magenta = "#FF00FF"; | ||
const orange = "#FF6700"; | ||
const blue = "#1589FF"; | ||
|
||
// deno run gen.ts | ||
// | ||
if (import.meta.main) { | ||
const path = [ | ||
{ x: 20, y: 20 }, | ||
{ x: 84, y: 20 }, | ||
{ x: 84, y: 84 }, | ||
{ x: 20, y: 84 }, | ||
]; | ||
const verticesAmount = Deno.args[0] ? parseInt(Deno.args[0]) : 100; | ||
|
||
const emptyTubeSVG = renderEmptyTube(); | ||
Deno.writeTextFileSync("tube-empty.svg", emptyTubeSVG); | ||
|
||
const emptyTubeCSS = renderTubeCSS("empty", "#ffffffaa"); | ||
Deno.writeTextFileSync("tube-empty.css", emptyTubeCSS); | ||
|
||
const greenTubeSVG = renderGreenTube( | ||
renderBubbles(path, verticesAmount, 30, green), | ||
); | ||
Deno.writeTextFileSync("tube-green.svg", greenTubeSVG); | ||
|
||
const greenTubeCSS = renderTubeCSS("green", green); | ||
Deno.writeTextFileSync("tube-green.css", greenTubeCSS); | ||
|
||
const purpleTubeSVG = renderPurpleTube( | ||
renderBubbles(path, verticesAmount, 30, purple), | ||
); | ||
Deno.writeTextFileSync("tube-purple.svg", purpleTubeSVG); | ||
|
||
const purpleTubeCSS = renderTubeCSS("purple", purple); | ||
Deno.writeTextFileSync("tube-purple.css", purpleTubeCSS); | ||
|
||
const yellowTubeSVG = renderYellowTube( | ||
renderBubbles(path, verticesAmount, 30, yellow), | ||
); | ||
Deno.writeTextFileSync("tube-yellow.svg", yellowTubeSVG); | ||
|
||
const yellowTubeCSS = renderTubeCSS("yellow", yellow); | ||
Deno.writeTextFileSync("tube-yellow.css", yellowTubeCSS); | ||
|
||
const turquoiseTubeSVG = renderTurquoiseTube( | ||
renderBubbles(path, verticesAmount, 30, turquoise), | ||
); | ||
Deno.writeTextFileSync("tube-turquoise.svg", turquoiseTubeSVG); | ||
|
||
const turquoiseTubeCSS = renderTubeCSS("turquoise", turquoise); | ||
Deno.writeTextFileSync("tube-turquoise.css", turquoiseTubeCSS); | ||
|
||
const magentaTubeSVG = renderMagentaTube( | ||
renderBubbles(path, verticesAmount, 30, magenta), | ||
); | ||
Deno.writeTextFileSync("tube-magenta.svg", magentaTubeSVG); | ||
|
||
const magentaTubeCSS = renderTubeCSS("magenta", magenta); | ||
Deno.writeTextFileSync("tube-magenta.css", magentaTubeCSS); | ||
|
||
const orangeTubeSVG = renderOrangeTube( | ||
renderBubbles(path, verticesAmount, 30, orange), | ||
); | ||
Deno.writeTextFileSync("tube-orange.svg", orangeTubeSVG); | ||
|
||
const orangeTubeCSS = renderTubeCSS("orange", orange); | ||
Deno.writeTextFileSync("tube-orange.css", orangeTubeCSS); | ||
|
||
const blueTubeSVG = renderBlueTube( | ||
renderBubbles(path, verticesAmount, 30, blue), | ||
); | ||
Deno.writeTextFileSync("tube-blue.svg", blueTubeSVG); | ||
|
||
const blueTubeCSS = renderTubeCSS("blue", blue); | ||
Deno.writeTextFileSync("tube-blue.css", blueTubeCSS); | ||
} | ||
|
||
function renderTubeCSS(name: string, color: string) { | ||
return `.border-tube-${name} { | ||
border-image-slice: 31; | ||
border-image-width: 28px; | ||
border-image-outset: 4px; | ||
border-image-repeat: stretch stretch; | ||
border-image-source: url("tube-${name}.svg"); | ||
border-style: solid; | ||
padding: 20px; | ||
border-radius: 2em; | ||
} | ||
.border-tube-${name}[class*=" glow"], | ||
.border-tube-${name}[class*="glow "] { | ||
box-shadow: 0 0 42px 8px ${color}; | ||
}\n`; | ||
} | ||
|
||
function renderGreenTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${green}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderPurpleTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${purple}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderYellowTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${yellow}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderTurquoiseTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${turquoise}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderMagentaTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${magenta}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderOrangeTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${orange}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderBlueTube(...children: string[]) { | ||
return renderEmptyTube( | ||
`<rect filter="blur(1px)" stroke="${blue}aa" stroke-width="28" x="16" y="16" width="68" height="68" rx="16" ry="16" />`, | ||
...children, | ||
); | ||
} | ||
|
||
function renderEmptyTube(...children: string[]) { | ||
return [ | ||
`<svg width='100' height='100' viewBox='0 0 100 100' fill='none' xmlns='http://www.w3.org/2000/svg'>`, | ||
...children, | ||
`<rect stroke="#fffffff0" stroke-width="2" x="3" y="3" width="94" height="94" rx="24" ry="24" />`, | ||
`<rect stroke="#fffffff0" stroke-width="2" x="29" y="29" width="42" height="42" rx="6" ry="6" />`, | ||
`<rect filter="blur(2px)" stroke="#ffffffaa" stroke-width="7.5" x="17.5" y="17.5" width="66" height="66" rx="12" ry="12" />`, | ||
`</svg>\n`, | ||
].join("\n"); | ||
} | ||
|
||
function renderBubbles( | ||
path: Vertex[], | ||
amount: number, | ||
totalBubbles: number, | ||
color: string, | ||
): string { | ||
const duration = 8; | ||
return Array.from( | ||
{ length: totalBubbles }, | ||
(_, i) => | ||
renderBubble( | ||
path, | ||
amount, | ||
Math.random(), | ||
color, | ||
duration, | ||
-i * (duration / totalBubbles), | ||
), | ||
).join(""); | ||
} | ||
|
||
function renderBubble( | ||
path: Vertex[], | ||
amount: number, | ||
seed: number, | ||
color: string, | ||
duration: number, | ||
delay = 0, | ||
): string { | ||
const vertices = generateVertices(path, amount, seed); | ||
return [ | ||
`<circle r="4" fill="${color}" fill-opacity="60%">`, | ||
`<animateMotion dur="${duration}s" repeatCount="indefinite" begin="${delay}s" `, | ||
`path="${renderVertices(vertices)}"`, | ||
"/>", | ||
"</circle>", | ||
].join(""); | ||
} | ||
|
||
function renderVertices(vertices: Vertex[]): string { | ||
let path = "M "; | ||
for (const vertex of vertices) { | ||
path += `${vertex.x},${vertex.y} `; | ||
} | ||
|
||
return path.trim() + " Z"; | ||
} | ||
|
||
interface Vertex { | ||
x: number; | ||
y: number; | ||
} | ||
|
||
/** | ||
* iteratePath iterates over a path of vertices divided into `amount` segments. | ||
*/ | ||
function* iteratePath( | ||
path: Vertex[], | ||
amount: number, | ||
): Iterable<Vertex> { | ||
const pathLength = path.length; | ||
for (let i = 0; i < amount; i++) { | ||
const t = i / (amount - 1); | ||
const index = t * (pathLength - 1); | ||
const indexFloor = Math.floor(index); | ||
const indexCeil = Math.ceil(index); | ||
const vertexA = path[indexFloor]; | ||
const vertexB = path[indexCeil]; | ||
const vertex = lerpVertex(vertexA, vertexB, index - indexFloor); | ||
yield vertex; | ||
} | ||
} | ||
|
||
/** | ||
* lerpVertex returns a vertex that is `t` percent between `a` and `b`. | ||
*/ | ||
function lerpVertex(a: Vertex, b: Vertex, t: number): Vertex { | ||
return { | ||
x: lerp(a.x, b.x, t), | ||
y: lerp(a.y, b.y, t), | ||
}; | ||
} | ||
|
||
/** | ||
* lerp returns a value that is `t` percent between `a` and `b`. | ||
*/ | ||
function lerp(a: number, b: number, t: number): number { | ||
return a + (b - a) * t; | ||
} | ||
|
||
/** | ||
* getOffset returns a random offset based on the noise at the given position. | ||
*/ | ||
function getOffset( | ||
maxOffset: number, | ||
theta: number, | ||
offsetX = 0, | ||
offsetY = offsetX, | ||
offsetR = 0.5, | ||
) { | ||
const x = Math.cos(theta) * offsetR + offsetX; | ||
const y = Math.sin(theta) * offsetR + offsetY; | ||
return (noise(x, y) - 0.5) * maxOffset; | ||
} | ||
|
||
/** | ||
* generateVertices generates a set of vertices based on a path and a seed | ||
* used for Simplex noise using the polar noise technique. | ||
* | ||
* @see | ||
* [Coding Challenge #136.1: Polar Perlin Noise Loops](https://youtu.be/ZI1dmHv3MeM) | ||
*/ | ||
function generateVertices( | ||
path: Vertex[], | ||
amount: number, | ||
seed: number, | ||
variance = 5, | ||
fractionDigits = 2, | ||
) { | ||
if ( | ||
path[0].x !== path[path.length - 1].x || | ||
path[0].y !== path[path.length - 1].y | ||
) { | ||
path.push({ x: path[0].x, y: path[0].y }); | ||
} | ||
|
||
const seedX = seed * 1e3; | ||
const seedY = seedX * -1; | ||
return Array.from(iteratePath(path, amount)) | ||
.map((v, i, { length }) => { | ||
const theta = (i / length) * Math.PI * 2; | ||
const offsetX = getOffset(variance, theta, seedX); | ||
const offsetY = getOffset(variance, theta, seedY); | ||
return { | ||
x: Number((v.x + offsetX).toFixed(fractionDigits)), | ||
y: Number((v.y + offsetY).toFixed(fractionDigits)), | ||
}; | ||
}); | ||
} |
Oops, something went wrong.