🎂
iOS(safari)でcanplaythroughイベントが発火されなかった時に解決した方法【React】
実装時の仕様
コンポーネントマウント時にvideoをプリロードしておいて、そのコンポーネント内のボタンを押下するとモーダルが表示され、その中でvideoが再生されます。
ボタンが押下可能になるのはvideoが最後まで再生されるするのに十分なデータが読み込まれた時のみです。canplaythroughイベントを使います。スタイルはemotionで当てています。
全体コード
import React, { useState, useRef } from 'react';
import Movie from './path/to/sample.mp4';
import { css } from '@emotion/css';
export const PreloadPage = () => {
const videoRef = useRef();
const video = videoRef.current;
const [isOpen, setIsOpen] = useState(false);
const [isAvailable, setIsAvailable] = useState(false);
const playMovie = () => {
if(isAvailable) {
setIsOpen(true);
video.play();
}
}
const endMovie = () => {
setIsOpen(false);
video.currentTime = 0;
}
const videoStyle = css`
visibility: ${ isOpen ? 'visible' : 'hidden' };
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%:
& video {
object-fit: cover;
}
`;
return (
<div>
<button onClick={playMovie}>START</button>
<div css={videoStyle}>
<video
ref={videoRef}
src={Movie}
onCanPlayThrough={() => setIsAvailable(true)}
onEnded={endMovie}
playsInline/>
</div>
</div>
)
}
まず上記のコードの感じで実装していました。
ただこれではisAvailableがtureにならず、動画が再生できません。
解決方法は割と簡単でした。以下のコードになります。
解決
解決方法は簡単で、video.load()を追加するだけでした。
以下に記載します。
import React, { useState, useRef, useEffect } from 'react';
import Movie from './path/to/sample.mp4';
import { css } from '@emotion/css';
export const PreloadPage = () => {
const videoRef = useRef();
const video = videoRef.current;
const [isOpen, setIsOpen] = useState(false);
const [isAvailable, setIsAvailable] = useState(false);
const playMovie = () => {
if(isAvailable) {
setIsOpen(true);
video.play();
}
}
const endMovie = () => {
setIsOpen(false);
video.currentTime = 0;
}
const videoStyle = css`
visibility: ${ isOpen ? 'visible' : 'hidden' };
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%:
& video {
object-fit: cover;
}
`;
// add
useEffect(() => {
video.load();
}, [])
return (
<div>
<button onClick={playMovie}>START</button>
<div css={videoStyle}>
<video
ref={videoRef}
src={Movie}
onCanPlayThrough={() => setIsAvailable(true)}
onEnded={endMovie}
playsInline/>
</div>
</div>
)
}
useEffectを使ってvideo.load()と記述することによってビデオがロードされ再生が可能になりました。iOS(safari)以外はそこらへんはよしなにやってくれるらしいです。実機端末での確認は大切ですね。
Discussion