1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

言葉に反応する演出を Vue + Composition API で実装する方法(OS Yamato構造解説)

Posted at

🎯 概要

「OS Yamato」では、メッセージで特定の言葉を入力すると、桜が舞ったり、星が流れたり、紙吹雪が舞ったりといった演出(エフェクト)が即時発生します。日本らしい侘び寂びを演出したいのです。

本記事ではその 演出構造の中核である以下の3つの要素を解説します:
1. useChatEffects.ts(キーワードマッチングとトリガー)
2. ChatEffect.vue(アニメーションの表示ロジック)
3. ChatView.vue(チャット本文での統合)

デモ動画 

IMG_1612.jpg

IMG_1613.jpg

① useChatEffects.ts の構造:言葉 → アニメーション変換

export function useChatEffects(chatEffect: Ref<any>, messageAnimationEnabled: Ref<boolean>) {
  const specialPatterns = [
    { pattern: /(愛してる|i love you)/i, effect: 'moon' },
    { pattern: /(星空|starry sky)/i, effect: 'starry' },
    { pattern: /(おめでとう|congrats)/i, effect: 'confetti' },
    { pattern: /(夏|summer)/i, effect: 'summer' }
    // さらに多数あり
  ]

  function maybePlayEffect(content: string): boolean {
    for (const { pattern, effect } of specialPatterns) {
      if (pattern.test(content)) {
        chatEffect.value.playEffect(effect)
        return true
      }
    }
    return false
  }

  return { maybePlayEffect }
}

✅ 特徴:
• RegExp による自然言語パターン抽出
• 日本語・英語・中国語・スペイン語など多言語対応
• chatEffect.value.playEffect(effectName) を呼び出すだけ

② ChatEffect.vue: トリガーされたエフェクトの描画


<Teleport to="body">
  <div v-if="effectType === 'rain'" class="effect-container">
    <div v-for="n in 50" :key="n" class="raindrop" :style="randomRainStyle()" />
  </div>

  <div v-if="effectType === 'starry'" class="effect-container">
    <div v-for="n in 50" :key="n" class="star" :style="randomStarStyle()" />
    <div v-for="n in meteorCount" :key="'m-' + n" class="meteor" :style="randomMeteorStyle()" />
  </div>

  <div v-if="effectType === 'confetti'" class="effect-container">
    <div v-for="n in 100" :key="n" class="confetti" :style="randomConfettiStyle(n)" />
  </div>

  <!-- 他にも 'moon', 'bubble', 'autumn', 'mishima' など全14種 -->
</Teleport>

✅ 特徴:
• effectType に応じて動的にエフェクトを描画
• randomXxxStyle() によってアニメーションを毎回変化させる
• triggerXxx() を通じてアニメーションの長さや画像を制御

③ ChatView.vue との統合

watch(messages, () => {
  const last = messages.value.at(-1)
  if (last && messageAnimationEnabled.value) {
    maybePlayEffect(last.content)
  }
})

✅ 特徴:
• 新しいチャットメッセージを検知して maybePlayEffect() を実行
• エフェクトが発動されると、ChatEffect.vue 側で演出が描画
• ローカル環境で完結し、ユーザー設定で ON/OFF も可能

📎 まとめ

Vue + Composition API + GraphQL で 言葉と連動するUI演出 を実現
汎用的なパターンマッチングにより、柔軟かつ多言語で動作
実装は「設定+1行呼び出し」で統合可能なモジュール構成

🔗 関連リンク

GitHub: https://github.com/osyamato/os-yamato
Try it: https://hanaco875.com

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?