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

Widget:GameSwitcher:修订间差异

来自OGAS数据中枢
弃权者留言 | 贡献
无编辑摘要
弃权者留言 | 贡献
无编辑摘要
第1行: 第1行:
<script type="text/javascript">
<noinclude>
document.addEventListener('DOMContentLoaded', function () {
本Widget为少女前线系列多游戏首页提供切换交互逻辑。
   var switcher = document.querySelector('.game-switcher');
不接受参数,直接在首页调用:<code><nowiki>{{#widget:GFHomepageSwitcher}}</nowiki></code>
  if (!switcher) return;
</noinclude><onlyinclude><script>
(function () {
   'use strict';


var heroWrap = document.getElementById('gs-hero-wrap');
  function init() {
var heroImg = null;
    var tabs = document.querySelectorAll('#gf-homepage .gf-tab');
    var heroSlides = document.querySelectorAll('#gf-homepage .gf-hero-slide');
    var contentPanels = document.querySelectorAll('#gf-homepage .gf-content-panel');


if (heroWrap && firstBtn) {
    if (!tabs.length) return;
  heroImg = document.createElement('img');
  heroImg.id = 'gs-hero-img';
  heroImg.src = firstBtn.dataset.hero;
  heroWrap.appendChild(heroImg);
}
var loaded = {};
  var prefetching = {};
  var storageKey = 'gs-cache-';


  // ============ 初始化双层头图 ============
    tabs.forEach(function (tab) {
  var layerStyle = 'position:absolute;top:0;left:0;width:100%;height:100%;background-size:cover;background-position:center;transition:opacity 0.4s ease';
      tab.addEventListener('click', function () {
        var game = this.getAttribute('data-game');


  var layerA = document.createElement('div');
        tabs.forEach(function (t) {
  var layerB = document.createElement('div');
          t.classList.remove('active');
  layerA.className = 'gs-hero-layer';
          t.setAttribute('aria-selected', 'false');
  layerB.className = 'gs-hero-layer';
        });
  layerA.style.cssText = layerStyle + ';opacity:1';
        this.classList.add('active');
  layerB.style.cssText = layerStyle + ';opacity:0';
        this.setAttribute('aria-selected', 'true');


  if (heroWrap) {
        heroSlides.forEach(function (s) {
    heroWrap.appendChild(layerA);
          s.classList.remove('active');
    heroWrap.appendChild(layerB);
          if (s.id === 'hero-' + game) {
  }
            s.classList.add('active');
          }
        });


  var activeLayer  = layerA;
        contentPanels.forEach(function (p) {
  var inactiveLayer = layerB;
          p.classList.remove('active');
 
          if (p.id === 'content-' + game) {
  // ============ 初始化按钮和头图 ============
            p.classList.add('active');
  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();
   }
   }


  // ============ 头图交叉溶解(双层互换) ============
   if (document.readyState === 'loading') {
function switchHero(url) {
     document.addEventListener('DOMContentLoaded', init);
  if (!heroImg || !url) return;
   } else {
   if (heroImg.src === url) return;
     init();
 
  // 先预加载
  var preload = new Image();
  preload.onload = function () {
    // 淡出
    heroImg.style.opacity = '0';
 
    // 等淡出真正结束再换图
    heroImg.addEventListener('transitionend', function handler() {
      heroImg.removeEventListener('transitionend', handler);
      heroImg.src = url;
      // 换完淡入
      heroImg.style.opacity = '1';
    });
  };
  preload.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 = '<div class="gs-loading">加载中...</div>';
 
    fetchPage(pageName).then(function (html) {
      loaded[panelId] = html;
      try { localStorage.setItem(storageKey + panelId, html); } catch (e) {}
      panel.innerHTML = html;
     }).catch(function () {
      panel.innerHTML = '<div class="gs-loading">加载失败,<a href="javascript:location.reload()">刷新</a>重试</div>';
    });
  }
 
  // ============ 预加载 ============
  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>
</script></onlyinclude>

2026年6月20日 (六) 16:57的版本

本Widget为少女前线系列多游戏首页提供切换交互逻辑。 不接受参数,直接在首页调用:{{#widget:GFHomepageSwitcher}} <script> (function () {

 'use strict';
 function init() {
   var tabs = document.querySelectorAll('#gf-homepage .gf-tab');
   var heroSlides = document.querySelectorAll('#gf-homepage .gf-hero-slide');
   var contentPanels = document.querySelectorAll('#gf-homepage .gf-content-panel');
   if (!tabs.length) return;
   tabs.forEach(function (tab) {
     tab.addEventListener('click', function () {
       var game = this.getAttribute('data-game');
       tabs.forEach(function (t) {
         t.classList.remove('active');
         t.setAttribute('aria-selected', 'false');
       });
       this.classList.add('active');
       this.setAttribute('aria-selected', 'true');
       heroSlides.forEach(function (s) {
         s.classList.remove('active');
         if (s.id === 'hero-' + game) {
           s.classList.add('active');
         }
       });
       contentPanels.forEach(function (p) {
         p.classList.remove('active');
         if (p.id === 'content-' + game) {
           p.classList.add('active');
         }
       });
     });
   });
 }
 if (document.readyState === 'loading') {
   document.addEventListener('DOMContentLoaded', init);
 } else {
   init();
 }

})(); </script>