238 lines
7.4 KiB
Python
238 lines
7.4 KiB
Python
#!/usr/bin/env python3
|
||||
|
|
"""
|
|||
|
|
丹阳公益婚恋平台 - 底部导航栏图标生成脚本
|
|||
|
|
圆润线性风格,两种状态(未选中/选中),三种尺寸
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
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) # 白色背景
|
|||
|
|
|
|||
|
|
|
|||
|
|
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,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def create_icon(icon_name, size, color):
|
|||
|
|
"""创建单个图标"""
|
|||
|
|
img = Image.new('RGBA', (size, size), color=(255, 255, 255, 0)) # 透明背景
|
|||
|
|
draw = ImageDraw.Draw(img)
|
|||
|
|
|
|||
|
|
func = ICON_FUNCTIONS.get(icon_name)
|
|||
|
|
if func:
|
|||
|
|
func(draw, size // 2, size // 2, size, color)
|
|||
|
|
|
|||
|
|
return img
|
|||
|
|
|
|||
|
|
|
|||
|
|
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)")
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|