mirror of
https://gitlab1.ptb.de/waltem01/Matrix
synced 2024-12-25 03:31:44 +00:00
relocating into library files, overlay and hover effect and scaling
This commit is contained in:
parent
0e33907fe9
commit
bde03f5da4
36
Webserver/src/lib/client/color.ts
Normal file
36
Webserver/src/lib/client/color.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export interface Color {
|
||||||
|
r: number;
|
||||||
|
g: number;
|
||||||
|
b: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getContrastColor(hexColor: string): string {
|
||||||
|
const rgb = hexToRgb(hexColor);
|
||||||
|
const luminance = getLuminance(rgb ?? ({ r: 0, g: 0, b: 0 } as Color));
|
||||||
|
return luminance > 0.5 ? '000000' : 'FFFFFF';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hexToRgb(hex: string): Color | null {
|
||||||
|
const result = /^([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
||||||
|
return result
|
||||||
|
? {
|
||||||
|
r: parseInt(result[1], 16),
|
||||||
|
g: parseInt(result[2], 16),
|
||||||
|
b: parseInt(result[3], 16)
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLuminance(rgb: Color): number {
|
||||||
|
const a = [rgb.r, rgb.g, rgb.b].map((v) => {
|
||||||
|
v /= 255;
|
||||||
|
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||||
|
});
|
||||||
|
return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rgbToHex(color: Color): string {
|
||||||
|
return (Object.entries(color) as [string, number][])
|
||||||
|
.map((e) => e[1].toString(16).padStart(2, '0'))
|
||||||
|
.join('');
|
||||||
|
}
|
21
Webserver/src/lib/client/gamepad.ts
Normal file
21
Webserver/src/lib/client/gamepad.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export interface Coordinates {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function detectGamepad() {
|
||||||
|
const gps = navigator.getGamepads();
|
||||||
|
if (gps.length === 0) return null;
|
||||||
|
|
||||||
|
return gps[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function gamepadButtonPress(gamepad: Gamepad | null, gameCoords: Coordinates) {
|
||||||
|
if (!gamepad) return;
|
||||||
|
|
||||||
|
gamepad.axes.forEach((axis, index) => {
|
||||||
|
if (Math.abs(axis) !== 1) return;
|
||||||
|
if (index === 1) gameCoords.y += axis;
|
||||||
|
if (index === 0) gameCoords.x += axis;
|
||||||
|
});
|
||||||
|
}
|
14
Webserver/src/lib/client/matrix.ts
Normal file
14
Webserver/src/lib/client/matrix.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export interface MatrixCell {
|
||||||
|
xIndex: number;
|
||||||
|
yIndex: number;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initializeMatrix(scaling: number): MatrixCell[][] {
|
||||||
|
return Array.from({ length: 192 / scaling }, (_, yIndex) =>
|
||||||
|
Array.from(
|
||||||
|
{ length: 192 / scaling },
|
||||||
|
(_, xIndex) => ({ xIndex, yIndex, color: '000000' }) as MatrixCell
|
||||||
|
)
|
||||||
|
) as MatrixCell[][];
|
||||||
|
}
|
3
Webserver/src/lib/client/miscellaneous.ts
Normal file
3
Webserver/src/lib/client/miscellaneous.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function toRadians(degrees: number): number {
|
||||||
|
return (degrees / 180) * Math.PI;
|
||||||
|
}
|
3
Webserver/src/lib/interfaces.ts
Normal file
3
Webserver/src/lib/interfaces.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export interface APIResponse {
|
||||||
|
success: boolean;
|
||||||
|
}
|
@ -1,13 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface MatrixResponse {
|
import { detectGamepad, gamepadButtonPress } from '$lib/client/gamepad';
|
||||||
success: boolean;
|
import { rgbToHex, type Color, getContrastColor } from '$lib/client/color';
|
||||||
}
|
import { initializeMatrix } from '$lib/client/matrix';
|
||||||
|
import { toRadians } from '$lib/client/miscellaneous';
|
||||||
interface MatrixCell {
|
import type { APIResponse } from '$lib/interfaces';
|
||||||
xIndex: number;
|
import { onMount } from 'svelte';
|
||||||
yIndex: number;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function get(event: SubmitEvent) {
|
async function get(event: SubmitEvent) {
|
||||||
clearMatrix();
|
clearMatrix();
|
||||||
@ -17,7 +14,7 @@
|
|||||||
const response = await fetch(`http://localhost:8080/${form.dataset.endpoint}`, {
|
const response = await fetch(`http://localhost:8080/${form.dataset.endpoint}`, {
|
||||||
mode: 'cors'
|
mode: 'cors'
|
||||||
});
|
});
|
||||||
const mdata = (await response.json()) as MatrixResponse;
|
const mdata = (await response.json()) as APIResponse;
|
||||||
if (!mdata.success) alert(`Error while processing '${form.dataset.endpoint}'!`);
|
if (!mdata.success) alert(`Error while processing '${form.dataset.endpoint}'!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +46,7 @@
|
|||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
body: fdata
|
body: fdata
|
||||||
});
|
});
|
||||||
const mdata = (await response.json()) as MatrixResponse;
|
const mdata = (await response.json()) as APIResponse;
|
||||||
if (!mdata.success) alert(`Error while processing '${form.dataset.endpoint}'!`);
|
if (!mdata.success) alert(`Error while processing '${form.dataset.endpoint}'!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +57,7 @@
|
|||||||
const y = parseInt((formData.get('y') as string) ?? '191');
|
const y = parseInt((formData.get('y') as string) ?? '191');
|
||||||
const r = parseInt((formData.get('r') as string) ?? '95');
|
const r = parseInt((formData.get('r') as string) ?? '95');
|
||||||
|
|
||||||
const hex = colorToHex();
|
const hex = rgbToHex(pixelColor);
|
||||||
for (let a = 0; a < 360; a++) {
|
for (let a = 0; a < 360; a++) {
|
||||||
const nx = Math.round(Math.cos(toRadians(a)) * r + x);
|
const nx = Math.round(Math.cos(toRadians(a)) * r + x);
|
||||||
const ny = Math.round(Math.sin(toRadians(a)) * r + y);
|
const ny = Math.round(Math.sin(toRadians(a)) * r + y);
|
||||||
@ -69,17 +66,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRadians(degrees: number): number {
|
|
||||||
return (degrees / 180) * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawRectangle(formData: FormData) {
|
function drawRectangle(formData: FormData) {
|
||||||
const x = parseInt((formData.get('x') as string) ?? '191');
|
const x = parseInt((formData.get('x') as string) ?? '191');
|
||||||
const y = parseInt((formData.get('y') as string) ?? '191');
|
const y = parseInt((formData.get('y') as string) ?? '191');
|
||||||
const w = parseInt((formData.get('w') as string) ?? '191');
|
const w = parseInt((formData.get('w') as string) ?? '191');
|
||||||
const h = parseInt((formData.get('h') as string) ?? '191');
|
const h = parseInt((formData.get('h') as string) ?? '191');
|
||||||
|
|
||||||
const hex = colorToHex();
|
const hex = rgbToHex(pixelColor);
|
||||||
for (let j = y; j < y + h; j++) for (let i = x; i < x + w; i++) matrix[j][i].color = hex;
|
for (let j = y; j < y + h; j++) for (let i = x; i < x + w; i++) matrix[j][i].color = hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +80,7 @@
|
|||||||
const x = parseInt((formData.get('x') as string) ?? '191');
|
const x = parseInt((formData.get('x') as string) ?? '191');
|
||||||
const y = parseInt((formData.get('y') as string) ?? '191');
|
const y = parseInt((formData.get('y') as string) ?? '191');
|
||||||
|
|
||||||
matrix[y][x].color = colorToHex();
|
matrix[y][x].color = rgbToHex(pixelColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearMatrix() {
|
function clearMatrix() {
|
||||||
@ -95,44 +88,82 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function colorSelect(formData: FormData) {
|
function colorSelect(formData: FormData) {
|
||||||
pixelColor[0] = parseInt((formData.get('r') as string) ?? '255');
|
pixelColor.r = parseInt((formData.get('r') as string) ?? '255');
|
||||||
pixelColor[1] = parseInt((formData.get('g') as string) ?? '255');
|
pixelColor.g = parseInt((formData.get('g') as string) ?? '255');
|
||||||
pixelColor[2] = parseInt((formData.get('b') as string) ?? '255');
|
pixelColor.b = parseInt((formData.get('b') as string) ?? '255');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setMousePixel(event: MouseEvent) {
|
async function setMousePixel(event: MouseEvent) {
|
||||||
if (!mousePressed) return;
|
if (!mousePressed) return;
|
||||||
|
|
||||||
const parent = event.target as HTMLTableCellElement;
|
const parent = event.target as HTMLTableCellElement;
|
||||||
const x = parseInt(parent.dataset.x ?? '0');
|
let x = parseInt(parent.dataset.x ?? '0');
|
||||||
const y = parseInt(parent.parentElement?.dataset.y ?? '0');
|
let y = parseInt(parent.parentElement?.dataset.y ?? '0');
|
||||||
|
|
||||||
matrix[y][x].color = colorToHex();
|
const next = rgbToHex(pixelColor);
|
||||||
|
if (matrix[y][x].color === next) return;
|
||||||
|
matrix[y][x].color = next;
|
||||||
|
|
||||||
const fdata = new FormData();
|
const fdata = new FormData();
|
||||||
|
const isScaled = scaling > 1;
|
||||||
|
if (isScaled) {
|
||||||
|
x *= scaling;
|
||||||
|
y *= scaling;
|
||||||
|
|
||||||
|
fdata.append('w', scaling.toString());
|
||||||
|
fdata.append('h', scaling.toString());
|
||||||
|
}
|
||||||
|
|
||||||
fdata.append('x', x.toString());
|
fdata.append('x', x.toString());
|
||||||
fdata.append('y', y.toString());
|
fdata.append('y', y.toString());
|
||||||
|
|
||||||
// TODO: Calling API via server-side
|
// TODO: Calling API via server-side
|
||||||
const response = await fetch('http://localhost:8080/pixel', {
|
const response = await fetch(`http://localhost:8080/${isScaled ? 'rectangle' : 'pixel'}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
mode: 'cors',
|
mode: 'cors',
|
||||||
body: fdata
|
body: fdata
|
||||||
});
|
});
|
||||||
const mdata = (await response.json()) as MatrixResponse;
|
const mdata = (await response.json()) as APIResponse;
|
||||||
if (!mdata.success) alert("Error while processing 'pixel'!");
|
if (!mdata.success) alert("Error while processing 'pixel'!");
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorToHex(): string {
|
function correctSlider(event: Event) {
|
||||||
return pixelColor.map((e) => e.toString(16).padStart(2, '0')).join('');
|
const slider = event.target as HTMLInputElement;
|
||||||
|
|
||||||
|
let current = parseInt(slider.value);
|
||||||
|
while (192 % current !== 0) current--;
|
||||||
|
|
||||||
|
scaling = current;
|
||||||
|
padding = 0.1875 * scaling;
|
||||||
|
|
||||||
|
matrix = initializeMatrix(scaling);
|
||||||
}
|
}
|
||||||
|
|
||||||
const matrix = Array.from({ length: 192 }, (_, yIndex) =>
|
let scaling = 3,
|
||||||
Array.from({ length: 192 }, (_, xIndex) => ({ xIndex, yIndex, color: '000000' }) as MatrixCell)
|
padding = 0.5625;
|
||||||
) as MatrixCell[][];
|
|
||||||
|
|
||||||
const pixelColor = [255, 255, 255];
|
|
||||||
let mousePressed = false;
|
let mousePressed = false;
|
||||||
|
let gamepad: Gamepad | null = null;
|
||||||
|
let matrix = initializeMatrix(scaling);
|
||||||
|
|
||||||
|
const gameCoords = { x: 0, y: 0 };
|
||||||
|
const pixelColor = { r: 255, g: 255, b: 255 } as Color;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
setInterval(() => {
|
||||||
|
if (gamepad) return;
|
||||||
|
gamepad = detectGamepad();
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setInterval(() => gamepadButtonPress(gamepad, gameCoords), 100);
|
||||||
|
|
||||||
|
window.addEventListener('gamepadconnected', (e) => {
|
||||||
|
if (e.gamepad.index === 0) gamepad = e.gamepad;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('gamepaddisconnected', (e) => {
|
||||||
|
if (e.gamepad.id === gamepad?.id) gamepad = null;
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1 class="text-4xl font-bold m-5 text-center">Matrix Control Panel</h1>
|
<h1 class="text-4xl font-bold m-5 text-center">Matrix Control Panel</h1>
|
||||||
@ -203,7 +234,7 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="border-2 border-black border-b-0 hover:bg-gray-200"
|
class="border-2 border-black border-b-0 hover:bg-gray-200 cursor-pointer"
|
||||||
id="color"
|
id="color"
|
||||||
name="color"
|
name="color"
|
||||||
type="submit"
|
type="submit"
|
||||||
@ -225,7 +256,7 @@
|
|||||||
name="x"
|
name="x"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="0"
|
value="0"
|
||||||
required
|
required
|
||||||
@ -237,7 +268,7 @@
|
|||||||
name="y"
|
name="y"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="0"
|
value="0"
|
||||||
required
|
required
|
||||||
@ -266,7 +297,7 @@
|
|||||||
name="x"
|
name="x"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="177"
|
value="177"
|
||||||
required
|
required
|
||||||
@ -278,7 +309,7 @@
|
|||||||
name="y"
|
name="y"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="0"
|
value="0"
|
||||||
required
|
required
|
||||||
@ -289,7 +320,7 @@
|
|||||||
name="w"
|
name="w"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="15"
|
value="15"
|
||||||
required
|
required
|
||||||
@ -301,7 +332,7 @@
|
|||||||
name="h"
|
name="h"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="15"
|
value="15"
|
||||||
required
|
required
|
||||||
@ -329,7 +360,7 @@
|
|||||||
name="x"
|
name="x"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="20"
|
value="20"
|
||||||
required
|
required
|
||||||
@ -341,7 +372,7 @@
|
|||||||
name="y"
|
name="y"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max={192 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="171"
|
value="171"
|
||||||
required
|
required
|
||||||
@ -352,7 +383,7 @@
|
|||||||
name="r"
|
name="r"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="95"
|
max={96 / scaling - 1}
|
||||||
step="1"
|
step="1"
|
||||||
value="20"
|
value="20"
|
||||||
required
|
required
|
||||||
@ -367,35 +398,35 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form class="grid grid-cols-8" method="POST" data-endpoint="text" on:submit|preventDefault={post}>
|
<form class="grid grid-cols-8" method="POST" data-endpoint="text" on:submit|preventDefault={post}>
|
||||||
<label class="border-2 border-black border-r-0 pl-1" for="text">Text:</label><label
|
<label class="border-2 border-black border-r-0 border-b-0 pl-1" for="text">Text:</label><label
|
||||||
class="border-2 border-black border-r-0 pl-1"
|
class="border-2 border-black border-r-0 border-b-0 pl-1"
|
||||||
for="tx">X:</label
|
for="tx">X:</label
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
class="border-2 border-black border-r-0 cursor-text pl-1 bg-gray-100"
|
class="border-2 border-black border-r-0 border-b-0 cursor-text pl-1 bg-gray-100"
|
||||||
id="tx"
|
id="tx"
|
||||||
name="x"
|
name="x"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max="20"
|
||||||
step="1"
|
step="1"
|
||||||
value="8"
|
value="8"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<label class="border-2 border-black border-r-0 pl-1" for="ty">Y:</label>
|
<label class="border-2 border-black border-r-0 border-b-0 pl-1" for="ty">Y:</label>
|
||||||
<input
|
<input
|
||||||
class="border-2 border-black border-r-0 cursor-text pl-1 bg-gray-100"
|
class="border-2 border-black border-r-0 border-b-0 cursor-text pl-1 bg-gray-100"
|
||||||
id="ty"
|
id="ty"
|
||||||
name="y"
|
name="y"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
max="191"
|
max="9"
|
||||||
step="1"
|
step="1"
|
||||||
value="9"
|
value="9"
|
||||||
required
|
required
|
||||||
/><label class="border-2 border-black border-r-0 pl-1" for="tin">Input:</label>
|
/><label class="border-2 border-black border-r-0 border-b-0 pl-1" for="tin">Input:</label>
|
||||||
<input
|
<input
|
||||||
class="border-2 border-black border-r-0 cursor-text pl-1 pr-1 bg-gray-100"
|
class="border-2 border-black border-r-0 border-b-0 cursor-text pl-1 pr-1 bg-gray-100"
|
||||||
id="tin"
|
id="tin"
|
||||||
name="text"
|
name="text"
|
||||||
type="text"
|
type="text"
|
||||||
@ -403,32 +434,72 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
class="border-2 border-black cursor-pointer hover:bg-gray-200"
|
class="border-2 border-black border-b-0 cursor-pointer hover:bg-gray-200"
|
||||||
id="text"
|
id="text"
|
||||||
name="text"
|
name="text"
|
||||||
type="submit"
|
type="submit"
|
||||||
value="Submit"
|
value="Submit"
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form class="grid grid-cols-3" on:submit|preventDefault>
|
||||||
|
<label class="border-2 border-black border-r-0 pl-1" for="text">Scaling:</label>
|
||||||
|
<div class="flex pl-1 pr-1 border-2 border-black border-r-0 bg-gray-100">
|
||||||
|
<input
|
||||||
|
class="w-full cursor-pointer"
|
||||||
|
id="scale"
|
||||||
|
name="scale"
|
||||||
|
type="range"
|
||||||
|
min="1"
|
||||||
|
max="64"
|
||||||
|
step="1"
|
||||||
|
bind:value={scaling}
|
||||||
|
on:change={correctSlider}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
class="border-2 border-black cursor-text pl-1 bg-gray-100"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="64"
|
||||||
|
step="1"
|
||||||
|
bind:value={scaling}
|
||||||
|
on:change={correctSlider}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
<table id="main" class="mt-5 border-2 border-black">
|
||||||
<table
|
|
||||||
class="mt-5"
|
|
||||||
on:mousedown|preventDefault={() => (mousePressed = true)}
|
|
||||||
on:mouseup|preventDefault={() => (mousePressed = false)}
|
|
||||||
>
|
|
||||||
{#each matrix as row}
|
{#each matrix as row}
|
||||||
<tr data-y={row[0].yIndex}>
|
<tr>
|
||||||
{#each row as cell}
|
{#each row as cell}
|
||||||
<td
|
<td style="padding: {padding}rem; background-color: #{cell.color}" />
|
||||||
data-x={cell.xIndex}
|
|
||||||
class="p-0.75"
|
|
||||||
style="background-color: #{cell.color};"
|
|
||||||
on:mouseenter={setMousePixel}
|
|
||||||
/>
|
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</table>
|
</table>
|
||||||
|
<div class="absolute z-10">
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
|
<table
|
||||||
|
id="overlay"
|
||||||
|
class="mt-5 border-2 border-transparent"
|
||||||
|
on:mousedown|preventDefault={() => (mousePressed = true)}
|
||||||
|
on:mouseup|preventDefault={() => (mousePressed = false)}
|
||||||
|
>
|
||||||
|
{#each matrix as row}
|
||||||
|
<tr data-y={row[0].yIndex}>
|
||||||
|
{#each row as cell}
|
||||||
|
<td
|
||||||
|
data-x={cell.xIndex}
|
||||||
|
style="padding: {padding}rem; background-color: #{getContrastColor(cell.color)}"
|
||||||
|
class="opacity-0 hover:opacity-50"
|
||||||
|
on:mousemove={setMousePixel}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,11 +2,7 @@
|
|||||||
export default {
|
export default {
|
||||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {}
|
||||||
spacing: {
|
|
||||||
0.75: '0.1875rem'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
plugins: []
|
plugins: []
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user