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

微件:GameSwitcher

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

<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-';
 // ============ 初始化双层头图 ============
 var layerStyle = 'position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;transition:opacity 0.4s ease';
 var layerA = document.createElement('div');
 var layerB = document.createElement('div');
 layerA.className = 'gs-hero-layer';
 layerB.className = 'gs-hero-layer';
 layerA.style.cssText = layerStyle + ';opacity:1';
 layerB.style.cssText = layerStyle + ';opacity:0';
 if (heroWrap) {
   heroWrap.appendChild(layerA);
   heroWrap.appendChild(layerB);
 }
 var activeLayer   = layerA;
 var inactiveLayer = layerB;
 // ============ 初始化按钮和头图 ============
 var firstBtn = switcher.querySelector('.gs-tab-btn');
 if (firstBtn) {
   firstBtn.classList.add('gs-active');
   firstBtn.setAttribute('aria-selected', 'true');
   if (heroWrap) {
     activeLayer.style.backgroundImage = 'url(' + firstBtn.dataset.hero + ')';
   }
 }
 // 默认面板直接可见,不走入场动画
 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);
   }
   panel.classList.add('gs-active');
   setTimeout(function () {
     panel.classList.add('gs-visible');
   }, 16);
   schedulePrefetchOthers();
 }
 // ============ 头图交叉溶解(双层互换) ============

function switchHero(url) {

 if (!heroWrap || !url) return;
 if (activeLayer.style.backgroundImage === 'url("' + url + '")') return;
 var img = new Image();
 img.onload = function () {
   // 先把新图写入非活跃层
   inactiveLayer.style.backgroundImage = 'url(' + url + ')';
   // 隔两帧确保浏览器已完成一次渲染,再触发 transition
   requestAnimationFrame(function () {
     requestAnimationFrame(function () {
       inactiveLayer.style.opacity = '1';
       activeLayer.style.opacity = '0';
       // transition 结束后再互换角色,防止快速点击时状态错乱
       var _active = activeLayer;
       var _inactive = inactiveLayer;
       setTimeout(function () {
         activeLayer   = _inactive;
         inactiveLayer = _active;
       }, 420);
     });
   });
 };
 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>