Нужно показать HTML5 Видео элементы управления при наведении или воспроизведении

Я хотел бы скрыть элементы управления для нескольких видео HTML5 на странице. Если пользователь парит над видео, должны появиться элементы управления. Если они нажимают кнопку воспроизведения, то элементы управления должны оставаться видимыми, даже если их мышь покидает элемент видео.

Я не могу заставить это работать, используя следующий код. Кто-нибудь может определить проблему?

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>

7 ответов


мы не можем реально контролировать это поведение, поскольку оно управляется внутренне браузером. Все, что мы можем сделать, это указать controls атрибут и браузер сделают все остальное.

например: в Firefox (v59b, когда это написано) элементы управления будут исчезать, когда мышь находится вне элемента, когда видео играет, даже если controls атрибут установлен - они будут показывать, если не играть, вид противоположности вы после. Нет никакого способа заставить элементы управления оставаться видимыми, когда пользователь перемещает мышь за пределы элемента video.

единственный способ правильно обрабатывать этот кросс-браузер и с точным желаемым поведением-создать пользовательский интерфейс управления для игрока. Это, конечно, означает, что для обработки различных событий требуется больше кода, чтобы вы могли обновлять и управлять пользовательским интерфейсом; это также может быть проблемой, когда дело доходит до платформы/браузера. Но с другой стороны, это даст вам контроль над зерном.

альтернативой является просмотр некоторых библиотек который обертывает элемент video в пользовательские элементы управления UI и видит, позволяют ли они заставить элементы управления оставаться видимыми с заданными условиями. См., например,videojs в качестве отправной точки.

небольшой, но неполный пример (добавьте функциональность, обработчики событий, дизайн по мере необходимости):

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>

проверить этот код . своя работа согласно вашим требованиям. Использовать


вы можете сделать это довольно легко в любых браузерах на основе webkit. См. ниже автономный пример со встроенным js, включая поддержку доступа к клавиатуре.

держать элементы управления открытыми во время игры является самым сложным аспектом. Это требует CSS псевдо-селекторов или доступа к теневому dom медиаплеера. Если вам нужна поддержка Firefox, вам может потребоваться изучить стиль shadow dom (или создать пользовательский контроли.)

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/big-buck-bunny-1080p@0.0.6/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/big-buck-bunny-1080p@0.0.6/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>

обновление собственного кода (jQuery, без клавиатуры/фокусировки):

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>

и с некоторыми очень основными пользовательскими элементами управления:

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/big-buck-bunny-1080p@0.0.6/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/big-buck-bunny-1080p@0.0.6/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>

Обновлено-единственное кросс-браузерное решение-пользовательские элементы управления

элементы управления Chrome и Firefox отображаются при наведении и воспроизведении. В качестве побочного бонуса он отзывчив. Примечание. Если вы хотите, чтобы он работал правильно для Firefox и Chrome в полноэкранном режиме, см. Plunker.

похоже, что не совсем возможно показывать элементы управления при воспроизведении видео, если событие mousemove не запускается. Сначала я подумал, что если можно было бы перемещать мышь 1px взад и вперед, тогда это было бы решением, хотя и хакерским и ресурсоемким. К сожалению, перемещение курсора мыши программно невозможно, потому что никто не ценит, что их мышь была захвачена независимо от того, насколько невинны намерения разработчика.

сначала я думал .focus() будет работать, но не для Firefox, поэтому после попытки найти решение для простого кросс-браузерного поведения он пришел к полному удалению элементов управления и создание пользовательских элементов управления. Он включает в себя тонну специальных стилей, которые специфичны для каждого браузера. См. эту статью: создание пользовательского видеоплеера HTML5 и Shadow DOM. Следующие демо-кратно плеер на jQuery версия статьи демо. Подробности и ссылки см. ридми.МД.

Plunker

Demo-Пользовательские Элементы Управления - Примечание: Firefox и Chrome в полноэкранном режиме см. 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>

если он все еще не работает, скопируйте и вставьте весь код в любой текстовый редактор и сохраните его с помощью .html расширение затем откройте этот файл в Firefox и / или Chrome. Это сработает 100%.


атрибут controls - Это логическое:

// 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.

надеюсь, это поможет.


проверить мой 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>

независимо от того, что вы установили на onplaying обработчик, вы все еще устанавливаете isPlaying быть ложным каждый раз, когда show() выполняется, поэтому он всегда будет снять контроль. Попробуйте немного изменить свою логику. Как ручка, когда видео останавливается, приостанавливается или иным образом изменяет состояние. Используйте их, чтобы изменить логику отображения элементов управления.