Compare commits

...

1 commit

Author SHA1 Message Date
Leif Niemczik b2e68cec12 first blockout integration of key visual 2022-11-29 00:33:43 +01:00
4 changed files with 225 additions and 21 deletions

View file

@ -18,7 +18,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
</head> </head>
<body> <body>
<header class='site-header'> <header class='site-header'>
<a href='/' class={title ? '' : 'invisible'}> <a href='/'>
<img class='logo' src='/design-resources/logo/slashes.svg' /> <img class='logo' src='/design-resources/logo/slashes.svg' />
</a> </a>
<input type='checkbox' id='nav-toggle' /> <input type='checkbox' id='nav-toggle' />
@ -115,6 +115,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
height: 100%; height: 100%;
margin: auto; margin: auto;
margin-left: 0.75rem; margin-left: 0.75rem;
margin-right: 5rem;
} }
nav { nav {
// mobile menu styles // mobile menu styles
@ -156,7 +157,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
} }
// desktop menu styles // desktop menu styles
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
flex-direction: row; flex-direction: row;
padding: 0; padding: 0;
position: relative; position: relative;
@ -205,7 +206,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
&.desktop-only { &.desktop-only {
height: 4.5rem; height: 4.5rem;
width: auto; width: auto;
gap: 1.5rem; gap: 0.75rem;
ul { ul {
flex-direction: row; flex-direction: row;
} }
@ -243,6 +244,17 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
} }
#hero {
position: relative;
svg {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: -1;
}
}
.gradient-bg { .gradient-bg {
background: var(--gradient); background: var(--gradient);
padding: 1.5rem; padding: 1.5rem;
@ -276,12 +288,12 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
padding: 1.5rem 0; padding: 1.5rem 0;
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 1024px) {
.desktop-only { .desktop-only {
display: none; display: none;
} }
} }
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
.mobile-only { .mobile-only {
display: none; display: none;
} }

View file

