← 返回網站更新紀錄
Site Changelog
提交原始紀錄
改善手機 LIVE 快訊跑馬燈速度與字型 (#65)
依文字寬度控制手機跑馬燈為約 24 px/s,改用繁中系統字型並支援減少動態效果。
- SHA
7ab6038e6fb68cefbf3d4c0703c568a4d61dab12
- 提交時間
- 作者
- nnneeeooo-nnneeeooo
- 提交者
- web-flow
變更檔案 (4)
-
static/app.js
modified · +18 / −0
-
static/site.css
modified · +21 / −1
-
templates/base.html
modified · +1 / −1
-
tests/test_mobile_layout.py
modified · +7 / −0
完整差異
@@ -77,6 +77,24 @@\n });\n }\n \n+ /* — LIVE ticker: keep a readable speed even as headline length changes — */\n+ var marqueeTrack = document.querySelector(".marquee-track");\n+ function setMarqueeDuration() {\n+ if (!marqueeTrack) return;\n+ var tickerPass = marqueeTrack.querySelector(".ticker-pass");\n+ if (!tickerPass) return;\n+ var pixelsPerSecond = window.matchMedia("(max-width: 720px)").matches ? 24 : 52;\n+ var seconds = Math.max(20, tickerPass.scrollWidth / pixelsPerSecond);\n+ marqueeTrack.style.setProperty("--ticker-duration", seconds.toFixed(1) + "s");\n+ }\n+ if (marqueeTrack) {\n+ setMarqueeDuration();\n+ if (document.fonts && document.fonts.ready) {\n+ document.fonts.ready.then(setMarqueeDuration);\n+ }\n+ window.addEventListener("resize", setMarqueeDuration);\n+ }\n+\n /* — home: category filter over feed rows ([data-cat]) — */\n var catBtns = Array.prototype.slice.call(document.querySelectorAll("[data-filter-cat]"));\n catBtns.forEach(function (b) {\n@@ -35,6 +35,7 @@ a:hover { color: var(--color-accent-600); }\n ↗ mark + hover color (owner: clickable-looking but NO underline);\n the track pauses on hover so items can actually be clicked. — */\n .marquee-track:hover { animation-play-state: paused; }\n+.ticker-pass { padding-right: 70px; font-size: 13px; white-space: nowrap; }\n .ticker-item { color: inherit; text-decoration: none; }\n a.ticker-item:hover { color: var(--color-accent); }\n a.ticker-item:hover .ticker-time { color: var(--color-accent); }\n@@ -56,7 +57,10 @@ a.ticker-item:hover .ticker-time { color: var(--color-accent); }\n box-shadow: 0 0 32px rgba(0, 0, 0, .08);\n }\n .pulse-dot { width: 8px; height: 8px; background: var(--color-accent); animation: avwire-pulse 2s infinite; }\n-.marquee-track { display: flex; width: max-content; animation: avwire-marquee 80s linear infinite; white-space: nowrap; }\n+.marquee-track {\n+ display: flex; width: max-content; white-space: nowrap;\n+ animation: avwire-marquee var(--ticker-duration, 80s) linear infinite;\n+}\n \n /* — Google-style search, deliberately the first row of every page — */\n .header-search-row {\n@@ -492,6 +496,22 @@ a.flash-row:hover { color: inherit; }\n }\n .nav-link.active { border-color: var(--color-accent); }\n .ticker-label { padding: 7px 10px; font-size: 11px; letter-spacing: .08em; }\n+ .marquee-track { --ticker-duration: 180s; }\n+ .ticker-pass {\n+ font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", "Noto Sans TC", sans-serif;\n+ font-size: 14px; font-weight: 500; line-height: 1.5; letter-spacing: .01em;\n+ -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;\n+ }\n+ .ticker-time {\n+ font-family: "SFMono-Regular", ui-monospace, "Roboto Mono", Consolas, monospace;\n+ font-size: 12px; font-weight: 700; letter-spacing: 0; font-variant-numeric: tabular-nums;\n+ }\n+}\n+\n+@media (prefers-reduced-motion: reduce) {\n+ .ticker-window { overflow-x: auto; }\n+ .marquee-track { animation: none; transform: none; }\n+ .marquee-track .ticker-pass:nth-child(2) { display: none; }\n }\n \n @media (max-width: 640px) {\n@@ -88,7 +88,7 @@\n <div class="ticker-window">\n <div class="marquee-track">\n {% for pass in range(2) %}\n- <span style="padding-right:70px;font-size:13px;white-space:nowrap;">\n+ <span class="ticker-pass">\n {%- for it in ticker_items -%}\n {%- if it.url -%}\n <a class="ticker-item" href="{{ it.url }}"{% if it.external %} target="_blank" rel="noopener"{% endif %}><b class="ticker-time{% if it.hot %} hot{% endif %}">{{ it.time }}</b> {{ it.text }}<span class="ticker-go">↗</span></a>\n@@ -25,6 +25,7 @@ def main() -> None:\n assert 'class="site-nav" id="site-nav"' in home\n assert 'class="utility-row"' in home\n assert 'class="ticker-window"' in home\n+ assert 'class="ticker-pass"' in home\n assert 'class="feed-copy"' in home\n assert 'class="article-page"' in article\n assert 'class="article-source-row"' in article\n@@ -34,9 +35,15 @@ def main() -> None:\n assert "grid-template-columns: repeat(2, minmax(0, 1fr))" in css\n assert ".center-col { order: 1;" in css\n assert ".feed-copy { grid-column: 1 / -1; }" in css\n+ assert "--ticker-duration: 180s" in css\n+ assert '"PingFang TC"' in css\n+ assert "@media (prefers-reduced-motion: reduce)" in css\n+ assert ".marquee-track .ticker-pass:nth-child(2) { display: none; }" in css\n assert 'setAttribute("aria-expanded"' in script\n assert 'event.key === "Escape"' in script\n assert 'matchMedia("(min-width: 721px)")' in script\n+ assert 'matches ? 24 : 52' in script\n+ assert 'style.setProperty("--ticker-duration"' in script\n \n print("test_mobile_layout: OK")\n