2012-06-11 10 views
5

Tengo una página con un objeto <audio> oculto que se inicia y detiene utilizando un botón personalizado a través de javascript. (La razón es que quiero personalizar el botón, y que dibujar un reproductor de audio parece destruir el rendimiento de renderizado en el iPad de todos modos). Un ejemplo simplificado (en CoffeeScript):El objeto de audio HTML5 no se reproduce en el iPad (cuando se llama desde un setTimeout)

// Works fine on all browsers 

constructor: (@_button, @_audio) -> 
    @_button.on 'click', @_play   // Bind button's click event with jQuery 

_play: (e) => 
    @_audio[0].play()     // Call play() on audio element 

El audio se reproduce bien cuando se activa desde una función ligada a un evento click, pero en realidad me gustaría añadir una animación para completar antes de que el archivo se reproduce por lo que poner .play() dentro de un setTimeout. Sin embargo yo no puedo conseguir que esto funcione:

// Will not play on iPad 

constructor: (@_button, @_audio) -> 
    @_button.on 'click', @_play   // Bind button's click event with jQuery 

_play: (e) => 
    setTimeout (=>      // Declare a 300ms timeout 
    @_audio[0].play()     // Call play() on audio element 
), 300 

He comprobado que @_audio (this._audio) es en su alcance y que existe su método play(). ¿Por qué esto no funciona en el iPad?

Editar: Si llega el caso, el caso de prueba simplificada anterior realidad hace trabajo. Vea la respuesta de @apsillers a continuación y mis comentarios al respecto.

+0

Solo para añadir He probado esto en el simulador _y_ un iPad real. –

+0

Supongo que no se puede reproducir un video/audio en iPad/iPhone sin una acción explícita del usuario ... para el consumo de ancho de banda – fcalderan

+0

Además, he puesto un 'try/catch' alrededor del método de reproducción y no hay parece ser un error lanzado. –

Respuesta

10

Ver Apple's iOS considerations guide:

... el JavaScript play() y Los métodos load() también están inactivos hasta que el usuario inicia la reproducción, a menos que el método play() o load() se desencadene por acción del usuario. En otras palabras, un botón Reproducir iniciado por el usuario funciona, pero un evento onLoad="play()" no.

Parece que su setTimeout() devolución de llamada no puede considerarse como una acción iniciada por el usuario, a pesar del hecho de que el setTimeout() en sí estaba en una función iniciada por el usuario.

Sugerencia: No tengo un dispositivo iOS para probar, pero tal vez hacer una pausa/reproducción inicial cuando el usuario presiona el botón aliviará esta restricción. Es decir, usted llama al play() y luego lo pausa de inmediato, luego realiza su llamada para animar y la función setTimeout() con la llamada play(). Esto hace que la función iniciada por el usuario permita que iOS sepa que está bien cargar y reproducir este video en el futuro.

+0

Tiene toda la razón @apsillers, lo que estaba haciendo no calificaba como "acción iniciada por el usuario". Sin embargo, hay una solución: investigué un poco más y es importante señalar que me equivoqué cuando escribí el caso de prueba simplificado en mi pregunta: de hecho, funciona. Vea mi respuesta a continuación para saber por qué. –

+0

La solución a mi pregunta original era continuar usando mi compleja secuencia de animación de varios tiempos de espera, pero ejecutar una función de una sola vez para el método '.play()', con una duración más larga apropiada. Esto luego corre 'en paralelo'. Obviamente no es infalible, ya que los temporizadores en la secuencia de animación pueden no ser precisos y 'play()' puede llamarse temprano. Pero parece funcionar bien en mi caso. –

1

Algunos dispositivos no puedes automatizar la llamada para jugar Sistema de audio has intentado usar un botón y haciendo clic en jugar para ver si funciona, entonces, ¿cómo funciona si abre un reproductor de audio para reproducirlo? jugar a los medios esta es la misma que la etiqueta de vídeo si no recuerdo mal

Creo cheques Safari para un evento para disparar el audio tanto ¿por qué funciona clic

1

@apsillers sugiere en su respuesta que podría reorganizar mi código para cumplir con el requisito de "acción iniciada por el usuario" del iPad. He hecho algunas excavaciones y esto resulta ser cierto.

El requisito parece ser que la llamada play() solamente puede estar dentro de una setTimeout (Por lo tanto, el ejemplo simplificado que di en la pregunta original hace el trabajo - en principio había play() varios setTimeout s de profundidad).

Así que este se trabajo:

constructor: (@_button, @_audio) -> 
    @_button.on 'click', @_play 

_play: (e) => 
    setTimeout (=> 
    @_audio[0].play()     // play() is only inside one setTimeout 
), 300 

Y esto también funcionaría:

constructor: (@_button, @_audio) -> 
    @_button.on 'click', => 
    setTimeout ((e) => 
     @_play(e) 
    ), 300 

_play: (e) => 
    @_audio[0].play()     // Still only inside one setTimeout 

Pero esto no funcionará:

constructor: (@_button, @_audio) -> 
    @_button.on 'click', @_play 

_play: (e) => 
    setTimeout (=> 
    // Something useful 
    setTimeout (=> 
     @_audio[0].play()    // play() is inside two setTimeouts 
    ), 300 
), 300 

Tampoco eso (mi original configuración):

constructor: (@_button, @_audio) -> 
    @_button.on 'click', @_play 

_play: (e) => 

    @_button 
    .animate { prop: value }, 300, => 

     setTimeout (=> 
     @_audio[0].play()    // play() still 'too deep' 
    ), 300 

En el último ejemplo, parece que la devolución de llamada animado de jQuery se llama desde otro setTimeout interna a la biblioteca para play() es nuevo 'demasiado profundo'.

+0

Tenga en cuenta que esto solo funciona si es 300ms o menos. – gkiely

Cuestiones relacionadas