HTML 5でのAudio使用ピットまとめ

8471 ワード

  • Cannot read property'catch'of undefined理由:play()を呼び出すと、現代のブラウザはpromiseを返します.実行に失敗した場合、Unhandled Promise Rejectionがトリガーされますが、低バージョンのブラウザではplay()を呼び出すとpromiseは返されません.解決:play()を呼び出す際に以下の処理を行い、playPromiseに対する判断
    var playPromise = document.querySelector('video').play();
    
    // In browsers that don’t yet support this functionality,
    // playPromise won’t be defined.
    if (playPromise !== undefined) {
      playPromise.then(function() {
        // Automatic playback started!
      }).catch(function(error) {
        // Automatic playback failed.
        // Show a UI element to let the user manually start playback.
      });
    }
    
    参考資料:HTML MediaElement.play() Returns a Promise
  • InvalidStateError:An attempt was made to use an object that is not,or is no longer,usable理由:srcが設定されていないaudioに対してcurrentTimeを直接設定するとINVALIDがトリガーされます.STATE_ERR異常です.この例外解決は、currentTime=0を設定してもトリガーされます.currentTimeを設定する前に、audioのsrcリファレンスを設定する必要があります.Offsets into the media resource
    media . currentTime [ = value ]
    Returns the current playback position, in seconds.
    Can be set, to seek to the given time.
    Will throw an INVALID_STATE_ERR exception if there is no selected media resource. Will throw an INDEX_SIZE_ERR exception if the given time is not within the ranges to which the user agent can seek.
  • NotAllowedError理由:play()を呼び出すとNotAllowedErrorのrejectがトリガーされる可能性があります.ブラウザが再生に失敗した場合があるためです.一般的なシーンでは、クリックしないでplay()を呼び出すか、イベントのコールバックで次のtickで呼び出されるplay、例えばsettimoutで呼び出されるplay、また、audio要素が複数作成されていますが、各audioがユーザークリックで呼び出されるplay()などではありません.
  • シーン1:クリックなどのイベントバインドを通過せずにplay()を直接呼び出し、NotAllowedErrorをトリガーします.解決方法は、play()を呼び出す部分をイベントコールバックに配置し、以下のコード:
    playButton.addEventListener("click", () => {
        audioElem.play()
    }, false);
    
  • シーン2:クリックイベントコールバックの次のtickでplay()を呼び出す場合の例コードは以下の通りであり、
    //       
    playButton.addEventListener("click", () => {
        setTimeout(() => {
            audioElem.play()
        }, 100)
    }, false);
    
    の場合、一部のバージョン【iOS 120.0.1親測有坑】でもNotAllowedError異常がトリガーされる場合があり、これを回避すべきであり、
    // hack 
    playButton.addEventListener("click", () => {
        audioElem.muted = true
        let p = audioElem.play()
        if (p !== undefined) {
            p.then(() => {
                audioElem.muted = false
                audioElem.pause()
                setTimeout(() => {
                    audioElem.play()
                }, 100)
            }).catch((e) => {
                console.log(e)
            })
        }
    }, false);
    
  • を解決するhack手段が考えられる
  • シーン3:複数のaudio要素が作成されていますが、各audioがユーザクリックによって呼び出されたplay()であるわけではありません.この場合、いくつかのバージョン「iOS 120.0.1親測有坑」もNotAllowedError異常をトリガーします.この場合、audio要素を1つだけ作成し、srcを変更して異なる音楽リソースを再生するのが最善の方法です.audioがイベントコールバックでプレイを呼び出したことがある限り、その後はプレイを直接呼び出すことができ、イベントコールバックで実行を再バインドする必要がなく、複数のaudioを作成してメモリの使用を減らすこともできます.
    playButton.addEventListener("click", () => {
        audioElem.src = "https://a.mp3"
        audioElem.play()
    }, false);
    
    //       ,    src   play
    audioElem.src = "https://b.mp3"
    audioElem.play()
    

  • iOSでページが非表示になったり表示されたりした場合、audio動作異常の原因を再生します.一部のiOSバージョンでは【iOS 120.0.1親測ピット】があり、ページの非表示とイベントの表示を傍受し、非表示時にpause()を呼び出して一時停止し、表示時にplay()を呼び出して再生を再開します.ホームキーを押すと、ページがシステムのバックグラウンドに入るとpause()が正常に呼び出され、audioは正常に一時停止されますが、再びページに入ると、イベントでplay()が呼び出されると異常が表示されます.
  • の最初の異常は、もし私たちが単純にaudioElem.play()を呼び出しただけで、間違いを投げ出すことはありませんが、audioは実際には本当に再生されていません.音はありません.
  • の2番目の例外は、表示イベントで次のコードのいずれかのシーンを実行するたびに、AbortError例外が投げ出される場合が多く、ごく少数の場合に正常に再生されます.
    //          
    addPageVisibilityListener(() => {
        //      
      audioElem.pause()
    },() => {
        //        
        
        //       src
        audioElem.src = "https://b.mp3"
        audioElem.play()
        
        //   load
        // audioElem.load()
        // audioElem.play()
    })
    

  • 解決:この2つの異常な動作はiOS 12.0.1システム自体のバグであるべきである.この2つの異常の発生を回避するには、次の2つの方法があります.
  • 方式1は、load()を表示し、canplaythroughを傍受し、この方式
  • の使用を推奨する.
    const playAudio = () => {
      audioElem1.removeEventListener('canplaythrough', playAudio)
      let p = audioElem.play()
      if (p !== undefined) {
        p.catch((e) => {
          console.log(e)
        })
      }
    }
    //          
    addPageVisibilityListener(() => {
        //      
      audioElem.pause()
    },() => {
        //        
        audioElem.load()
        audioElem.addEventListener('canplaythrough', playAudio)
    })
    
  • 方式2では、settimeoutおよびretryによりhackがこのような異常の発生を回避する
  • .
    let playAudio = (retry: boolean) => {
        let p = audioElem.play();
        if (p !== undefined) {
            p.catch((e) => {
                if (retry) {
                    setTimeout(() => {
                        playAudio(false);
                    }, 0);
                }
            });
        }
    }
    //          
    addPageVisibilityListener(() => {
        //      
      audioElem.pause()
    },() => {
        //        
        setTimeout(() => {
            playAudio(true)
        }, 500)
    })