缩略图插件
This commit is contained in:
1 parent
065b6466f1
commit
96a0db09d8
9 files changed
+984
-59
No files matched your search
@@ -0,0 +1,102 @@
|
||||
// 动态缩略图的档位配置。数值与主插件 js/animated-profiles.js 保持一致,
|
||||
// 后台插件没有参数面板,所以这里只当默认值来源,改这里就等于改后台插件的默认渲染参数。
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 每项是 [细节分辨率, 帧率, 编码质量],作为搜索路径上的锚点:
|
||||
* - 细节分辨率决定画面保留多少细节(输出画布始终是画布边长,小分辨率会被放大填满);
|
||||
* - 帧率决定每秒采样多少帧,只改采样密度不改时间轴,每档的帧延迟会自动重算;
|
||||
* - 编码质量是 0–1 的 WebP 质量。
|
||||
*
|
||||
* 数组顺序是从最高画质到最低画质的搜索方向。运行时会在相邻锚点之间插值成一条更密的
|
||||
* 路径,再按目标体积在路径上定位,取命中位置附近若干档完整生成,挑出符合体积的那一档。
|
||||
*/
|
||||
const ANIMATED_PRIORITIES = {
|
||||
// 分辨率优先:先把帧率压到底,再降分辨率。
|
||||
resolution: [
|
||||
[400, 18, 1.0], [400, 15, 0.8], [400, 12, 0.6], [400, 9, 0.4], [400, 6, 0.2],
|
||||
[200, 18, 1.0], [200, 15, 0.8], [200, 12, 0.6], [200, 9, 0.4], [200, 6, 0.2],
|
||||
],
|
||||
// 帧率优先:先把分辨率压到底,再降帧率。
|
||||
framerate: [
|
||||
[400, 18, 1.0], [400, 18, 0.5], [350, 15, 1.0], [350, 15, 0.5], [300, 9, 1.0],
|
||||
[300, 9, 0.5], [250, 6, 1.0], [250, 6, 0.5], [200, 9, 1.0], [200, 6, 0.5],
|
||||
],
|
||||
// 交替进行:分辨率和帧率交替下降。
|
||||
balanced: [
|
||||
[400, 15, 0.8], [400, 12, 0.6], [400, 9, 0.4], [400, 6, 0.2], [200, 15, 0.8],
|
||||
[200, 12, 0.6], [200, 9, 0.4], [200, 6, 0.2], [100, 3, 0.5], [100, 3, 0.1],
|
||||
],
|
||||
};
|
||||
|
||||
const DEFAULT_PRIORITY = 'resolution';
|
||||
|
||||
/** 可选的输出画布边长(正方形)。后台插件默认用 200。 */
|
||||
const ANIMATED_OUTPUT_SIZES = [200, 400];
|
||||
const DEFAULT_OUTPUT_SIZE = 200;
|
||||
|
||||
/** 目标体积的默认建议值:每秒动画配 25 KB,夹在 25–100 KB 之间。 */
|
||||
const SIZE_PER_SECOND = 25;
|
||||
const SIZE_SUGGESTION_RANGE = [25, 100];
|
||||
|
||||
// 相邻锚点之间插多少个采样点。数值越大,越容易压在目标体积附近,粗估阶段耗时略增。
|
||||
const PATH_STEPS_PER_SEGMENT = 6;
|
||||
|
||||
function suggestSize(seconds) {
|
||||
const [min, max] = SIZE_SUGGESTION_RANGE;
|
||||
return Math.max(min, Math.min(max, Math.round((Number(seconds) || 0) * SIZE_PER_SECOND)));
|
||||
}
|
||||
|
||||
function animatedProfiles(priority, outputSize) {
|
||||
const limit = outputSize || DEFAULT_OUTPUT_SIZE;
|
||||
const plan = ANIMATED_PRIORITIES[priority] || ANIMATED_PRIORITIES[DEFAULT_PRIORITY];
|
||||
return plan.map(([size, fps, quality]) => ({
|
||||
size: Math.min(size, limit),
|
||||
fps,
|
||||
quality,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 把锚点路径插值成更密的采样路径,供按目标体积定位使用。
|
||||
* 分辨率取整、帧率至少 1、质量保留两位小数;首尾锚点原样保留。
|
||||
*/
|
||||
function expandAnimatedPath(priority, outputSize) {
|
||||
const limit = outputSize || DEFAULT_OUTPUT_SIZE;
|
||||
const anchors = ANIMATED_PRIORITIES[priority] || ANIMATED_PRIORITIES[DEFAULT_PRIORITY];
|
||||
const lerp = (from, to, t) => from + (to - from) * t;
|
||||
const path = [];
|
||||
for (let index = 0; index < anchors.length - 1; index++) {
|
||||
const [fromSize, fromFps, fromQuality] = anchors[index];
|
||||
const [toSize, toFps, toQuality] = anchors[index + 1];
|
||||
for (let step = 0; step < PATH_STEPS_PER_SEGMENT; step++) {
|
||||
const t = step / PATH_STEPS_PER_SEGMENT;
|
||||
path.push({
|
||||
size: Math.min(limit, Math.round(lerp(fromSize, toSize, t))),
|
||||
fps: Math.max(1, Math.round(lerp(fromFps, toFps, t))),
|
||||
quality: Math.round(lerp(fromQuality, toQuality, t) * 100) / 100,
|
||||
});
|
||||
}
|
||||
}
|
||||
const [size, fps, quality] = anchors[anchors.length - 1];
|
||||
path.push({ size: Math.min(size, limit), fps, quality });
|
||||
return path;
|
||||
}
|
||||
|
||||
const api = {
|
||||
ANIMATED_PRIORITIES,
|
||||
DEFAULT_PRIORITY,
|
||||
ANIMATED_OUTPUT_SIZES,
|
||||
DEFAULT_OUTPUT_SIZE,
|
||||
SIZE_PER_SECOND,
|
||||
SIZE_SUGGESTION_RANGE,
|
||||
PATH_STEPS_PER_SEGMENT,
|
||||
animatedProfiles,
|
||||
expandAnimatedPath,
|
||||
suggestSize,
|
||||
};
|
||||
if (typeof window !== 'undefined') window.SpineAnimatedProfiles = api;
|
||||
else if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user