"use strict"; (function () { var modal = document.getElementById("modal"); var closeBtn = document.getElementById("modalClose"); var appBar = document.getElementById("appBar"); var wrapper = document.getElementById("wrapper"); var lastImage = document.getElementById("lastImage"); var chapterNumEl = document.getElementById("chapterNum"); var SHOW_DELAY = 1000; // 最後の画像が表示領域に入ってからモーダルを出すまでの待ち時間 var LOAD_FALLBACK = 15000; // image1〜19 の一部が読めなくても最後の画像を読み込む保険 var CHAPTER_MIN = 120; var CHAPTER_MAX = 160; var timer = null; var shown = false; // モーダル表示中か var armed = true; // 次のトリガーを受け付けるか var loaded = false; // 最後の画像の読み込みが完了したか var requested = false; // 最後の画像の読み込みを開始済みか if (chapterNumEl) { var randomChapter = Math.floor(Math.random() * (CHAPTER_MAX - CHAPTER_MIN + 1)) + CHAPTER_MIN; chapterNumEl.textContent = randomChapter; } // 最後の画像以外の wrapper 内画像 var others = []; if (wrapper) { Array.prototype.forEach.call(wrapper.querySelectorAll("img"), function (img) { if (img !== lastImage) others.push(img); }); } // image1〜19 がすべて読み込み終わってから image20 を読み込む。 // こうすると image20 は必ず「レイアウト確定済みの画像群の一番下」に配置され、 // 初期表示時に上部へ来て誤発火することが構造的に起きない。 function loadLastImage() { if (requested || !lastImage || !lastImage.dataset.src) return; requested = true; lastImage.addEventListener("load", function () { loaded = true; check(); }); lastImage.setAttribute("src", lastImage.dataset.src); } function maybeLoadLastImage() { if (requested) return; for (var i = 0; i < others.length; i++) { if (!others[i].complete) return; } loadLastImage(); } others.forEach(function (img) { img.addEventListener("load", maybeLoadLastImage); img.addEventListener("error", maybeLoadLastImage); }); maybeLoadLastImage(); setTimeout(loadLastImage, LOAD_FALLBACK); // 最後の画像が「1pxでも」表示領域に入っているか function isLastImageVisible() { if (!loaded) return false; var rect = lastImage.getBoundingClientRect(); var vh = window.innerHeight || document.documentElement.clientHeight; return rect.top < vh && rect.bottom > 0; } function showModal() { timer = null; shown = true; modal.classList.add("is-show"); } function hideModal() { shown = false; modal.classList.remove("is-show"); appBar.classList.add("is-show"); wrapper.classList.add("has-appbar"); armed = !isLastImageVisible(); } function check() { if (isLastImageVisible()) { // 一度入ったらタイマー開始。以降は画面外に出てもキャンセルしない if (armed && !shown && !timer) { timer = setTimeout(showModal, SHOW_DELAY); armed = false; } } else if (!shown) { // 画像が画面外に出たら次のトリガーに備える armed = true; } } window.addEventListener("scroll", check, { passive: true }); window.addEventListener("resize", check, { passive: true }); closeBtn.addEventListener("click", hideModal); })();