2026-10-27 TUE UTC 5:36 AM TPE 1:36 PM 每小時自動更新 中文 EN
LIVE快訊
←返回網站更新紀錄 Site Changelog

提交原始紀錄

Fix logo flight remnant and animate team mark (#408)

SHA
5013d8c5285bc14526bf6995832ed69a22a3cd78
提交時間
作者
neo · 125815713+nnneeeooo-nnneeeooo@users.noreply.github.com
提交者
GitHub · noreply@github.com

變更檔案(6)

  • pipeline/build.py modified · +1 / −0
  • static/site.css modified · +1 / −1
  • static/team-logo.js added · +37 / −0
  • static/team.css modified · +61 / −0
  • templates/team.html modified · +6 / −1
  • tests/test_logo_assets.py modified · +6 / −0

完整差異

diff --git a/pipeline/build.py b/pipeline/build.py\nindex 163571efe..5f0743122 100644\n--- a/pipeline/build.py\n+++ b/pipeline/build.py\n@@ -109,6 +109,7 @@ def static_asset_version() -> str:\n         "briefing.css",\n         "briefing.js",\n         "team.css",\n+        "team-logo.js",\n         "team-neo.jpg",\n         "radar.css",\n         "radar.js",\ndiff --git a/static/site.css b/static/site.css\nindex 248dea933..a56631aa8 100644\n--- a/static/site.css\n+++ b/static/site.css\n@@ -73,7 +73,7 @@ a.ticker-item:hover .ticker-time { color: var(--color-accent); }\n   object-fit: contain; background: transparent;\n }\n .brand-link.is-flying { z-index: 3; }\n-.brand-link.is-flying .brand-logo { clip-path: inset(0 0 0 23.5%); }\n+.brand-link.is-flying .brand-logo { clip-path: inset(0 0 0 25%); }\n .brand-flight-sprite {\n   position: absolute; top: 0; left: 0; width: 25%; height: 100%;\n   visibility: hidden; pointer-events: none;\ndiff --git a/static/team-logo.js b/static/team-logo.js\nnew file mode 100644\nindex 000000000..d570c2079\n--- /dev/null\n+++ b/static/team-logo.js\n@@ -0,0 +1,37 @@\n+/* A brief radar-like flourish for the founder's wing mark. */\n+(function () {\n+  "use strict";\n+\n+  var trigger = document.querySelector("[data-team-logo-play]");\n+  if (!trigger || !window.matchMedia) return;\n+  var reduced = window.matchMedia("(prefers-reduced-motion: reduce)");\n+  var timer = 0;\n+\n+  function play(restart) {\n+    if (reduced.matches || document.documentElement.dataset.theme === "8bit") return;\n+    if (trigger.classList.contains("is-playing")) {\n+      if (!restart) return;\n+      trigger.classList.remove("is-playing");\n+      void trigger.offsetWidth;\n+    }\n+    trigger.classList.add("is-playing");\n+    window.clearTimeout(timer);\n+    timer = window.setTimeout(function () {\n+      trigger.classList.remove("is-playing");\n+    }, 2400);\n+  }\n+\n+  trigger.addEventListener("pointerenter", function (event) {\n+    if (event.pointerType === "mouse" || event.pointerType === "pen") play(false);\n+  });\n+  trigger.addEventListener("click", function () { play(true); });\n+\n+  if ("IntersectionObserver" in window) {\n+    var observer = new IntersectionObserver(function (entries) {\n+      if (!entries.some(function (entry) { return entry.isIntersecting; })) return;\n+      observer.disconnect();\n+      window.setTimeout(function () { play(false); }, 350);\n+    }, { threshold: .6 });\n+    observer.observe(trigger);\n+  }\n+})();\ndiff --git a/static/team.css b/static/team.css\nindex 4c367f96a..e1851456c 100644\n--- a/static/team.css\n+++ b/static/team.css\n@@ -13,7 +13,68 @@\n .team-introduction { grid-area: intro; min-width: 0; align-self: end; padding-top: 20px; }\n .team-introduction h2 { display: flex; align-items: center; gap: 16px; margin: 0 0 24px; font-size: clamp(88px, 9vw, 120px); font-weight: 800; line-height: 1; letter-spacing: -.065em; }\n .team-name-logo { display: block; width: .9em; height: .9em; flex: 0 0 auto; object-fit: contain; }\n+.team-logo-trigger {\n+  position: relative; display: block; flex: 0 0 .9em; width: .9em; height: .9em;\n+  padding: 0; border: 0; background: transparent; color: inherit;\n+  font: inherit; line-height: 1; cursor: pointer; isolation: isolate;\n+  -webkit-tap-highlight-color: transparent;\n+}\n+.team-logo-trigger .team-name-logo { position: relative; z-index: 2; width: 100%; height: 100%; }\n+.team-logo-trigger::before, .team-logo-trigger::after {\n+  content: ""; position: absolute; inset: -.09em; border: 2px solid #f2643c;\n+  border-radius: 50%; opacity: 0; pointer-events: none;\n+}\n+.team-logo-trigger::after { border-color: #f2a23a; }\n+.team-logo-orbit {\n+  position: absolute; inset: -.16em; z-index: 1; border: 2px solid transparent;\n+  border-top-color: #ec3013; border-right-color: #f2643c;\n+  border-bottom-color: #f2a23a; border-radius: 50%; opacity: 0;\n+  pointer-events: none;\n+}\n+.team-logo-signal {\n+  position: absolute; top: 17%; left: 78%; z-index: 1;\n+  width: 6px; height: 6px; border-radius: 50%; background: #f2a23a;\n+  box-shadow: 0 0 0 3px color-mix(in srgb, #f2a23a 22%, transparent);\n+  opacity: 0; pointer-events: none;\n+}\n+.team-logo-trigger.is-playing .team-name-logo { animation: team-logo-wake 1.8s cubic-bezier(.2,.75,.2,1) both; }\n+.team-logo-trigger.is-playing::before { animation: team-logo-echo 1.45s .12s ease-out both; }\n+.team-logo-trigger.is-playing::after { animation: team-logo-echo 1.45s .36s ease-out both; }\n+.team-logo-trigger.is-playing .team-logo-orbit { animation: team-logo-orbit 2.1s ease-in-out both; }\n+.team-logo-trigger.is-playing .team-logo-signal { animation: team-logo-signal 1.8s .13s ease-in-out both; }\n+.team-logo-trigger:focus-visible { outline: 3px solid var(--color-accent); outline-offset: 12px; border-radius: 50%; }\n+@keyframes team-logo-wake {\n+  0% { transform: translateY(8px) rotate(-22deg) scale(.72); }\n+  24% { transform: translateY(-8px) rotate(11deg) scale(1.11); }\n+  47% { transform: translateY(2px) rotate(-7deg) scale(.94); }\n+  68% { transform: translateY(-3px) rotate(4deg) scale(1.04); }\n+  100% { transform: none; }\n+}\n+@keyframes team-logo-echo {\n+  0% { transform: scale(.38); opacity: .85; }\n+  100% { transform: scale(1.52); opacity: 0; }\n+}\n+@keyframes team-logo-orbit {\n+  0% { transform: rotate(-100deg) scale(.65); opacity: 0; }\n+  17% { opacity: .9; }\n+  70% { opacity: .85; }\n+  100% { transform: rotate(440deg) scale(1.08); opacity: 0; }\n+}\n+@keyframes team-logo-signal {\n+  0%, 100% { transform: rotate(-90deg) translateX(0) scale(.3); opacity: 0; }\n+  20% { opacity: 1; }\n+  65% { transform: rotate(225deg) translateX(65px) scale(1); opacity: 1; }\n+  85% { opacity: 0; }\n+}\n+@media (prefers-reduced-motion: reduce) {\n+  .team-logo-trigger.is-playing .team-name-logo,\n+  .team-logo-trigger.is-playing::before,\n+  .team-logo-trigger.is-playing::after,\n+  .team-logo-trigger.is-playing .team-logo-orbit,\n+  .team-logo-trigger.is-playing .team-logo-signal { animation: none; }\n+}\n .team-name-logo--pixel { display: none; }\n+[data-theme="8bit"] .team-logo-trigger { display: none; }\n [data-theme="8bit"] .team-name-logo--classic { display: none; }\n [data-theme="8bit"] .team-name-logo--pixel { display: block; image-rendering: pixelated; }\n .team-page .team-role { margin: 0 0 26px; font-size: 14px; font-weight: 700; line-height: 1.8; }\ndiff --git a/templates/team.html b/templates/team.html\nindex 28324c27f..5ac651362 100644\n--- a/templates/team.html\n+++ b/templates/team.html\n@@ -1,6 +1,7 @@\n {% extends "base.html" %}\n {% block head_extra %}\n <link rel="stylesheet" href="{{ base }}/assets/team.css?v={{ asset_version }}">\n+<script src="{{ base }}/assets/team-logo.js?v={{ asset_version }}" defer></script>\n {% endblock %}\n {% block content %}\n <main class="team-page" id="team-main">\n@@ -19,7 +20,11 @@\n \n       <div class="team-introduction">\n         <h2 id="team-name">\n-          <img class="team-name-logo team-name-logo--classic" src="{{ base }}/assets/skytical-team-mark.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n+          <button class="team-logo-trigger" type="button" data-team-logo-play aria-label="{{ '重播翼標動畫' if lang == 'zh' else 'Replay wing animation' }}">\n+            <span class="team-logo-orbit" aria-hidden="true"></span>\n+            <span class="team-logo-signal" aria-hidden="true"></span>\n+            <img class="team-name-logo team-name-logo--classic" src="{{ base }}/assets/skytical-team-mark.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n+          </button>\n           <img class="team-name-logo team-name-logo--pixel" src="{{ base }}/assets/skytical-mark-8bit.svg?v={{ asset_version }}" width="88" height="88" alt="" aria-hidden="true">\n           <span>{{ team.name }}</span>\n         </h2>\ndiff --git a/tests/test_logo_assets.py b/tests/test_logo_assets.py\nindex 6de054d6b..963222e54 100644\n--- a/tests/test_logo_assets.py\n+++ b/tests/test_logo_assets.py\n@@ -92,6 +92,12 @@ def main() -> None:\n     assert 'data-brand-sprite' in home\n     assert f'/assets/logo-flight.js?v={build.ASSET_VERSION}' in home\n     assert (ROOT / "site" / "assets" / "logo-flight.js").is_file()\n+    assert '.brand-logo { clip-path: inset(0 0 0 25%); }' in css\n+    team = (ROOT / "site" / "team" / "index.html").read_text(encoding="utf-8")\n+    assert 'data-team-logo-play' in team\n+    assert f'/assets/team-logo.js?v={build.ASSET_VERSION}' in team\n+    assert (ROOT / "site" / "assets" / "team-logo.js").is_file()\n+    assert 'class="team-inline-logo team-name-logo--classic"' in team\n     assert f'/assets/skytical-mark.svg?v={build.ASSET_VERSION}' in home\n     assert f'/assets/skytical-social.png?v={build.ASSET_VERSION}' in home\n     assert "/assets/logo-fix.css" not in home

你發現了隱藏主題!

8-bit主題已解鎖。 按下「重新整理」就會直接套用,之後可隨時在主題選單切換。