From 67bb6baeb1bebb02eb29cc52e98c9625d015d7af Mon Sep 17 00:00:00 2001 From: peterwolf-pl <84736182+peterwolf-pl@users.noreply.github.com> Date: Thu, 3 Jul 2025 03:01:07 +0200 Subject: [PATCH 1/8] Clean up flip page container --- LetterComposer.jsx | 7 ++++++- PrintModule.jsx | 20 +++++++++++++++++--- index.css | 29 +++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/LetterComposer.jsx b/LetterComposer.jsx index b569a9c..9f79c41 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -18,6 +18,7 @@ export default function LetterComposer({ onMoveLineToPage }) { const [activeLetter, setActiveLetter] = useState(null); const [ghostPos, setGhostPos] = useState({ x: 0, y: 0, visible: false }); const [isDragging, setIsDragging] = useState(false); + const [pickupAnim, setPickupAnim] = useState(false); const kasztaRef = useRef(); const wierszownikRef = useRef(); const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH); @@ -58,6 +59,8 @@ export default function LetterComposer({ onMoveLineToPage }) { e.preventDefault(); const width = await getImageWidth(field.img); setActiveLetter({ ...field, width }); + setPickupAnim(true); + setTimeout(() => setPickupAnim(false), 300); let x = 0, y = 0; if (e.touches && e.touches[0]) { x = e.touches[0].clientX; @@ -153,6 +156,7 @@ export default function LetterComposer({ onMoveLineToPage }) { setSlots(updatedSlots); }; + const scale = kasztaW / KASZTA_WIDTH; const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty @@ -209,7 +213,8 @@ export default function LetterComposer({ onMoveLineToPage }) { pointerEvents: "none", zIndex: 1000, opacity: 1, - filter: "drop-shadow(2px 2px 2px #999)" + filter: "drop-shadow(2px 2px 2px #999)", + animation: pickupAnim ? "letter-pop 0.3s ease-out forwards" : undefined }} /> ); diff --git a/PrintModule.jsx b/PrintModule.jsx index e7bfe40..10252f1 100644 --- a/PrintModule.jsx +++ b/PrintModule.jsx @@ -5,6 +5,7 @@ const A4_HEIGHT = 1123; export default function PrintModule({ lines, onBack }) { const [pageW, setPageW] = useState(A4_WIDTH); + const [animReady, setAnimReady] = useState(false); // Dynamiczne skalowanie dwóch kartek w oknie useEffect(() => { @@ -25,6 +26,11 @@ export default function PrintModule({ lines, onBack }) { const scale = pageW / A4_WIDTH; const pageH = pageW * (A4_HEIGHT / A4_WIDTH); + useEffect(() => { + const t = setTimeout(() => setAnimReady(true), 500); + return () => clearTimeout(t); + }, []); + // Lustrzane odbicie: linie od dołu, każda linia od końca i flipped poziomo const mirroredLines = [...lines]; @@ -123,15 +129,23 @@ export default function PrintModule({ lines, onBack }) { display: "flex", flexDirection: "column", alignItems: "flex-start", - justifyContent: "flex-start" + justifyContent: "flex-start", + transform: animReady + ? "translateX(0) rotateX(0deg)" + : `translateX(-${pageW + 48 * scale}px) rotateX(180deg)`, + "--dx": `${pageW + 48 * scale}px`, + animation: animReady + ? "right-page-flip 1s ease forwards" + : "none", + transformStyle: "preserve-3d", + backfaceVisibility: "hidden" }} >
Date: Mon, 1 Sep 2025 17:54:26 +0200 Subject: [PATCH 2/8] Use blacha image for page composer background --- LetterComposer copy.jsx | 328 ---------------------------------------- LetterComposer.jsx | 75 ++++----- PageComposer.jsx | 7 +- PrintModule.jsx | 1 - aApp.jsx | 35 ----- assets/.gitkeep | 0 index.css | 15 +- 7 files changed, 56 insertions(+), 405 deletions(-) delete mode 100644 LetterComposer copy.jsx delete mode 100644 aApp.jsx create mode 100644 assets/.gitkeep diff --git a/LetterComposer copy.jsx b/LetterComposer copy.jsx deleted file mode 100644 index 71c44a5..0000000 --- a/LetterComposer copy.jsx +++ /dev/null @@ -1,328 +0,0 @@ -import React, { useRef, useState, useEffect } from "react"; - -const KASZTA_WIDTH = 1618; -const KASZTA_HEIGHT = 1080; -const SLOTS_COUNT = 20; - -function getImageWidth(src) { - return new Promise((resolve) => { - const img = new window.Image(); - img.onload = () => resolve(img.width); - img.src = src; - }); -} - -export default function LetterComposer() { - const [letterFields, setLetterFields] = useState([]); - const [slots, setSlots] = useState(Array(SLOTS_COUNT).fill(null)); - const [activeLetter, setActiveLetter] = useState(null); - const [ghostPos, setGhostPos] = useState({ x: 0, y: 0, visible: false }); - const [isDragging, setIsDragging] = useState(false); - const kasztaRef = useRef(); - const wierszownikRef = useRef(); - const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH); - - // BLOKUJ SCROLL strony - useEffect(() => { - const oldOverflow = document.body.style.overflow; - document.body.style.overflow = "hidden"; - return () => { document.body.style.overflow = oldOverflow; }; - }, []); - - // Responsywna szerokość kaszty - useEffect(() => { - function handleResize() { - if (kasztaRef.current) { - const parentW = kasztaRef.current.parentElement.offsetWidth; - setKasztaW(Math.min(parentW, KASZTA_WIDTH)); - } - } - handleResize(); - window.addEventListener("resize", handleResize); - return () => window.removeEventListener("resize", handleResize); - }, []); - - useEffect(() => { - fetch('/poz.json') - .then(res => res.json()) - .then(setLetterFields) - .catch(() => setLetterFields([])); - }, []); - - // DRAG START (mouse/touch na field) - const handleFieldDragStart = async (field, e) => { - e.preventDefault(); - const width = await getImageWidth(field.img); - setActiveLetter({ ...field, width }); - let x = 0, y = 0; - if (e.touches && e.touches[0]) { - x = e.touches[0].clientX; - y = e.touches[0].clientY; - } else if (e.clientX && e.clientY) { - x = e.clientX; - y = e.clientY; - } - setGhostPos({ x, y, visible: true }); - setIsDragging(true); - }; - - // DRAG MOVE & DROP (document-level, działa na iOS) - useEffect(() => { - if (!isDragging) return; - const moveGhost = (e) => { - setGhostPos({ - x: e.clientX, - y: e.clientY, - visible: true - }); - }; - const moveGhostTouch = (e) => { - if (e.touches && e.touches[0]) { - setGhostPos({ - x: e.touches[0].clientX, - y: e.touches[0].clientY, - visible: true - }); - } - }; - const handleDrop = (e) => { - let x = 0, y = 0; - if (e.changedTouches && e.changedTouches[0]) { - x = e.changedTouches[0].clientX; - y = e.changedTouches[0].clientY; - } else if (e.clientX && e.clientY) { - x = e.clientX; - y = e.clientY; - } - if (wierszownikRef.current) { - const rect = wierszownikRef.current.getBoundingClientRect(); - if ( - x >= rect.left && - x <= rect.right && - y >= rect.top && - y <= rect.bottom - ) { - placeLetter(); - } else { - setActiveLetter(null); - setGhostPos({ x: 0, y: 0, visible: false }); - } - } - setIsDragging(false); - }; - document.addEventListener("mousemove", moveGhost); - document.addEventListener("touchmove", moveGhostTouch, { passive: false }); - document.addEventListener("mouseup", handleDrop); - document.addEventListener("touchend", handleDrop, { passive: false }); - return () => { - document.removeEventListener("mousemove", moveGhost); - document.removeEventListener("touchmove", moveGhostTouch); - document.removeEventListener("mouseup", handleDrop); - document.removeEventListener("touchend", handleDrop); - }; - // eslint-disable-next-line - }, [isDragging]); - - // Umieszcza literę na wierszowniku - function placeLetter() { - let idx = slots.lastIndexOf(null); - if (idx === -1) idx = 0; - const updatedSlots = [...slots]; - updatedSlots[idx] = { ...activeLetter, id: Math.random().toString(36) }; - setSlots(updatedSlots); - setActiveLetter(null); - setGhostPos({ x: 0, y: 0, visible: false }); - setIsDragging(false); - } - - // Kaszta – klik/tap poza polem kaszty anuluje ghosta - function handleKasztaBackgroundClick(e) { - setActiveLetter(null); - setGhostPos({ x: 0, y: 0, visible: false }); - setIsDragging(false); - } - - // Usuwanie liter z wierszownika - const removeLetterFromSlot = (i) => { - const updatedSlots = [...slots]; - updatedSlots[i] = null; - setSlots(updatedSlots); - }; - - const scale = kasztaW / KASZTA_WIDTH; - const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); - const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty - - function renderLettersOnLine() { - let right = 0; - let visibleSlots = []; - for (let i = slots.length - 1; i >= 0; i--) { - const slot = slots[i]; - if (!slot) continue; - right += slot.width * scale; - visibleSlots.push( -
removeLetterFromSlot(i)} - title="Kliknij, aby usunąć literę z wierszownika" - > - {slot.char} -
- ); - } - return visibleSlots.reverse(); - } - - // Ghost litera - const renderGhostLetter = () => { - if (!activeLetter || !ghostPos.visible) return null; - return ( - {activeLetter.char} - ); - }; - - return ( -
- {/* Kaszta */} -
- {/* Tło łapiące kliknięcie na kaszcie */} -
- Kaszta zecerska - {/* Fieldy na kaszcie – tylko drag start */} - {letterFields.map(field => ( -
- - {/* Wierszownik */} -
- -
- {renderLettersOnLine()} -
-
- Educational Game for the Muzeum Książki Artystycznej w Łodzi by peterwolf.pl -
- {renderGhostLetter()} - -
- - - ); -} diff --git a/LetterComposer.jsx b/LetterComposer.jsx index 9f79c41..f5c724e 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -3,6 +3,9 @@ import React, { useRef, useState, useEffect } from "react"; const KASZTA_WIDTH = 1618; const KASZTA_HEIGHT = 1080; const SLOTS_COUNT = 20; +const LINE_OFFSET_RIGHT = 340; +const LINE_OFFSET_BOTTOM = 240; +const LETTER_HEIGHT = 96; function getImageWidth(src) { return new Promise((resolve) => { @@ -22,6 +25,7 @@ export default function LetterComposer({ onMoveLineToPage }) { const kasztaRef = useRef(); const wierszownikRef = useRef(); const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH); + const [wierszownikSize, setWierszownikSize] = useState({ w: 0, h: 0 }); // BLOKUJ SCROLL strony useEffect(() => { @@ -160,23 +164,32 @@ export default function LetterComposer({ onMoveLineToPage }) { const scale = kasztaW / KASZTA_WIDTH; const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty + const lineScale = wierszownikSize.w ? lineW / wierszownikSize.w : scale; + const letterScale = lineScale * 2; + const lineStartX = wierszownikSize.w + ? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale + : 0; + const lineStartY = wierszownikSize.h + ? (wierszownikSize.h - LINE_OFFSET_BOTTOM) * lineScale + : 0; function renderLettersOnLine() { + if (!wierszownikSize.w) return null; let right = 0; let visibleSlots = []; for (let i = slots.length - 1; i >= 0; i--) { const slot = slots[i]; if (!slot) continue; - right += slot.width * scale; + right += slot.width * letterScale; visibleSlots.push(
@@ -206,10 +219,10 @@ export default function LetterComposer({ onMoveLineToPage }) { alt={activeLetter.char} style={{ position: "fixed", - left: ghostPos.x - (activeLetter.width * scale) / 2, - top: ghostPos.y - (96 * scale), - width: activeLetter.width * scale, - height: 96 * scale, + left: ghostPos.x - (activeLetter.width * letterScale) / 2, + top: ghostPos.y - (LETTER_HEIGHT * letterScale), + width: activeLetter.width * letterScale, + height: LETTER_HEIGHT * letterScale, pointerEvents: "none", zIndex: 1000, opacity: 1, @@ -229,7 +242,6 @@ export default function LetterComposer({ onMoveLineToPage }) { minHeight: "100vh", display: "flex", flexDirection: "column", - background: "#f5f6f8", alignItems: "center", justifyContent: "stretch", overflow: "hidden", @@ -324,37 +336,30 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "relative", width: lineW, - minHeight: 116 * scale, + height: wierszownikSize.h + ? wierszownikSize.h * lineScale + : 116 * scale, margin: "1px auto 0px auto", borderRadius: 8 * scale, - background: "#a6a3a8", touchAction: "none", flexShrink: 0, boxSizing: "border-box" }} > -
+ setWierszownikSize({ + w: e.target.naturalWidth, + h: e.target.naturalHeight + }) + } + draggable={false} style={{ - position: "absolute", - left: -5 * scale, - top: 96 * scale + 16 * scale, - width: lineW + (10 * scale), - height: 8 * scale, - background: "#111", - borderRadius: 8 * scale, - zIndex: 1 - }} - /> -
{renderLettersOnLine()} diff --git a/PageComposer.jsx b/PageComposer.jsx index 3c267e9..30d7a1f 100644 --- a/PageComposer.jsx +++ b/PageComposer.jsx @@ -205,7 +205,6 @@ export default function PageComposer({ style={{ minHeight: "100vh", width: "100vw", - background: "#f5f6f8", display: "flex", flexDirection: "column", alignItems: "center", @@ -230,7 +229,11 @@ export default function PageComposer({
- -

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - ) -} - -export default App diff --git a/assets/.gitkeep b/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/index.css b/index.css index 33389ba..64dc43e 100644 --- a/index.css +++ b/index.css @@ -12,9 +12,13 @@ -webkit-user-select: none; -ms-user-select: none; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light; + color: #213547; + background-color: #d4d4d4; + background-image: url('/assets/bg.png'); + background-repeat: repeat; + background-size: var(--bg-tile-size); + --bg-tile-size: 64px; font-synthesis: none; text-rendering: optimizeLegibility; @@ -37,6 +41,10 @@ body { place-items: center; min-width: 320px; min-height: 100vh; + background-color: #d4d4d4; + background-image: url('/assets/bg.png'); + background-repeat: repeat; + background-size: var(--bg-tile-size); } h1 { @@ -66,7 +74,6 @@ button:focus-visible { @media (prefers-color-scheme: light) { :root { color: #213547; - background-color: #ffffff; } a:hover { color: #747bff; From 458267c4cda7928a9e39d366eb84d275bc8be132 Mon Sep 17 00:00:00 2001 From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com> Date: Mon, 1 Sep 2025 18:02:24 +0200 Subject: [PATCH 3/8] Align letters using top-right offsets --- LetterComposer.jsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/LetterComposer.jsx b/LetterComposer.jsx index f5c724e..4b08679 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -3,8 +3,11 @@ import React, { useRef, useState, useEffect } from "react"; const KASZTA_WIDTH = 1618; const KASZTA_HEIGHT = 1080; const SLOTS_COUNT = 20; -const LINE_OFFSET_RIGHT = 340; -const LINE_OFFSET_BOTTOM = 240; +// Starting point for the top-right corner of the first letter +// measured from the wierszownik photo edges. +// Offsets are scaled with the image dimensions. +const LINE_OFFSET_RIGHT = 120; +const LINE_OFFSET_TOP = 170; const LETTER_HEIGHT = 96; function getImageWidth(src) { @@ -170,7 +173,7 @@ export default function LetterComposer({ onMoveLineToPage }) { ? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale : 0; const lineStartY = wierszownikSize.h - ? (wierszownikSize.h - LINE_OFFSET_BOTTOM) * lineScale + ? LINE_OFFSET_TOP * lineScale : 0; function renderLettersOnLine() { @@ -187,7 +190,7 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "absolute", left: lineStartX - right, - top: lineStartY - LETTER_HEIGHT * letterScale, + top: lineStartY, width: slot.width * letterScale, height: LETTER_HEIGHT * letterScale, zIndex: 3, @@ -220,7 +223,7 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "fixed", left: ghostPos.x - (activeLetter.width * letterScale) / 2, - top: ghostPos.y - (LETTER_HEIGHT * letterScale), + top: ghostPos.y, width: activeLetter.width * letterScale, height: LETTER_HEIGHT * letterScale, pointerEvents: "none", From 47edcb570df8beef050ec963f672f3c855246b5e Mon Sep 17 00:00:00 2001 From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com> Date: Mon, 1 Sep 2025 19:21:19 +0200 Subject: [PATCH 4/8] Revert wierszownik swap and drop blacha backdrop --- LetterComposer.jsx | 63 +++++++++++++++++++++------------------------- PageComposer.jsx | 1 - 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/LetterComposer.jsx b/LetterComposer.jsx index 4b08679..c592a0b 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -3,11 +3,6 @@ import React, { useRef, useState, useEffect } from "react"; const KASZTA_WIDTH = 1618; const KASZTA_HEIGHT = 1080; const SLOTS_COUNT = 20; -// Starting point for the top-right corner of the first letter -// measured from the wierszownik photo edges. -// Offsets are scaled with the image dimensions. -const LINE_OFFSET_RIGHT = 120; -const LINE_OFFSET_TOP = 170; const LETTER_HEIGHT = 96; function getImageWidth(src) { @@ -28,7 +23,6 @@ export default function LetterComposer({ onMoveLineToPage }) { const kasztaRef = useRef(); const wierszownikRef = useRef(); const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH); - const [wierszownikSize, setWierszownikSize] = useState({ w: 0, h: 0 }); // BLOKUJ SCROLL strony useEffect(() => { @@ -167,17 +161,9 @@ export default function LetterComposer({ onMoveLineToPage }) { const scale = kasztaW / KASZTA_WIDTH; const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty - const lineScale = wierszownikSize.w ? lineW / wierszownikSize.w : scale; - const letterScale = lineScale * 2; - const lineStartX = wierszownikSize.w - ? (wierszownikSize.w - LINE_OFFSET_RIGHT) * lineScale - : 0; - const lineStartY = wierszownikSize.h - ? LINE_OFFSET_TOP * lineScale - : 0; + const letterScale = scale * 2; function renderLettersOnLine() { - if (!wierszownikSize.w) return null; let right = 0; let visibleSlots = []; for (let i = slots.length - 1; i >= 0; i--) { @@ -189,8 +175,8 @@ export default function LetterComposer({ onMoveLineToPage }) { key={slot.id} style={{ position: "absolute", - left: lineStartX - right, - top: lineStartY, + left: lineW - right, + top: `${16 * letterScale}px`, width: slot.width * letterScale, height: LETTER_HEIGHT * letterScale, zIndex: 3, @@ -223,7 +209,7 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "fixed", left: ghostPos.x - (activeLetter.width * letterScale) / 2, - top: ghostPos.y, + top: ghostPos.y - (LETTER_HEIGHT * letterScale) / 2, width: activeLetter.width * letterScale, height: LETTER_HEIGHT * letterScale, pointerEvents: "none", @@ -339,30 +325,37 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "relative", width: lineW, - height: wierszownikSize.h - ? wierszownikSize.h * lineScale - : 116 * scale, + minHeight: 116 * letterScale, margin: "1px auto 0px auto", - borderRadius: 8 * scale, + borderRadius: 8 * letterScale, + background: "#a6a3a8", touchAction: "none", flexShrink: 0, boxSizing: "border-box" }} > - Wierszownik - setWierszownikSize({ - w: e.target.naturalWidth, - h: e.target.naturalHeight - }) - } - draggable={false} +
+
{renderLettersOnLine()} diff --git a/PageComposer.jsx b/PageComposer.jsx index 30d7a1f..7e605c1 100644 --- a/PageComposer.jsx +++ b/PageComposer.jsx @@ -229,7 +229,6 @@ export default function PageComposer({
Date: Mon, 1 Sep 2025 19:21:24 +0200 Subject: [PATCH 5/8] Anchor letter placement and page margins --- LetterComposer.jsx | 20 ++++++++++++-------- PageComposer.jsx | 15 +++++++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/LetterComposer.jsx b/LetterComposer.jsx index c592a0b..56e65f4 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -4,6 +4,8 @@ const KASZTA_WIDTH = 1618; const KASZTA_HEIGHT = 1080; const SLOTS_COUNT = 20; const LETTER_HEIGHT = 96; +const LINE_OFFSET_RIGHT = 120; +const LINE_OFFSET_TOP = 170; function getImageWidth(src) { return new Promise((resolve) => { @@ -162,6 +164,8 @@ export default function LetterComposer({ onMoveLineToPage }) { const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty const letterScale = scale * 2; + const offsetRight = LINE_OFFSET_RIGHT * scale; + const offsetTop = LINE_OFFSET_TOP * scale; function renderLettersOnLine() { let right = 0; @@ -175,8 +179,8 @@ export default function LetterComposer({ onMoveLineToPage }) { key={slot.id} style={{ position: "absolute", - left: lineW - right, - top: `${16 * letterScale}px`, + left: lineW - offsetRight - right, + top: offsetTop, width: slot.width * letterScale, height: LETTER_HEIGHT * letterScale, zIndex: 3, @@ -337,9 +341,9 @@ export default function LetterComposer({ onMoveLineToPage }) {
{lines.map((line, i) => { @@ -259,9 +266,9 @@ export default function PageComposer({ flexDirection: "row", alignItems: "flex-end", justifyContent: "flex-end", - margin: `${30 * scale}px ${20 * scale}px ${-24 * scale}px 0`, + margin: `0 0 ${-24 * scale}px 0`, minHeight: 96 / 3 * scale, - maxWidth: `calc(100% - ${40 * scale}px)`, + maxWidth: `calc(100% - ${sheetOffsetRight}px)`, cursor: dragIndex === null ? "grab" : "default", userSelect: "none", touchAction: "none", @@ -289,8 +296,8 @@ export default function PageComposer({
Date: Mon, 1 Sep 2025 19:21:29 +0200 Subject: [PATCH 6/8] Use wierszownik photo with scaled baseline --- LetterComposer.jsx | 62 +++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 34 deletions(-) diff --git a/LetterComposer.jsx b/LetterComposer.jsx index 56e65f4..8c3ee89 100644 --- a/LetterComposer.jsx +++ b/LetterComposer.jsx @@ -4,8 +4,9 @@ const KASZTA_WIDTH = 1618; const KASZTA_HEIGHT = 1080; const SLOTS_COUNT = 20; const LETTER_HEIGHT = 96; -const LINE_OFFSET_RIGHT = 120; -const LINE_OFFSET_TOP = 170; +const LINE_OFFSET_RIGHT = 340; +const LINE_OFFSET_BOTTOM = 240; +const WIERSZOWNIK_SRC = "/assets/wierszownik.jpg"; function getImageWidth(src) { return new Promise((resolve) => { @@ -25,6 +26,13 @@ export default function LetterComposer({ onMoveLineToPage }) { const kasztaRef = useRef(); const wierszownikRef = useRef(); const [kasztaW, setKasztaW] = useState(KASZTA_WIDTH); + const [wierszownikDims, setWierszownikDims] = useState({ width: 1, height: 1 }); + + useEffect(() => { + const img = new window.Image(); + img.onload = () => setWierszownikDims({ width: img.width, height: img.height }); + img.src = WIERSZOWNIK_SRC; + }, []); // BLOKUJ SCROLL strony useEffect(() => { @@ -160,12 +168,14 @@ export default function LetterComposer({ onMoveLineToPage }) { }; - const scale = kasztaW / KASZTA_WIDTH; + const kasztaScale = kasztaW / KASZTA_WIDTH; const kasztaH = kasztaW * (KASZTA_HEIGHT / KASZTA_WIDTH); const lineW = kasztaW * 0.8; // WIERSZOWNIK 80% kaszty - const letterScale = scale * 2; - const offsetRight = LINE_OFFSET_RIGHT * scale; - const offsetTop = LINE_OFFSET_TOP * scale; + const wierszScale = lineW / wierszownikDims.width; + const lineH = wierszownikDims.height * wierszScale; + const letterScale = wierszScale * 2; + const offsetRight = LINE_OFFSET_RIGHT * wierszScale; + const offsetTop = lineH - LINE_OFFSET_BOTTOM * wierszScale - LETTER_HEIGHT * letterScale; function renderLettersOnLine() { let right = 0; @@ -299,10 +309,10 @@ export default function LetterComposer({ onMoveLineToPage }) { aria-label="Wybierz czcionkę" style={{ position: "absolute", - left: Math.min(field.x1, field.x2) * scale, - top: Math.min(field.y1, field.y2) * scale, - width: Math.abs(field.x2 - field.x1) * scale, - height: Math.abs(field.y2 - field.y1) * scale, + left: Math.min(field.x1, field.x2) * kasztaScale, + top: Math.min(field.y1, field.y2) * kasztaScale, + width: Math.abs(field.x2 - field.x1) * kasztaScale, + height: Math.abs(field.y2 - field.y1) * kasztaScale, border: "0px solid #2563eb", background: "rgba(96,165,250,0.0)", borderRadius: "10px", @@ -329,37 +339,21 @@ export default function LetterComposer({ onMoveLineToPage }) { style={{ position: "relative", width: lineW, - minHeight: 116 * letterScale, + height: lineH, margin: "1px auto 0px auto", - borderRadius: 8 * letterScale, - background: "#a6a3a8", touchAction: "none", flexShrink: 0, boxSizing: "border-box" }} > -
-
{renderLettersOnLine()} From e5bec0d123329878b5a0603e041657a996dd4192 Mon Sep 17 00:00:00 2001 From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com> Date: Mon, 1 Sep 2025 19:42:09 +0200 Subject: [PATCH 7/8] Scale sheet offsets and fix line spacing --- PageComposer.jsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/PageComposer.jsx b/PageComposer.jsx index da73726..ae6b152 100644 --- a/PageComposer.jsx +++ b/PageComposer.jsx @@ -19,22 +19,31 @@ export default function PageComposer({ }) { const [pageW, setPageW] = useState(A4_WIDTH); const wrapperRef = useRef(); + const [sheetDims, setSheetDims] = useState({ width: A4_WIDTH, height: A4_HEIGHT }); + + useEffect(() => { + const img = new Image(); + img.src = "/assets/blacha.png"; + img.onload = () => { + setSheetDims({ width: img.width, height: img.height }); + }; + }, []); useEffect(() => { function handleResize() { const maxW = window.innerWidth * 0.95; const stopkaH = 40 + 18; const maxH = window.innerHeight - stopkaH - 32; - const byHeight = maxH * (A4_WIDTH / A4_HEIGHT); - setPageW(Math.min(A4_WIDTH, maxW, byHeight)); + const byHeight = maxH * (sheetDims.width / sheetDims.height); + setPageW(Math.min(sheetDims.width, maxW, byHeight)); } handleResize(); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); - }, []); + }, [sheetDims]); - const scale = pageW / A4_WIDTH; - const pageH = pageW * (A4_HEIGHT / A4_WIDTH); + const scale = pageW / sheetDims.width; + const pageH = pageW * (sheetDims.height / sheetDims.width); const sheetOffsetRight = SHEET_OFFSET_RIGHT * scale; const sheetOffsetTop = SHEET_OFFSET_TOP * scale; @@ -236,8 +245,7 @@ export default function PageComposer({ backgroundImage: "url(/assets/blacha.png)", backgroundRepeat: "no-repeat", backgroundPosition: "center", - backgroundSize: "cover", - border: "4px solid #222", + backgroundSize: "100% 100%", borderRadius: 6 * scale, width: pageW, height: pageH, @@ -266,8 +274,8 @@ export default function PageComposer({ flexDirection: "row", alignItems: "flex-end", justifyContent: "flex-end", - margin: `0 0 ${-24 * scale}px 0`, - minHeight: 96 / 3 * scale, + margin: `0 0 ${8 * scale}px 0`, + minHeight: (96 / 3) * scale, maxWidth: `calc(100% - ${sheetOffsetRight}px)`, cursor: dragIndex === null ? "grab" : "default", userSelect: "none", From d368f4db602da0d147e30b1d720f30a55472643e Mon Sep 17 00:00:00 2001 From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com> Date: Mon, 1 Sep 2025 19:54:06 +0200 Subject: [PATCH 8/8] Double letter size on sheet --- PageComposer.jsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/PageComposer.jsx b/PageComposer.jsx index ae6b152..3bcabe1 100644 --- a/PageComposer.jsx +++ b/PageComposer.jsx @@ -4,6 +4,8 @@ const A4_WIDTH = 796; const A4_HEIGHT = 1123; const SHEET_OFFSET_RIGHT = 120; const SHEET_OFFSET_TOP = 170; +const LETTER_SCALE = 2; +const LETTER_BASE_HEIGHT = 96 / 3; @@ -186,7 +188,7 @@ export default function PageComposer({ return { background: "#e3f2ff", borderRadius: 4 * scale, - minHeight: 32 * scale, + minHeight: LETTER_BASE_HEIGHT * LETTER_SCALE * scale, outline: "2px dashed #28b0ef", transition: "background 0.12s", }; @@ -275,7 +277,7 @@ export default function PageComposer({ alignItems: "flex-end", justifyContent: "flex-end", margin: `0 0 ${8 * scale}px 0`, - minHeight: (96 / 3) * scale, + minHeight: LETTER_BASE_HEIGHT * LETTER_SCALE * scale, maxWidth: `calc(100% - ${sheetOffsetRight}px)`, cursor: dragIndex === null ? "grab" : "default", userSelect: "none", @@ -290,8 +292,8 @@ export default function PageComposer({ key={j} src={letter.img} alt={letter.char} - width={(letter.width / 3) * scale} - height={(96 / 3) * scale} + width={(letter.width / 3) * scale * LETTER_SCALE} + height={LETTER_BASE_HEIGHT * scale * LETTER_SCALE} style={{ marginLeft: 0, pointerEvents: "none" }} draggable={false} /> @@ -324,8 +326,8 @@ export default function PageComposer({ key={j} src={letter.img} alt={letter.char} - width={(letter.width / 3) * scale} - height={(96 / 3) * scale} + width={(letter.width / 3) * scale * LETTER_SCALE} + height={LETTER_BASE_HEIGHT * scale * LETTER_SCALE} style={{ marginLeft: 0, pointerEvents: "none" }} draggable={false} />