箸が転がっても面白い
父 「アレクサ、ボンジョビかけて」
アレクサ 「ッス..シャッフル再生しマス...」
子 「おとうさん、ボンジョビって何?歌手?」
父 「バンドだよ。昔の。アメリカの。」
「歌とか楽器の集まりの名前がボンジョビで、歌う人の名前が、ジョンボンジョビ。」
子 「ジョンジョビ?」
父 「ボンジョビの、ジョンボンジョビ。」
子 「ボンボンジョビ!ケラケラ」
父 (子供は単純でいいな・・・)
子 「おとうさん、もっと作って!全部言って!!」
父 (全部ってなんだよ・・・)
子 「ジョンボンジョビ!ジョンジョンボビ!合ってる?ケラケラ」
父 (シャッフルすればいいのか・・・)
作ったやつ
環境
・Vue.js
・Web Speech API
・Bootstrap
・Netlify
作り方
やりたいことはざっくり以下の通りです。
- "Jon","Bon","Jovi","Bovi" からランダムに3つ入力
- 入力結果をWeb Speech APIで発話させる
1. 実装
<CodePen>
違う言葉で遊びたい場合は、CodePenに登録してForkすると簡単に変更できます。
See the Pen Joke with Vue.js + BootStrap by mitoreterusuki (@mitoreterusuki) on CodePen.
ボタン、フィールドの各要素はBootstrapを使って装飾しています。
html コード例
<div id="app" class="text-center">
<p class="display-4">
<img src="./title.png" class="img-fluid">
</p>
<div class="mb-3">
<button class="btn btn-dark btn-lg mr-2" @click="addToMessage('Jon')">Jon</button>
<button class="btn btn-dark btn-lg mr-2" @click="addToMessage('Bon')">Bon</button>
<button class="btn btn-dark btn-lg mr-2" @click="addToMessage('Jovi')">Jovi</button>
<button class="btn btn-dark btn-lg mr-2" @click="addToMessage('Bovi')">Bovi</button>
<button class="btn btn-warning btn-lg mr-2" @click="challenge">Challenge</button>
</div>
<p>
<div class="form-floating mb-3">
<input class="form-control form-control-lg" placeholder="It's my name" v-model="message" />
<label for="floatingInput">It's my name</label>
</div>
<button class="btn btn-primary btn-lg ml-2" @click="say">Speak</button>
<a :href="getTweetURL()" target="_blank" rel="noopener noreferrer" class="btn btn-info btn-lg">Tweet</a>
<button class="btn btn-secondary btn-lg ml-2" @click="clear">Clear</button>
</p>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js'></script><script src="./script.js"></script>
各ボタンはクリックで文字列をフィールドに追加、Challengeボタンで文字列のうち3つをランダムにフィールドに追加しています。
・ メニュー下部の要素(出力・初期化)
ボタンの機能は以下の通りです。
- Speak:フィールドの文字列をWeb Speech APIで読み上げ
- Tweet:フィールドの文字列をTwitterに投稿
- Clear:フィールドの文字列を削除
Web Speech APIによる音声読み上げは言語指定できますが、一通り試して
最もしっくりきた日本語を指定しております。
JavaScript コード例
const app = new Vue({
el: '#app',
data: {
message: '',
options: ['Jon', 'Bon', 'Jovi', 'Bovi'],
},
mounted() {
this.setupSpeech();
},
methods: {
async say() {
return new Promise((res) => {
this.speech.text = this.message;
this.speech.onend = () => res();
speechSynthesis.speak(this.speech);
});
},
clear() {
this.message = '';
},
addToMessage(text) {
this.message += text + ' '; // 半角スペースを追加
},
challenge() {
this.clear(); // messageフィールドをクリア
const selectedOptions = this.getRandomOptions(3);
this.message = selectedOptions.join(' ') + ' '; // 選択肢と半角スペースを追加
},
getRandomOptions(count) {
const optionsCopy = this.options.slice();
const selectedOptions = [];
while (selectedOptions.length < count && optionsCopy.length > 0) {
const randomIndex = Math.floor(Math.random() * optionsCopy.length);
const option = optionsCopy.splice(randomIndex, 1)[0];
selectedOptions.push(option);
}
return selectedOptions;
},
getTweetURL() {
const tweetMessage = encodeURIComponent(this.message); // メッセージをURLエンコード
return `https://twitter.com/intent/tweet?text=${tweetMessage}&url=jbjv.netlify.app`; // ツイートのURLを生成
},
setupSpeech() {
this.speech = new SpeechSynthesisUtterance();
this.speech.lang = 'ja-jp';
window.speechSynthesis.onvoiceschanged = () => {
const voices = window.speechSynthesis.getVoices();
this.speech.voice = voices.find(voice => voice.lang == 'ja-JP');
console.log(this.speech.voice);
};
}
},
});
2. 公開
Netlifyを使って、Webアプリとして公開しましょう。
おわりに
週末これ作ってたら、こどもは喜んだけど、妻には苦い顔をされました。
童心に帰るのは計画的に!