7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【ウマ娘】エイプリルフール2025特設サイトの構造を調べる【BoC'z we\n】

Posted at

ウマ娘プロジェクトでは毎年4月1日に特設Webサイトが期間限定で公開されている

その特徴のひとつに比較的モダンな技術スタックで制作されていることが挙げられる

2024年にはNuxt.jsが採用されていたが今回はAstroが採用されている

リリース4周年時にはDMMGAMESの特設サイトもAstroでリプレイスされている

Webフロントエンド部門のtechブログや講演が無いか探したが2025年4月時点では発見できなかった

そこで筆者の理解できる範囲で本サイトの構造を調査した

Astro活用例

Astro自体の説明は割愛する

スコープ付きスタイル

Astroコンポーネントでstyleタグを記載するとそのコンポーネント専用のスタイルとなる

スコープされたコンポーネントにはdata-astro-cid-xxx属性が付与される

Astro ImageTools

画像アセットを様々なサイズ・拡張子で出力して表示の出し分けが可能

該当要素にはクラス名にastro-imagetools-pictureが付与される

マークアップ大観

ここから主にHTML/CSSの構造に注目する

本サイトはおよそ5つのブロックから構成されていた
(本記事では上から2つまでに絞って記載する)

image.png

下記classがsection要素に追加される
(参考:hoisted.*.js)

  • ページローディング完了 → scene-sound
  • 音声ON/OFF選択完了 → scene-kv
  • kv(キービジュアル)アニメ終了 → scene-end

attentionコンポーネント

image.png

横幅900px以下の横向き時のみ表示される(CSSで制御)

また端末の向きを変えたり一定の画面サイズに変化することで再読み込みが走る

introコンポーネント

image.png

ローディング→音声選択→アニメーション実行 までの表示を担う

loadingコンポーネント

ezgif-1aed16bba8692c.gif

ページ読み込み中に表示されるCSSアニメーション

animation-delayで周期ズレの実現

sound-selectコンポーネント

image.png

transformでコンポーネント自身を上下左右中央揃え

{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate3d(-50%, -50%, 0);
}

videoコンポーネント

image.png

シンプルなvideo要素

黒背景の上からopacity20%の被せで控えめな表示

kvコンポーネント

音声ON/OFFが選択されるまではopacityvisibilityで非表示
section要素にscene-kvクラスが付与されてからアニメーションタイムラインが開始される

boczwen.umamusume.jp_(iPhone 14 Pro Max) (1).png

最初に[ぼかし背景]と[中央ロゴ]を表示

boczwen.umamusume.jp_(iPhone 14 Pro Max) (2).png

そのあと[通常背景]と[斜めロゴ]、[キャラ画像]を表示という流れ

横幅がおよそ768px以上の場合フェードインとスケールダウンアニメが入るが、
768px未満の場合は左から右へスライド移動するアニメが入る

sound-selectコンポーネントが表示されている状態でintroコンポーネントのclassにscene-kvを手動で追加するとkvコンポーネントのアニメーションが表示される

また Ctrl + Z および Ctrl + Y で繰り返し再生することも可能

image.png

また数秒経過するかSKIPボタンを押下するとkvコンポーネントがフェードアウトする

その後introコンポーネント自体が消滅する

アニメーション

画像のアニメーションは基本transitionで制御されている

画像アニメーションのための基本マークアップ構造
<div>
  <figure>
  
    <!-- ここからAstro ImageTools -->
    <picture>
      <source/>
      <source/>
      ...
      <img/>
    </picture>
    <!-- ここまでAstro ImageTools -->
    
  </figure>
</div>

共通セットアップ処理

本サイトの画像要素のほとんどに仕込まれているアニメ

画像読み込み中

picture要素 - 初期状態
figure picture {
    opacity: calc(1 - var(--opacity, 1));
    transition: opacity .3s cubic-bezier(.165,.84,.44,1);
}

var(--opacity, 1)とあるが画像読み込み完了前は変数--opacityが与えられていないため、第二引数の1が選ばれる

よってこの時点ではopacity0

画像読み込み完了後

img要素
<img
    src="(略)"
    onload="parentElement.style.setProperty('--z-index', 1);
    parentElement.style.setProperty('--opacity', 0);"
/>

画像読み込みが完了するとonloadが発火して親picture要素にスタイル情報が付与される

onloadの値もAstro ImageToolsによって自動生成されている

picture要素 - フェードイン
figure picture {
    opacity: calc(1 - var(--opacity, 1));
    transition: opacity .3s cubic-bezier(.165,.84,.44,1);
}

初期状態とスタイルは同一だが、
CSS変数が与えられた瞬間から0.3秒かけてopacity1になる

これにて画像の準備が完了する

中央ロゴのアニメーション

ezgif-8b4224eb6200bc.gif

フェードイン開始前

フェードイン前
img {
    opacity: 0;
}

scene-kvクラスが与えられる前の状態

フェードイン+スケールアップ

img要素 - フェードイン
.scene-kv img {
    animation: anime-logo 4s .3s both;
}

@keyframes anime-logo {
    0% {
        opacity: 0;
        transform: translateZ(0) scale(.5);
        animation-timing-function: cubic-bezier(.895,.03,.685,.22)
    }

    12% {
        opacity: 1;
        transform: translateZ(0) scale(.9);
        animation-timing-function: cubic-bezier(.25,.25,.75,.75)
    }

    to {
        opacity: 1;
        transform: translateZ(0) scale(1.2)
    }
}

scene-kvクラスが付与されてから0.3秒待機してフェードイン+スケールアップアニメーションを4秒間実行

0.3秒待つことで必ず親picture要素のセットアップを終えてからアニメを実行する

フェードアウト

div要素 - フェードアウト
.scene-kv div {
    opacity: 0;
    transition: opacity .5s 2.2s cubic-bezier(.19,1,.22,1);
}

scene-kvクラスが付与されて2.2秒経過後に親div要素にてフェードアウトアニメを0.5秒間実行

フェードインアニメを仕掛ける先は 子要素(img/picture要素)
フェードアウトアニメを仕掛ける先は 親要素(ラッパー/コンテナ要素)

キャラ画像のアニメーション(PC版)

ezgif-8c2236b1c3f551.gif

div要素 - 初期状態
div {
    opacity: 0;
    transform: translateZ(0) scale(1.15);
}

若干拡大させた状態で待機

div要素 - フェードイン+スケールダウン
.scene-kv div {
    opacity: 1;
    transform: translateZ(0) scale(1);
    transition: opacity 1.8s 2.2s cubic-bezier(.165,.84,.44,1), transform 2s 2.2s cubic-bezier(.77,0,.175,1);
}

scene-kvクラスが付与されてから2.2秒待機してフェードイン+スケールダウンアニメーションを1.8秒間実行

このように待機秒数を設定することにより[キャラ画像]が[中央ロゴ]と入れ替わる形で出現する

キャラ画像にフェードアウトアニメが存在しないためか
このフェードインアニメは 親要素(ラッパー/コンテナ要素) に仕掛けられている

まとめ

導入部分を軽く見ただけでも新しい技術スタックや個人的に参考になる点が多く見受けられ、最高のコンテンツを作る会社というビジョンに嘘偽りは無いと実感した

本記事で記載した内容は一部にすぎないがアニメーションの実装内容については多少追跡しやすくなったのではないだろうか

今後の特設サイトにもぜひ注目したい

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?