new-sakura:花枝朝向与分叉修正,素材包一键导出上传到对象存储

- gen_new_sakura.py:花朵加各自旋转角(原来一律朝上,像贴纸);分叉从垂直下垂改成向右上斜;
  新增 --asset-base / --out —— 填前缀就引用对象存储位图,留空仍是内联 SVG。
- export_assets.py(新增):从 gen_new_sakura 的同一份 SVG 出图(直接 import,不另画一套),
  用无头 Chrome 透明底渲染后转 WebP;--upload 一次传 CSS + 5 张 webp + 生成的 INSTALL.txt,
  公开地址按 data/r2.txt 的 image_bucket_cdn_host(与 app.ini 同名)拼。
- serve.upload_files 支持逐文件 cacheControl。原来硬编码 immutable,而 INSTALL.txt 与镜像
  CSS 的键不带版本 —— 重新生成后浏览器/CDN 一年内都还是旧内容。现在位图 1 天、CSS 5 分钟、
  README 60 秒;selftest 补一条覆盖断言。
- 文档更正两处事实:①自定义域 r2.337845818.best 实测可匿名读(S3 endpoint 仍 403);
  ②服务端有 gzip,内联版 51KB 实际传输 3.9KB,比位图版(2.7KB + 5 图 48.9KB)小得多,
  所以「换位图省 CSS 体积」不成立,位图只在素材要复用到非 CSS 场景时才有意义。
  另记:曾以 immutable 传过的键必须去控制台清缓存,重传不会让边缘失效。
This commit is contained in:
tamakyi
2026-10-22 11:06:13 +08:00
parent 48a1938fb2
commit 5c0de805c6
8 changed files with 372 additions and 20 deletions
+38 -12
View File
@@ -56,15 +56,16 @@ def branch(wood, light, deep, heart, flip=False):
fl = ' transform="translate(420,0) scale(-1,1)"' if flip else ""
# 花要比枝小得多、数量多几朵。早先 5 朵 scale≈1 的花配一根细枝,缩到 300px
# 显示时是一串糖葫芦;真实樱花枝是"小碎花沿枝密布",所以花收到 0.42~0.62。
# 第四项是旋转角:一朵朵都朝正上方会像贴纸糊上去的,真实花枝上朝向各异
blossoms = [
(302, 74, 0.62), (256, 110, 0.55), (214, 138, 0.60), (172, 176, 0.58),
(352, 42, 0.50), (140, 214, 0.46), (116, 246, 0.40), (326, 128, 0.42),
(302, 74, 0.62, 14), (256, 110, 0.55, -22), (214, 138, 0.60, 34), (172, 176, 0.58, -6),
(352, 42, 0.50, 48), (140, 214, 0.46, 20), (116, 246, 0.40, -32), (326, 128, 0.42, 28),
]
petals = [(272, 158, 22), (318, 150, -18), (196, 92, 40), (150, 196, -26)]
body = []
for x, y, s in blossoms:
for x, y, s, rot in blossoms:
body.append(
f'<g transform="translate({x} {y}) scale({s}) translate(-50 -50)">'
f'<g transform="translate({x} {y}) rotate({rot}) scale({s}) translate(-50 -50)">'
'<g fill="%s">' % light
+ "".join(f'<ellipse cx="50" cy="27" rx="14" ry="21" transform="rotate({a} 50 50)"/>'
for a in (0, 72, 144, 216, 288))
@@ -81,7 +82,7 @@ def branch(wood, light, deep, heart, flip=False):
f'<g{fl}>'
f'<path d="M2 318C74 276 112 212 176 184c62-28 102-72 146-118 26-28 52-46 82-58" '
f'fill="none" stroke="{wood}" stroke-width="4.2" stroke-linecap="round" opacity=".62"/>'
f'<path d="M176 184c-12-34-20-60-16-94M262 126c16-28 32-48 46-58" '
f'<path d="M176 184c-4-30 4-56 16-78M262 126c16-28 32-48 46-58" '
f'fill="none" stroke="{wood}" stroke-width="2.8" stroke-linecap="round" opacity=".5"/>'
+ "".join(body) + "</g></svg>"
)
@@ -124,7 +125,22 @@ DARK = dict(
)
def uris(p):
def uris(p, asset_base=""):
"""asset_base 非空 -> 引用对象存储上的位图(键名与 export_assets.py 的产物一致)。
内联 SVG 是默认:gzip 后约 3.9KB、零额外请求。位图版 CSS 只有 10.6KB,但要多个
5 个请求共 ~49KB,总传输反而更大 —— 它存在的理由是素材能被非 CSS 场景复用。
换之前先确认桶已对公网可读(自定义域或 r2.dev),否则主题会整片 403。
"""
if asset_base:
b = asset_base.rstrip("/")
return {
"blossom_l": 'url("%s/blossom.webp")' % b,
"petal_l": 'url("%s/petal.webp")' % b,
"branch_l": 'url("%s/branch-left.webp")' % b,
"branch_r": 'url("%s/branch-right.webp")' % b,
"pattern": 'url("%s/pattern.webp")' % b,
}
return {
"blossom_l": uri(blossom(p["light"], p["deep"], p["heart"])),
"petal_l": uri(petal(p["light"])),
@@ -214,9 +230,9 @@ def scope_block(prefix, layers, indent=" "):
)
def build():
u = uris(LIGHT)
d = uris(DARK)
def build(asset_base=""):
u = uris(LIGHT, asset_base)
d = uris(DARK, asset_base)
vars_light = "\n".join([
f" --sk-blossom: {u['blossom_l']};",
@@ -368,7 +384,17 @@ html[data-theme="dark"][data-theme-style="new-sakura"] .uk-alert-primary {
if __name__ == "__main__":
css = build()
with open(OUT, "w", encoding="utf-8", newline="\n") as f:
import argparse
ap = argparse.ArgumentParser(description="生成 new-sakura 主题 CSS")
ap.add_argument("--asset-base", default="",
help="引用对象存储位图的前缀,如 https://r2.example.com/themes/new-sakura;"
"留空 = 内联 SVG(默认)")
ap.add_argument("--out", default=OUT, help="输出路径")
args = ap.parse_args()
css = build(args.asset_base)
with open(args.out, "w", encoding="utf-8", newline="\n") as f:
f.write(css)
print("已生成 %s(%.1f KB)" % (OUT, len(css.encode()) / 1024))
print("已生成 %s(%.1f KB)%s" % (args.out, len(css.encode()) / 1024,
" [位图版]" if args.asset_base else " [内联 SVG]"))