web-dev-qa-db-fra.com

Besoin d'afficher les contrôles vidéo HTML5 en survol ou en lecture

Je voudrais masquer les contrôles de plusieurs vidéos HTML5 sur une page. Si l'utilisateur survole la vidéo, les commandes doivent apparaître. S'ils cliquent sur le bouton de lecture, les commandes doivent rester visibles même si leur souris quitte l'élément vidéo.

Je n'arrive pas à faire fonctionner cela en utilisant le code suivant. Quelqu'un peut-il repérer le problème?

var $video = $('.video');

$video.on('mouseover', show);
$video.on('mouseleave', hide);

function show() {
    $(this).attr('controls', '');
}

function hide() {
    var isPlaying = false;

    this.onplaying = function() {
        isPlaying = true;
    }

    if (!isPlaying) {
        $(this).removeAttr('controls');
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<video class="video">
    <source src="http://download.blender.org/Peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4" type="video/mp4">
</video>
11
Cofey

Nous ne pouvons pas vraiment contrôler ce comportement car il est géré en interne par le navigateur. Tout ce que nous pouvons faire est de spécifier l'attribut controls et le navigateur fera le reste.

Par exemple: dans Firefox (v59b lorsque cela est écrit), les contrôles disparaissent lorsque la souris est en dehors de l'élément lorsque la vidéo est en cours de lecture, même si l'attribut controls est défini - ils s'affichent en cas de non-lecture, type de c'est le contraire que vous recherchez. Il n'existe aucun moyen de forcer les contrôles à rester visibles lorsque l'utilisateur déplace la souris en dehors de l'élément vidéo.

La seule façon de gérer correctement ce navigateur croisé et avec le comportement souhaité exact est de créer une interface utilisateur de contrôles personnalisée pour le joueur. Cela signifie bien sûr que plus de code est nécessaire pour gérer les différents événements afin que vous puissiez mettre à jour et gérer l'interface utilisateur; cela peut également être un défi en ce qui concerne l'aspect spécifique à la plate-forme/au navigateur. Mais cela vous donnera en revanche le contrôle du grain.

Une alternative est de regarder certaines bibliothèques qui enveloppent l'élément vidéo dans des contrôles d'interface utilisateur personnalisés et de voir si elles vous permettent de forcer les contrôles à rester visibles dans les conditions données. Voir par exemple videojs comme point de départ.

Un exemple petit mais incomplet (ajoutez des fonctionnalités, des gestionnaires d'événements, concevez au besoin):

var $container = $("#video1");
var $video = $container.children("video"), video = $video[0]
var $controls = $container.children(".controls");
var $play = $controls.children("button");

// control visibility
$container.on("mouseover mouseout", function(e) {
  $controls.css("display", e.type === "mouseout" && video.paused ? "none" : "block");
});

// play or pause
$play.on("click", toggle);
$video.on("click", toggle);

function toggle() {
  video[video.paused ? "play" : "pause"]();
}

// todo: cover more events (seeked, error etc.)
$video.on("play pause ended", updateUI);

// update control UI elements (todo: update time/progress etc.)
function updateUI() {
  $play.text(video.paused ? "Play" : "Pause")
}
.container {
  position:relative;
  display:inline-block;
  font-size:0;
  }
.container > .controls {
  position:absolute;
  bottom:0;
  width:100%;
  background:rgba(255,255,255,0.3);
  padding:7px;
  box-sizing:content-box;
  z-index:10000;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id=video1 class=container>
  <video width=640 muted src="//media.w3.org/2010/05/sintel/trailer.mp4"></video>
  <div class=controls style="display:none">
    <button>Play</button>
  </div>
</div>
10
user1693593

Vérifiez ce code. son fonctionnement selon vos besoins. Utilisez la fonction $('.video').get(0).paused dans la condition if pour définir isplaying = True.

var $video = $('.video');

$video.on('mouseover', show);
$video.on('mouseleave', hide);

function show() {
    $(this).attr('controls', '');
}

function hide() {
    var isPlaying = false;
    if(!$('.video').get(0).paused) {
        isPlaying = true;
    }
    if (!isPlaying) {
        $(this).removeAttr('controls');
    }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<video class="video">
    <source src="http://download.blender.org/Peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4" type="video/mp4">
</video>
3
Khan M

Vous pouvez le faire assez facilement dans n'importe quel navigateur Web. Voir ci-dessous pour un exemple autonome avec js en ligne, y compris la prise en charge de l'accès au clavier.

Garder les commandes ouvertes pendant le jeu est l'aspect le plus délicat. Il nécessite des pseudo-sélecteurs CSS ou un accès au shadow shadow du lecteur multimédia. Si vous devez prendre en charge Firefox, vous devrez peut-être étudier le style du dom ombre (ou créer des contrôles personnalisés).

video[controls]::-webkit-media-controls-panel {
    display: flex !important;
    opacity: 1 !important;
}

/* not required */
video {
    width: 15em;
    height: auto;
}
/* /not required */
<video 
    src="https://cdn.jsdelivr.net/npm/[email protected]/video.mp4"
    type="video/mp4"
    onmouseover="dataset.over=true;controls=true"
    onmouseout="delete dataset.over;if(paused) controls=false;"
    onplay="controls=true"
    onpause="if(!dataset.over && !dataset.focus) controls=false"
    onfocusin="dataset.focus=true; controls=true"
    onfocusout="delete dataset.focus; if(paused) controls=false;">
</video>

<video
    src="https://cdn.jsdelivr.net/npm/[email protected]/video.mp4"
    type="video/mp4"
    onmouseover="dataset.over=true;controls=true"
    onmouseout="delete dataset.over;if(paused)controls=false;"
    onplay="controls=true"
    onpause="if(!dataset.over&&!dataset.focus)controls=false"
    onfocusin="dataset.focus=true; controls=true"
    onfocusout="delete dataset.focus;if(paused)controls=false;">
</video>

Mise à jour de votre propre code (jQuery, pas de considérations clavier/focus):

var $video = $('.video');

$video.on('mouseover', mouseover);
$video.on('mouseout', mouseout);

function mouseover() {
    this.dataset.over = true;
    this.controls = true;
}

function mouseout() {
    delete this.dataset.over;
    if (this.paused)
        this.controls = false;
}
video[controls]::-webkit-media-controls-panel {
    display: flex !important;
    opacity: 1 !important;
}

/* not required */
video {
    width: 15em;
    height: auto;
}
/* /not required */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<video class="video">
    <source src="http://download.blender.org/Peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4" type="video/mp4">
</video>

Et avec quelques contrôles personnalisés très basiques:

document.querySelectorAll('.video').forEach($el => {
    let timeout;
    $el.addEventListener('play', () => {
        $el.dataset.playing = '';
    });
    $el.addEventListener('pause', () => {
        delete $el.dataset.playing;
    });
  
    $el.addEventListener('timeupdate', (e) => {
        const $scrub = $el.parentElement.querySelector('.video__scrub');
        const pos = $el.currentTime / $el.duration; 
        const increment = (pos - $scrub.value)/10;
    
        const update = () => {
            clearTimeout(timeout);
            $scrub.value = +$scrub.value + increment;
            if (!$el.paused)
                timeout = setTimeout(update, 50);
        };
        update();
    });
});

document.querySelectorAll('.video__toggle').forEach($el =>
    $el.addEventListener('click', () => {
        $video = $el.parentElement.previousElementSibling;
        $video.paused ? $video.play() : $video.pause();
    }));

document.querySelectorAll('.video__scrub').forEach($el => {
     $el.addEventListener('input', () => {
         const $video = $el.parentElement.previousElementSibling;
         $video.pause();
         $video.currentTime = $el.value * $video.duration;
     });
});
video {
    width: 100%;
    height: 100%;
}

.video__controls {
    display: flex;
    position: absolute;
    bottom: 0;
    width: 100%;
    background: rgba(255,255,255,0.3);
    padding: 0.2em 0;
    opacity: 0;
    visibility: hidden;
    transition: 0.2s visibility, 0.2s opacity;
}

.video__wrap:hover .video__controls,
.video[data-playing] ~ .video__controls {
    opacity: 1;
    visibility: visible;
}

.video__toggle {
    background: none;
    border: none;
    font-size: 1.2em;
    height: 2em;
    width: 2em;
}

.video__toggle::before {
    content: '▶️';
}

.video[data-playing] ~ .video__controls .video__toggle::before {
    content: '⏸';
}

.video__scrub {
    width: calc(100% - 5em);
    margin: 0;
}

/* not required */
.video__wrap {
    display: inline-block;
    position: relative;
    width: 40%;
    height: 56.25%;
}
<div class="video__wrap">
    <video class="video" src="https://cdn.jsdelivr.net/npm/[email protected]/video.mp4" type="video/mp4"></video>
    <nav class="video__controls">
        <button class="video__toggle"></button>
        <input class="video__scrub" type="range" min=0 max=1 value=0 step=0.001>
    </nav>
</div>

<div class="video__wrap">
    <video class="video" src="https://cdn.jsdelivr.net/npm/[email protected]/video.mp4" type="video/mp4"></video>
    <nav class="video__controls">
        <button class="video__toggle"></button>
        <input class="video__scrub" type="range" min=0 max=1 value=0 step=0.001>
    </nav>
</div>
2
som

Peu importe ce que vous définissez sur le gestionnaire onplaying, vous définissez toujours isPlaying sur false chaque fois que la fonction show() est exécutée, ce qui supprimera toujours les contrôles. Essayez de changer un peu votre logique. Comme la poignée lorsque la vidéo s'arrête, se met en pause ou change d'état. Utilisez-les pour changer votre logique en affichant les commandes.

1
Branco

Mise à jour - La seule solution multi-navigateur est les contrôles personnalisés

Les contrôles Chrome et Firefox sont visibles en survol et en lecture. En bonus, il est réactif. Remarque: Si vous souhaitez le voir fonctionner correctement pour Firefox et Chrome en plein écran, consultez le Plunker????.

Il semble qu'il ne soit pas entièrement possible d'afficher les commandes lors de la lecture de la vidéo, sauf si l'événement mousemove est déclenché. Au début, je me suis dit que si l'on pouvait déplacer la souris de 1px d'avant en arrière, ce serait la solution, quoique hacky et consommatrice de ressources. Malheureusement, déplacer le curseur de la souris par programme est impossible car personne n'apprécie vraiment que sa souris soit détournée, quelle que soit l'innocence des intentions d'un développeur.

Au début, je pensais que .focus() fonctionnerait, mais pas pour Firefox, donc après avoir essayé de trouver une solution pour un comportement cross-browser simple, il s'agissait de supprimer complètement les contrôles et de créer des contrôles personnalisés. Cela implique une tonne de styles spéciaux spécifiques à chaque navigateur. Voir cet article: Création d'un lecteur vidéo HTML5 personnalisé et du DOM fantôme . La démo suivante est une version jQuery à plusieurs joueurs de l'article démo . Pour plus de détails et de références, voir README.md .

Plunker????

Démo - Contrôles personnalisés - Remarque: Firefox et Chrome en mode plein écran voir Plunker???? .

<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>

  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
  <style>
  html {
    box-sizing: border-box;
  }
  
  *,
  *::before,
  *::after {
    box-sizing: inherit;
  }
  
  body {
    padding: 0;
    display: flex;
    flex-flow: column nowrap;
    min-height: 100vh;
    background: linear-gradient(135def, #7c1599 0%, #921099 48%, #7e4ae8 100%);
    background-size: cover;
    align-items: center;
    justify-content: center;
  }
  
  .cover {
    max-width: 750px;
    border: 5px solid rgba(0, 0, 0, 0.2);
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
    position: relative;
    font-size: 0;
    overflow: hidden;
  }
  
  .video {
    width: 100%;
  }
  
  .toggle {
    background: none;
    border: 0;
    line-height: 1;
    color: white;
    text-align: center;
    outline: 0;
    padding: 0;
    cursor: pointer;
    max-width: 50px;
  }
  
  .toggle:focus {
    border-color: #ffc600;
  }
  
  .volume {
    width: 10px;
    height: 30px;
  }
  
  .panel {
    display: flex;
    position: absolute;
    bottom: 0;
    width: 100%;
    transform: translateY(100%) translateY(-5px);
    transition: all 0.3s;
    flex-wrap: wrap;
    background: rgba(0, 0, 0, 0.1);
    z-index: 2147483648;
    left: 0;
  }
  
  .cover:hover .panel,
  .panel.active {
    transform: translateY(0);
  }
  
  .panel:hover .progress,
  .panel.active .progress {
    height: 15px;
  }
  
  .panel > * {
    flex: 1;
  }
  
  .progress {
    flex: 10;
    position: relative;
    display: flex;
    flex-basis: 100%;
    height: 5px;
    transition: height 0.3s;
    background: rgba(0, 0, 0, 0.5);
    cursor: ew-resize;
  }
  
  .bar {
    width: 50%;
    background: #ffc600;
    flex: 0;
    flex-basis: 50%;
  }
  /* unholy css to style input type="range" */
  
  input[type=range] {
    -webkit-appearance: none;
    background: transparent;
    width: 100%;
    margin: 12px 3px;
  }
  
  input[type=range]:focus {
    outline: none;
  }
  
  input[type=range]::-webkit-slider-runnable-track {
    width: 100%;
    height: 5px;
    cursor: pointer;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0);
    background: rgba(255, 255, 255, 0.8);
    border-radius: 1.3px;
    border: 0.2px solid rgba(1, 1, 1, 0);
  }
  
  input[type=range]::-webkit-slider-thumb {
    box-shadow: 0 0 0 rgba(0, 0, 0, 0), 0 0 0 rgba(13, 13, 13, 0);
    height: 1.5em;
    width: 1.5em;
    border-radius: 20px;
    background: #ffc600;
    cursor: pointer;
    -webkit-appearance: none;
    margin-top: -8px;
    box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
  }
  
  input[type=range]:focus::-webkit-slider-runnable-track {
    background: #ffc600;
  }
  
  input[type=range]::-moz-range-track {
    width: 100%;
    height: 3px;
    cursor: pointer;
    box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0px 0px 1px rgba(13, 13, 13, 0);
    background: #fff;
    border-radius: 1.3px;
    border: 0.2px solid rgba(1, 1, 1, 0);
  }
  
  input[type=range]::-moz-range-thumb {
    box-shadow: 0px 0px 0px rgba(0, 0, 0, 0), 0px 0px 0px rgba(13, 13, 13, 0);
    height: 1.5em;
    width: 1.5em;
    border: 0;
    border-radius: 20px;
    background: #ffc600;
    cursor: pointer;
  }
  /* full screen button styling */
  
  .fullscreen {
    margin-right: 7px;
    background: none;
    border: 1px solid white;
    border: 0;
    line-height: 1;
    color: white;
    text-align: center;
    outline: 0;
    padding: 0 0 5px 0;
    cursor: pointer;
    max-width: 30px;
    font-size: 1.3rem;
  }
  /* Because video needed a defined hieght in order for object-fit: fill to work. */
  
  video {
    height: 100%;
    object-fit: fill;
  }
  /* hide the default Chrome video player styling */
  
  video::-webkit-media-controls-overlay-Enclosure {
    display: none !important;
  }
  
  video::-webkit-media-controls-Enclosure {
    display: none !important;
  }
  
  video::-webkit-media-controls {
    display: none !important;
  }
  /*  Needed to hide player controls in Safari Only */
  
  video::-webkit-media-controls-panel {
    display: none !important;
  }
  
  video::-webkit-media-controls-play-button {
    display: none !important;
  }
  
  video::-webkit-media-controls-current-time-display {
    display: none !important;
  }
  
  video::-webkit-media-controls-time-remaining-display {
    display: none !important;
  }
  
  video::-webkit-media-controls-timeline {
    display: none !important;
  }
  
  video::-webkit-media-controls-mute-button {
    display: none !important;
  }
  
  video::-webkit-media-controls-volume-slider {
    display: none !important;
  }
  
  video::-webkit-media-controls-fullscreen-button {
    display: none !important;
  }
  
  video::-internal-media-controls-download-button {
    display: none !important;
  }
  /* Firefox Shadow DOM Fix */
  
  *::-moz-list-bullet,
  *::-moz-list-number {
    display: none !important;
  }
  
  *::-moz-meter-bar {
    display: none !important;
  }
  
  :-moz-full-screen:not(:root)::backdrop {
    display: none !important;
  }
  
  *::backdrop {
    display: none !important;
  }
  
  :fullscreen:not(:root) {
    display: none !important;
  }
  /* New addition to removal of User Agent StyleSheet for Firefox. Removed dotted border around range. */
  
  input[type="range"]::-moz-focus-outer {
    border: 0;
  }
  </style>

</head>

<body>

  <div id='V0' class='cover'></div>

  <div id='V1' class='cover'></div>

  <div id='V2' class='cover'></div>

  <div id='V3' class='cover'></div>

  <template id='controls'>
    <div class="panel">
      <div class="progress">
        <div class="bar"></div>
      </div>
      <button class="toggle" title="Play/Pause">
        <i class="fa fa-play fa-3x"></i>
      </button>
      <input type="range" class="volume" min="0" max="1" step="0.05" value="0.70">
      <button class='fullscreen'>
        <i class='fa fa-expand fa-2x'></i>
      </button>
    </div>
  </template>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
 
  <script>
var mp4 = ['005609.mp4', '005610.mp4', '005611.mp4', '005612.mp4'];
var webm = ['041157.mp4', '041153.mp4', '041154.mp4', '041156.mp4'];

function init(VMp4, VWebm) {

  var VArray = Array.from(document.querySelectorAll('.cover'));

  VArray.map(function(V, idx) {
    var ID = V.id;
    return players(ID, idx, VMp4, VWebm);
  });
}

function players(id, IDX, vMp4, vWebm) {

  var V = document.getElementById(id);
  console.log(V);

  var frag = document.createDocumentFragment();
  var tag = document.createElement('video');
  var src0 = document.createElement('source');
  var src1 = document.createElement('source');

  tag.classList.add('video');
  tag.controls = false;
  tag.width = '320';
  tag.style.background = '#000';
  tag.poster = `https://gincore.net/images/video-play-2.png`;
  // Set Paths
  var mUrl = `https://storage04.dropshots.com/photos6000/photos/1381926/20170326/`;
  var wUrl = `https://storage04.dropshots.com/photos7000/photos/1381926/20180214/`;
  src0.type = 'video/mp4';
  src1.type = 'video/webm';
  src0.src = mUrl + vMp4[IDX];
  src1.src = wUrl + vWebm[IDX];

  frag.appendChild(tag);
  tag.appendChild(src0);
  tag.appendChild(src1);

  V.appendChild(frag);

  var controls = document.querySelector('#controls').content;
  var clone = document.importNode(controls, true);
  V.appendChild(clone);
}

init(mp4, webm);


$(".cover").each(function() {
  
  var C = $(this)[0].id;
  
  var $ctl = $(this).find('.panel');
  var $vid = $(this).find('.video');
  var $tog = $(this).find('.toggle');
  var $prg = $(this).find('.progress');
  var $bar = $(this).find('.bar');
  var $vol = $(this).find('.volume');
  var $tfs = $(this).find('.fullscreen')

  var ctl = $ctl[0];
  var vid = $vid[0];
  var tog = $tog[0];
  var prg = $prg[0];
  var bar = $bar[0];
  var vol = $vol[0];
  var tfs = $tfs[0];

  function togglePlay() {
    var playPause = vid.paused ? 'play' : 'pause';
    vid[playPause]();
    $tog.find('.fa').toggleClass('fa-play fa-pause');
  }

  function updateVolume() {
    vid.volume = this.value;
  }

  function updateProgress() {
    var perc = (vid.currentTime / vid.duration) * 100;
    bar.style.flexBasis = `${perc}%`;
  }

  function seekTrack(e) {
    var seekTime = (e.offsetX / prg.offsetWidth) * vid.duration;
    vid.currentTime = seekTime;
  }

  var isFullScreen = function() {
    return !!(document.webkitFullscreenElement || document.mozFullScreenElement || document.fullscreenElement);
  };

  function toggleFS() {
    if (!isFullScreen()) {
      if (vid.requestFullscreen) {
        vid.requestFullscreen();
      } else if (vid.webkitRequestFullScreen) {
        vid.webkitRequestFullScreen();
      } else if (document.getElementById(C).mozRequestFullScreen) {
        document.getElementById(C).mozRequestFullScreen();
      } else if (vid.msRequestFullscreen) {
        vid.msRequestFullscreen();
      }
      $tfs.find('.fa').removeClass('fa-expand').addClass('fa-compress');
      $ctl.removeClass('active');
      $('.panel').css('z-index', '-1');
      $('#' + C + " .panel").css('z-index',"2147483648");
    } else {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      } else if (document.webkitExitFullscreen) {
        document.webkitExitFullscreen();
      } else if (document.mozCancelFullScreen) {
        document.mozCancelFullScreen();
      } else if (document.msCancelFullscreen) {
        document.msCancelFullscreen();
      }
      $tfs.find('.fa').addClass('fa-expand').removeClass('fa-compress');
      if (!vid.pause || !vid.ended) {
        $ctl.addClass('active');
      }
      $('.panel').css('z-index', '2147483648');
    }
  }

  function go() {
    $ctl.addClass('active');
    $tog.find('.fa').removeClass('fa-play').addClass('fa-pause');
  }

  function stop() {
    $ctl.removeClass('active');
    $tog.find('.fa').removeClass('fa-pause').addClass('fa-play');
  }

  $vid.on('click', togglePlay);

  $tog.on('click', togglePlay);

  $vid.on('timeupdate', updateProgress);

  $vid.on('playing', go);

  $vid.on('ended pause', stop);

  $vol.on('input', updateVolume);

  var mousedown = false;

  $prg.on('click', seekTrack);

  $prg.on('mousemove', function(e) {
    mousedown && seekTrack(e);
  });

  $prg.on('mousedown', function() {
    mousedown = true;
  });

  $prg.on('mouseup', function() {
    mousedown = false;
  });

  $tfs.on('click', toggleFS);

});
  </script>
</body>

</html>

????Si cela ne fonctionne toujours pas, copiez et collez ensuite tout le code sur n'importe quel éditeur de texte et enregistrez-le avec un .html extension puis ouvrez ce fichier dans Firefox et/ou Chrome. Cela fonctionnera à 100%.

1
zer00ne

Découvrez mon JSFiddle .

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<video class=video>

    <source src="http://download.blender.org/Peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4" type="video/mp4">
</video>

<style>

    video[controls]::-webkit-media-controls-panel {
        display:flex!important;
        opacity:1!important
    }
</style>

<script>

    var $video=$(".video");
    $video.on("mouseover",function() {
        this.dataset.over = true;
        this.controls = true;
    }),
    $video.on("mouseout",function() {
        this.dataset.over = false;
        if (this.paused)
            this.controls = false;
    })
</script>
1
user9178142

L'attribut controls est booléen:

// by id video.
document.getElementById("video1").controls = true; //show
document.getElementById("video1").controls = false; //hide.

//or all video by tag name:
document.getElementsByTagName("video").controls = true; //show
document.getElementsByTagName("video").controls = false; //hide.

J'espère que ça aide.

1
toto