diff --git a/artifacts/tabbar-icons-preview.png b/artifacts/tabbar-icons-preview.png new file mode 100644 index 0000000..fec4a8e Binary files /dev/null and b/artifacts/tabbar-icons-preview.png differ diff --git a/miniapp/assets/tabbar/DESIGN.md b/miniapp/assets/tabbar/DESIGN.md index 03f10e9..9b926f9 100644 --- a/miniapp/assets/tabbar/DESIGN.md +++ b/miniapp/assets/tabbar/DESIGN.md @@ -9,10 +9,10 @@ | 属性 | 规范值 | |------|--------| | 图标尺寸 | 81 × 81 px (基础) | -| 安全区域 | 56 × 56 px (边距 12px) | -| 线宽 | 2px | +| 安全区域 | 53 × 53 px(最小边距 14px) | +| 线宽 | 4px(缩放到导航栏后约 1.3px) | | 圆角 | 端点圆角 + 转折圆角 | -| 填充 | 无(纯线性) | +| 填充 | 默认态线性;选中态填充核心爱心 | ## 颜色规范 @@ -25,28 +25,28 @@ ## 图标清单 ### 1. 首页 (home) -- **意象**: 温馨小屋 -- **元素**: 屋顶三角 + 房屋主体 + 门 + 烟囱 +- **意象**: 爱心小屋 +- **元素**: 圆角屋顶 + 房屋主体 + 门内爱心 - **文件**: `home.png` / `home-active.png` ### 2. 找对象 (discover) -- **意象**: 双心相连,象征缘分匹配 -- **元素**: 两个相交的爱心 +- **意象**: 双人缘分 +- **元素**: 两个人物轮廓 + 中央爱心 - **文件**: `discover.png` / `discover-active.png` ### 3. 消息 (message) -- **意象**: 对话沟通 -- **元素**: 圆角气泡 + 小三角 + 文字线条 +- **意象**: 真诚沟通 +- **元素**: 圆角气泡 + 爱心 - **文件**: `message.png` / `message-active.png` ### 4. 活动 (activity) -- **意象**: 日程安排 -- **元素**: 日历主体 + 挂环 + 日期标记点 +- **意象**: 公益相遇 +- **元素**: 日历主体 + 挂环 + 爱心 - **文件**: `activity.png` / `activity-active.png` ### 5. 我的 (mine) -- **意象**: 个人用户 -- **元素**: 圆形头部 + 半圆身体轮廓 +- **意象**: 个人守护 +- **元素**: 单人轮廓 + 胸前爱心 - **文件**: `mine.png` / `mine-active.png` ## 导出尺寸 diff --git a/miniapp/assets/tabbar/activity-active.png b/miniapp/assets/tabbar/activity-active.png index c3bac2c..9127cad 100644 Binary files a/miniapp/assets/tabbar/activity-active.png and b/miniapp/assets/tabbar/activity-active.png differ diff --git a/miniapp/assets/tabbar/activity-active_2x.png b/miniapp/assets/tabbar/activity-active_2x.png index 185ef1d..49eb0e8 100644 Binary files a/miniapp/assets/tabbar/activity-active_2x.png and b/miniapp/assets/tabbar/activity-active_2x.png differ diff --git a/miniapp/assets/tabbar/activity-active_3x.png b/miniapp/assets/tabbar/activity-active_3x.png index 2b552e3..eb7c1ee 100644 Binary files a/miniapp/assets/tabbar/activity-active_3x.png and b/miniapp/assets/tabbar/activity-active_3x.png differ diff --git a/miniapp/assets/tabbar/activity.png b/miniapp/assets/tabbar/activity.png index 6ec1367..39d86bb 100644 Binary files a/miniapp/assets/tabbar/activity.png and b/miniapp/assets/tabbar/activity.png differ diff --git a/miniapp/assets/tabbar/activity_2x.png b/miniapp/assets/tabbar/activity_2x.png index 1a770f5..7514e94 100644 Binary files a/miniapp/assets/tabbar/activity_2x.png and b/miniapp/assets/tabbar/activity_2x.png differ diff --git a/miniapp/assets/tabbar/activity_3x.png b/miniapp/assets/tabbar/activity_3x.png index 3a00d47..27c7590 100644 Binary files a/miniapp/assets/tabbar/activity_3x.png and b/miniapp/assets/tabbar/activity_3x.png differ diff --git a/miniapp/assets/tabbar/discover-active.png b/miniapp/assets/tabbar/discover-active.png index 12bd35d..7b0e80a 100644 Binary files a/miniapp/assets/tabbar/discover-active.png and b/miniapp/assets/tabbar/discover-active.png differ diff --git a/miniapp/assets/tabbar/discover-active_2x.png b/miniapp/assets/tabbar/discover-active_2x.png index 218a928..b80a3b9 100644 Binary files a/miniapp/assets/tabbar/discover-active_2x.png and b/miniapp/assets/tabbar/discover-active_2x.png differ diff --git a/miniapp/assets/tabbar/discover-active_3x.png b/miniapp/assets/tabbar/discover-active_3x.png index e71a11c..ba81ebe 100644 Binary files a/miniapp/assets/tabbar/discover-active_3x.png and b/miniapp/assets/tabbar/discover-active_3x.png differ diff --git a/miniapp/assets/tabbar/discover.png b/miniapp/assets/tabbar/discover.png index 4f4ff4d..2cc6665 100644 Binary files a/miniapp/assets/tabbar/discover.png and b/miniapp/assets/tabbar/discover.png differ diff --git a/miniapp/assets/tabbar/discover_2x.png b/miniapp/assets/tabbar/discover_2x.png index 230b1bf..4f282ad 100644 Binary files a/miniapp/assets/tabbar/discover_2x.png and b/miniapp/assets/tabbar/discover_2x.png differ diff --git a/miniapp/assets/tabbar/discover_3x.png b/miniapp/assets/tabbar/discover_3x.png index 7ac4977..16021f7 100644 Binary files a/miniapp/assets/tabbar/discover_3x.png and b/miniapp/assets/tabbar/discover_3x.png differ diff --git a/miniapp/assets/tabbar/home-active.png b/miniapp/assets/tabbar/home-active.png index b1a90c0..d03c392 100644 Binary files a/miniapp/assets/tabbar/home-active.png and b/miniapp/assets/tabbar/home-active.png differ diff --git a/miniapp/assets/tabbar/home-active_2x.png b/miniapp/assets/tabbar/home-active_2x.png index 0ee0660..adf3c74 100644 Binary files a/miniapp/assets/tabbar/home-active_2x.png and b/miniapp/assets/tabbar/home-active_2x.png differ diff --git a/miniapp/assets/tabbar/home-active_3x.png b/miniapp/assets/tabbar/home-active_3x.png index ef429c8..616c13f 100644 Binary files a/miniapp/assets/tabbar/home-active_3x.png and b/miniapp/assets/tabbar/home-active_3x.png differ diff --git a/miniapp/assets/tabbar/home.png b/miniapp/assets/tabbar/home.png index b677a50..855469a 100644 Binary files a/miniapp/assets/tabbar/home.png and b/miniapp/assets/tabbar/home.png differ diff --git a/miniapp/assets/tabbar/home_2x.png b/miniapp/assets/tabbar/home_2x.png index 0cf4617..60e3f53 100644 Binary files a/miniapp/assets/tabbar/home_2x.png and b/miniapp/assets/tabbar/home_2x.png differ diff --git a/miniapp/assets/tabbar/home_3x.png b/miniapp/assets/tabbar/home_3x.png index 1971aa0..424545e 100644 Binary files a/miniapp/assets/tabbar/home_3x.png and b/miniapp/assets/tabbar/home_3x.png differ diff --git a/miniapp/assets/tabbar/message-active.png b/miniapp/assets/tabbar/message-active.png index 28e1602..a47fb25 100644 Binary files a/miniapp/assets/tabbar/message-active.png and b/miniapp/assets/tabbar/message-active.png differ diff --git a/miniapp/assets/tabbar/message-active_2x.png b/miniapp/assets/tabbar/message-active_2x.png index 862e1f1..b999b97 100644 Binary files a/miniapp/assets/tabbar/message-active_2x.png and b/miniapp/assets/tabbar/message-active_2x.png differ diff --git a/miniapp/assets/tabbar/message-active_3x.png b/miniapp/assets/tabbar/message-active_3x.png index 0a14cae..0c34fda 100644 Binary files a/miniapp/assets/tabbar/message-active_3x.png and b/miniapp/assets/tabbar/message-active_3x.png differ diff --git a/miniapp/assets/tabbar/message.png b/miniapp/assets/tabbar/message.png index 66cea8e..0a28d21 100644 Binary files a/miniapp/assets/tabbar/message.png and b/miniapp/assets/tabbar/message.png differ diff --git a/miniapp/assets/tabbar/message_2x.png b/miniapp/assets/tabbar/message_2x.png index 0d19bbf..9120d97 100644 Binary files a/miniapp/assets/tabbar/message_2x.png and b/miniapp/assets/tabbar/message_2x.png differ diff --git a/miniapp/assets/tabbar/message_3x.png b/miniapp/assets/tabbar/message_3x.png index 84340b4..ac61fc5 100644 Binary files a/miniapp/assets/tabbar/message_3x.png and b/miniapp/assets/tabbar/message_3x.png differ diff --git a/miniapp/assets/tabbar/mine-active.png b/miniapp/assets/tabbar/mine-active.png index 5512830..42432ec 100644 Binary files a/miniapp/assets/tabbar/mine-active.png and b/miniapp/assets/tabbar/mine-active.png differ diff --git a/miniapp/assets/tabbar/mine-active_2x.png b/miniapp/assets/tabbar/mine-active_2x.png index 220337b..0918678 100644 Binary files a/miniapp/assets/tabbar/mine-active_2x.png and b/miniapp/assets/tabbar/mine-active_2x.png differ diff --git a/miniapp/assets/tabbar/mine-active_3x.png b/miniapp/assets/tabbar/mine-active_3x.png index a84a5a1..1e580eb 100644 Binary files a/miniapp/assets/tabbar/mine-active_3x.png and b/miniapp/assets/tabbar/mine-active_3x.png differ diff --git a/miniapp/assets/tabbar/mine.png b/miniapp/assets/tabbar/mine.png index af25004..a6a61cd 100644 Binary files a/miniapp/assets/tabbar/mine.png and b/miniapp/assets/tabbar/mine.png differ diff --git a/miniapp/assets/tabbar/mine_2x.png b/miniapp/assets/tabbar/mine_2x.png index 17aa03b..65ad6a5 100644 Binary files a/miniapp/assets/tabbar/mine_2x.png and b/miniapp/assets/tabbar/mine_2x.png differ diff --git a/miniapp/assets/tabbar/mine_3x.png b/miniapp/assets/tabbar/mine_3x.png index 063536b..8617cbd 100644 Binary files a/miniapp/assets/tabbar/mine_3x.png and b/miniapp/assets/tabbar/mine_3x.png differ diff --git a/miniapp/assets/tabbar/svg/activity-active.svg b/miniapp/assets/tabbar/svg/activity-active.svg index b307bb5..9800aa4 100644 --- a/miniapp/assets/tabbar/svg/activity-active.svg +++ b/miniapp/assets/tabbar/svg/activity-active.svg @@ -1,12 +1,5 @@ - - - - - - - - - - + 公益日历 + + diff --git a/miniapp/assets/tabbar/svg/activity.svg b/miniapp/assets/tabbar/svg/activity.svg index cabe840..0281adb 100644 --- a/miniapp/assets/tabbar/svg/activity.svg +++ b/miniapp/assets/tabbar/svg/activity.svg @@ -1,16 +1,5 @@ - - - - - - - - - - - - - - + 公益日历 + + diff --git a/miniapp/assets/tabbar/svg/discover-active.svg b/miniapp/assets/tabbar/svg/discover-active.svg index c3d8213..d1fb03f 100644 --- a/miniapp/assets/tabbar/svg/discover-active.svg +++ b/miniapp/assets/tabbar/svg/discover-active.svg @@ -1,5 +1,5 @@ - - - + 双人缘分 + + diff --git a/miniapp/assets/tabbar/svg/discover.svg b/miniapp/assets/tabbar/svg/discover.svg index 50911f6..5c0de4c 100644 --- a/miniapp/assets/tabbar/svg/discover.svg +++ b/miniapp/assets/tabbar/svg/discover.svg @@ -1,7 +1,5 @@ - - - - - + 双人缘分 + + diff --git a/miniapp/assets/tabbar/svg/home-active.svg b/miniapp/assets/tabbar/svg/home-active.svg index 82b3b7c..cade286 100644 --- a/miniapp/assets/tabbar/svg/home-active.svg +++ b/miniapp/assets/tabbar/svg/home-active.svg @@ -1,7 +1,5 @@ - - - - - + 爱心小屋 + + diff --git a/miniapp/assets/tabbar/svg/home.svg b/miniapp/assets/tabbar/svg/home.svg index 9c1c93e..d4b6043 100644 --- a/miniapp/assets/tabbar/svg/home.svg +++ b/miniapp/assets/tabbar/svg/home.svg @@ -1,11 +1,5 @@ - - - - - - - - - + 爱心小屋 + + diff --git a/miniapp/assets/tabbar/svg/message-active.svg b/miniapp/assets/tabbar/svg/message-active.svg index 972ab82..9513c73 100644 --- a/miniapp/assets/tabbar/svg/message-active.svg +++ b/miniapp/assets/tabbar/svg/message-active.svg @@ -1,8 +1,5 @@ - - - - - - + 爱心对话 + + diff --git a/miniapp/assets/tabbar/svg/message.svg b/miniapp/assets/tabbar/svg/message.svg index 93c0e2f..b60b468 100644 --- a/miniapp/assets/tabbar/svg/message.svg +++ b/miniapp/assets/tabbar/svg/message.svg @@ -1,11 +1,5 @@ - - - - - - - - - + 爱心对话 + + diff --git a/miniapp/assets/tabbar/svg/mine-active.svg b/miniapp/assets/tabbar/svg/mine-active.svg index d67cacb..495d9be 100644 --- a/miniapp/assets/tabbar/svg/mine-active.svg +++ b/miniapp/assets/tabbar/svg/mine-active.svg @@ -1,7 +1,5 @@ - - - - - + 个人守护 + + diff --git a/miniapp/assets/tabbar/svg/mine.svg b/miniapp/assets/tabbar/svg/mine.svg index 3fe8be2..483a4b3 100644 --- a/miniapp/assets/tabbar/svg/mine.svg +++ b/miniapp/assets/tabbar/svg/mine.svg @@ -1,10 +1,5 @@ - - - - - - - - + 个人守护 + + diff --git a/scripts/generate_tabbar_icons.py b/scripts/generate_tabbar_icons.py index 335bcb7..a8a9960 100644 --- a/scripts/generate_tabbar_icons.py +++ b/scripts/generate_tabbar_icons.py @@ -1,236 +1,114 @@ #!/usr/bin/env python3 -""" -丹阳公益婚恋平台 - 底部导航栏图标生成脚本 -圆润线性风格,两种状态(未选中/选中),三种尺寸 -""" +"""生成善缘丹阳小程序底部导航 SVG 与透明 PNG。""" + +from pathlib import Path +import math from PIL import Image, ImageDraw -import math -import os -# 主题色配置 -COLOR_UNSELECTED = (153, 153, 153) # #999999 -COLOR_SELECTED = (233, 107, 107) # #E96B6B -COLOR_BG = (255, 255, 255) # 白色背景 +ROOT = Path(__file__).resolve().parents[1] / "miniapp" / "assets" / "tabbar" +COLORS = {"normal": "#999999", "active": "#E96B6B"} +TITLES = { + "home": "爱心小屋", + "discover": "双人缘分", + "message": "爱心对话", + "activity": "公益日历", + "mine": "个人守护", +} - -def sin_deg(x): - return math.sin(math.radians(x)) - -def cos_deg(x): - return math.cos(math.radians(x)) - - -def draw_heart_points(cx, cy, scale): - """计算心形路径点""" - pts = [] - for t in range(0, 360, 3): - rad = t * 3.14159 / 180 - x = scale * 16 * (sin_deg(rad) ** 3) - y = -scale * (13 * cos_deg(rad) - 5 * cos_deg(2*rad) - 2 * cos_deg(3*rad) - cos_deg(4*rad)) - pts.append((cx + x, cy + y)) - return pts - - -def draw_rounded_rect(draw, x, y, w, h, r, color, width=2): - """绘制圆角矩形(无填充,只描边)""" - r = min(r, w // 2, h // 2) - draw.rounded_rectangle([x, y, x + w, y + h], radius=r, outline=color, width=width) - - -def draw_house(draw, cx, cy, size, color, width=2): - """绘制房子图标 - 首页""" - s = size * 0.35 - # 房子主体 - body_x, body_y = cx - s * 0.7, cy - s * 0.1 - body_w, body_h = s * 1.4, s * 0.7 - draw_rounded_rect(draw, body_x, body_y, body_w, body_h, 4, color, width) - - # 屋顶(三角形) - roof_points = [ - (cx, body_y - s * 0.5), - (body_x - s * 0.15, body_y), - (body_x + body_w + s * 0.15, body_y) - ] - draw.polygon(roof_points, outline=color, width=width) - - # 门 - door_w, door_h = s * 0.28, s * 0.4 - door_x = cx - door_w / 2 - door_y = body_y + body_h - door_h - draw_rounded_rect(draw, door_x, door_y, door_w, door_h, 2, color, width) - - # 烟囱 - chimney_w, chimney_h = s * 0.12, s * 0.22 - chimney_x = cx + s * 0.35 - chimney_y = body_y - s * 0.35 - draw_rounded_rect(draw, chimney_x, chimney_y, chimney_w, chimney_h, 2, color, width) - - -def draw_hearts(draw, cx, cy, size, color, width=2): - """绘制双心图标 - 找对象""" - s = size * 0.016 - offset = s * 4 - - # 左心 - left_pts = draw_heart_points(cx - offset, cy - offset * 0.3, s) - right_pts = draw_heart_points(cx + offset, cy - offset * 0.3, s) - - for pts in [left_pts, right_pts]: - for i in range(len(pts)): - p1 = pts[i] - p2 = pts[(i + 1) % len(pts)] - draw.line([p1, p2], fill=color, width=width) - - -def draw_chat_bubble(draw, cx, cy, size, color, width=2): - """绘制对话气泡图标 - 消息""" - s = size * 0.28 - # 气泡主体 - bubble_x, bubble_y = cx - s, cy - s * 0.6 - bubble_w, bubble_h = s * 2, s * 1.2 - draw_rounded_rect(draw, bubble_x, bubble_y, bubble_w, bubble_h, 8, color, width) - - # 小三角 - tail_points = [ - (bubble_x + s * 0.1, bubble_y + bubble_h), - (bubble_x + s * 0.3, bubble_y + bubble_h + s * 0.25), - (bubble_x + s * 0.5, bubble_y + bubble_h) - ] - draw.polygon(tail_points, outline=color, width=width) - - # 内部文字线条 - line_y_start = bubble_y + s * 0.2 - for i in range(3): - line_y = line_y_start + i * s * 0.22 - draw.line([(bubble_x + s * 0.15, line_y), (bubble_x + s * 1.5, line_y)], fill=color, width=width) - - -def draw_calendar(draw, cx, cy, size, color, width=2): - """绘制日历图标 - 活动""" - s = size * 0.26 - # 日历主体 - cal_x, cal_y = cx - s, cy - s * 0.55 - cal_w, cal_h = s * 2, s * 1.3 - draw_rounded_rect(draw, cal_x, cal_y, cal_w, cal_h, 6, color, width) - - # 顶部横线(分隔年月和日期) - draw.line([(cal_x + s * 0.05, cal_y + s * 0.25), (cal_x + cal_w - s * 0.05, cal_y + s * 0.25)], fill=color, width=width) - - # 日期点 - dot_r = 2 if size >= 162 else 1 - positions = [ - (cal_x + s * 0.3, cal_y + s * 0.55), - (cal_x + s * 0.7, cal_y + s * 0.55), - (cal_x + s * 1.1, cal_y + s * 0.55), - (cal_x + s * 0.5, cal_y + s * 0.9), - (cal_x + s * 0.9, cal_y + s * 0.9), - ] - for dx, dy in positions: - draw.ellipse([dx - dot_r, dy - dot_r, dx + dot_r, dy + dot_r], fill=color) - - # 左上角挂环 - ring_r = 4 if size >= 162 else 2 - draw.ellipse([cal_x + s * 0.25 - ring_r, cal_y - ring_r, cal_x + s * 0.25 + ring_r, cal_y + ring_r], outline=color, width=width) - draw.ellipse([cal_x + s * 1.75 - ring_r, cal_y - ring_r, cal_x + s * 1.75 + ring_r, cal_y + ring_r], outline=color, width=width) - - -def draw_user(draw, cx, cy, size, color, width=2): - """绘制用户图标 - 我的""" - s = size * 0.3 - - # 头部(圆形) - head_r = s * 0.38 - head_x, head_y = cx, cy - s * 0.25 - draw.ellipse([head_x - head_r, head_y - head_r, head_x + head_r, head_y + head_r], outline=color, width=width) - - # 身体(半椭圆) - body_top = cy + s * 0.15 - body_bottom = cy + s * 0.75 - body_left = cx - s * 0.45 - body_right = cx + s * 0.45 - draw.arc([body_left, body_top, body_right, body_bottom], start=180, end=360, fill=color, width=width) - - # 身体两侧竖线 - side_offset = s * 0.4 - draw.line([(cx - side_offset, body_top), (cx - side_offset, body_bottom)], fill=color, width=width) - draw.line([(cx + side_offset, body_top), (cx + side_offset, body_bottom)], fill=color, width=width) - - -ICON_FUNCTIONS = { - "home": draw_house, - "discover": draw_hearts, - "message": draw_chat_bubble, - "activity": draw_calendar, - "mine": draw_user, +SVG_BODY = { + "home": """""", + "discover": """""", + "message": """""", + "activity": """""", + "mine": """""", } -def create_icon(icon_name, size, color): - """创建单个图标""" - img = Image.new('RGBA', (size, size), color=(255, 255, 255, 0)) # 透明背景 - draw = ImageDraw.Draw(img) +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]} + + +""" - func = ICON_FUNCTIONS.get(icon_name) - if func: - func(draw, size // 2, size // 2, size, color) - return img +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(): - output_dir = r"E:\develop\公益婚恋服务平台\miniapp\assets\tabbar" - os.makedirs(output_dir, exist_ok=True) - - icons = [ - ("home", "首页"), - ("discover", "找对象"), - ("message", "消息"), - ("activity", "活动"), - ("mine", "我的"), - ] - - sizes_map = [ - (81, ""), # 1x - (162, "_2x"), # 2x - (243, "_3x"), # 3x - ] - - print("=" * 50) - print("丹阳公益婚恋平台 - 底部导航栏图标生成") - print("=" * 50) - print() - - total = 0 - for icon_name, label in icons: - print(f"🎨 {label} ({icon_name}):") - - for size, suffix in sizes_map: - # 未选中状态 - img_u = create_icon(icon_name, size, color=COLOR_UNSELECTED) - path_u = os.path.join(output_dir, f"{icon_name}{suffix}.png") - img_u.save(path_u, 'PNG') - - # 选中状态 - img_s = create_icon(icon_name, size, color=COLOR_SELECTED) - path_s = os.path.join(output_dir, f"{icon_name}-active{suffix}.png") - img_s.save(path_s, 'PNG') - - print(f" ✓ {os.path.basename(path_u)} ({size}×{size}, {os.path.getsize(path_u)} bytes)") - print(f" ✓ {os.path.basename(path_s)} ({size}×{size}, {os.path.getsize(path_s)} bytes)") - total += 2 - - print() - - print("=" * 50) - print(f"✅ 共生成 {total} 个图标文件") - print(f"📁 保存路径: {output_dir}") - print() - print("文件清单:") - for f in sorted(os.listdir(output_dir)): - if f.endswith('.png'): - filepath = os.path.join(output_dir, f) - print(f" {f} ({os.path.getsize(filepath)} bytes)") + (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__": diff --git a/ui-tests/tabbar-icons.mjs b/ui-tests/tabbar-icons.mjs new file mode 100644 index 0000000..161c5bd --- /dev/null +++ b/ui-tests/tabbar-icons.mjs @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { join } from 'node:path' + +const root = join(process.cwd(), 'miniapp', 'assets', 'tabbar') +const icons = { + home: '爱心小屋', + discover: '双人缘分', + message: '爱心对话', + activity: '公益日历', + mine: '个人守护', +} + +for (const [name, title] of Object.entries(icons)) { + const normal = readFileSync(join(root, 'svg', `${name}.svg`), 'utf8') + const active = readFileSync(join(root, 'svg', `${name}-active.svg`), 'utf8') + + assert.match(normal, new RegExp(`${title}`), `${name} 缺少主题化标题`) + assert.match(normal, /#999999/i, `${name} 默认态颜色错误`) + assert.match(active, /#E96B6B/i, `${name} 选中态颜色错误`) + + for (const suffix of ['', '_2x', '_3x']) { + const expectedSize = suffix === '' ? 81 : suffix === '_2x' ? 162 : 243 + for (const state of ['', '-active']) { + const png = readFileSync(join(root, `${name}${state}${suffix}.png`)) + assert.equal(png.readUInt32BE(16), expectedSize) + assert.equal(png.readUInt32BE(20), expectedSize) + assert.equal(png[25], 6, `${name}${state}${suffix} 必须为透明 RGBA PNG`) + } + } +} + +console.log('Tabbar icon contracts passed')