feat(settings): screen setup
This commit is contained in:
13
app/components/Settings/TopScreen/Background.vue
Normal file
13
app/components/Settings/TopScreen/Background.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import BACKGROUND_IMAGE from "/assets/images/home/top-screen/background.webp";
|
||||
|
||||
const [backgroundImage] = useImages(BACKGROUND_IMAGE);
|
||||
|
||||
useRender((ctx) => {
|
||||
ctx.drawImage(backgroundImage!, 0, 0);
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
render: () => null,
|
||||
});
|
||||
</script>
|
||||
79
app/components/Settings/TopScreen/Calendar.vue
Normal file
79
app/components/Settings/TopScreen/Calendar.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import CALENDAR_IMAGE from "/assets/images/home/top-screen/calendar/calendar.webp";
|
||||
import LAST_ROW_IMAGE from "/assets/images/home/top-screen/calendar/last-row.webp";
|
||||
import DAY_SELECTOR_IMAGE from "/assets/images/home/top-screen/calendar/day-selector.webp";
|
||||
|
||||
const [calendarImage, lastRowImage, daySelectorImage] = useImages(
|
||||
CALENDAR_IMAGE,
|
||||
LAST_ROW_IMAGE,
|
||||
DAY_SELECTOR_IMAGE,
|
||||
);
|
||||
|
||||
useRender((ctx) => {
|
||||
ctx.fillStyle = "black";
|
||||
ctx.font = "7px NDS7";
|
||||
|
||||
const CALENDAR_COLS = 7;
|
||||
const CALENDAR_ROWS = 5;
|
||||
const CALENDAR_LEFT = 128;
|
||||
const CALENDAR_TOP = 64;
|
||||
|
||||
ctx.fillStyle = "#343434";
|
||||
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
|
||||
const firstDay = new Date(year, month, 1).getDay();
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
ctx.drawImage(calendarImage!, CALENDAR_LEFT - 3, CALENDAR_TOP - 33);
|
||||
|
||||
const extraRow = CALENDAR_COLS * CALENDAR_ROWS - daysInMonth - firstDay < 0;
|
||||
if (extraRow) {
|
||||
ctx.drawImage(lastRowImage!, CALENDAR_LEFT - 3, CALENDAR_TOP + 79);
|
||||
}
|
||||
|
||||
for (let col = 0; col < CALENDAR_ROWS + (extraRow ? 1 : 0); col += 1) {
|
||||
for (let row = 0; row < CALENDAR_COLS; row += 1) {
|
||||
const cellIndex = col * CALENDAR_COLS + row;
|
||||
const day = cellIndex - firstDay + 1;
|
||||
|
||||
if (day > 0 && day <= daysInMonth) {
|
||||
const dayText = day.toString();
|
||||
const { actualBoundingBoxRight: width } = ctx.measureText(dayText);
|
||||
|
||||
const cellLeft = CALENDAR_LEFT + row * 16;
|
||||
const cellTop = CALENDAR_TOP + col * 16;
|
||||
|
||||
if (now.getDate() === day) {
|
||||
ctx.drawImage(daySelectorImage!, cellLeft, cellTop);
|
||||
}
|
||||
|
||||
ctx.fillText(
|
||||
dayText,
|
||||
cellLeft + Math.floor((15 - width) / 2),
|
||||
cellTop + 11,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillStyle = "black";
|
||||
ctx.font = "10px NDS10";
|
||||
ctx.letterSpacing = "2px";
|
||||
|
||||
const timeText = `${month}/${year}`;
|
||||
const { actualBoundingBoxRight: width } = ctx.measureText(timeText);
|
||||
|
||||
ctx.fillText(
|
||||
timeText,
|
||||
CALENDAR_LEFT + Math.floor((111 - width) / 2),
|
||||
CALENDAR_TOP - 20,
|
||||
);
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
render: () => null,
|
||||
});
|
||||
</script>
|
||||
88
app/components/Settings/TopScreen/Clock.vue
Normal file
88
app/components/Settings/TopScreen/Clock.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import CLOCK_IMAGE from "/assets/images/home/top-screen/clock.webp";
|
||||
|
||||
const CENTER_X = 63;
|
||||
const CENTER_Y = 95;
|
||||
|
||||
const [clockImage] = useImages(CLOCK_IMAGE);
|
||||
|
||||
function drawLine(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x0: number,
|
||||
y0: number,
|
||||
x1: number,
|
||||
y1: number,
|
||||
width: number,
|
||||
) {
|
||||
const dx = Math.abs(x1 - x0);
|
||||
const dy = Math.abs(y1 - y0);
|
||||
const sx = x0 < x1 ? 1 : -1;
|
||||
const sy = y0 < y1 ? 1 : -1;
|
||||
let err = dx - dy;
|
||||
|
||||
const drawThickPixel = (x: number, y: number) => {
|
||||
const isVertical = dy > dx;
|
||||
|
||||
if (width === 1) {
|
||||
ctx.fillRect(x, y, 1, 1);
|
||||
} else if (isVertical) {
|
||||
const offset = Math.floor((width - 1) / 2);
|
||||
ctx.fillRect(x - offset, y, width, 1);
|
||||
} else {
|
||||
const offset = Math.floor((width - 1) / 2);
|
||||
ctx.fillRect(x, y - offset, 1, width);
|
||||
}
|
||||
};
|
||||
|
||||
while (true) {
|
||||
drawThickPixel(x0, y0);
|
||||
|
||||
if (x0 === x1 && y0 === y1) break;
|
||||
|
||||
const e2 = 2 * err;
|
||||
if (e2 > -dy) {
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx) {
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useRender((ctx) => {
|
||||
ctx.drawImage(clockImage!, 13, 45);
|
||||
|
||||
const now = new Date();
|
||||
|
||||
const renderHand = (
|
||||
value: number,
|
||||
color: string,
|
||||
length: number,
|
||||
width: number,
|
||||
) => {
|
||||
const angle = value * Math.PI * 2 - Math.PI / 2;
|
||||
const endX = Math.round(CENTER_X + Math.cos(angle) * length);
|
||||
const endY = Math.round(CENTER_Y + Math.sin(angle) * length);
|
||||
ctx.fillStyle = color;
|
||||
drawLine(ctx, CENTER_X, CENTER_Y, endX, endY, width);
|
||||
};
|
||||
|
||||
renderHand(now.getMinutes() / 60, "#797979", 30, 2);
|
||||
renderHand(
|
||||
now.getHours() / 12 + now.getMinutes() / 60 / 12,
|
||||
"#797979",
|
||||
23,
|
||||
2,
|
||||
);
|
||||
renderHand(now.getSeconds() / 60, "#49db8a", 35, 2);
|
||||
|
||||
ctx.fillStyle = "#494949";
|
||||
ctx.fillRect(CENTER_X - 2, CENTER_Y - 2, 5, 5);
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
render: () => null,
|
||||
});
|
||||
</script>
|
||||
56
app/components/Settings/TopScreen/StatusBar.vue
Normal file
56
app/components/Settings/TopScreen/StatusBar.vue
Normal file
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import STATUS_BAR_IMAGE from "/assets/images/home/top-screen/status-bar/status-bar.webp";
|
||||
import GBA_DISPLAY_IMAGE from "/assets/images/home/top-screen/status-bar/gba-display.webp";
|
||||
import STARTUP_MODE_IMAGE from "/assets/images/home/top-screen/status-bar/startup-mode.webp";
|
||||
import BATTERY_IMAGE from "/assets/images/home/top-screen/status-bar/battery.webp";
|
||||
|
||||
const [statusBarImage, gbaDisplayImage, startupModeImage, batteryImage] =
|
||||
useImages(
|
||||
STATUS_BAR_IMAGE,
|
||||
GBA_DISPLAY_IMAGE,
|
||||
STARTUP_MODE_IMAGE,
|
||||
BATTERY_IMAGE,
|
||||
);
|
||||
|
||||
useRender((ctx) => {
|
||||
const TEXT_Y = 11;
|
||||
|
||||
ctx.drawImage(statusBarImage!, 0, 0);
|
||||
|
||||
ctx.fillStyle = "#ffffff";
|
||||
ctx.font = "7px NDS7";
|
||||
|
||||
// username
|
||||
ctx.fillText("pihkaal", 3, TEXT_Y);
|
||||
|
||||
// time + date
|
||||
const fillNumberCell = (value: number, cellX: number, offset: number) => {
|
||||
const text = value.toFixed().padStart(2, "0");
|
||||
const { actualBoundingBoxRight: width } = ctx.measureText(text);
|
||||
|
||||
const x = cellX * 16;
|
||||
ctx.fillText(text, Math.floor(x + offset + (16 - width) / 2), TEXT_Y);
|
||||
};
|
||||
|
||||
const now = new Date();
|
||||
|
||||
fillNumberCell(now.getHours(), 9, 1);
|
||||
if (Math.floor(now.getMilliseconds() / 500) % 2 == 0) {
|
||||
ctx.fillText(":", 159, TEXT_Y);
|
||||
}
|
||||
fillNumberCell(now.getMinutes(), 10, -1);
|
||||
|
||||
fillNumberCell(now.getDate(), 11, 1);
|
||||
ctx.fillText("/", 190, TEXT_Y);
|
||||
fillNumberCell(now.getMonth() + 1, 12, -1);
|
||||
|
||||
// icons
|
||||
ctx.drawImage(gbaDisplayImage!, 210, 2);
|
||||
ctx.drawImage(startupModeImage!, 226, 2);
|
||||
ctx.drawImage(batteryImage!, 242, 4);
|
||||
});
|
||||
|
||||
defineOptions({
|
||||
render: () => null,
|
||||
});
|
||||
</script>
|
||||
13
app/components/Settings/TopScreen/TopScreen.vue
Normal file
13
app/components/Settings/TopScreen/TopScreen.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import Background from "./Background.vue";
|
||||
import Calendar from "./Calendar.vue";
|
||||
import Clock from "./Clock.vue";
|
||||
import StatusBar from "./StatusBar.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Background />
|
||||
<Calendar />
|
||||
<Clock />
|
||||
<StatusBar />
|
||||
</template>
|
||||
Reference in New Issue
Block a user