231 lines
5.0 KiB
Svelte
231 lines
5.0 KiB
Svelte
<script lang="ts">
|
|
import _ from 'lodash';
|
|
import { createEventDispatcher, onMount } from 'svelte';
|
|
const dispatch = createEventDispatcher();
|
|
|
|
const START_COUNT = 27;
|
|
const MAX_COUNT = 33;
|
|
const MARGIN_SIZE = 16;
|
|
|
|
let horizontalCount = START_COUNT;
|
|
let verticalCount = START_COUNT;
|
|
let blockSize = 64;
|
|
let cornerBlocks = 2;
|
|
|
|
let horizontalMargin = 0;
|
|
let verticalMargin = 0;
|
|
|
|
$: updateCounts();
|
|
function updateCounts() {
|
|
const gridWidth = window.innerWidth - MARGIN_SIZE;
|
|
const gridHeight = window.innerHeight - MARGIN_SIZE;
|
|
|
|
const [longerSide, shorterSide] =
|
|
gridWidth > gridHeight ? [gridWidth, gridHeight] : [gridHeight, gridWidth];
|
|
|
|
function discrepancy(count: number) {
|
|
const blockSize = longerSide / count;
|
|
return shorterSide / blockSize - Math.floor(shorterSide / blockSize);
|
|
}
|
|
|
|
let finalCount = START_COUNT;
|
|
let count = START_COUNT;
|
|
let bestDiscrepancy = discrepancy(count);
|
|
while (count < MAX_COUNT) {
|
|
count += 2;
|
|
const currentDiscrepancy = discrepancy(count);
|
|
if (currentDiscrepancy < bestDiscrepancy) {
|
|
bestDiscrepancy = currentDiscrepancy;
|
|
finalCount = count;
|
|
}
|
|
}
|
|
|
|
const longerCount = finalCount;
|
|
const shorterCount = Math.round(longerCount * (shorterSide / longerSide));
|
|
|
|
[horizontalCount, verticalCount] =
|
|
gridWidth > gridHeight ? [longerCount, shorterCount] : [shorterCount, longerCount];
|
|
blockSize = longerSide / finalCount;
|
|
horizontalMargin = (window.innerWidth - blockSize * horizontalCount) / 2;
|
|
verticalMargin = (window.innerHeight - blockSize * verticalCount) / 2;
|
|
cornerBlocks = shorterCount > 8 ? 3 : Math.max(1, Math.floor(shorterCount / 4));
|
|
dispatch('change', {
|
|
horizontalCount,
|
|
verticalCount,
|
|
blockSize,
|
|
horizontalMargin,
|
|
verticalMargin
|
|
});
|
|
}
|
|
const updateCountsOnResize = _.throttle(updateCounts, 200);
|
|
|
|
onMount(() => {
|
|
window.addEventListener('resize', updateCountsOnResize);
|
|
updateCounts();
|
|
});
|
|
</script>
|
|
|
|
<div
|
|
class="background"
|
|
class:even-vertical={verticalCount % 2 === 0}
|
|
style="--horizontal-count: {horizontalCount};
|
|
--vertical-count: {verticalCount};
|
|
--block-size: {blockSize}px;
|
|
--corner-blocks: {cornerBlocks};
|
|
--horizontal-margin: {horizontalMargin}px;
|
|
--vertical-margin: {verticalMargin}px;"
|
|
>
|
|
<div class="corners">
|
|
<div class="corner top left"></div>
|
|
<div class="corner top right"></div>
|
|
<div class="corner bottom left"></div>
|
|
<div class="corner bottom right"></div>
|
|
</div>
|
|
<div class="edges">
|
|
{#each ['top', 'bottom'] as edge}
|
|
<div class="edge {edge}">
|
|
{#each [...Array(horizontalCount - cornerBlocks * 2).keys()] as _}
|
|
<div class="block"></div>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
{#each ['left', 'right'] as edge}
|
|
<div class="edge {edge}">
|
|
{#each [...Array(verticalCount - cornerBlocks * 2).keys()] as _}
|
|
<div class="block"></div>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
<div class="grid">
|
|
{#each [...Array(verticalCount).keys()] as x}
|
|
<div class="row">
|
|
{#each [...Array(horizontalCount).keys()] as y}
|
|
<div class="block"></div>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.background {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
|
|
pointer-events: none;
|
|
|
|
--corner-color: white;
|
|
--corner-height: calc(var(--vertical-margin) + var(--corner-blocks) * var(--block-size));
|
|
--corner-width: calc(var(--horizontal-margin) + var(--corner-blocks) * var(--block-size));
|
|
}
|
|
|
|
.corner {
|
|
background-color: var(--corner-color);
|
|
|
|
position: absolute;
|
|
&.top {
|
|
top: 0;
|
|
height: var(--corner-height);
|
|
}
|
|
&.bottom {
|
|
bottom: 0;
|
|
height: var(--corner-height);
|
|
}
|
|
&.left {
|
|
left: 0;
|
|
width: var(--corner-width);
|
|
}
|
|
&.right {
|
|
right: 0;
|
|
width: var(--corner-width);
|
|
}
|
|
}
|
|
|
|
.edges {
|
|
& .edge {
|
|
position: absolute;
|
|
display: flex;
|
|
}
|
|
|
|
& .top {
|
|
top: 0;
|
|
}
|
|
|
|
& .bottom {
|
|
bottom: 0;
|
|
}
|
|
|
|
& .top,
|
|
& .bottom {
|
|
width: calc(100vw - var(--corner-width) * 2);
|
|
height: calc(var(--margin-size) * 2);
|
|
left: var(--corner-width);
|
|
}
|
|
|
|
& .left {
|
|
left: 0;
|
|
}
|
|
|
|
& .right {
|
|
right: 0;
|
|
}
|
|
|
|
& .left,
|
|
& .right {
|
|
flex-direction: column;
|
|
height: calc(100vh - var(--corner-height) * 2);
|
|
width: calc(var(--margin-size) * 2);
|
|
top: var(--corner-height);
|
|
}
|
|
|
|
& .block {
|
|
display: inline-block;
|
|
height: var(--block-size);
|
|
width: var(--block-size);
|
|
|
|
background: white;
|
|
&:nth-child(odd) {
|
|
background: black;
|
|
}
|
|
}
|
|
}
|
|
|
|
.background.even-vertical .edges .right .block {
|
|
background: black;
|
|
&:nth-child(odd) {
|
|
background: white;
|
|
}
|
|
}
|
|
|
|
.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(var(--horizontal-count), var(--block-size));
|
|
grid-template-rows: repeat(var(--vertical-count), var(--block-size));
|
|
position: absolute;
|
|
top: var(--vertical-margin);
|
|
left: var(--horizontal-margin);
|
|
|
|
& .row {
|
|
display: contents;
|
|
}
|
|
|
|
& .block {
|
|
background: black;
|
|
border-right: 1px solid gray;
|
|
border-bottom: 1px solid gray;
|
|
}
|
|
|
|
& .row:first-child .block {
|
|
border-top: 1px solid gray;
|
|
}
|
|
|
|
& .block:first-child {
|
|
border-left: 1px solid gray;
|
|
}
|
|
}
|
|
</style>
|