新增3.4版本支持
This commit is contained in:
1 parent
4c5033d2f9
commit
9635428f78
21 files changed
+5425
-51
No files matched your search
+58
-10
@@ -137,7 +137,7 @@
|
||||
<span id="speedVal">1.0x</span>
|
||||
</label>
|
||||
<label title="用于以 Premultiplied Alpha(PMA)方式导出的贴图">
|
||||
<input type="checkbox" id="pmaCheck"> 预乘
|
||||
<input type="checkbox" id="pmaCheck" disabled> 预乘
|
||||
</label>
|
||||
<div class="spacer"></div>
|
||||
<button id="thumbBtn" title="使用当前帧生成 400×400 缩略图">🖼 生成缩略图</button>
|
||||
@@ -237,9 +237,11 @@
|
||||
let skeleton = null, state = null, stateData = null, skeletonData = null;
|
||||
let renderer = null;
|
||||
let activeRuntimeDir = null;
|
||||
let activeVersion = null;
|
||||
let currentAnim = 0, currentSkin = null;
|
||||
let playing = true, loop = true, speed = 1;
|
||||
let premultipliedAlpha = false;
|
||||
let previewSettings = null;
|
||||
let timeKeeper = null, lastTime = 0, running = false;
|
||||
let seeking = false; // 用户正在拖动时间轴
|
||||
let duration = 0; // 当前动画时长
|
||||
@@ -414,6 +416,13 @@
|
||||
}
|
||||
|
||||
async function setEagleCustomThumbnail(itemId, thumbnailPath) {
|
||||
if (/\.(atlas\.txt|skel\.bytes)$/i.test(filePath)) {
|
||||
const response = await fetch('http://localhost:41595/api/item/info?id=' + encodeURIComponent(itemId));
|
||||
const info = await response.json();
|
||||
if (!response.ok || info.status !== 'success' || info.data.name + '.' + info.data.ext !== path.basename(filePath))
|
||||
throw new Error('Eagle 尚未同步复合后缀名称,请重新打开素材库');
|
||||
if (info.data.ext === 'txt') throw new Error('此条目仍按 TXT 登记,请在 Eagle 重新生成缩略图并重新打开素材库');
|
||||
}
|
||||
const response = await fetch('http://localhost:41595/api/item/setCustomThumbnail', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -457,16 +466,18 @@
|
||||
octx.translate(size / 2, size / 2);
|
||||
octx.scale(scale, -scale);
|
||||
octx.translate(-cx, -cy);
|
||||
let offRenderer = renderer;
|
||||
let offRenderer = null;
|
||||
try {
|
||||
offRenderer = clippingRenderer.createRenderer(spine, octx, activeRuntimeDir);
|
||||
} catch (e) { /* 沿用原 renderer,可能无法正确绑定离屏 ctx */ }
|
||||
offRenderer.premultipliedAlpha = premultipliedAlpha;
|
||||
if (offRenderer && 'triangleRendering' in offRenderer) offRenderer.triangleRendering = true;
|
||||
offRenderer.draw(skeleton);
|
||||
octx.restore();
|
||||
offRenderer.premultipliedAlpha = premultipliedAlpha;
|
||||
offRenderer.draw(skeleton);
|
||||
} finally {
|
||||
if (offRenderer && offRenderer.dispose) offRenderer.dispose();
|
||||
octx.restore();
|
||||
}
|
||||
}
|
||||
await drawSpineBadge(octx, size, size);
|
||||
require('../js/spine-badge.js').drawVersion(octx, size, size, activeVersion);
|
||||
|
||||
// Eagle 的 *_thumbnail.png 通常实际保存 WebP;优先使用 WebP 显著减小体积。
|
||||
let out = off.toDataURL('image/webp', 0.9);
|
||||
@@ -487,7 +498,7 @@
|
||||
// 兼容旧版 Eagle:保留原先的磁盘写入方案,但明确提示可能需要手动刷新列表。
|
||||
try {
|
||||
fs.writeFileSync(outPath, buf);
|
||||
setStatus('缩略图已写入;当前 Eagle 版本未能自动刷新,请按 Cmd/Ctrl+R', 6000);
|
||||
setStatus('缩略图已写入;自动刷新失败:' + ((e && e.message) || String(e)), 6000);
|
||||
log('Eagle API 更新失败,已回退写入: ' + ((e && e.message) || String(e)));
|
||||
} catch (e2) {
|
||||
setStatus('生成缩略图失败: ' + ((e2 && e2.message) || String(e2)), 6000);
|
||||
@@ -798,6 +809,27 @@
|
||||
|
||||
// ---------- 加载资源并与运行时交互 ----------
|
||||
async function loadAssets() {
|
||||
if (!utils.extOf(path.basename(filePath))) {
|
||||
// Registering txt/bytes is required by Eagle's extension dispatch.
|
||||
// Ordinary text remains readable and never starts Spine discovery.
|
||||
document.getElementById('toolbar').style.display = 'none';
|
||||
document.getElementById('timelineBar').style.display = 'none';
|
||||
let message = '此文件不是 .atlas.txt 或 .skel.bytes 格式的 Spine 资源。';
|
||||
if (/\.txt$/i.test(filePath)) {
|
||||
const fd = fs.openSync(filePath, 'r');
|
||||
try {
|
||||
const buffer = Buffer.alloc(256 * 1024);
|
||||
const count = fs.readSync(fd, buffer, 0, buffer.length, 0);
|
||||
message = buffer.subarray(0, count).toString('utf8');
|
||||
if (fs.fstatSync(fd).size > count) message += '\n\n(仅显示前 256 KB)';
|
||||
} finally { fs.closeSync(fd); }
|
||||
}
|
||||
showError(path.basename(filePath), message);
|
||||
const box = overlay.querySelector('.box');
|
||||
box.style.cssText = 'max-width:none;width:100%;height:100%;overflow:auto;text-align:left';
|
||||
document.getElementById('errorTitle').style.color = 'var(--text)';
|
||||
return;
|
||||
}
|
||||
// Eagle 只能按 .json 扩展名把文件交给格式扩展。查看器必须再次检查内容,
|
||||
// 防止普通 JSON 被拿去匹配库中其他 Spine 骨骼资源。
|
||||
if (utils.extOf(path.basename(filePath)) === '.json' && utils.classifySkeletonJson(filePath) !== 'spine') {
|
||||
@@ -814,12 +846,21 @@
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const settings = require('../js/spine-preview-settings.js');
|
||||
const userData = await settings.resolveDataPath(typeof eagle !== 'undefined' ? eagle.app : null);
|
||||
previewSettings = settings.createStore(userData, filePath, urlParams.get('id'));
|
||||
const saved = previewSettings.read();
|
||||
if (saved !== undefined) premultipliedAlpha = saved;
|
||||
document.getElementById('pmaCheck').checked = premultipliedAlpha;
|
||||
} catch (e) { log('读取预乘设置失败: ' + e.message); }
|
||||
|
||||
// 检测版本并选择运行时
|
||||
const version = utils.detectVersion(assets.skeletonPath);
|
||||
activeVersion = version;
|
||||
const runtimeDir = utils.chooseRuntimeDir(version, assets.isBinary);
|
||||
if (!runtimeDir) {
|
||||
showError('版本不支持', '无法识别的 Spine 版本:' + (version || '未知') + '\n支持的版本:3.6 / 3.7 / 3.8 / 4.0 / 4.1 / 4.2 / 4.3');
|
||||
showError('版本不支持', '无法识别的 Spine 版本:' + (version || '未知') + '\n支持的版本:3.4 / 3.6 / 3.7 / 3.8 / 4.0 / 4.1 / 4.2 / 4.3');
|
||||
return;
|
||||
}
|
||||
activeRuntimeDir = runtimeDir;
|
||||
@@ -877,6 +918,7 @@
|
||||
|
||||
renderer = clippingRenderer.createRenderer(spine, ctx, runtimeDir);
|
||||
renderer.premultipliedAlpha = premultipliedAlpha;
|
||||
document.getElementById('pmaCheck').disabled = false;
|
||||
|
||||
fillAnimations();
|
||||
fillSkins();
|
||||
@@ -945,7 +987,13 @@
|
||||
premultipliedAlpha = e.target.checked;
|
||||
if (renderer) renderer.premultipliedAlpha = premultipliedAlpha;
|
||||
draw();
|
||||
setStatus(premultipliedAlpha ? '已开启预乘 Alpha 渲染' : '已关闭预乘 Alpha 渲染', 1800);
|
||||
try {
|
||||
if (!previewSettings) throw new Error('素材设置尚未就绪');
|
||||
previewSettings.write(premultipliedAlpha);
|
||||
setStatus((premultipliedAlpha ? '预乘已开启' : '预乘已关闭') + ' · 已记住此素材的设置', 1800);
|
||||
} catch (err) {
|
||||
setStatus('预乘已切换,但设置保存失败:' + err.message, 5000);
|
||||
}
|
||||
});
|
||||
document.getElementById('reloadBtn').addEventListener('click', () => {
|
||||
window.location.reload();
|
||||
|
||||
Reference in new issue
Block a user