読むのが面倒なので、読み上げて貰おう
📅 October 31, 2020
•⏱️4 min read
どうも、黙読が苦手すぎてタイポを発見できないおじさんです。 今日は、自分のブログのタイポをサクッと見つけるために、ブログに読み上げ機能をつけてみました。AWS pollyとか使って、ピタゴラスイッチして遊ぼうと思ったのですが、なんと、webのAPIに読み上げ機能が既にありました。すごいぞweb API!!!
どれくらい使えるの?
canIuse
どれくらい使えるの?どうせお試し機能で、ほとんどのブラウザで未対応でしょう?と思って、canIuseを確認したところ、なんと意外とサポートされてるではありませんか!!すごいぞSpeech API!!!
詳しくはこちらを
しかも、読み上げだけじゃなくて、音声認識のAPIもあるみたい。ただ、Web Speech API Speech Recognitionの方は流石に、読み上げほどのサポートはされてない。
実際に使ってみる
ということで、早速、このブログに入れてみました。 「え?表示されないよ?」って方は、サポート対象外の数少ないブラウザをご利用されているかと思います。 もしくは、僕のコードがバグっているか。
import React, {useState, useEffect} from "react";
import styled from '@emotion/styled'
export const SpeechButton = ({content}) => {
const [rate, setRate] = useState(1.5)
const uttr = new SpeechSynthesisUtterance(content)
uttr.rate = rate;
const [isStart, startSpeech] = useIsBoolean({
trueFunc: () => speechSynthesis.cancel(),
falseFunc: () => speechSynthesis.speak(uttr),
})
console.log(speechSynthesis.speaking)
const [isStop, stopSpeech] = useIsBoolean({
trueFunc: () => speechSynthesis.resume(),
falseFunc: () => speechSynthesis.pause(),
})
const onRateChange = (e) => {
const rate = e.target.value;
setRate(rate);
}
useEffect(() => {
uttr.rate = rate;
return () => {
speechSynthesis.cancel();
}
}, [rate])
const startText = isStart ? '終了' : '読み上げ'
const pauseText = isStop ? '再開' : 'ストップ'
const stopButton = isStart && <Button style={{backgroundColor: 'red'}}onClick={stopSpeech}>{pauseText}</Button>
const changeRate = !isStart && (
<>
<label>Speed</label>
<input
type="number"
min="0.1"
max="5"
step="0.1"
value={rate}
onChange={onRateChange}
></input>
<Input
type="range"
min="0.1"
max="5"
step="0.1"
value={rate}
onChange={onRateChange}
></Input>
</>
)
if (!('speechSynthesis' in window)) return
return (
<div>
<Button style={{backgroundColor: 'blue'}} onClick={startSpeech}>{startText}</Button>
{stopButton}
{changeRate}
</div>
)
};
export const useIsBoolean = ({defaultBool = false, trueFunc, falseFunc}) => {
const [bool, setBoolean] = useState(defaultBool);
const onClick = () => {
setBoolean(bool => !bool);
if (bool) {
trueFunc();
return
}
falseFunc()
}
return [bool, onClick]
};
const Button = styled.button`
color: #fff;
border-radius: 0.5rem;
box-shadow: 2px 2px 3px 1px #666;
-moz-box-shadow: 2px 2px 3px 1px #666;
-webkit-box-shadow: 2px 2px 3px 1px #666;
margin-bottom: 2rem;
margin-right: 0.6rem;
`
const Input = styled.input`
background-color: #c7c7c7;
height: 3.5px;
width: 6rem;
`
なんか、ぐじゃぐじゃ書いてますが、こんな感じで使えます。
speechSynthesis.speak(
new SpeechSynthesisUtterance("ここのテキストを読み上げてくれます。")
);
ハマリポイント
buildエラー
このブログは、GatsByを使っているので、デプロイするとNetlifyでSSGされます。
そのときにもちろん、ブラウザにしかないSpeechSynthesisUtterance
コイツラを呼ぼうとするとエラーになります。
クライアントサイドでしか動かないコードをuseEffect内に移動するなど方法はありますが、面倒だったので、雑に、windowオブジェクトがない時は、コンポーネントを読み込まないようにしました。
const speechButton = (typeof window !== 'undefined') && <SpeechButton content={content} />
終わらない読み上げ
Speech APIの仕様っぽいのですが、リロードしても、ページバックしても読み上げ続けます。 なんとうざい仕様でしょうか。 仕方ないので、useEffectのクリーンアップ関数で、止めるようにしました。
useEffect(() => {
return () => {
speechSynthesis.cancel();
}
}, [])
ホントは、問答無用でキャンセルを呼ぶよりも、読み上げてるかどうかSpeechSynthesis.speaking
で調べた方が良さそうですが、正しくリロード後など読み上げ状況を返してくれないので、こうなりました・・・。
あれ、awsの勉強しようと思ってたのに、私は何をしてるのだろうか・・・。
ほいじゃーまたー。