backup codes
This commit is contained in:
@@ -167,7 +167,7 @@ async def animation_task():
|
|||||||
try:
|
try:
|
||||||
# 动画参数
|
# 动画参数
|
||||||
frame_count = 20
|
frame_count = 20
|
||||||
frame_delay = 10 # 帧延迟(毫秒)
|
frame_delay = 100 # 帧延迟(毫秒)
|
||||||
|
|
||||||
await uasyncio.sleep_ms(2 * 1000)
|
await uasyncio.sleep_ms(2 * 1000)
|
||||||
print(f"开始JPG动画,帧延迟: {frame_delay}ms")
|
print(f"开始JPG动画,帧延迟: {frame_delay}ms")
|
||||||
@@ -188,7 +188,7 @@ async def animation_task():
|
|||||||
# 每轮清理一次内存
|
# 每轮清理一次内存
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
|
||||||
frame += 1
|
frame += 2
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"动画帧错误: {e}")
|
print(f"动画帧错误: {e}")
|
||||||
@@ -211,11 +211,12 @@ def cb_progress(data):
|
|||||||
|
|
||||||
def start():
|
def start():
|
||||||
# 初始化液晶屏
|
# 初始化液晶屏
|
||||||
display.init_display(config.get("bl_mode") != "gpio")
|
#display.init_display(config.get("bl_mode") != "gpio")
|
||||||
|
display.init_display(False, buffer_size=5000)
|
||||||
display.brightness(int(config.get("brightness", 10)))
|
display.brightness(int(config.get("brightness", 10)))
|
||||||
display.show_jpg("/rom/images/T1.jpg", 80, 80)
|
display.show_jpg("/rom/images/T1.jpg", 80, 80)
|
||||||
gc.collect()
|
gc.collect()
|
||||||
display.message("WiFi connect ...")
|
cb_progress("WiFi connect ...")
|
||||||
|
|
||||||
if not wifi_manager.connect(cb_progress):
|
if not wifi_manager.connect(cb_progress):
|
||||||
gc.collect()
|
gc.collect()
|
||||||
@@ -227,6 +228,7 @@ def start():
|
|||||||
machine.reset()
|
machine.reset()
|
||||||
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
|
display.load_ui()
|
||||||
|
|
||||||
# init web server
|
# init web server
|
||||||
from rom.nanoweb import Nanoweb
|
from rom.nanoweb import Nanoweb
|
||||||
@@ -289,7 +291,7 @@ def start():
|
|||||||
lcd_status = {
|
lcd_status = {
|
||||||
"ready": display.is_ready(),
|
"ready": display.is_ready(),
|
||||||
"brightness": display.brightness(),
|
"brightness": display.brightness(),
|
||||||
"ui_type": config.get("ui_type", "default"),
|
"ui_type": display.ui_type,
|
||||||
}
|
}
|
||||||
await request.write(json.dumps(lcd_status))
|
await request.write(json.dumps(lcd_status))
|
||||||
|
|
||||||
@@ -383,7 +385,7 @@ def start():
|
|||||||
loop.create_task(animation_task())
|
loop.create_task(animation_task())
|
||||||
|
|
||||||
gc.collect()
|
gc.collect()
|
||||||
display.message(f"success: {gc.mem_free()}...")
|
print(f"success: {gc.mem_free()}...")
|
||||||
|
|
||||||
# run!
|
# run!
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
|
|||||||
@@ -30,11 +30,16 @@ class Display:
|
|||||||
self._brightness = 80 # 默认亮度80%
|
self._brightness = 80 # 默认亮度80%
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
self._pos_y0 = 10
|
self._pos_y0 = 10
|
||||||
self.en_font = '/rom/fonts/en-8x16.rfont'
|
self.en_font = '/rom/fonts/en0.rfont'
|
||||||
self.cn_font = '/rom/fonts/cn-22x24.bfont'
|
self.vector_font = '/rom/fonts/en.hfont'
|
||||||
self.vector_font = '/rom/fonts/en-32x32.hfont'
|
self.cn_font = '/rom/fonts/cn.bfont'
|
||||||
|
self.time_font = '/rom/fonts/time.bfont'
|
||||||
# 前景色、背景色、提示框背景色
|
# 前景色、背景色、提示框背景色
|
||||||
self._COLORS = (0xFE19, 0x0000, 0x7800)
|
self._COLORS = (0xFE19, 0x0000, 0x7800)
|
||||||
|
self.ui_type = 'default'
|
||||||
|
# ui数据
|
||||||
|
self.ui_data = {}
|
||||||
|
self.ticks = 0 # 据此切换多行显示
|
||||||
|
|
||||||
def init_display(self, bl_pwm=True, buffer_size=2048):
|
def init_display(self, bl_pwm=True, buffer_size=2048):
|
||||||
"""初始化液晶屏,默认2048够用且不易有内存碎片"""
|
"""初始化液晶屏,默认2048够用且不易有内存碎片"""
|
||||||
@@ -137,6 +142,99 @@ class Display:
|
|||||||
]
|
]
|
||||||
self.window("配置设备网络连接", tips, "portal ip: 192.168.4.1")
|
self.window("配置设备网络连接", tips, "portal ip: 192.168.4.1")
|
||||||
|
|
||||||
|
# 更新ui数据
|
||||||
|
def update_ui(self, city=None, weather=None, advice=None, aqi=None, lunar=None, envdat=None):
|
||||||
|
self.ticks += 1
|
||||||
|
if self.ui_type == 'default':
|
||||||
|
# 中文的城市名称
|
||||||
|
if city is not None and city != self.ui_data.get('city'):
|
||||||
|
self.ui_data['city'] = city
|
||||||
|
if self.tft.write(self.cn_font, city, 15,10) == 0:
|
||||||
|
# 城市可能未包括,则显示??
|
||||||
|
self.tft.write(self.cn_font, '??', 15,10)
|
||||||
|
# 天气符号: 0-7 (晴、云、雨、雷、雪、雾、风、未知)
|
||||||
|
if weather is not None and weather != self.ui_data.get('weather'):
|
||||||
|
self.tft.jpg(f"/rom/images/{weather}.jpg",165,10,st7789.SLOW)
|
||||||
|
self.ui_data['weather'] = weather
|
||||||
|
# 建议信息可能有很多条,需要轮换展示
|
||||||
|
if advice is not None and advice != self.ui_data.get('advice'):
|
||||||
|
self.ui_data['advice'] = advice
|
||||||
|
# AQI等级分成0-5级,分别对应优、良、中、差、污、恶
|
||||||
|
if aqi is not None and aqi != self.ui_data.get('aqi'):
|
||||||
|
_t = (('优',0x07E0),('良',0xFFE0),('中',0xFD20),('差',0xF800),('污',0x8010),('恶',0x7800))
|
||||||
|
_l,_c = _t[aqi]
|
||||||
|
self.tft.fill_rect(105, 8, 40, 25, _c)
|
||||||
|
self.tft.write(self.cn_font, _l, 114,10, 0,_c)
|
||||||
|
self.ui_data['aqi'] = aqi
|
||||||
|
# 农历日期,需要和当前日期轮换展示
|
||||||
|
if lunar is not None and lunar != self.ui_data.get('lunar'):
|
||||||
|
self.ui_data['lunar'] = lunar
|
||||||
|
# 环境数据
|
||||||
|
if envdat is not None:
|
||||||
|
t,rh = envdat.get('t'),envdat.get('rh')
|
||||||
|
pm,ap = envdat.get('pm'),envdat.get('ap')
|
||||||
|
# 填充后再更新文本
|
||||||
|
if t is not None and t != self.ui_data.get('t'):
|
||||||
|
self.ui_data['t'] = t
|
||||||
|
self.tft.fill_rect(35,179,40,16,0)
|
||||||
|
self.tft.draw(self.vector_font, str(t), 35,187,0xFFFF,0.5)
|
||||||
|
if rh is not None and rh != self.ui_data.get('rh'):
|
||||||
|
self.ui_data['rh'] = rh
|
||||||
|
self.tft.fill_rect(110,179,40,16,0)
|
||||||
|
self.tft.draw(self.vector_font, str(rh), 110,187,0xFFFF,0.5)
|
||||||
|
if pm is not None and pm != self.ui_data.get('pm'):
|
||||||
|
self.ui_data['pm'] = pm
|
||||||
|
self.tft.fill_rect(35,213,40,16,0)
|
||||||
|
self.tft.draw(self.vector_font, str(pm), 35,221,0xFFFF,0.5)
|
||||||
|
if ap is not None and ap != self.ui_data.get('ap'):
|
||||||
|
self.ui_data['ap'] = ap
|
||||||
|
self.tft.fill_rect(110,213,40,16,0)
|
||||||
|
self.tft.draw(self.vector_font, str(ap), 110,221,0xFFFF,0.5)
|
||||||
|
# 处理日期
|
||||||
|
from machine import RTC
|
||||||
|
y,m,d,_w,H,M,*_ = RTC().datetime()
|
||||||
|
w = ('一','二','三','四','五','六','天')[_w]
|
||||||
|
if m!=self.ui_data.get('month') or d!=self.ui_data.get('day'):
|
||||||
|
self.ui_data['month'] = m
|
||||||
|
self.ui_data['day'] = d
|
||||||
|
self.ui_data['weekday'] = w
|
||||||
|
# just stop lunar, wait refresh
|
||||||
|
self.ui_data['lunar'] = None
|
||||||
|
# 切换日期显示
|
||||||
|
lunar = self.ui_data.get('lunar')
|
||||||
|
if lunar is not None and self.ticks & 0x01:
|
||||||
|
w = self.tft.write(self.cn_font, f'{lunar} 星期{w}', 15,135)
|
||||||
|
else:
|
||||||
|
w = self.tft.write(self.cn_font, f'{m:2d}月{d:2d}日 星期{w}', 15,135)
|
||||||
|
print(f'todo: fill date.tail: {w}')
|
||||||
|
# 处理时间显示
|
||||||
|
if H != self.ui_data.get('hour'):
|
||||||
|
self.ui_data['hour'] = H
|
||||||
|
self.tft.write(self.time_font,f'{H:02d}:',25,80,0xF080)
|
||||||
|
if M != self.ui_data.get('minute'):
|
||||||
|
self.ui_data['minute'] = M
|
||||||
|
self.tft.write(self.time_font,f'{M:02d}',135,80,0xFF80)
|
||||||
|
# 处理建议显示
|
||||||
|
advice = self.ui_data.get('advice')
|
||||||
|
if isinstance(advice, list) and advice:
|
||||||
|
i = self.ticks % len(advice)
|
||||||
|
c = advice[i]
|
||||||
|
w = self.tft.write(self.cn_font, advice[i], 15,45)
|
||||||
|
print(f'todo: fill advice.tail: {w}')
|
||||||
|
|
||||||
|
# 初始化ui固定元素
|
||||||
|
def load_ui(self):
|
||||||
|
if self.ui_type == 'default':
|
||||||
|
# 默认黑色背景
|
||||||
|
self.tft.fill(0)
|
||||||
|
# 固定的环境数据图标
|
||||||
|
self.tft.jpg("/rom/images/t.jpg",11,177,st7789.SLOW)
|
||||||
|
self.tft.jpg("/rom/images/rh.jpg",85,177,st7789.SLOW)
|
||||||
|
self.tft.jpg("/rom/images/pm.jpg",11,209,st7789.SLOW)
|
||||||
|
self.tft.jpg("/rom/images/ap.jpg",85,208,st7789.SLOW)
|
||||||
|
|
||||||
|
# 更新其他默认数据
|
||||||
|
self.update_ui()
|
||||||
|
|
||||||
# 全局液晶屏实例
|
# 全局液晶屏实例
|
||||||
display = Display()
|
display = Display()
|
||||||
|
|||||||
@@ -1,15 +1,49 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
CN_TEXT='配置设备网络连接热点自动进入页面0123456789 abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ!%*+,-./()[]\"<>=?℃'
|
CN_TEXT='北京月日星期一二三四五六末优良中差污恶能见度配置设备网络连接热点自动进入页面0123456789 abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ!%*+,-./()[]\"<>=?℃'
|
||||||
|
TIME_TEXT='0123456789: .-'
|
||||||
|
|
||||||
SRC_DIR=./assets
|
SRC_DIR=./assets
|
||||||
DST_DIR=../src/rom/fonts
|
DST_DIR=../src/rom/fonts
|
||||||
OTF_FONT=$SRC_DIR/NotoSansSC-Regular.otf
|
README=$DST_DIR/README.md
|
||||||
|
|
||||||
python3 pyfont_to_bin.py $SRC_DIR/romand.py $DST_DIR/en-32x32.hfont
|
# 中文黑体源字体
|
||||||
python3 pyfont_to_bin.py $SRC_DIR/vga1_8x16.py $DST_DIR/en-8x16.rfont
|
CN_OTF_FONT=$SRC_DIR/STHeiti.ttc
|
||||||
python3 pyfont_to_bin.py $SRC_DIR/vga1_16x32.py $DST_DIR/en-16x32.rfont
|
# 英文时间源字体
|
||||||
|
TIME_OTF_FONT=$SRC_DIR/BloodWaxItalic.otf
|
||||||
|
|
||||||
python3 font2bitmap.py -s "$CN_TEXT" $OTF_FONT 22 > $SRC_DIR/cn-22x24.py
|
echo "# 字体文件说明" > $README
|
||||||
python3 pyfont_to_bin.py $SRC_DIR/cn-22x24.py $DST_DIR/cn-22x24.bfont
|
echo "" >>$README
|
||||||
|
|
||||||
|
echo "1. en0.rfont/en1.rfont 英文字体" >> $README
|
||||||
|
echo "> 顺序排列,相等大小、可快速定位" >> $README
|
||||||
|
echo "" >>$README
|
||||||
|
python3 pyfont_to_bin.py $SRC_DIR/vga1_8x16.py $DST_DIR/en0.rfont
|
||||||
|
python3 pyfont_to_bin.py $SRC_DIR/vga1_16x32.py $DST_DIR/en1.rfont
|
||||||
|
|
||||||
|
echo "2. en.hfont 矢量英文字体" >> $README
|
||||||
|
echo "> 顺序排列,带索引表,只画线无背景色,可自由缩放,显示速度快" >> $README
|
||||||
|
echo "" >>$README
|
||||||
|
python3 pyfont_to_bin.py $SRC_DIR/romand.py $DST_DIR/en.hfont
|
||||||
|
|
||||||
|
echo "3. cn.bfont 中文字体" >> $README
|
||||||
|
echo "> 有字符MAP表(当前顺序查找、需实现二叉树查找)" >> $README
|
||||||
|
echo "" >>$README
|
||||||
|
python3 font2bitmap.py -s "$CN_TEXT" $CN_OTF_FONT 22 > $SRC_DIR/cn.py
|
||||||
|
python3 pyfont_to_bin.py $SRC_DIR/cn.py $DST_DIR/cn.bfont
|
||||||
|
|
||||||
|
echo "4. time.bfont 时间字体" >> $README
|
||||||
|
echo "> 有字符MAP表(当前顺序查找、需实现二叉树查找)" >> $README
|
||||||
|
echo "" >>$README
|
||||||
|
python3 font2bitmap.py -s "$TIME_TEXT" $TIME_OTF_FONT 48 > $SRC_DIR/time.py
|
||||||
|
python3 pyfont_to_bin.py $SRC_DIR/time.py $DST_DIR/time.bfont
|
||||||
|
|
||||||
|
|
||||||
|
# 开始转换图片文件
|
||||||
|
SRC_DIR=./assets
|
||||||
|
DST_DIR=../src/rom/images
|
||||||
|
|
||||||
|
# 透明色转换成黑色背景,质量95%
|
||||||
|
python3 png_to_jpg.py -d $SRC_DIR/ --color black --quality 100
|
||||||
|
cp -v $SRC_DIR/*.jpg $DST_DIR/
|
||||||
|
|||||||
253
utils/icons.html
Normal file
253
utils/icons.html
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>常用天气符号</title>
|
||||||
|
<!-- 引入Font Awesome -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 20px;
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
.weather-icon {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
width: 150px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
.weather-icon:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
}
|
||||||
|
.weather-icon i {
|
||||||
|
font-size: 48px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.weather-icon .icon-name {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.weather-icon .icon-class {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 5px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.fas.fa-sun { color: #FFD700; }
|
||||||
|
.fas.fa-cloud-sun { color: #87CEEB; }
|
||||||
|
.fas.fa-cloud { color: #A9A9A9; }
|
||||||
|
.fas.fa-cloud-rain { color: #4682B4; }
|
||||||
|
.fas.fa-bolt { color: #FF4500; }
|
||||||
|
.fas.fa-snowflake { color: #B0E0E6; }
|
||||||
|
.fas.fa-smog { color: #696969; }
|
||||||
|
.fas.fa-wind { color: #708090; }
|
||||||
|
.fas.fa-cloud-showers-heavy { color: #1E90FF; }
|
||||||
|
.fas.fa-question-circle { color: #32CD32; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>常用天气符号</h1>
|
||||||
|
<div class="container">
|
||||||
|
<!-- 晴天 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-sun"></i>
|
||||||
|
<div class="icon-name">晴天</div>
|
||||||
|
<div class="icon-class">fas fa-sun</div>
|
||||||
|
</div>
|
||||||
|
<!-- 多云转晴 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-cloud-sun"></i>
|
||||||
|
<div class="icon-name">多云转晴</div>
|
||||||
|
<div class="icon-class">fas fa-cloud-sun</div>
|
||||||
|
</div>
|
||||||
|
<!-- 多云 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-cloud"></i>
|
||||||
|
<div class="icon-name">多云</div>
|
||||||
|
<div class="icon-class">fas fa-cloud</div>
|
||||||
|
</div>
|
||||||
|
<!-- 雨天 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-cloud-rain"></i>
|
||||||
|
<div class="icon-name">雨天</div>
|
||||||
|
<div class="icon-class">fas fa-cloud-rain</div>
|
||||||
|
</div>
|
||||||
|
<!-- 雷雨 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-bolt"></i>
|
||||||
|
<div class="icon-name">雷雨</div>
|
||||||
|
<div class="icon-class">fas fa-bolt</div>
|
||||||
|
</div>
|
||||||
|
<!-- 雪天 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-snowflake"></i>
|
||||||
|
<div class="icon-name">雪天</div>
|
||||||
|
<div class="icon-class">fas fa-snowflake</div>
|
||||||
|
</div>
|
||||||
|
<!-- 雾天 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-smog"></i>
|
||||||
|
<div class="icon-name">雾天</div>
|
||||||
|
<div class="icon-class">fas fa-smog</div>
|
||||||
|
</div>
|
||||||
|
<!-- 大风 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-wind"></i>
|
||||||
|
<div class="icon-name">大风</div>
|
||||||
|
<div class="icon-class">fas fa-wind</div>
|
||||||
|
</div>
|
||||||
|
<!-- 暴雨 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-cloud-showers-heavy"></i>
|
||||||
|
<div class="icon-name">暴雨</div>
|
||||||
|
<div class="icon-class">fas fa-cloud-showers-heavy</div>
|
||||||
|
</div>
|
||||||
|
<!-- 太阳雨 -->
|
||||||
|
<div class="weather-icon">
|
||||||
|
<i class="fas fa-question-circle"></i>
|
||||||
|
<div class="icon-name">未知</div>
|
||||||
|
<div class="icon-class">fas fa-question-circle</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 可以添加一些交互效果,例如点击图标复制类名
|
||||||
|
document.querySelectorAll('.weather-icon').forEach(icon => {
|
||||||
|
icon.addEventListener('click', function() {
|
||||||
|
const iconClass = this.querySelector('.icon-class').textContent;
|
||||||
|
navigator.clipboard.writeText(iconClass).then(() => {
|
||||||
|
alert('已复制类名: ' + iconClass);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 添加下载按钮
|
||||||
|
const downloadBtn = document.createElement('button');
|
||||||
|
downloadBtn.innerHTML = '📥 下载所有图标为PNG (48x48)';
|
||||||
|
downloadBtn.style.cssText = `
|
||||||
|
display: block;
|
||||||
|
margin: 30px auto;
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 18px;
|
||||||
|
background: linear-gradient(135deg, #4CAF50, #2E7D32);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
`;
|
||||||
|
downloadBtn.onmouseover = () => {
|
||||||
|
downloadBtn.style.transform = 'translateY(-2px)';
|
||||||
|
downloadBtn.style.boxShadow = '0 6px 12px rgba(0,0,0,0.2)';
|
||||||
|
};
|
||||||
|
downloadBtn.onmouseout = () => {
|
||||||
|
downloadBtn.style.transform = 'translateY(0)';
|
||||||
|
downloadBtn.style.boxShadow = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 插入按钮到页面
|
||||||
|
document.querySelector('.container').after(downloadBtn);
|
||||||
|
|
||||||
|
// 映射Font Awesome类名到Unicode字符
|
||||||
|
const faUnicodeMap = {
|
||||||
|
'fa-sun': '\uf185',
|
||||||
|
'fa-cloud-sun': '\uf6c4',
|
||||||
|
'fa-cloud': '\uf0c2',
|
||||||
|
'fa-cloud-rain': '\uf73d',
|
||||||
|
'fa-bolt': '\uf0e7',
|
||||||
|
'fa-snowflake': '\uf2dc',
|
||||||
|
'fa-smog': '\uf75f',
|
||||||
|
'fa-wind': '\uf72e',
|
||||||
|
'fa-cloud-showers-heavy': '\uf740',
|
||||||
|
'fa-question-circle': '\uf059'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下载所有图标
|
||||||
|
downloadBtn.addEventListener('click', async () => {
|
||||||
|
downloadBtn.disabled = true;
|
||||||
|
downloadBtn.innerHTML = '⏳ 正在生成PNG图片...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 等待字体加载
|
||||||
|
await document.fonts.ready;
|
||||||
|
|
||||||
|
const zip = new JSZip();
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
|
// 获取所有图标元素
|
||||||
|
const iconElements = document.querySelectorAll('.weather-icon i');
|
||||||
|
|
||||||
|
iconElements.forEach((iconEl, index) => {
|
||||||
|
const className = Array.from(iconEl.classList)
|
||||||
|
.find(cls => cls.startsWith('fa-'));
|
||||||
|
|
||||||
|
if (!className) return;
|
||||||
|
|
||||||
|
const iconName = className.replace('fa-', '');
|
||||||
|
const color = getComputedStyle(iconEl).color;
|
||||||
|
const unicodeChar = faUnicodeMap[className] || '\uf071';
|
||||||
|
|
||||||
|
// 创建Canvas
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = 48;
|
||||||
|
canvas.height = 48;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// 设置透明背景
|
||||||
|
ctx.clearRect(0, 0, 48, 48);
|
||||||
|
|
||||||
|
// 绘制图标
|
||||||
|
ctx.font = 'normal normal 900 40px "Font Awesome 6 Free"';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.fillText(unicodeChar, 24, 24);
|
||||||
|
|
||||||
|
// 将Canvas转换为PNG并添加到ZIP
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
zip.file(`${iconName}.png`, blob);
|
||||||
|
}, 'image/png');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建ZIP文件
|
||||||
|
setTimeout(async () => {
|
||||||
|
const content = await zip.generateAsync({type: 'blob'});
|
||||||
|
saveAs(content, 'weather-icons.zip');
|
||||||
|
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
downloadBtn.innerHTML = '✅ 下载完成!再次下载';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
downloadBtn.innerHTML = '📥 下载所有图标为PNG (48x48)';
|
||||||
|
}, 3000);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error);
|
||||||
|
downloadBtn.disabled = false;
|
||||||
|
downloadBtn.innerHTML = '❌ 下载失败,点击重试';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -148,10 +148,10 @@ def convert_to_bfont(namespace, input_file, output_file=None):
|
|||||||
"bpp": bpp, # Store BPP value
|
"bpp": bpp, # Store BPP value
|
||||||
}
|
}
|
||||||
|
|
||||||
# Update output filename if needed (only if it doesn't already include dimensions)
|
# Update output filename if needed
|
||||||
if output_file and not output_file.endswith(f"-{width}x{height}.bfont"):
|
if output_file and os.path.isdir(output_file):
|
||||||
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
output_dir = os.path.dirname(output_file)
|
output_dir = output_file
|
||||||
output_name = f"{base_name}-{width}x{height}.bfont"
|
output_name = f"{base_name}-{width}x{height}.bfont"
|
||||||
output_file = os.path.join(output_dir, output_name)
|
output_file = os.path.join(output_dir, output_name)
|
||||||
|
|
||||||
@@ -221,10 +221,10 @@ def convert_to_hfont(namespace, input_file, output_file=None):
|
|||||||
if size > max_char_size:
|
if size > max_char_size:
|
||||||
max_char_size = size
|
max_char_size = size
|
||||||
|
|
||||||
# Update output filename if needed (only if it doesn't already include dimensions)
|
# Update output filename if needed
|
||||||
if output_file and not output_file.endswith(f"-{width}x{height}.hfont"):
|
if output_file and os.path.isdir(output_file):
|
||||||
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
output_dir = os.path.dirname(output_file)
|
output_dir = output_file
|
||||||
output_name = f"{base_name}-{width}x{height}.hfont"
|
output_name = f"{base_name}-{width}x{height}.hfont"
|
||||||
output_file = os.path.join(output_dir, output_name)
|
output_file = os.path.join(output_dir, output_name)
|
||||||
|
|
||||||
@@ -293,10 +293,10 @@ def convert_to_rfont(namespace, input_file, output_file=None):
|
|||||||
bytes_per_line = (width + 7) // 8
|
bytes_per_line = (width + 7) // 8
|
||||||
bytes_per_char = bytes_per_line * height
|
bytes_per_char = bytes_per_line * height
|
||||||
|
|
||||||
# Update output filename if needed (only if it doesn't already include dimensions)
|
# Update output filename if needed
|
||||||
if output_file and not output_file.endswith(f"-{width}x{height}.rfont"):
|
if output_file and os.path.isdir(output_file):
|
||||||
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
output_dir = os.path.dirname(output_file)
|
output_dir = output_file
|
||||||
output_name = f"{base_name}-{width}x{height}.rfont"
|
output_name = f"{base_name}-{width}x{height}.rfont"
|
||||||
output_file = os.path.join(output_dir, output_name)
|
output_file = os.path.join(output_dir, output_name)
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@ def convert_to_binary(input_file, output_file=None):
|
|||||||
else:
|
else:
|
||||||
output_path = os.path.dirname(input_file)
|
output_path = os.path.dirname(input_file)
|
||||||
|
|
||||||
output_name = "" # Will be set by each conversion function
|
output_file = output_path
|
||||||
|
|
||||||
# Parse the pyfont file using exec()
|
# Parse the pyfont file using exec()
|
||||||
namespace = {}
|
namespace = {}
|
||||||
@@ -421,6 +421,7 @@ def convert_to_binary(input_file, output_file=None):
|
|||||||
# Detect the font type
|
# Detect the font type
|
||||||
font_type = detect_font_type(namespace, input_file)
|
font_type = detect_font_type(namespace, input_file)
|
||||||
|
|
||||||
|
'''
|
||||||
# Generate default output file name if not provided or directory specified
|
# Generate default output file name if not provided or directory specified
|
||||||
if output_file is None:
|
if output_file is None:
|
||||||
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
base_name = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
@@ -452,6 +453,7 @@ def convert_to_binary(input_file, output_file=None):
|
|||||||
output_name = f"{base_name}.bin"
|
output_name = f"{base_name}.bin"
|
||||||
|
|
||||||
output_file = os.path.join(output_path, output_name)
|
output_file = os.path.join(output_path, output_name)
|
||||||
|
'''
|
||||||
|
|
||||||
# Convert based on the detected format
|
# Convert based on the detected format
|
||||||
if font_type == "bfont":
|
if font_type == "bfont":
|
||||||
|
|||||||
Reference in New Issue
Block a user