#!/usr/bin/env python3 """生成善缘丹阳小程序底部导航 SVG 与透明 PNG。""" from pathlib import Path import math from PIL import Image, ImageDraw ROOT = Path(__file__).resolve().parents[1] / "miniapp" / "assets" / "tabbar" COLORS = {"normal": "#999999", "active": "#E96B6B"} TITLES = { "home": "爱心小屋", "discover": "双人缘分", "message": "爱心对话", "activity": "公益日历", "mine": "个人守护", } SVG_BODY = { "home": """""", "discover": """""", "message": """""", "activity": """""", "mine": """""", } def svg(name: str, active: bool) -> str: color = COLORS["active" if active else "normal"] fill = color if active else "none" return f""" {TITLES[name]} {SVG_BODY[name]} """ def heart_points(cx: float, cy: float, scale: float): points = [] for degree in range(0, 361, 3): angle = math.radians(degree) x = scale * 16 * math.sin(angle) ** 3 y = -scale * (13 * math.cos(angle) - 5 * math.cos(2 * angle) - 2 * math.cos(3 * angle) - math.cos(4 * angle)) points.append((cx + x, cy + y)) return points def render(name: str, size: int, active: bool) -> Image.Image: aa = 4 factor = size / 81 * aa image = Image.new("RGBA", (size * aa, size * aa), (255, 255, 255, 0)) draw = ImageDraw.Draw(image) color = COLORS["active" if active else "normal"] width = max(1, round(4 * factor)) p = lambda value: round(value * factor) def line(points): draw.line([(p(x), p(y)) for x, y in points], fill=color, width=width, joint="curve") def ellipse(box, fill=None): draw.ellipse(tuple(p(v) for v in box), outline=color, fill=fill, width=width) def rounded(box, radius): draw.rounded_rectangle(tuple(p(v) for v in box), radius=p(radius), outline=color, width=width) def heart(cx, cy, scale): points = [(p(x), p(y)) for x, y in heart_points(cx, cy, scale)] if active: draw.polygon(points, fill=color) else: draw.line(points, fill=color, width=width, joint="curve") if name == "home": line([(18, 38), (40.5, 19), (63, 38)]) rounded((22, 35, 59, 63), 7) line([(31, 63), (31, 49), (50, 49), (50, 63)]) heart(40.5, 47.5, .32) elif name == "discover": ellipse((19, 22, 33, 36)) ellipse((48, 22, 62, 36)) draw.arc((p(14), p(39), p(43), p(67)), 190, 315, fill=color, width=width) draw.arc((p(38), p(39), p(67), p(67)), 225, 350, fill=color, width=width) heart(40.5, 48, .48) elif name == "message": rounded((14, 17, 67, 58), 12) line([(27, 58), (15, 66), (18, 53)]) heart(40.5, 37, .52) elif name == "activity": rounded((17, 20, 64, 64), 8) line([(17, 33), (64, 33)]) line([(28, 15), (28, 25)]) line([(53, 15), (53, 25)]) heart(40.5, 48, .45) else: ellipse((32.5, 19, 48.5, 35)) draw.arc((p(20), p(39), p(61), p(78)), 185, 355, fill=color, width=width) heart(40.5, 52, .36) return image.resize((size, size), Image.Resampling.LANCZOS) def main(): (ROOT / "svg").mkdir(parents=True, exist_ok=True) for name in TITLES: for active in (False, True): state = "-active" if active else "" (ROOT / "svg" / f"{name}{state}.svg").write_text(svg(name, active), encoding="utf-8") for size, suffix in ((81, ""), (162, "_2x"), (243, "_3x")): render(name, size, active).save(ROOT / f"{name}{state}{suffix}.png") print("已生成 10 个 SVG 和 30 个 PNG 导航图标") if __name__ == "__main__": main()