first blockout integration of key visual
This commit is contained in:
parent
e809b7ea9e
commit
b2e68cec12
|
@ -18,7 +18,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
</head>
|
||||
<body>
|
||||
<header class='site-header'>
|
||||
<a href='/' class={title ? '' : 'invisible'}>
|
||||
<a href='/'>
|
||||
<img class='logo' src='/design-resources/logo/slashes.svg' />
|
||||
</a>
|
||||
<input type='checkbox' id='nav-toggle' />
|
||||
|
@ -115,6 +115,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
height: 100%;
|
||||
margin: auto;
|
||||
margin-left: 0.75rem;
|
||||
margin-right: 5rem;
|
||||
}
|
||||
nav {
|
||||
// mobile menu styles
|
||||
|
@ -156,7 +157,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
}
|
||||
|
||||
// desktop menu styles
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
flex-direction: row;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
@ -205,7 +206,7 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
&.desktop-only {
|
||||
height: 4.5rem;
|
||||
width: auto;
|
||||
gap: 1.5rem;
|
||||
gap: 0.75rem;
|
||||
ul {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -243,6 +244,17 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
#hero {
|
||||
position: relative;
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.gradient-bg {
|
||||
background: var(--gradient);
|
||||
padding: 1.5rem;
|
||||
|
@ -276,12 +288,12 @@ const permaTitle = 'Hacking in Parallel 2022 //// Berlin'
|
|||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@media screen and (max-width: 1024px) {
|
||||
.desktop-only {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
.mobile-only {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import Layout from '../layouts/Layout.astro'
|
|||
|
||||
<Layout>
|
||||
<article>
|
||||
<header>
|
||||
<div>
|
||||
<header id='hero'>
|
||||
<div class='hero-content'>
|
||||
<h1><img src='/logo.svg' alt='Hacking in Parallel ////' /></h1>
|
||||
<time>December 27-30 2022</time>
|
||||
</div>
|
||||
|
@ -54,10 +54,10 @@ import Layout from '../layouts/Layout.astro'
|
|||
>.
|
||||
</p>
|
||||
<p>
|
||||
We also need and welcome support during HiP. For our ACC
|
||||
"Angel Coordination Center" we have on the 5th floor, on the roof, a
|
||||
nice big room with terrace and view over Berlin available as Heaven.
|
||||
Just register in our <a href='https://engelsystem.hip-berlin.de/'
|
||||
We also need and welcome support during HiP. For our ACC "Angel
|
||||
Coordination Center" we have on the 5th floor, on the roof, a nice big
|
||||
room with terrace and view over Berlin available as Heaven. Just
|
||||
register in our <a href='https://engelsystem.hip-berlin.de/'
|
||||
>Angel Coordination Center</a
|
||||
>.
|
||||
</p>
|
||||
|
@ -84,19 +84,27 @@ import Layout from '../layouts/Layout.astro'
|
|||
</article>
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
import '../scripts/visual.ts'
|
||||
</script>
|
||||
|
||||
<style lang='scss'>
|
||||
header {
|
||||
width: 100%;
|
||||
padding: 1.5rem;
|
||||
min-height: 50vh;
|
||||
min-height: 64vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.hero-content {
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
h1 {
|
||||
display: block;
|
||||
font-size: 6.4ch;
|
||||
line-height: 1.1;
|
||||
// margin: 0;
|
||||
color: var(--headline-color);
|
||||
img {
|
||||
max-width: 100%;
|
||||
|
@ -107,7 +115,7 @@ import Layout from '../layouts/Layout.astro'
|
|||
}
|
||||
.tickets {
|
||||
width: 100%;
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
margin: 0 -1.5rem 3rem;
|
||||
width: calc(100% + 3rem);
|
||||
}
|
||||
|
@ -117,7 +125,7 @@ import Layout from '../layouts/Layout.astro'
|
|||
}
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
line-height: 1.2;
|
||||
|
|
|
@ -22,8 +22,7 @@ import Layout from '../../layouts/Layout.astro'
|
|||
</p>
|
||||
<h2>Font</h2>
|
||||
<p>
|
||||
We are using <a
|
||||
href='https://www.ibm.com/plex/'>IBM Plex Mono Medium</a
|
||||
We are using <a href='https://www.ibm.com/plex/'>IBM Plex Mono Medium</a
|
||||
>.
|
||||
</p>
|
||||
<section>
|
||||
|
@ -243,13 +242,13 @@ import Layout from '../../layouts/Layout.astro'
|
|||
.grid-2col {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
.grid-3col {
|
||||
display: grid;
|
||||
@media screen and (min-width: 768px) {
|
||||
@media screen and (min-width: 1024px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
gap: 2rem;
|
||||
|
|
185
src/scripts/visual.ts
Normal file
185
src/scripts/visual.ts
Normal 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);
|
Loading…
Reference in a new issue