diff --git a/release/UNIVERSAL_SpineViewer/viewer/spine.html b/release/UNIVERSAL_SpineViewer/viewer/spine.html index 055af0c..31e2bd8 100644 --- a/release/UNIVERSAL_SpineViewer/viewer/spine.html +++ b/release/UNIVERSAL_SpineViewer/viewer/spine.html @@ -491,11 +491,11 @@ // baseFitScale 为自动适配用的基准缩放;panX/panY/zoom 为用户平移/缩放叠加。 // fitScale = baseFitScale * zoom;fitCx/fitCy 是骨骼包围盒中心(世界坐标)。 let fitScale = 1, fitCx = 0, fitCy = 0; - function captureStableFitBounds() { - const bounds = getBounds(skeleton); + function captureStableFitBounds(bounds = getBounds(skeleton)) { stableFitBounds = bounds && bounds.width > 0 && bounds.height > 0 ? { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height } : null; + return stableFitBounds !== null; } function fitToCanvas() { @@ -504,7 +504,7 @@ const bounds = stableFitBounds; const cw = canvas.clientWidth, ch = canvas.clientHeight; if (!bounds || bounds.width <= 0 || bounds.height <= 0) { - baseFitScale = 1; baseFitCx = 0; baseFitCy = 0; fitScale = 1; fitCx = 0; fitCy = 0; return; + baseFitScale = 1; baseFitCx = 0; baseFitCy = 0; fitScale = zoom; fitCx = 0; fitCy = 0; return; } const pad = 0.1; const sx = (cw * (1 - pad * 2)) / bounds.width; @@ -1440,7 +1440,7 @@ } } - function setAnimationByName(name, index) { + async function setAnimationByName(name, index) { if (!state) return; const anim = findAnimByName(name); if (!anim) return; @@ -1452,6 +1452,11 @@ state.update(0); state.apply(skeleton); updateWorld(skeleton); + if (!stableFitBounds) { + const geometryBounds = getBounds(skeleton); + const bounds = await measureVisibleBounds(skeleton, geometryBounds, 1, () => {}) || geometryBounds; + if (currentAnim === resolvedIndex && captureStableFitBounds(bounds)) fitToCanvas(); + } draw(); } diff --git a/tests/setup-pose-zoom.test.js b/tests/setup-pose-zoom.test.js index 1f1e9fe..77d63ae 100644 --- a/tests/setup-pose-zoom.test.js +++ b/tests/setup-pose-zoom.test.js @@ -31,9 +31,11 @@ for (const root of roots) { const fit = functionBody(script, 'fitToCanvas', 'draw'); assert.match(fit, /const bounds = stableFitBounds;/); assert.doesNotMatch(fit, /getBounds\(/, 'wheel/resize fitting must not sample an animated frame'); + assert.match(fit, /fitScale = zoom;/, 'wheel zoom must still work before a drawable stable bound exists'); const capture = functionBody(script, 'captureStableFitBounds', 'fitToCanvas'); assert.match(capture, /getBounds\(skeleton\)/); + assert.match(script, /async function setAnimationByName[\s\S]*if \(!stableFitBounds\)[\s\S]*measureVisibleBounds\(skeleton, geometryBounds, 1[\s\S]*captureStableFitBounds\(bounds\)/); assert.match(css, /#status \{\s*position: absolute; left: 50%;[^}]*transform: translateX\(-50%\)/, 'status notice must be horizontally centered'); const manifest = JSON.parse(fs.readFileSync(path.join(root, 'manifest.json'), 'utf8')); diff --git a/viewer/spine.html b/viewer/spine.html index 055af0c..31e2bd8 100644 --- a/viewer/spine.html +++ b/viewer/spine.html @@ -491,11 +491,11 @@ // baseFitScale 为自动适配用的基准缩放;panX/panY/zoom 为用户平移/缩放叠加。 // fitScale = baseFitScale * zoom;fitCx/fitCy 是骨骼包围盒中心(世界坐标)。 let fitScale = 1, fitCx = 0, fitCy = 0; - function captureStableFitBounds() { - const bounds = getBounds(skeleton); + function captureStableFitBounds(bounds = getBounds(skeleton)) { stableFitBounds = bounds && bounds.width > 0 && bounds.height > 0 ? { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height } : null; + return stableFitBounds !== null; } function fitToCanvas() { @@ -504,7 +504,7 @@ const bounds = stableFitBounds; const cw = canvas.clientWidth, ch = canvas.clientHeight; if (!bounds || bounds.width <= 0 || bounds.height <= 0) { - baseFitScale = 1; baseFitCx = 0; baseFitCy = 0; fitScale = 1; fitCx = 0; fitCy = 0; return; + baseFitScale = 1; baseFitCx = 0; baseFitCy = 0; fitScale = zoom; fitCx = 0; fitCy = 0; return; } const pad = 0.1; const sx = (cw * (1 - pad * 2)) / bounds.width; @@ -1440,7 +1440,7 @@ } } - function setAnimationByName(name, index) { + async function setAnimationByName(name, index) { if (!state) return; const anim = findAnimByName(name); if (!anim) return; @@ -1452,6 +1452,11 @@ state.update(0); state.apply(skeleton); updateWorld(skeleton); + if (!stableFitBounds) { + const geometryBounds = getBounds(skeleton); + const bounds = await measureVisibleBounds(skeleton, geometryBounds, 1, () => {}) || geometryBounds; + if (currentAnim === resolvedIndex && captureStableFitBounds(bounds)) fitToCanvas(); + } draw(); }