From eb2a7f7d916d4de081a2881cf205489e9ca6bf27 Mon Sep 17 00:00:00 2001 From: tianmo Date: Sat, 19 Sep 2026 10:15:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A8=E6=80=81=E7=BC=A9=E7=95=A5=E5=9B=BE?= =?UTF-8?q?=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _locales/zh_CN.json | 4 ++-- .../UNIVERSAL_SpineViewer/_locales/zh_CN.json | 4 ++-- .../UNIVERSAL_SpineViewer/viewer/spine.css | 18 +++++++++++---- .../UNIVERSAL_SpineViewer/viewer/spine.html | 23 +++++++++++++++---- tests/apng-thumbnail.test.js | 8 ++++++- viewer/spine.css | 18 +++++++++++---- viewer/spine.html | 23 +++++++++++++++---- 7 files changed, 73 insertions(+), 25 deletions(-) diff --git a/_locales/zh_CN.json b/_locales/zh_CN.json index 5d2e985..86b3337 100644 --- a/_locales/zh_CN.json +++ b/_locales/zh_CN.json @@ -21,7 +21,7 @@ "thumbnail": "设为当前素材缩略图", "thumbnailTitle": "将当前帧设为当前素材的 400×400 自定义缩略图", "animatedThumbnailTitle": "生成动画缩略图", - "animatedThumbnailNote": "生成后将替换当前素材缩略图,插件无法自动恢复原图。将自动降低帧数、颜色精度和内部细节;仍超标时会把尺寸从 400×400 逐级调低。", + "animatedThumbnailNote": "生成后将替换所选素材的缩略图,插件无法自动恢复原图。将自动降低帧数、颜色精度和内部细节;仍超标时会把尺寸从 400×400 逐级调低。", "animatedRange": "动画范围", "animatedStart": "开始", "animatedEnd": "结束", @@ -40,7 +40,7 @@ "animatedTooLarge": "动画缩略图超过 50 MB,请缩短范围或选择更低质量", "animatedThumbUpdated": "动画缩略图已更新 · {frames} 帧 · {size} KB", "groupThumbnail": "为关联资源生成缩略图", - "groupThumbnailTitle": "列出当前 Spine 关联资源,确认后批量生成缩略图", + "groupThumbnailTitle": "左键为关联资源生成当前帧缩略图,右键生成当前动画缩略图", "groupDialogTitle": "为关联资源生成缩略图", "groupDialogSummary": "将生成 {generate} 项,跳过 {skip} 项。", "groupDialogNote": "只会为勾选的关联资源生成缩略图。图片和已有自定义缩略图的资源默认不勾选;若勾选后者,原缩略图将被替换且插件无法自动恢复。", diff --git a/release/UNIVERSAL_SpineViewer/_locales/zh_CN.json b/release/UNIVERSAL_SpineViewer/_locales/zh_CN.json index 5d2e985..86b3337 100644 --- a/release/UNIVERSAL_SpineViewer/_locales/zh_CN.json +++ b/release/UNIVERSAL_SpineViewer/_locales/zh_CN.json @@ -21,7 +21,7 @@ "thumbnail": "设为当前素材缩略图", "thumbnailTitle": "将当前帧设为当前素材的 400×400 自定义缩略图", "animatedThumbnailTitle": "生成动画缩略图", - "animatedThumbnailNote": "生成后将替换当前素材缩略图,插件无法自动恢复原图。将自动降低帧数、颜色精度和内部细节;仍超标时会把尺寸从 400×400 逐级调低。", + "animatedThumbnailNote": "生成后将替换所选素材的缩略图,插件无法自动恢复原图。将自动降低帧数、颜色精度和内部细节;仍超标时会把尺寸从 400×400 逐级调低。", "animatedRange": "动画范围", "animatedStart": "开始", "animatedEnd": "结束", @@ -40,7 +40,7 @@ "animatedTooLarge": "动画缩略图超过 50 MB,请缩短范围或选择更低质量", "animatedThumbUpdated": "动画缩略图已更新 · {frames} 帧 · {size} KB", "groupThumbnail": "为关联资源生成缩略图", - "groupThumbnailTitle": "列出当前 Spine 关联资源,确认后批量生成缩略图", + "groupThumbnailTitle": "左键为关联资源生成当前帧缩略图,右键生成当前动画缩略图", "groupDialogTitle": "为关联资源生成缩略图", "groupDialogSummary": "将生成 {generate} 项,跳过 {skip} 项。", "groupDialogNote": "只会为勾选的关联资源生成缩略图。图片和已有自定义缩略图的资源默认不勾选;若勾选后者,原缩略图将被替换且插件无法自动恢复。", diff --git a/release/UNIVERSAL_SpineViewer/viewer/spine.css b/release/UNIVERSAL_SpineViewer/viewer/spine.css index e3a57ce..3a70237 100644 --- a/release/UNIVERSAL_SpineViewer/viewer/spine.css +++ b/release/UNIVERSAL_SpineViewer/viewer/spine.css @@ -112,16 +112,24 @@ } .thumb-dialog-input.hidden { display: none; } .thumb-dialog-error { color: var(--error); min-height: 18px; } - .animated-thumb-card { width: min(520px, 92vw); } + .animated-thumb-card { width: min(480px, 92vw); gap: 10px; padding: 16px; } .thumb-dialog-note.warning { color: #e6b35c; } - .animated-thumb-form { display: grid; gap: 12px; padding: 4px 0; } + .animated-thumb-card .thumb-dialog-note { font-size: 13px; } + .animated-thumb-form { display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; gap: 10px 14px; padding: 2px 0; } .animated-thumb-label { color: var(--text-dim); } - .animated-thumb-range { display: flex; align-items: center; gap: 8px; } + .animated-thumb-range { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } .animated-thumb-range label { display: flex; align-items: center; gap: 6px; } .animated-thumb-range input { width: 88px; } - .animated-thumb-quality { display: flex; align-items: center; justify-content: space-between; gap: 16px; color: var(--text-dim); } + .animated-thumb-quality { grid-column: 1 / -1; display: grid; grid-template-columns: 96px minmax(0, 1fr); align-items: center; gap: 14px; color: var(--text-dim); } .animated-thumb-size { display: flex; align-items: center; gap: 7px; color: var(--text); } - .animated-thumb-size input { width: 108px; } + .animated-thumb-size input { width: 88px; } + .animated-thumb-form input[type="number"] { + box-sizing: border-box; height: 32px; padding: 4px 8px; + background: var(--panel-2); color: var(--text); + border: 1px solid var(--border); border-radius: 4px; + } + .animated-thumb-form input[type="number"]:focus { outline: none; border-color: var(--accent); } + .animated-thumb-card .thumb-dialog-actions { margin-top: 2px; } /* 收窄下拉,避免在窄窗口下把右侧按钮挤到第二行(展开后的选项不受影响)*/ #toolbar #suffixSelect { max-width: 140px; } #stage { position: absolute; inset: 0; display: flex; overflow: hidden; } diff --git a/release/UNIVERSAL_SpineViewer/viewer/spine.html b/release/UNIVERSAL_SpineViewer/viewer/spine.html index f3c0312..adb5a82 100644 --- a/release/UNIVERSAL_SpineViewer/viewer/spine.html +++ b/release/UNIVERSAL_SpineViewer/viewer/spine.html @@ -29,7 +29,7 @@ 尾缀 - + @@ -1042,7 +1042,7 @@ } } - async function generateGroupThumbnails() { + async function generateGroupThumbnails(animated = false) { if (!skeleton || !renderer || !activeAssets) { setStatus(t('viewer.noSkeletonForThumb')); return; } const groupThumbBtn = document.getElementById('groupThumbBtn'); groupThumbBtn.disabled = true; @@ -1056,8 +1056,17 @@ if (rows.length && rows.every((row) => row.state === 'unavailable')) setStatus(t('viewer.groupNoTargets'), 4000); return; } - setStatus(t('viewer.generating'), Infinity); - const rendered = await renderCurrentFrameThumbnail(); + let rendered; + const animation = (skeletonData.animations || [])[currentAnim]; + if (animated && !isStaticAnimation && currentAnim !== SETUP_POSE_INDEX && animation && animationDuration(animation) > STATIC_EPSILON) { + const options = await confirmAnimatedThumbnail(animation); + if (!options) return; + setStatus(t('viewer.animatedGenerating', { current: 0, total: '…' }), Infinity); + rendered = await renderAnimatedThumbnail(animation, options); + } else { + setStatus(t('viewer.generating'), Infinity); + rendered = await renderCurrentFrameThumbnail(); + } const result = await groupThumbnails.applyThumbnailTargets({ targets, initialSkipped: rows.length - targets.length, @@ -1858,7 +1867,11 @@ document.getElementById('reloadBtn').addEventListener('click', () => { window.location.reload(); }); - document.getElementById('groupThumbBtn').addEventListener('click', generateGroupThumbnails); + document.getElementById('groupThumbBtn').addEventListener('click', () => generateGroupThumbnails()); + document.getElementById('groupThumbBtn').addEventListener('contextmenu', (event) => { + event.preventDefault(); + generateGroupThumbnails(true); + }); document.getElementById('thumbBtn').addEventListener('click', generateThumbnail); document.getElementById('thumbBtn').addEventListener('contextmenu', (event) => { event.preventDefault(); diff --git a/tests/apng-thumbnail.test.js b/tests/apng-thumbnail.test.js index 0830055..37a72a4 100644 --- a/tests/apng-thumbnail.test.js +++ b/tests/apng-thumbnail.test.js @@ -41,9 +41,13 @@ assert.equal(parsed[5].data.readUInt32BE(0), 2); for (const root of ['.', 'release/UNIVERSAL_SpineViewer']) { const html = fs.readFileSync(root + '/viewer/spine.html', 'utf8'); + const css = fs.readFileSync(root + '/viewer/spine.css', 'utf8'); const script = html.match(/