PageSourceSearch

https://eleanor2026.com/etg/assets/animejs.js

js eleanor2026.com collected 2026-10-06 18:43:01 UTC 27,796 bytes, 804 lines download raw bytes

1/**
2 * animejs.js — 映画『エレノアってグレイト』公式サイト
3 * .layers 内の全アニメーションを担当(anime.js v3)
4 * window load 時に自動起動、二重実行防止済み
5 */
6
7"use strict";
8
9// ヘルパー: NodeList の各要素に opacity:1 を強制設定
10function forceVisible(nodes) {
11  nodes.forEach(function (el) {
12    el.style.opacity = "1";
13  });
14}
15
16// =============================================================================
17// Layers アニメーション — .layers 内の各レイヤーを動かす
18// 各レイヤー: .bg, .two, .bw, .chair
19// =============================================================================
20var _layersAnimStarted = false;
21function startLayersAnimation() {
22  if (_layersAnimStarted) return;
23  _layersAnimStarted = true;
24
25  if (typeof anime === "undefined") {
26    console.warn("[animejs.js] anime.js が読み込まれていません");
27    return;
28  }
29
30  // 各レイヤーを取得(sp620/pc620で表示切替、非表示側はdisplay:noneなので負荷なし)
31  var allBg = document.querySelectorAll(".layers .bg");
32  var allTwo = document.querySelectorAll(".layers .two");
33  var allBw = document.querySelectorAll(".layers .bw");
34  var allChair = document.querySelectorAll(".layers .chair");
35  var allColorFill = document.querySelectorAll(".layers .color-fill");
36
37  if (!allBg.length && !allTwo.length && !allBw.length && !allChair.length)
38    return;
39
40  // .bw は非表示
41  allBw.forEach(function (el) {
42    el.style.display = "none";
43  });
44
45  // マスクSVGのURL取得ヘルパー
46  function maskSrc(el) {
47    return el.classList.contains("pc620")
48      ? "etg/assets/img/layer/layer_silhouette_outline_mask.svg"
49      : "etg/assets/img/layer_s/layer_silhouette_outline_mask.svg";
50  }
51
52  // アスペクト比取得ヘルパー(color-fill / two のサイズ合わせ)
53  function aspectRatio(el) {
54    return el.classList.contains("pc620") ? "2880 / 1903" : "900 / 840";
55  }
56
57  // ==========================================================================
58  // 0. clip-path + baloon-logo 準備(ローディング完了後に開始)
59  // ==========================================================================
60  var clipPath = document.querySelector("#kv-clip path");
61  var clipStart, clipEnd;
62
63  if (clipPath) {
64    // 開始形: 全体を覆う大きな矩形(M+14C の同じコマンド数で矩形を近似)
65    // 14セグメント: 上辺4 + 右辺3 + 下辺4 + 左辺3
66    clipStart =
67      "M 0.0 0.0 C 0.0 0.0, 0.13 0.0, 0.27 0.0 C 0.40 0.0, 0.53 0.0, 0.67 0.0 C 0.80 0.0, 0.93 0.0, 1.0 0.0 C 1.0 0.0, 1.0 0.12, 1.0 0.25 C 1.0 0.38, 1.0 0.50, 1.0 0.63 C 1.0 0.75, 1.0 0.88, 1.0 1.0 C 1.0 1.0, 0.87 1.0, 0.73 1.0 C 0.60 1.0, 0.47 1.0, 0.33 1.0 C 0.20 1.0, 0.07 1.0, 0.0 1.0 C 0.0 1.0, 0.0 0.88, 0.0 0.75 C 0.0 0.63, 0.0 0.50, 0.0 0.38 C 0.0 0.25, 0.0 0.12, 0.0 0.0 C 0.0 0.0, 0.0 0.0, 0.0 0.0 Z";
68
69    // 終了形: 手描き風clip-path
70    var isPC = window.matchMedia("(min-width: 620px)").matches;
71    clipEnd =
72      "M 0.10 0.11 C 0.14 0.085, 0.30 0.095, 0.40 0.09 C 0.50 0.085, 0.60 0.10, 0.70 0.095 C 0.78 0.09, 0.84 0.085, 0.87 0.11 C 0.90 0.14, 0.915 0.17, 0.91 0.21 C 0.905 0.31, 0.915 0.43, 0.91 0.56 C 0.905 0.70, 0.915 0.82, 0.91 0.88 C 0.905 0.93, 0.89 0.96, 0.86 0.97 C 0.83 0.985, 0.75 0.975, 0.65 0.98 C 0.55 0.985, 0.45 0.975, 0.35 0.98 C 0.25 0.985, 0.13 0.99, 0.10 0.97 C 0.07 0.95, 0.05 0.93, 0.05 0.88 C 0.045 0.80, 0.05 0.65, 0.05 0.56 C 0.045 0.43, 0.05 0.31, 0.05 0.21 C 0.045 0.17, 0.06 0.14, 0.10 0.11 Z";
73  }
74
75  // baloon-logo / copy-vertical は初期非表示
76  var baloon = document.querySelector(".baloon-logo");
77  var copyVertical = document.querySelector(".copy-vertical");
78  if (baloon) {
79    baloon.style.opacity = "0";
80    baloon.style.top = "2%";
81    baloon.style.width = "26%";
82    baloon.style.maxWidth = "300px";
83    baloon.style.transform = "translateX(-50%)";
84    baloon.style.cursor = "default";
85    baloon.style.pointerEvents = "none";
86  }
87  if (copyVertical) copyVertical.style.opacity = "0";
88
89  // ロゴ + ハンバーガー + サイドナビは初期非表示
90  var siteLogoEl = document.querySelector(".site-logo");
91  var navbtnEl = document.querySelector(".navbtn");
92  var sideNavEl = document.querySelector(".side-nav");
93  if (siteLogoEl) siteLogoEl.style.opacity = "0";
94  if (navbtnEl) navbtnEl.style.opacity = "0";
95  if (sideNavEl) sideNavEl.style.opacity = "0";
96
97  // ローディング完了後にclip-path展開を開始する関数
98  function startClipReveal() {
99    if (!clipPath) return;
100    // clip-pathを全画面矩形で適用
101    clipPath.setAttribute("d", clipStart);
102    document.querySelector(".layers").style.clipPath = "url(#kv-clip)";
103
104    // 全画面 → 手描き風形状にクリップ
105    anime({
106      targets: clipPath,
107      d: [clipStart, clipEnd],
108      duration: 2500,
109      delay: 0,
110      easing: "easeInOutCubic",
111      complete: function () {
112        // clip-path完了 → baloon-logo を下からフェードイン
113        if (baloon) {
114          anime({
115            targets: baloon,
116            opacity: [0, 1],
117            translateY: ["2%", "0%"],
118            duration: 1200,
119            easing: "easeOutCubic",
120            complete: function () {
121              // 「喋ってる」アニメーション:
122              // anime.jsのフェードインが残したtransformをリセットしてから
123              // 自前loopで全transformを一括制御する
124              baloon.style.transformOrigin = "50% 100%";
125              // anime.jsが付けたtranslateY等をクリアし、CSSだけで位置を確定
126              baloon.style.transform = "translateX(-50%)";
127
128              var bAnim = { amp: 0, t: 0, vibT: 0 };
129              // 振幅をゼロから徐々に上げる(フェードイン→ループの繋ぎ)
130              anime({
131                targets: bAnim,
132                amp: [0, 1],
133                duration: 800,
134                easing: "easeInOutSine",
135              });
136              // pulse: 約2.4秒周期
137              anime({
138                targets: bAnim,
139                t: [0, Math.PI * 2],
140                duration: 2400,
141                easing: "linear",
142                loop: true,
143              });
144              // 微振動: 約0.6秒周期
145              anime({
146                targets: bAnim,
147                vibT: [0, Math.PI * 2],
148                duration: 600,
149                easing: "linear",
150                loop: true,
151              });
152              function updateBaloon() {
153                var a = bAnim.amp;
154                var sx = 1 + Math.sin(bAnim.t) * 0.02 * a;
155                var sy = 1 - Math.sin(bAnim.t) * 0.02 * a;
156                var tx = Math.sin(bAnim.vibT) * 1.2 * a;
157                var rot = Math.sin(bAnim.vibT) * 0.4 * a;
158                baloon.style.transform =
159                  "translateX(-50%) scaleX(" +
160                  sx +
161                  ") scaleY(" +
162                  sy +
163                  ") translateX(" +
164                  tx +
165                  "px) rotate(" +
166                  rot +
167                  "deg)";
168                requestAnimationFrame(updateBaloon);
169              }
170              updateBaloon();
171              // ロゴ + ハンバーガーフェードイン
172              if (siteLogoEl) {
173                anime({
174                  targets: siteLogoEl,
175                  opacity: [0, 1],
176                  duration: 800,
177                  delay: 200,
178                  easing: "easeOutCubic",
179                });
180              }
181              if (navbtnEl) {
182                anime({
183                  targets: navbtnEl,
184                  opacity: [0, 1],
185                  scale: [0.6, 1],
186                  duration: 600,
187                  delay: 400,
188                  easing: "easeOutBack",
189                });
190              }
191              // サイドナビフェードイン
192              if (sideNavEl) {
193                anime({
194                  targets: sideNavEl,
195                  opacity: [0, 1],
196                  translateY: [-10, 0],
197                  duration: 800,
198                  delay: 600,
199                  easing: "easeOutCubic",
200                });
201              }
202            },
203          });
204        }
205        // copy-vertical: baloonと並行で少し遅れてフェードイン
206        if (copyVertical) {
207          anime({
208            targets: copyVertical,
209            opacity: [0, 1],
210            translateY: [20, 0],
211            duration: 1000,
212            delay: 600,
213            easing: "easeOutCubic",
214          });
215        }
216      },
217    });
218  }
219
220  // --- タイムライン定数 ---
221  var T_FILL_START = 0; // color-fill ローディング開始
222  var T_FILL_DURATION = 2000; // ローディングfill時間
223  var T_TWO_START = 1200; // .two water fill 開始
224  var T_TWO_DURATION = 2000; // .two fill時間
225  var T_BG_START = 2200;
225 // .bg フェードイン開始
226  var T_CHAIR_START = 3000; // .chair フェードイン開始
227  // T_FRAME_*, T_CREDITS_START は削除済み(セクション6・7で使用していた)
228
229  // ==========================================================================
230  // 1. color-fill: シルエット形状で下からfill(ローディング演出)+ %カウンター
231  // ==========================================================================
232  var fillCounter = document.getElementById("fill-percent");
233  var fillCounterWrap = document.querySelector(".fill-counter");
234
235  allColorFill.forEach(function (el) {
236    var src = maskSrc(el);
237    var ar = aspectRatio(el);
238    el.style.aspectRatio = ar;
239    el.style.webkitMaskImage = "url(" + src + ")";
240    el.style.maskImage = "url(" + src + ")";
241    el.style.webkitMaskSize = "100% 100%";
242    el.style.maskSize = "100% 100%";
243    el.style.webkitMaskRepeat = "no-repeat";
244    el.style.maskRepeat = "no-repeat";
245    // 初期: 下からfill途中 + 70%スケール
246    el.style.webkitClipPath = "inset(100% 0 0 0)";
247    el.style.clipPath = "inset(100% 0 0 0)";
248    el.style.transform = "scale(0.7)";
249    el.style.transformOrigin = "center center";
250    el.style.opacity = "1";
251  });
252
253  var fillAnim = { progress: 0 };
254  anime({
255    targets: fillAnim,
256    progress: 1,
257    duration: T_FILL_DURATION,
258    delay: T_FILL_START,
259    easing: "easeInOutCubic",
260    update: function () {
261      var p = fillAnim.progress;
262      var pct = Math.round(p * 100);
263      // 下からfill
264      var top = (1 - p) * 100;
265      // scale: 0.7 → 1
266      var sc = 0.7 + p * 0.3;
267      allColorFill.forEach(function (el) {
268        el.style.webkitClipPath = "inset(" + top + "% 0 0 0)";
269        el.style.clipPath = "inset(" + top + "% 0 0 0)";
270        el.style.transform = "scale(" + sc + ")";
271      });
272      if (fillCounter) fillCounter.textContent = pct;
273    },
274    complete: function () {
275      if (fillCounterWrap) {
276        anime({
277          targets: fillCounterWrap,
278          opacity: [1, 0],
279          duration: 400,
280          easing: "easeInOutCubic",
281        });
282      }
283    },
284  });
285
286  // ==========================================================================
287  // 2. .two: water fill アニメーション(SVGマスク + グラデーション)
288  // ==========================================================================
289  allTwo.forEach(function (el) {
290    var src = maskSrc(el);
291    var ar = aspectRatio(el);
292    el.style.opacity = "0";
293    el.style.aspectRatio = ar;
294    el.style.webkitMaskImage =
295      "url(" + src + "), linear-gradient(to top, black 0%, transparent 0%)";
296    el.style.maskImage =
297      "url(" + src + "), linear-gradient(to top, black 0%, transparent 0%)";
298    el.style.webkitMaskComposite = "destination-in";
299    el.style.maskComposite = "intersect";
300    el.style.webkitMaskSize = "100% 100%, 100% 100%";
301    el.style.maskSize = "100% 100%, 100% 100%";
302    el.style.webkitMaskRepeat = "no-repeat, no-repeat";
303    el.style.maskRepeat = "no-repeat, no-repeat";
304  });
305
306  var twoAnim = { progress: 0 };
307  anime({
308    targets: twoAnim,
309    progress: 1,
310    duration: T_TWO_DURATION,
311    delay: T_TWO_START,
312    easing: "easeInOutCubic",
313    update: function () {
314      var p = twoAnim.progress;
315      var pPct = p * 100;
316      var grad =
317        "linear-gradient(to top, black " +
318        pPct +
319        "%, transparent " +
320        pPct +
321        "%)";
322      var twoOp = Math.min(1, p * 1.8);
323      var fillOp = Math.max(0, 1 - p * 1.8);
324      allTwo.forEach(function (el) {
325        var src = maskSrc(el);
326        el.style.webkitMaskImage = "url(" + src + "), " + grad;
327        el.style.maskImage = "url(" + src + "), " + grad;
328        el.style.opacity = twoOp;
329      });
330      allColorFill.forEach(function (el) {
331        el.style.opacity = fillOp;
332      });
333    },
334    complete: function () {
335      allTwo.forEach(function (el) {
336        forceVisible([el]);
337        el.classList.add("float-kv");
338      });
339      allColorFill.forEach(function (el) {
340        el.style.opacity = "0";
341      });
342    },
343  });
344
345  // ==========================================================================
346  // 3. SVG アウトライン stroke アニメーション(fillと同時開始)
347  // ==========================================================================
348  var outlinePaths = document.querySelectorAll(
349    ".silhouette-outline .outline-path",
350  );
351  if (outlinePaths.length) {
352    outlinePaths.forEach(function (path) {
353      var len = path.getTotalLength();
354      path.style.strokeDasharray = len;
355      path.style.strokeDashoffset = len;
356      path.style.opacity = "1";
357    });
358    anime({
359      targets: outlinePaths,
360      strokeDashoffset: [anime.setDashoffset, 0],
361      duration: T_TWO_DURATION + 300,
362      delay: T_TWO_START,
363      easing: "easeInOutCubic",
364      complete: function () {
365        anime({
366          targets: outlinePaths,
367          opacity: [1, 0],
368          duration: 600,
369          delay: 100,
370          easing: "easeInOutCubic",
371        });
372      },
373    });
374  }
375
376  // ==========================================================================
377  // 4. .bg フェードイン
378  // ==========================================================================
379  if (allBg.length) {
380    anime({
381      targets: allBg,
382      opacity: [0, 1],
383      duration: 2000,
384      delay: T_BG_START,
385      easing: "easeInOutCubic",
386      complete: function () {
387        forceVisible(allBg);
388      },
389    });
390  }
391
392  // ==========================================================================
393  // 5. .chair フェードイン
394  // ==========================================================================
395  if (allChair.length) {
396    anime({
397      targets: allChair,
398      opacity: [0, 1],
399      translateY: [30, 0],
400      duration: 2500,
401      delay: T_CHAIR_START,
402      easing: "easeInOutCubic",
403      complete: function () {
404        forceVisible(allChair);
405      },
406      begin: function () {
407        setTimeout(startClipReveal, 3000);
408      },
409    });
410  }
411
412  // ==========================================================================
413  // 5b. great! テキスト — 各文字グループを順番にフェードイン
414  // ==========================================================================
415  // 表示中のもののみ(display:none除外)をアニメ対象に
416  var allGreatChars = document.querySelectorAll(".layers .great-char");
417  var greatChars = Array.from(allGreatChars).filter(function (el) {
418    return getComputedStyle(el).display !== "none";
419  });
420  if (greatChars.length) {
421    greatChars.forEach(function (el) {
422      el.style.opacity = "0";
423    });
424    anime({
425      targets: greatChars,
426      opacity: [0, 1],
427      translateY: [10, 0],
428      duration: 600,
429      delay: anime.stagger(150, { start: 600 }),
430      easing: "easeOutCubic",
431      complete: function () {
432        // great表示完了後、fillアニメ終了に合わせてフェードアウト
433        anime({
434          targets: greatChars,
435          opacity: [1, 0],
436          duration: 400,
437          delay: anime.stagger(80, { start: T_FILL_DURATION - 1650 }),
438          easing: "easeInCubic",
439          complete: function () {
440            greatChars.forEach(function (el) {
441              el.style.opacity = "0";
442            });
443          },
444        });
445      },
446    });
447  }
448
449  // セクション6・7は削除済み(.layers-frame / .infos はHTMLから除去済み)
450
451  // ==========================================================================
452  // 7b. .kv-bottom-copy フェードイン → 500ms後に .kv-sublead-widgets もフェードイン
453  // chairが入ってくる3秒台に引っ張って、ページ下半も早めに埋まるように
454  // ==========================================================================
455  var kvBottomCopy = document.querySelector(".kv-bottom-copy");
456  var kvSubleadWidgets = document.querySelector(".kv-sublead-widgets");
457
458  // kv-sublead-widgets は初期非表示
459  if (kvSubleadWidgets) {
460    kvSubleadWidgets.style.opacity = "0";
461  }
462
463  if (kvBottomCopy) {
464    kvBottomCopy.style.opacity = "0";
465    anime({
466      targets: kvBottomCopy,
467      opacity: [0, 1],
468      translateY: [12, 0],
469      duration: 900,
470      delay: 3200, // 5000 → 3200ms
471      easing: "easeOutCubic",
472      complete: function () {
473        // kv-bottom-copy 完了から500ms後に kv-sublead-widgets をフェードイン
474        if (kvSubleadWidgets) {
475          anime({
476            targets: kvSubleadWidgets,
477            opacity: [0, 1],
478            translateY: [10, 0],
479            duration: 700,
480            delay: 300,
481            easing: "easeOutCubic",
482          });
483        }
484      },
485    });
486  } else if (kvSubleadWidgets) {
487    // kv-bottom-copy がない場合のフォールバック
488    anime({
489      targets: kvSubleadWidgets,
490      opacity: [0, 1],
491      translateY: [10, 0],
492      duration: 700,
493      delay: 4200,
494      easing: "easeOutCubic",
495    });
496  }
497
498  // ==========================================================================
499  // 8. #top_credits — インラインSVG
500  //    a) SVG全体を下からフェードイン
501  //    b) art三つをふわふわループ
502  // ==========================================================================
503  var topCredits = document.getElementById("top_credits");
504
505  if (topCredits) {
506    // 初期状態: 非表示
507    topCredits.style.opacity = "0";
508    var isPC950 = window.matchMedia("(min-width: 950px)").matches;
509    topCredits.style.transform = isPC950
510      ? "translateX(30px)"
511      : "translateY(40px)";
512
513    // PC: 右からスライドイン / SP: 下からスライドイン
514    anime({
515      targets: topCredits,
516      opacity: [0, 1],
517      translateX: isPC950 ? [30, 0] : [0, 0],
518      translateY: isPC950 ? [0, 0] : [40, 0],
519      duration: isPC950 ? 1800 : 2000,
520      delay: isPC950 ? 3200 : 4000,
521      easing: "easeOutCubic",
522      complete: function () {
523        // art三つをそれぞれ違うループアニメ
524        var icEyemask = document.querySelector("#ic-eyemask");
525        var icPizza = document.querySelector("#ic-pizza");
526        var icDrink = document.querySelector("#ic-drink");
527
528        // eyemask: 左右に傾く揺れ + 微スケール(仮面をつけてるような揺れ)
529        if (icEyemask) {
530          var emBase = { x: 2251.4, y: 814.5 };
531          var emAnim = { oY: 0, rot: 0, sc: 1 };
532          anime({
533            targets: emAnim,
534            oY: [-8, 8],
535            rot: [-5, 5],
536            sc: [0.96, 1.04],
537            duration: 3500,
538            easing: "easeInOutSine",
539            direction: "alternate",
540            loop: true,
541            update: function () {
542              icEyemask.setAttribute(
543                "transform",
544                "translate(" +
545                  emBase.x +
546                  " " +
547                  (emBase.y + emAnim.oY) +
548                  ") rotate(" +
549                  emAnim.rot +
550                  " 207 146.5) scale(" +
551                  emAnim.sc +
552                  ")",
553              );
554            },
555          });
556        }
557
558        // pizza: 円を描く軌道(X+Y同時、位相ずらし)+ 微回転
559        if (icPizza) {
560          var pzBase = { x: 50.2, y: 1245.8 };
561          var pzAnim = { oX: 0, oY: 0, rot: 0 };
562          // X軸: sinカーブ
563          anime({
564            targets: pzAnim,
565            oX: [-10, 10],
566            rot: [-6, 6],
567            duration: 3000,
568            easing: "easeInOutSine",
569            direction: "alternate",
570            loop: true,
571          });
572          // Y軸: 位相をずらして円軌道に
573          anime({
574            targets: pzAnim,
575            oY: [-10, 10],
576            duration: 2400,
577            easing: "easeInOutSine",
578            direction: "alternate",
579            loop: true,
580          });
581          // 描画ループ
582          function updatePizza() {
583            icPizza.setAttribute(
584              "transform",
585              "translate(" +
586                (pzBase.x + pzAnim.oX) +
587                " " +
588                (pzBase.y + pzAnim.oY) +
589                ") rotate(" +
590                pzAnim.rot +
591                " 157 185)",
592            );
593            requestAnimationFrame(updatePizza);
594          }
595          updatePizza();
596        }
597
598        // lorel: クリックでその場でくるっと回転
599        // SVG要素なので scaleX で擬似的に縦軸回転を表現
600        var lorelData = [
601          { sel: "#ic-lorel-cannes", x: 63.6, y: 48.4, w: 613, h: 367 },
602          { sel: "#ic-lorel-tiff", x: 777.2, y: 46.7, w: 365, h: 256 },
603        ];
604        lorelData.forEach(function (d) {
605          var el = document.querySelector(d.sel);
606          if (!el) return;
607          var spinning = false;
608          var cx = d.x + d.w / 2; // 中心X
609          el.style.cursor = "pointer";
610          el.addEventListener("click", function () {
611            if (spinning) return;
612            spinning = true;
613            var rot = { angle: 0 };
614            anime({
615              targets: rot,
616              angle: [0, 360],
617              duration: 800,
618              easing: "easeInOutCubic",
619              update: function () {
620                // cosでscaleXを-1〜1に変化 → 縦軸回転の見た目
621                var sx = Math.cos((rot.angle * Math.PI) / 180);
622                el.setAttribute(
623                  "transform",
624                  "translate(" +
625                    cx +
626                    " " +
627                    d.y +
628                    ") scale(" +
629                    sx +
630                    " 1) translate(" +
631                    (-cx + d.x) +
632                    " 0)",
633                );
634              },
635              complete: function () {
636                el.setAttribute(
637                  "transform",
638                  "translate(" + d.x + " " + d.y + ")",
639                );
640                spinning = false;
641              },
642            });
643          });
644        });
645
646        // ローレル自動回転: 1つ目完了後に2つ目が回る
647        function spinLorel(d, onComplete) {
648          var el = document.querySelector(d.sel);
649          if (!el) {
650            if (onComplete) onComplete();
651            return;
652          }
653          var cx = d.x + d.w / 2;
654          var rot = { angle: 0 };
655          anime({
656            targets: rot,
657            angle: [0, 360],
658            duration: 800,
659            easing: "easeInOutCubic",
660            update: function () {
661              var sx = Math.cos((rot.angle * Math.PI) / 180);
662              el.setAttribute(
663                "transform",
664                "translate(" +
665                  cx +
666                  " " +
667                  d.y +
668                  ") scale(" +
669                  sx +
670                  " 1) translate(" +
671                  (-cx + d.x) +
672                  " 0)",
673              );
674            },
675            complete: function () {
676              el.setAttribute(
677                "transform",
678                "translate(" + d.x + " " + d.y + ")",
679              );
680              if (onComplete) onComplete();
681            },
682          });
683        }
684        // 1秒待ってからカンヌ→TIFFの順に回転
685        setTimeout(function () {
686          spinLorel(lorelData[0], function () {
687            spinLorel(lorelData[1]);
688          });
689        }, 1000);
690
691        // drink: 上下ゆらゆら + 微回転(グラスの中身が揺れる感じ)
692        if (icDrink) {
693          var drBase = { x: 412.2, y: 1191.4 };
694          var drAnim = { oY: 0, rot: 0 };
695          anime({
696            targets: drAnim,
697            oY: [-14, 14],
698            rot: [-4, 4],
699            duration: 2800,
700            easing: "easeInOutQuad",
701            direction: "alternate",
702            loop: true,
703            update: function () {
704              icDrink.setAttribute(
705                "transform",
706                "translate(" +
707                  drBase.x +
708                  " " +
709                  (drBase.y + drAnim.oY) +
710                  ") rotate(" +
711                  drAnim.rot +
712                  " 96 208)",
713              );
714            },
715          });
716        }
717      },
718    });
719  }
720  // ==========================================================================
721}
722
723// グローバルに公開
724window.startLayersAnimation = startLayersAnimation;
725
726// =============================================================================
727// 起動条件:
728//   「.two と .bg の画像をプリロード済み」+ 「anime.js 読み込み完了」
729//   → window load(全リソース待ち)より大幅に早く開始できる
730// =============================================================================
731var _pageLoaded = false;
732var _animeReady = typeof anime !== "undefined";
733
734function _tryStart() {
735  if (_pageLoaded && _animeReady) {
736    startLayersAnimation();
737  }
738}
739
740// KVのアニメに必要な画像(two + bg)のロード完了を待って開始
741function _waitForKVImages() {
742  // 表示中の .two と .bg 画像を取得(sp620/pc620どちらか)
743  var targets = Array.from(
744    document.querySelectorAll(".layers .two, .layers .bg")
745  ).filter(function(el) {
746    return getComputedStyle(el).display !== "none";
747  });
748
749  if (!targets.length) {
750    // 要素が見つからなければ従来通り window load 待ち
751    window.addEventListener("load", function() { _pageLoaded = true; _tryStart(); });
752    return;
753  }
754
755  var loaded = 0;
756  function onLoad() {
757    loaded++;
758    if (loaded >= targets.length) {
759      _pageLoaded = true;
760      _tryStart();
761    }
762  }
763
764  targets.forEach(function(img) {
765    if (img.complete && img.naturalWidth > 0) {
766      onLoad(); // キャッシュ済みは即座にカウント
767    } else {
768      img.addEventListener("load", onLoad);
769      img.addEventListener("error", onLoad); // エラー時も待機しない
770    }
771  });
772}
773
774// DOM準備完了実行
775if (document.readyState === "loading") {
776  document.addEventListener("DOMContentLoaded", _waitForKVImages);
777} else {
778  _waitForKVImages();
779}
780
781// anime.js: async 読み込みのため未到着ならポーリングで待機
782if (_animeReady) {
783  _tryStart();
784} else {
785  var _animeWait = 0;
786  var _animeTimer = setInterval(function () {
787    _animeWait += 10;
788    if (typeof anime !== "undefined") {
789      clearInterval(_animeTimer);
790      _animeReady = true;
791      _tryStart();
792    } else if (_animeWait >= 5000) {
793      clearInterval(_animeTimer);
794      console.warn("[animejs.js] anime.js の読み込みがタイムアウトしました");
795      // anime.js なくても画像は表示させる
796      _pageLoaded = true;
797      forceVisible(
798        document.querySelectorAll(
799          ".layers .bg, .layers .two, .layers .chair, .baloon-logo, .copy-vertical, .site-logo, .navbtn, .side-nav, #top_credits, .kv-bottom-copy, .kv-sublead-widgets",
800        ),
801      );
802    }
803  }, 10);
804}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.