AstroBox 壁纸生成器

编辑器状态、控件与导出

模板状态、图片导入、控件显隐、错误状态、导出条件与输出命名。

页面状态模型

编辑器维护以下主要状态:

  • 解析后的模板数组;
  • 当前模板索引和是否手动选择过模板;
  • 每个模板 ID 对应的一份编辑状态;
  • 唯一用户图片与其 object URL;
  • 当前模板的素材和蒙版资源表;
  • 配置、资源、渲染三类错误;
  • 配置加载、资源加载和应用进度状态。

每个模板的编辑状态结构:

{
  transform: { x, y, scale, rotation },
  layers: {
    [layerId]: {
      opacity,
      blur,
      backdropBlur,
      blendMode,
      amount,
      color,
      recolor,
      text?: {
        content, x, y, width, height,
        font, fontSize, fontWeight, color,
        letterSpacing, lineHeight, align, verticalAlign,
        fontVariations
      },
      glass?: {
        visible,
        transform: { x, y, scaleX, scaleY, rotation },
        geometry,
        material
      }
    }
  }
}

text.fontVariations 保存除 wght 外所有可变字体轴的当前值;glass 状态整体保存几何与材质,编辑器直接修改其子字段。

初始值

初次加载模板时:

  • 共享变换使用模板的 scale/rotation 默认值,位置为 0;
  • 有用户图片时立即执行动态 cover 和边界约束;
  • 每个图层属性使用解析后的默认值;
  • 换色组使用各自 default
  • text 图层使用默认内容(按 maxLength 截断)、默认字号/字重/字距/行高/对齐,fontVariations 使用选中字体各轴默认值(不含 wght);
  • glass 图层使用解析后的几何与材质默认值。

配置重新加载与状态合并

配置重新加载后,以新的模板声明为权威,同时尽量保留同模板 ID 的用户状态:

  • 数值重新限制到新的控制范围;
  • transform 按新模板和当前图片重新约束;
  • 已删除图层不再保留;
  • 新图层使用新默认值;
  • 不再允许的混合模式回退到新默认值;
  • 不再允许的整体颜色和换色值回退到新默认值;
  • text 内容重新按新 maxLength 截断,字号/字重/字距/行高/对齐/文本框重新限制到新范围,字体选项被删除时回退到新默认字体,可变轴值按新字体的 min/max 重设;
  • 新出现或已删除的可变字体轴会同步到新字体的轴集合。

因此模板和图层 ID 应保持稳定,配置作者不应在无迁移需要时随意改变 ID。

图片导入与替换

  • 文件输入接受 image/*
  • 每次只读取一个文件。
  • 文件通过 object URL 加载成图片对象。
  • 成功替换后,所有模板的 transform 重置为基于新图片的初始变换。
  • 每层效果通过状态合并保留。
  • 旧 object URL 在不再使用时释放。
  • 文件输入值每次处理后清空,允许再次选择同一个文件。

如果当前模板没有 wallpaper 图层,界面不显示选择图片按钮,也不要求输入图片。

模板选择

下拉项显示:

deviceKey · watchface.name

当前设备先通过 AstroBox 官方资源设备表解析成资源设备 ID;断连时如果只剩已知的云端 IoT 型号,会通过兼容表转换为同一 ID。模板中的已知旧代号也会在匹配前转换。精确匹配成功时自动选择匹配项。用户手动选择任一模板后,本次页面状态不再因设备信息的后续更新自动切走。

识别过程中显示“正在识别当前设备”。资源设备表无法识别时和配置中没有对应模板时,会分别显示对应原因,两种情况都只支持导出。

连接状态优先来自全局当前设备。如果路由指向另一台设备,则以后端的已连接设备列表为准。设备地址比较忽略大小写和首尾空白,不会因列表项缺少重复的 connected 字段把真实已连接设备误判为断连。

共享取景控件

只有同时满足以下条件时显示“壁纸取景”:

  • 已导入图片;
  • 当前模板至少有一个 wallpaper 图层;
  • 当前模板状态可用。

提供:

  • 缩放滑块、数值输入、重置;
  • 旋转滑块、数值输入、重置;
  • 居中;
  • 适配;
  • 全部重置。

缩放数值以百分比显示,旋转以度显示。缩放控件的最小值和最大值会按当前角度动态更新。

舞台同时支持鼠标/单指拖动和双指缩放,详见取景变换、手势与边界算法

图层控件显隐

只有图层至少存在一个可调属性时,才显示该图层控制区。可调属性包括:

  • opacity
  • blur
  • backdropBlur
  • amount
  • blendMode
  • asset 整体 color
  • 任一可调 recolor 组;
  • text 层的 contentfontfontSizefontWeightcolorletterSpacinglineHeightalignverticalAlign 或任一 textBox 维度的可调属性;
  • glass 层始终显示控制区。

固定值不显示控件,但始终参与渲染。

数值控件

每个可调数值同时提供:

  • range 滑块;
  • number 输入框;
  • 单项重置按钮。

显示单位:

属性显示
opacity百分比
blurpx
backdropBlurpx
amount百分比,负值代表暗色覆盖
scale百分比
rotation
fontSizepx
letterSpacingpx

输入的非有限数值被忽略,合法数值会限制到控制范围。

离散控件

  • 混合模式下拉框只列出配置的 options,使用中文标签显示已知模式。
  • 整体素材颜色显示配置 options 的色块。
  • 每个可调换色组显示自己 options 的色块,标题使用组 name,缺失时使用组 id
  • text 层的字体下拉框列出 font.optionsname
  • text 层的文字颜色使用 text.color 的色块。
  • glass 层提供几何形状切换、各材质参数滑块、着色取色器,以及高光/阴影混合模式下拉框。

不提供的图层操作

编辑器没有图层新增、删除、复制、排序、素材替换或蒙版编辑。图层结构始终来自 JSON。

错误分层

配置错误

包括配置请求失败、JSON 语法错误、字段校验错误和缺少安装关联。配置错误会替代整个编辑器内容显示。

资源错误

当前模板任一 asset 或 mask 加载失败时显示资源错误,并阻止导出和应用。

渲染错误

实时舞台捕获统一渲染器异常并显示错误。渲染错误存在时阻止导出和应用;下次成功渲染会清除旧错误。

导出就绪条件

必须满足:

  • 当前模板存在;
  • 当前模板编辑状态存在;
  • 当前没有资源加载;
  • 没有资源错误;
  • 没有渲染错误;
  • 如果模板有 wallpaper 图层,已经导入用户图片。

配置加载过程或配置错误本身不会进入具有模板的可导出页面状态。

PNG 导出

导出调用统一渲染器创建与模板 Canvas 等尺寸的 Canvas,并通过 toBlob("image/png") 生成 PNG。

文件名:

{watchface.name 或 template.id}-wallpaper.png

浏览器下载链接点击后立即移除,对应 object URL 在 1 秒后释放。

预览、导出和应用一致性

舞台、导出和应用都传入相同的模板、编辑状态、用户图片和已加载资源,并调用同一个 drawWallpaper。预览中的任何差异都应作为渲染问题处理,而不是分别调整导出或应用代码。

大纲