@ -4,8 +4,8 @@ import Layout from '../layouts/Layout.astro'
<Layout> <Layout>
<article> <article>
<header> <header id='hero'>
<div> <div class='hero-content'>
<h1><img src='/logo.svg' alt='Hacking in Parallel ////' /></h1> <h1><img src='/logo.svg' alt='Hacking in Parallel ////' /></h1>
<time>December 27-30 2022</time> <time>December 27-30 2022</time>
</div> </div>
@ -54,10 +54,10 @@ import Layout from '../layouts/Layout.astro'
>. >.
</p> </p>
<p> <p>
We also need and welcome support during HiP. For our ACC We also need and welcome support during HiP. For our ACC "Angel
"Angel Coordination Center" we have on the 5th floor, on the roof, a Coordination Center" we have on the 5th floor, on the roof, a nice big
nice big room with terrace and view over Berlin available as Heaven. room with terrace and view over Berlin available as Heaven. Just
Just register in our <a href='https://engelsystem.hip-berlin.de/' register in our <a href='https://engelsystem.hip-berlin.de/'
>Angel Coordination Center</a >Angel Coordination Center</a
>. >.
</p> </p>
@ -84,19 +84,27 @@ import Layout from '../layouts/Layout.astro'
</article> </article>
</Layout> </Layout>
<script>
import '../scripts/visual.ts'
</script>
<style lang='scss'> <style lang='scss'>
header { header {
width: 100%; width: 100%;
padding: 1.5rem; padding: 1.5rem;
min-height: 50vh; min-height: 64vh;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.hero-content {
padding: 1.5rem 1rem;
}
h1 { h1 {
display: block; display: block;
font-size: 6.4ch; font-size: 6.4ch;
line-height: 1.1; line-height: 1.1;
// margin: 0;
color: var(--headline-color); color: var(--headline-color);
img { img {
max-width: 100%; max-width: 100%;
@ -107,7 +115,7 @@ import Layout from '../layouts/Layout.astro'
} }
.tickets { .tickets {
width: 100%; width: 100%;
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
margin: 0 -1.5rem 3rem; margin: 0 -1.5rem 3rem;
width: calc(100% + 3rem); width: calc(100% + 3rem);
} }
@ -117,7 +125,7 @@ import Layout from '../layouts/Layout.astro'
} }
h2 { h2 {
font-size: 2rem; font-size: 2rem;
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
font-size: 2.2rem; font-size: 2.2rem;
} }
line-height: 1.2; line-height: 1.2;

View file

@ -22,8 +22,7 @@ import Layout from '../../layouts/Layout.astro'
</p> </p>
<h2>Font</h2> <h2>Font</h2>
<p> <p>
We are using <a We are using <a href='https://www.ibm.com/plex/'>IBM Plex Mono Medium</a
href='https://www.ibm.com/plex/'>IBM Plex Mono Medium</a
>. >.
</p> </p>
<section> <section>
@ -243,13 +242,13 @@ import Layout from '../../layouts/Layout.astro'
.grid-2col { .grid-2col {
display: grid; display: grid;
gap: 2rem; gap: 2rem;
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
} }
.grid-3col { .grid-3col {
display: grid; display: grid;
@media screen and (min-width: 768px) { @media screen and (min-width: 1024px) {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
} }
gap: 2rem; gap: 2rem;

185
src/scripts/visual.ts Normal file
View file

@ -0,0 +1,185 @@
import { SVG, on as SVGOn, Svg } from "@svgdotjs/svg.js"
import { colord } from "colord"
import RandomGen from "random-seed"
interface Config {
canvas?: Svg;
seed?: string;
keepSeed: boolean;
width: number;
height: number;
circleSize: number;
padding: number;
stepSize: number;
lineChance: number;
maxLineLength: number;
colorsPerLine: number;
coloredDots?: boolean;
blockout?: { left: number, top: number, bottom: number, right: number }
}
let config: Config = {
seed: "",
keepSeed: false,
width: 800,
height: 800,
padding: 25,
circleSize: 10,
stepSize: 50,
lineChance: .1,
maxLineLength: 3,
colorsPerLine: 3,
coloredDots: true,
}
const colors = [
"#FED61E",
"#44B4E9",
"#008317",
"#002E5C",
"#003A3E",
"#6C2400",
"#FF9900",
]
// calculate absolute pposition with padding
function calcPosition({ x, y, padding, numElements, reducedDims, subtractHalfCircle = true }) {
const pos: Record<string, number> = {
posX: x / numElements.x * reducedDims.width + padding,
posY: y / numElements.y * reducedDims.height + padding
}
if (subtractHalfCircle) {
for (const key in pos) {
pos[key] -= reducedDims.halfCircle
}
}
return pos
}
function drawElement({ x, y, canvas, occupationMap, random, lineChance, lineLengthLimit, numElements, padding, circleSize, colorsPerLine, coloredDots, reducedDims, blockedDimensions }: Config & { x: number, y: number, occupationMap: number[][], random: RandomGen, lineLengthLimit: number, numElements: { x: number, y: number }, reducedDims: { width: number, height: number, halfCircle: number }, blockedDimensions: { top: number, left: number, right: number, bottom: number } }) {
let length = null
if (random.random() < lineChance!) length = random.intBetween(2, lineLengthLimit)
if (occupationMap[x].includes(y) || !canvas) return
if (length && x != numElements.x && y != 0 && (x < blockedDimensions.left - 1 || x >= blockedDimensions.right || y < blockedDimensions.top || y >= blockedDimensions.bottom - 1)) {
// Line
const { posX, posY } = calcPosition({ x, y, padding, numElements, reducedDims, subtractHalfCircle: false, })
const overshootX = Math.min(x + length - numElements.x, x + length - blockedDimensions.left);
const overshootY = Math.min((y - length) * -1, (y - blockedDimensions.bottom + length) * -1);
if (y >= blockedDimensions.bottom) { console.log(x, y, length, overshootY) };
const overshoot = Math.max(overshootX, overshootY)
if (overshoot > 0) length -= overshoot
const { posX: endX, posY: endY } = calcPosition({
x: x + length,
y: y - length,
padding,
numElements,
reducedDims,
subtractHalfCircle: false,
})
const gradient = canvas!.gradient("linear", (add) => {
for (let i = 0; i < colorsPerLine; i++) {
add.stop({ offset: i / (colorsPerLine - 1), color: colors[random(colors.length)] })
}
}).transform({
rotate: -45,
origin: { x: .5, y: .5 }
})
canvas.line(posX, posY, endX, endY).stroke({
width: circleSize,
linecap: "round"
}).attr({ "stroke": gradient })
for (let i = 0; i <= length; i++) {
if (x + i <= numElements.x) occupationMap[x + i].push(y - i)
}
} else {
// Circle
const { posX, posY } = calcPosition({ x, y, padding, numElements, reducedDims })
canvas
.circle(circleSize)
.fill(coloredDots ? colors[random(colors.length)] : "#333")
.move(posX, posY)
occupationMap[x].push(y)
}
}
function render(configParams: Config) {
// config.canvas?.remove()
if (config.seed === "") config.seed = RandomGen.create().string(16)
const { seed, width, height, padding, circleSize, stepSize, maxLineLength, blockout } = configParams
const random = RandomGen.create(seed)
const reducedDims = {
width: width - 2 * padding,
height: height - 2 * padding,
halfCircle: circleSize / 2
}
const numElements = {
x: Math.floor(reducedDims.width / stepSize),
y: Math.floor(reducedDims.height / stepSize)
}
const blockedDimensions = {
top: Math.floor((blockout!.top - padding) / stepSize),
right: Math.floor((blockout!.right + padding) / stepSize),
bottom: Math.floor((blockout!.bottom + padding) / stepSize),
left: Math.floor((blockout!.left - padding) / stepSize),
}
const lineLengthLimit = maxLineLength;
// Create 2D array of points in grid
let occupationMap: number[][] = Array.from({ length: numElements.x + 1 }, (_: never, x: number) => {
let k = [];
if (x >= blockedDimensions.left && x < blockedDimensions.right) {
for (let index = blockedDimensions.top; index < blockedDimensions.bottom - 2; index++) {
k.push(index)
}
}
return k
})
for (let x = 0; x <= numElements.x; x++) {
for (let y = 0; y <= numElements.y; y++) {
drawElement({ x, y, occupationMap, random, numElements, lineLengthLimit, reducedDims, blockedDimensions, ...config })
}
}
}
SVGOn(document, "DOMContentLoaded", () => {
const hero = document.querySelector("#hero")!
const dimens = hero.getBoundingClientRect()
const { width, height } = dimens;
config.width = width
config.height = height
config.canvas = SVG().viewbox(0, 0, width, height).size(width, height).addTo('#hero')
const block = hero.children[0]
config.blockout = block.getBoundingClientRect()
render(config)
window.addEventListener('resize', () => {
config.canvas!.clear()
const hero = document.querySelector("#hero")!
const dimens = hero.getBoundingClientRect()
const { width, height } = dimens;
config.width = width
config.height = height
config.canvas!.viewbox(0, 0, width, height).size(width, height)
config.blockout = block.getBoundingClientRect()
render(config)
})
})
console.log(config.canvas);