feat(settings/user/color): implement basic color picker without animations

This commit is contained in:
2025-12-27 16:48:10 +01:00
parent 5a023749ad
commit 22c9fe2742
6 changed files with 126 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
<script setup lang="ts">
const GRID_SIZE = 4;
const GRID_START_X = 32;
const GRID_START_Y = 40;
const CELL_SIZE = 16;
const SPACING = 16;
const ANIMATION_SPEED = 475;
const app = useAppStore();
const { assets } = useAssets();
let selectedCol = app.color.col;
let selectedRow = app.color.row;
let selectorX = GRID_START_X + selectedCol * (CELL_SIZE + SPACING) - 4;
let selectorY = GRID_START_Y + selectedRow * (CELL_SIZE + SPACING) - 4;
const select = (col: number, row: number) => {
selectedCol = col;
selectedRow = row;
app.setColor(col, row);
};
useKeyDown((key) => {
switch (key) {
case "NDS_UP":
if (selectedRow > 0) select(selectedCol, selectedRow - 1);
break;
case "NDS_RIGHT":
if (selectedCol < GRID_SIZE - 1) select(selectedCol + 1, selectedRow);
break;
case "NDS_DOWN":
if (selectedRow < GRID_SIZE - 1) select(selectedCol, selectedRow + 1);
break;
case "NDS_LEFT":
if (selectedCol > 0) select(selectedCol - 1, selectedRow);
break;
}
});
useScreenClick((x, y) => {
const relativeX = x - GRID_START_X;
const relativeY = y - GRID_START_Y;
const col = Math.floor(relativeX / (CELL_SIZE + SPACING));
const row = Math.floor(relativeY / (CELL_SIZE + SPACING));
const cellLocalX = relativeX % (CELL_SIZE + SPACING);
const cellLocalY = relativeY % (CELL_SIZE + SPACING);
if (
rectContains([0, 0, GRID_SIZE, GRID_SIZE], [col, row]) &&
rectContains([0, 0, CELL_SIZE + 1, CELL_SIZE + 1], [cellLocalX, cellLocalY])
) {
select(col, row);
}
});
useRender((ctx, deltaTime) => {
ctx.drawImage(assets.settings.bottomScreen.user.colorPalette, 16, 32);
// animate
const finalSelectorX = GRID_START_X + selectedCol * (CELL_SIZE + SPACING) - 4;
const finalSelectorY = GRID_START_Y + selectedRow * (CELL_SIZE + SPACING) - 4;
const dx = finalSelectorX - selectorX;
const dy = finalSelectorY - selectorY;
if (dx > 0) {
selectorX += ANIMATION_SPEED * (deltaTime / 1000);
if (selectorX > finalSelectorX) selectorX = finalSelectorX;
} else if (dx < 0) {
selectorX -= ANIMATION_SPEED * (deltaTime / 1000);
if (selectorX < finalSelectorX) selectorX = finalSelectorX;
} else if (dy > 0) {
selectorY += ANIMATION_SPEED * (deltaTime / 1000);
if (selectorY > finalSelectorY) selectorY = finalSelectorY;
} else if (dy < 0) {
selectorY -= ANIMATION_SPEED * (deltaTime / 1000);
if (selectorY < finalSelectorY) selectorY = finalSelectorY;
}
// selector
ctx.fillStyle = APP_COLORS[selectedRow]![selectedCol]!;
const offsets = [0, 3, 7, 11, 15, 19, 22];
for (const offset of offsets) {
ctx.fillRect(selectorX + offset, selectorY + 0, 2, 1);
ctx.fillRect(selectorX + offset, selectorY + 23, 2, 1);
ctx.fillRect(selectorX + 0, selectorY + offset, 1, 2);
ctx.fillRect(selectorX + 23, selectorY + offset, 1, 2);
}
// preview
ctx.fillRect(192, 96, 32, 32);
});
defineOptions({
render: () => null,
});
</script>