a
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
创建Material Design风格的图标
|
||||
使用简单的几何图形模拟Material图标
|
||||
"""
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import os
|
||||
|
||||
# Material Design 颜色
|
||||
COLORS = {
|
||||
'primary': '#6200EE',
|
||||
'primary_variant': '#3700B3',
|
||||
'secondary': '#03DAC6',
|
||||
'background': '#FFFFFF',
|
||||
'surface': '#FFFFFF',
|
||||
'error': '#B00020',
|
||||
'on_primary': '#FFFFFF',
|
||||
'on_secondary': '#000000',
|
||||
'on_background': '#000000',
|
||||
'on_surface': '#000000',
|
||||
'on_error': '#FFFFFF'
|
||||
}
|
||||
|
||||
# 图标配置
|
||||
ICONS = {
|
||||
'home': {
|
||||
'outline': 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z',
|
||||
'filled': 'M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z'
|
||||
},
|
||||
'apps': {
|
||||
'outline': 'M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z',
|
||||
'filled': 'M4 8h4V4H4v4zm6 12h4v-4h-4v4zm-6 0h4v-4H4v4zm0-6h4v-4H4v4zm6 0h4v-4h-4v4zm6-10v4h4V4h-4zm-6 4h4V4h-4v4zm6 6h4v-4h-4v4zm0 6h4v-4h-4v4z'
|
||||
},
|
||||
'chat': {
|
||||
'outline': 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z',
|
||||
'filled': 'M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z'
|
||||
},
|
||||
'layers': {
|
||||
'outline': 'M11.99 18.54l-7.37-5.73L3 14.07l9 7 9-7-1.63-1.27-7.38 5.74zM12 16l7.36-5.73L21 9l-9-7-9 7 1.63 1.27L12 16z',
|
||||
'filled': 'M11.99 18.54l-7.37-5.73L3 14.07l9 7 9-7-1.63-1.27-7.38 5.74zM12 16l7.36-5.73L21 9l-9-7-9 7 1.63 1.27L12 16z'
|
||||
},
|
||||
'person': {
|
||||
'outline': 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z',
|
||||
'filled': 'M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z'
|
||||
}
|
||||
}
|
||||
|
||||
def create_material_icon(name, style='outline', size=48):
|
||||
"""创建Material Design图标"""
|
||||
# 创建图像
|
||||
img = Image.new('RGBA', (size, size), (255, 255, 255, 0))
|
||||
draw = ImageDraw.Draw(img)
|
||||
|
||||
# 设置颜色
|
||||
if style == 'outline':
|
||||
color = COLORS['on_surface'] # 灰色
|
||||
fill_color = None
|
||||
else: # filled
|
||||
color = COLORS['primary'] # 紫色
|
||||
fill_color = COLORS['primary']
|
||||
|
||||
# 简化:使用几何图形代替SVG路径
|
||||
center = size // 2
|
||||
|
||||
if name == 'home':
|
||||
# 房子图标
|
||||
if style == 'outline':
|
||||
# 轮廓
|
||||
draw.rectangle([center-10, center-6, center+10, center+6], outline=color, width=2)
|
||||
draw.polygon([
|
||||
(center-12, center-6),
|
||||
(center, center-14),
|
||||
(center+12, center-6)
|
||||
], outline=color, width=2)
|
||||
else:
|
||||
# 填充
|
||||
draw.rectangle([center-10, center-6, center+10, center+6], fill=fill_color)
|
||||
draw.polygon([
|
||||
(center-12, center-6),
|
||||
(center, center-14),
|
||||
(center+12, center-6)
|
||||
], fill=fill_color)
|
||||
|
||||
elif name == 'apps':
|
||||
# 九宫格图标
|
||||
cell_size = size // 4
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
x1 = cell_size + i * cell_size - 4
|
||||
y1 = cell_size + j * cell_size - 4
|
||||
x2 = x1 + cell_size - 4
|
||||
y2 = y1 + cell_size - 4
|
||||
|
||||
if style == 'outline':
|
||||
draw.rectangle([x1, y1, x2, y2], outline=color, width=2)
|
||||
else:
|
||||
draw.rectangle([x1, y1, x2, y2], fill=fill_color)
|
||||
|
||||
elif name == 'chat':
|
||||
# 聊天图标
|
||||
if style == 'outline':
|
||||
draw.rounded_rectangle([8, 8, size-8, size-8], radius=8, outline=color, width=2)
|
||||
# 对话气泡尾巴
|
||||
draw.polygon([
|
||||
(size-20, size-8),
|
||||
(size-12, size-8),
|
||||
(size-16, size-2)
|
||||
], outline=color, width=2)
|
||||
else:
|
||||
draw.rounded_rectangle([8, 8, size-8, size-8], radius=8, fill=fill_color)
|
||||
# 对话气泡尾巴
|
||||
draw.polygon([
|
||||
(size-20, size-8),
|
||||
(size-12, size-8),
|
||||
(size-16, size-2)
|
||||
], fill=fill_color)
|
||||
|
||||
elif name == 'layers':
|
||||
# 层叠图标
|
||||
if style == 'outline':
|
||||
for i in range(3):
|
||||
offset = i * 4
|
||||
draw.rounded_rectangle(
|
||||
[8+offset, 8+offset, size-8-offset, size-8-offset],
|
||||
radius=6, outline=color, width=2
|
||||
)
|
||||
else:
|
||||
for i in range(3):
|
||||
offset = i * 4
|
||||
alpha = 255 - i * 60
|
||||
layer_color = fill_color + hex(alpha)[2:].zfill(2)
|
||||
draw.rounded_rectangle(
|
||||
[8+offset, 8+offset, size-8-offset, size-8-offset],
|
||||
radius=6, fill=layer_color
|
||||
)
|
||||
|
||||
elif name == 'person':
|
||||
# 人物图标
|
||||
if style == 'outline':
|
||||
# 头部
|
||||
draw.ellipse([center-8, center-12, center+8, center+4], outline=color, width=2)
|
||||
# 身体
|
||||
draw.rectangle([center-6, center+4, center+6, center+12], outline=color, width=2)
|
||||
else:
|
||||
# 头部
|
||||
draw.ellipse([center-8, center-12, center+8, center+4], fill=fill_color)
|
||||
# 身体
|
||||
draw.rectangle([center-6, center+4, center+6, center+12], fill=fill_color)
|
||||
|
||||
return img
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
icon_dir = 'static/images/icons'
|
||||
os.makedirs(icon_dir, exist_ok=True)
|
||||
|
||||
print("创建Material Design图标...")
|
||||
|
||||
for name in ICONS.keys():
|
||||
# 创建轮廓图标
|
||||
outline_img = create_material_icon(name, 'outline')
|
||||
outline_path = os.path.join(icon_dir, f'{name}_outline.png')
|
||||
outline_img.save(outline_path)
|
||||
print(f"创建: {outline_path}")
|
||||
|
||||
# 创建填充图标
|
||||
filled_img = create_material_icon(name, 'filled')
|
||||
filled_path = os.path.join(icon_dir, f'{name}_filled.png')
|
||||
filled_img.save(filled_path)
|
||||
print(f"创建: {filled_path}")
|
||||
|
||||
print("✅ 图标创建完成!")
|
||||
|
||||
# 创建预览
|
||||
create_preview(icon_dir)
|
||||
|
||||
def create_preview(icon_dir):
|
||||
"""创建图标预览"""
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Material Design图标预览</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background: #F5F5F5;
|
||||
color: #212121;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: #FFFFFF;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid #EEEEEE;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
color: #6200EE;
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
color: #757575;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.icon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.icon-card {
|
||||
background: #FAFAFA;
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.icon-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.icon-preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.icon-outline, .icon-filled {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-label {
|
||||
font-size: 12px;
|
||||
color: #757575;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.icon-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #212121;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.icon-desc {
|
||||
font-size: 14px;
|
||||
color: #616161;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.color-scheme {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-top: 32px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid #EEEEEE;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.color-box {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.color-text {
|
||||
font-size: 14px;
|
||||
color: #616161;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 32px;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid #EEEEEE;
|
||||
color: #9E9E9E;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.icon-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>Material Design图标</h1>
|
||||
<p>玩Android应用底部导航图标</p>
|
||||
</div>
|
||||
|
||||
<div class="icon-grid">
|
||||
"""
|
||||
|
||||
icon_names = {
|
||||
'home': '首页',
|
||||
'apps': '项目',
|
||||
'chat': '公众号',
|
||||
'layers': '体系',
|
||||
'person': '我的'
|
||||
}
|
||||
|
||||
icon_descs = {
|
||||
'home': '应用首页,展示最新文章和Banner',
|
||||
'apps': '开源项目浏览和分类查看',
|
||||
'chat': '技术公众号文章订阅',
|
||||
'layers': '技术知识体系结构',
|
||||
'person': '用户中心和个人设置'
|
||||
}
|
||||
|
||||
for name, chinese in icon_names.items():
|
||||
html += f"""
|
||||
<div class="icon-card">
|
||||
<div class="icon-name">{chinese}</div>
|
||||
<div class="icon-preview">
|
||||
<div class="icon-outline">
|
||||
<img src="{name}_outline.png" alt="{name} outline" width="48" height="48">
|
||||
<div class="icon-label">未选中</div>
|
||||
</div>
|
||||
<div class="icon-filled">
|
||||
<img src="{name}_filled.png" alt="{name} filled" width="48" height="48">
|
||||
<div class="icon-label">选中</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="icon-desc">{icon_descs[name]}</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
html += """
|
||||
</div>
|
||||
|
||||
<div class="color-scheme">
|
||||
<div class="color-item">
|
||||
<div class="color-box" style="background: #6200EE;"></div>
|
||||
<div class="color-text">主色 (Primary)</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-box" style="background: #3700B3;"></div>
|
||||
<div class="color-text">主色变体</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-box" style="background: #03DAC6;"></div>
|
||||
<div class="color-text">次要色</div>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<div class="color-box" style="background: #757575;"></div>
|
||||
<div class="color-text">未选中状态</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>图标尺寸: 48×48像素 | 生成时间: 2026-03-17</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
preview_path = os.path.join(icon_dir, 'preview.html')
|
||||
with open(preview_path, 'w', encoding='utf-8') as f:
|
||||
f.write(html)
|
||||
|
||||
print(f"预览文件: {preview_path}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user