iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
AI 自動化

用 LINE Bot 和 Agent Skill 自動化整理筆記與 IG 發文系列 第 24 篇

Day 24|HTML 模板算圖卡:為什麼 1080×1350 不是美觀問題

  • 分享至 

  • xImage
  •  

昨天講完 caption,那是整條流程裡唯一沒有版型管的自由文字。今天回到程式這一側,看圖卡本身是怎麼算出來的,還有 Day 19 留的那個問題:1080×1350 這個數字,為什麼我說它不是美觀考量。

圖卡是一張網頁截圖

先講機制。render_cards.py 沒有用任何繪圖函式庫去畫圖。它做的事是:把 deck.json 裡的每張 slide 組成一份 HTML,開一個 Chromium,把視窗設成 1080×1350,截圖存成 PNG。

組 HTML 分三層。最外面是 _shell.html,只有一個 <div class="card">,是所有卡片共用的殼。殼裡放 base.css 填完顏色之後的內容,再把對應版型的 HTML 塞進去。版型是 cover.html、word.html、spectrum.html、quiz.html 四選一,看 slide 的 type。

佔位符的做法很土:模板裡寫 {{WORD}}、{{EXPLAIN}},程式用字串取代把值換進去。CSS 也是同一套,{{BG}}、{{ACCENT}} 這些從 config.yaml 的 brand 區塊來。沒有模板引擎,沒有什麼框架,一個 fill() 函式二十行不到。

截圖那段是這樣:

page = browser.new_context(
    viewport={"width": W, "height": H}, device_scale_factor=1
).new_page()
for i, hp, png in pages:
    page.goto(hp.resolve().as_uri())
    page.wait_for_timeout(300)  # 等字型與 emoji
    page.screenshot(path=str(png), clip={"x": 0, "y": 0, "width": W, "height": H})

每張卡先寫成 card-01.html 這種暫存檔,截完圖就刪掉,最後留在資料夾裡的只有 PNG。另外有個 --html-only 參數,只吐 HTML 不開瀏覽器,要調 CSS 的時候,可以直接用瀏覽器開那份 HTML 來看,不用每次等截圖。

用網頁當畫布,對不會設計的人有個實際的好處:字太大就改 CSS 裡的一個數字,不用學任何繪圖 API。

尺寸寫在三個地方

render_cards.py 開頭有一行 W, H = 1080, 1350,視窗跟截圖範圍都吃這兩個值。base.css 裡 html, body 跟 .card 也各寫死了 width: 1080px; height: 1350px;。三處對得上,截出來才不會多一條邊或少一截。

這也代表我要是哪天想改尺寸,得同時動 Python 跟 CSS 兩邊,沒有單一來源。目前沒改過,就先這樣,但這是我讀程式時看到的一個小地雷。

為什麼是 1080×1350

這個數字的比例是 4:5。render_cards.py 開頭的註解寫得很直接:全部輸出同一個尺寸,不是美觀考量,IG 會把輪播裡所有圖裁成第一張的比例。

這句話的重點是「同一個尺寸」,不是「4:5 比較好看」。輪播是一組圖,IG 只認第一張的比例,後面每一張都照它裁。要是我的封面是 4:5,結果某張單字卡不小心是正方形,那張就會被切。切掉的地方可能剛好是例句,小孩看到半句英文。

references/ig-api.md 把這件事列在限制裡,叫「最常見的翻車點」。排錯表裡「輪播圖被裁掉」的原因也只有一個:各張比例不一致,處理方式是重跑 render_cards.py,全部 1080×1350。

所以這個尺寸對我來說是一條規格,不是設計選擇。不過程式碼跟文件只說了「要統一」,沒有說為什麼挑 4:5 而不是別的比例,這個我沒辦法替它補理由。能確定的是,統一比 4:5 本身重要。

另外有兩處在守這條規格。build_preview.py 的預覽頁,圖片容器 CSS 用 aspect-ratio: 4/5,所以我在預覽看到的框,形狀跟 IG 上會看到的一樣。預覽頁上也放了一行字:所有圖會被裁成第一張的比例,全部 1080×1350 才不會被切。

版型跟著尺寸走

尺寸固定之後,版型裡很多數字就有了依據。

.card 的 padding 是上下 76px、左右 72px,右上角固定一個 46px 的圓點。單字卡的字級大概是這樣:單字本體 108px,解釋 44px,英文例句 40px,中文例句 34px。這些數字都是對著 1080 寬的畫布量出來的,換了尺寸就得重調。

排版用的是 flex。卡片是直向的 flex 容器,單字卡的插圖區塊用 flex: 1,吃掉剩下的高度,最小高度 380px;例句框用 margin-top: auto 推到底部。意思是文字少的時候,插圖區會撐大,文字多的時候,插圖區會縮到 380px 為止。

這個設計有一個我自己要承認的限制。html, body 設了 overflow: hidden,高度又是死的 1350px。所以要是 AI 寫的解釋太長,插圖縮到最小還是塞不下,多出來的東西會直接被切掉,不會報錯,也不會自動縮字。Day 20 講過欄位字數沒有程式在檢查,這就是後果發生的地方:圖照樣算出來,只是底部少一塊。我現在靠第 5 階段看預覽,用眼睛抓這種圖。

兩個細節

字型跟 emoji 是這支程式唯一依賴運行環境的地方。config.yaml 預設的字型是 'Noto Sans TC', 'PingFang TC', 'Microsoft JhengHei', 'Segoe UI Emoji', sans-serif,Chromium 是用機器上實際裝了的字型去算。機器上沒有,就退回 sans-serif,圖算得出來,只是字長得不一樣。設定檔裡我也特別註記:要填這台機器上真的裝了的字型。所以截圖前有一句 wait_for_timeout(300),註解是「等字型與 emoji」,簡單粗暴,但夠用。

另一個是 device_scale_factor=1,輸出就是剛好 1080×1350 個像素,沒有放大成兩倍。檔案不會變大,也不會跟 IG 要的尺寸對不上。

小結

圖卡是 HTML 加 CSS 組出來、用 Chromium 截成 PNG 的。尺寸固定 1080×1350,因為 IG 輪播以第一張的比例為準,其他張一不一致就會被切。這個數字在 Python、CSS、預覽頁三個地方各守一次,版型的字級跟間距也都是為它量的。已知的洞是文字太長會被默默切掉,現在靠預覽把關。

明天講安全設計:教材是資料不是指令,以及發布前的兩道閘門。


上一篇
Day 23|Caption 人設:不准瞎掰的真實起因,也不討互動
下一篇
Day 25|安全設計:教材是資料不是指令,以及兩道發布閘門
系列文
用 LINE Bot 和 Agent Skill 自動化整理筆記與 IG 發文 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言