test-card/src/lib/BackgroundGrid.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>