打开/关闭菜单
26
6679
46
1.2万
OGAS数据中枢
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

微件:GameSwitcher

来自OGAS数据中枢
弃权者留言 | 贡献2026年6月20日 (六) 11:18的版本

<script type="text/javascript"> document.addEventListener('DOMContentLoaded', function () {

 var switcher = document.querySelector('.game-switcher');
 if (!switcher) return;
 var heroWrap = document.getElementById('gs-hero-wrap');
 var loaded = {};
 var prefetching = {};
 var storageKey = 'gs-cache-';
 // ============ 初始化头图 ============
 // 用 CSS background-image 接管头图,避免 img 标签的切换闪白问题
 var firstBtn = switcher.querySelector('.gs-tab-btn');
 if (heroWrap && firstBtn) {
   heroWrap.style.backgroundImage = 'url(' + firstBtn.dataset.hero + ')';
   heroWrap.dataset.heroSrc = firstBtn.dataset.hero;
 }
 // ============ 初始化按钮和默认面板 ============
 if (firstBtn) {
   firstBtn.classList.add('gs-active');
   firstBtn.setAttribute('aria-selected', 'true');
 }
 var defaultPanel = switcher.querySelector('.gs-content-panel.gs-active');
 if (defaultPanel) {
   // 默认面板直接可见,不走入场动画
   defaultPanel.classList.add('gs-visible');
 }
 // ============ 点击 ============
 switcher.addEventListener('click', function (e) {
   var btn = e.target.closest('.gs-tab-btn');
   if (btn) activate(btn);
 });
 // ============ 键盘 ============
 switcher.addEventListener('keydown', function (e) {
   var btn = e.target.closest('.gs-tab-btn');
   if (!btn) return;
   if (e.key === 'Enter' || e.key === ' ') {
     e.preventDefault();
     activate(btn);
   }
   if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
     e.preventDefault();
     var btns = Array.from(switcher.querySelectorAll('.gs-tab-btn'));
     var idx = btns.indexOf(btn);
     var next = e.key === 'ArrowRight' ? idx + 1 : idx - 1;
     if (next >= 0 && next < btns.length) btns[next].focus();
   }
 });
 // ============ 悬停预加载 ============
 switcher.addEventListener('mouseenter', function (e) {
   var btn = e.target.closest('.gs-tab-btn');
   if (!btn) return;
   var panelId = btn.dataset.panel;
   var pageName = btn.dataset.page;
   if (pageName && !loaded[panelId] && !prefetching[panelId]) {
     prefetch(panelId, pageName);
   }
 }, true);
 // ============ 激活面板 ============
 function activate(btn) {
   if (btn.classList.contains('gs-active')) return;
   var panelId = btn.dataset.panel;
   var heroSrc = btn.dataset.hero;
   var pageName = btn.dataset.page;
   switchHero(heroSrc);
   // 按钮高亮
   switcher.querySelectorAll('.gs-tab-btn').forEach(function (b) {
     b.classList.remove('gs-active');
     b.setAttribute('aria-selected', 'false');
   });
   btn.classList.add('gs-active');
   btn.setAttribute('aria-selected', 'true');
   // 隐藏当前面板
   switcher.querySelectorAll('.gs-content-panel').forEach(function (p) {
     p.classList.remove('gs-visible');
     p.classList.remove('gs-active');
   });
   // 找到或创建目标面板
   var panel = document.getElementById(panelId);
   if (!panel) {
     panel = document.createElement('div');
     panel.id = panelId;
     panel.className = 'gs-content-panel';
     switcher.querySelector('.gs-content').appendChild(panel);
     loadContent(panel, panelId, pageName);
   }
   // display:block,16ms 后再加 gs-visible 触发 transition
   panel.classList.add('gs-active');
   setTimeout(function () {
     panel.classList.add('gs-visible');
   }, 16);
   schedulePrefetchOthers();
 }
 // ============ 头图交叉溶解 ============
 function switchHero(url) {
   if (!heroWrap || !url) return;
   if (heroWrap.dataset.heroSrc === url) return;
   // 移除上一次未完成的叠加层
   var old = heroWrap.querySelector('.gs-hero-overlay');
   if (old) heroWrap.removeChild(old);
   var overlay = document.createElement('div');
   overlay.className = 'gs-hero-overlay';
   overlay.style.backgroundImage = 'url(' + url + ')';
   heroWrap.appendChild(overlay);
   // 预加载新图,加载完再淡入
   var img = new Image();
   img.onload = function () {
     requestAnimationFrame(function () {
       requestAnimationFrame(function () {
         overlay.classList.add('gs-hero-overlay--in');
         setTimeout(function () {
           heroWrap.style.backgroundImage = 'url(' + url + ')';
           heroWrap.dataset.heroSrc = url;
           if (heroWrap.contains(overlay)) heroWrap.removeChild(overlay);
         }, 420);
       });
     });
   };
   img.onerror = function () {
     if (heroWrap.contains(overlay)) heroWrap.removeChild(overlay);
   };
   img.src = url;
 }
 // ============ 内容加载 ============
 function loadContent(panel, panelId, pageName) {
   if (loaded[panelId]) {
     panel.innerHTML = loaded[panelId];
     return;
   }
   try {
     var stored = localStorage.getItem(storageKey + panelId);
     if (stored) {
       loaded[panelId] = stored;
       panel.innerHTML = stored;
       return;
     }
   } catch (e) {}
   if (!pageName) return;

panel.innerHTML = '

加载中...

';

   fetchPage(pageName).then(function (html) {
     loaded[panelId] = html;
     try { localStorage.setItem(storageKey + panelId, html); } catch (e) {}
     panel.innerHTML = html;
   }).catch(function () {

panel.innerHTML = '

加载失败,<a href="javascript:location.reload()">刷新</a>重试

';

   });
 }
 // ============ 预加载 ============
 function prefetch(panelId, pageName) {
   prefetching[panelId] = true;
   try {
     var stored = localStorage.getItem(storageKey + panelId);
     if (stored) {
       loaded[panelId] = stored;
       prefetching[panelId] = false;
       return;
     }
   } catch (e) {}
   fetchPage(pageName).then(function (html) {
     loaded[panelId] = html;
     try { localStorage.setItem(storageKey + panelId, html); } catch (e) {}
   }).finally(function () {
     prefetching[panelId] = false;
   });
 }
 function schedulePrefetchOthers() {
   setTimeout(function () {
     switcher.querySelectorAll('.gs-tab-btn').forEach(function (btn) {
       var panelId = btn.dataset.panel;
       var pageName = btn.dataset.page;
       if (!loaded[panelId] && !prefetching[panelId] && pageName) {
         prefetch(panelId, pageName);
       }
     });
   }, 500);
 }
 // ============ API 请求 ============
 function fetchPage(pageName) {
   var url = mw.util.wikiScript('api') +
     '?action=parse&page=' + encodeURIComponent(pageName) +
     '&prop=text&format=json&disablelimitreport=1&origin=*';
   return fetch(url)
     .then(function (r) { return r.json(); })
     .then(function (data) {
       if (data.parse && data.parse.text) return data.parse.text['*'];
       throw new Error('empty');
     });
 }

}); </script>