-
Notifications
You must be signed in to change notification settings - Fork 0
PR_26179_CHARLIE_036-sprites-animation-preview #273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,10 @@ const editorHistory = { | |
| redoStack: [], | ||
| undoStack: [], | ||
| }; | ||
| const animationPreview = { | ||
| frameIndex: 0, | ||
| timerId: null, | ||
| }; | ||
|
|
||
| function gridLabel(size) { | ||
| return `Sprite Creator ${size} by ${size} pixel canvas`; | ||
|
|
@@ -467,12 +471,12 @@ function renderPixelsToCanvas(canvas, paintedPixels, gridSize) { | |
| } | ||
| } | ||
|
|
||
| function renderPreview() { | ||
| function renderPreview(frame = currentFrame()) { | ||
| const canvas = document.querySelector("[data-sprites-preview-canvas]"); | ||
| if (!canvas) { | ||
| return; | ||
| } | ||
| renderPixelsToCanvas(canvas, editorState.paintedPixels, editorState.gridSize); | ||
| renderPixelsToCanvas(canvas, frame.paintedPixels, editorState.gridSize); | ||
| } | ||
|
|
||
| function renderFrameStrip() { | ||
|
|
@@ -558,6 +562,63 @@ function deleteFrame() { | |
| updateHistoryControls(); | ||
| } | ||
|
|
||
| function updateAnimationControls(isPlaying) { | ||
| const playButton = document.querySelector("[data-sprites-play-animation]"); | ||
| const stopButton = document.querySelector("[data-sprites-stop-animation]"); | ||
| if (playButton) { | ||
| playButton.disabled = isPlaying; | ||
| } | ||
| if (stopButton) { | ||
| stopButton.disabled = !isPlaying; | ||
| } | ||
| } | ||
|
|
||
| function animationStatusText(prefix, frameIndex = editorState.activeFrameIndex) { | ||
| return `${prefix} Frame ${frameIndex + 1} of ${editorState.frames.length}. Unsaved preview only.`; | ||
| } | ||
|
|
||
| function showAnimationFrame(frameIndex) { | ||
| const normalizedIndex = frameIndex % editorState.frames.length; | ||
| animationPreview.frameIndex = normalizedIndex; | ||
| renderPreview(editorState.frames[normalizedIndex]); | ||
| const status = document.querySelector("[data-sprites-animation-status]"); | ||
| if (status) { | ||
| status.textContent = animationStatusText("Playing", normalizedIndex); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Play Preview runs, this line rewrites Useful? React with 👍 / 👎. |
||
| } | ||
| } | ||
|
|
||
| function stopAnimationPreview(message = "Animation preview stopped.") { | ||
| if (animationPreview.timerId) { | ||
| clearInterval(animationPreview.timerId); | ||
| animationPreview.timerId = null; | ||
| } | ||
| renderPreview(currentFrame()); | ||
| updateAnimationControls(false); | ||
| const status = document.querySelector("[data-sprites-animation-status]"); | ||
| if (status) { | ||
| status.textContent = `${message} Selected Frame ${currentFrame().frameNumber} is shown.`; | ||
| } | ||
| } | ||
|
|
||
| function playAnimationPreview() { | ||
| const status = document.querySelector("[data-sprites-animation-status]"); | ||
| if (editorState.frames.length < 2) { | ||
| if (status) { | ||
| status.textContent = "Animation preview needs at least two unsaved frames."; | ||
| } | ||
| return; | ||
| } | ||
| if (animationPreview.timerId) { | ||
| clearInterval(animationPreview.timerId); | ||
| } | ||
| animationPreview.frameIndex = 0; | ||
| updateAnimationControls(true); | ||
| showAnimationFrame(animationPreview.frameIndex); | ||
| animationPreview.timerId = setInterval(() => { | ||
| showAnimationFrame((animationPreview.frameIndex + 1) % editorState.frames.length); | ||
| }, DEFAULT_FRAME_DURATION_MS); | ||
|
Comment on lines
+617
to
+619
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the user starts Play Preview with two frames and then clicks Delete Frame, the frame list can shrink to one while this interval keeps firing; the controls stay in the playing state and the status becomes Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| function exportPreviewPng() { | ||
| const canvas = document.querySelector("[data-sprites-preview-canvas]"); | ||
| const status = document.querySelector("[data-sprites-export-status]"); | ||
|
|
@@ -701,6 +762,18 @@ function wireFrameActions() { | |
| } | ||
| } | ||
|
|
||
| function wireAnimationActions() { | ||
| const playButton = document.querySelector("[data-sprites-play-animation]"); | ||
| if (playButton) { | ||
| playButton.addEventListener("click", playAnimationPreview); | ||
| } | ||
|
|
||
| const stopButton = document.querySelector("[data-sprites-stop-animation]"); | ||
| if (stopButton) { | ||
| stopButton.addEventListener("click", () => stopAnimationPreview()); | ||
| } | ||
| } | ||
|
|
||
| function wireHistoryActions() { | ||
| const undoButton = document.querySelector("[data-sprites-undo]"); | ||
| if (undoButton) { | ||
|
|
@@ -741,6 +814,7 @@ wireDrawingTools(); | |
| wirePaletteButtons(); | ||
| wireCanvasActions(); | ||
| wireFrameActions(); | ||
| wireAnimationActions(); | ||
| wireHistoryActions(); | ||
| wireZoomControls(); | ||
| wireExportButton(); | ||
|
|
@@ -751,3 +825,4 @@ setZoomLevel(editorState.zoomLevel); | |
| updateHistoryControls(); | ||
| renderPreview(); | ||
| renderFrameStrip(); | ||
| updateAnimationControls(false); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # PR_26179_CHARLIE_036-sprites-animation-preview | ||
|
|
||
| Team: CHARLIE | ||
|
|
||
| Mode: Batch governance stacked feature workflow | ||
|
|
||
| Base branch: PR_26179_CHARLIE_035-sprites-frame-editing | ||
|
|
||
| Branch: PR_26179_CHARLIE_036-sprites-animation-preview | ||
|
|
||
| ## Summary | ||
|
|
||
| Added unsaved animation preview playback for Sprite Creator. Play Preview cycles the right preview canvas through unsaved page-session frames, and Stop Preview returns the preview to the selected frame. No persistence, database, API, schema, save-to-library, or publishing behavior was introduced. | ||
|
|
||
| ## Branch Validation | ||
|
|
||
| PASS | ||
|
|
||
| - Built from `PR_26179_CHARLIE_035-sprites-frame-editing`. | ||
| - Scope stayed limited to unsaved animation preview. | ||
| - No DB/API/schema changes. | ||
| - No saved product data or browser-owned authoritative product data added. | ||
| - No start_of_day files changed. | ||
| - ZIP package created at the requested path. | ||
|
|
||
| ## Requirement Checklist | ||
|
|
||
| PASS - Animation preview controls added. | ||
|
|
||
| PASS - Preview requires unsaved frame strip frames only. | ||
|
|
||
| PASS - Play cycles through unsaved frames in the right preview canvas. | ||
|
|
||
| PASS - Stop returns the preview to the selected frame. | ||
|
|
||
| PASS - No persistence added. | ||
|
|
||
| PASS - Targeted Playwright coverage updated. | ||
|
|
||
| ## Validation Lane | ||
|
|
||
| PASS - `node --check assets/toolbox/sprites/js/index.js` | ||
|
|
||
| PASS - `node --check dev/tests/playwright/tools/SpritesToolShell.spec.mjs` | ||
|
|
||
| PASS - `git diff --check -- toolbox/sprites/index.html assets/toolbox/sprites/js/index.js dev/tests/playwright/tools/SpritesToolShell.spec.mjs` | ||
|
|
||
| PASS - Runtime guard scan found no prohibited inline style/script/event-handler or stale placeholder text in touched runtime files. | ||
|
|
||
| PASS - `npx playwright test dev/tests/playwright/tools/SpritesToolShell.spec.mjs --workers=1 --reporter=list` | ||
|
|
||
| ## Manual Validation Notes | ||
|
|
||
| 1. Open `toolbox/sprites/index.html`. | ||
| 2. Draw Frame 1. | ||
| 3. Add Frame 2 and draw a different draft. | ||
| 4. Click Play Preview and confirm the right preview cycles frames. | ||
| 5. Click Stop Preview and confirm the selected frame is shown. | ||
| 6. Confirm no save, publish, API, database, or library behavior is introduced. | ||
|
|
||
| ## ZIP | ||
|
|
||
| `dev/workspace/zip/PR_26179_CHARLIE_036-sprites-animation-preview_delta.zip` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The root AGENTS.md Packaging rule requires the BUILD ZIP at Useful? React with 👍 / 👎. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| assets/toolbox/sprites/js/index.js | ||
| dev/tests/playwright/tools/SpritesToolShell.spec.mjs | ||
| docs_build/dev/reports/PR_26179_CHARLIE_035-sprites-frame-editing.md | ||
| docs_build/dev/reports/PR_26179_CHARLIE_036-sprites-animation-preview.md | ||
| docs_build/dev/reports/codex_changed_files.txt | ||
| docs_build/dev/reports/codex_review.diff | ||
| toolbox/sprites/index.html |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While Play Preview is running, this writes animation frames into the same
[data-sprites-preview-canvas]thatexportPreviewPnglater downloads, even though the export panel promises the unsaved editor draft. If a user previews motion and clicks Download PNG before stopping, the file contains whichever animation frame the timer last rendered rather than the selected frame; stop playback or use a separate animation canvas before exporting.Useful? React with 👍 / 👎.