需要在悬停或播放时显示HTML5视频控件

时间:2018-02-08 20:00:46

标签: javascript jquery html5 controls html5-video

我想在页面上隐藏多个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>

8 个答案:

答案 0 :(得分:10)

我们无法真正控制此行为,因为它是由浏览器在内部管理的。我们所能做的就是指定controls属性,浏览器将完成其余的工作。

例如:在Firefox中(v59b写入时),即使设置了controls属性,当视频播放时鼠标位于元素外部时控件也会淡出 - 如果没有播放,它们将显示与你相反的那种。当用户将鼠标移到视频元素之外时,无法强制控件保持可见。

正确处理此跨浏览器并具有确切所需行为的唯一方法是为播放器构建自定义控件UI。这当然意味着需要更多代码来处理各种事件,以便您可以更新和管理UI;在平台/浏览器特定外观方面,这也可能是一个挑战。但另一方面它会给你粮食控制。

另一种方法是查看一些将视频元素包装到自定义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>

答案 1 :(得分:3)

检查此代码。它根据您的要求工作。使用$('.video').get(0).paused条件中的if功能设置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>

答案 2 :(得分:3)

您可以在任何基于webkit的浏览器中轻松完成此操作。请参阅下面的内联js的自包含示例,包括对键盘访问的支持。

在比赛期间保持控制打开是最棘手的方面。它需要CSS伪选择器或访问媒体播放器的影子dom。如果你需要支持Firefox,你可能需要研究阴影dom的样式(或创建自定义控件)。

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

/* not required */
video {
    width: 15em;
    height: auto;
}
/* /not required */
&#13;
<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>
&#13;
&#13;
&#13;

更新您自己的代码(jQuery,没有键盘/焦点注意事项):

&#13;
&#13;
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;
}
&#13;
video[controls]::-webkit-media-controls-panel {
    display: flex !important;
    opacity: 1 !important;
}

/* not required */
video {
    width: 15em;
    height: auto;
}
/* /not required */
&#13;
<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>
&#13;
&#13;
&#13;

使用一些非常基本的自定义控件:

&#13;
&#13;
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;
     });
});
&#13;
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%;
}
&#13;
<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>
&#13;
&#13;
&#13;

答案 3 :(得分:1)

无论您在onplaying处理程序上设置什么,每次执行isPlaying时,您仍然将show()设置为false,因此它将始终删除控件。尝试改变你的逻辑。像视频停止,暂停或以其他方式更改状态时的句柄。使用它们来改变显示控件的逻辑。

答案 4 :(得分:1)

已更新 - 唯一的跨浏览器解决方案是自定义控件

在悬停和播放时可以看到Chrome和Firefox控件。作为一个副作用,它是响应。注意:如果您希望全屏显示适用于Firefox和Chrome的功能,请参阅Plunker

除非发生mousemove事件,否则在播放视频时显示控件似乎并不完全可行。起初我认为,如果一个人可以来回移动鼠标1px那么这就是解决方案,尽管是hacky和资源消耗。不幸的是,以编程方式移动鼠标光标是不可能的,因为没有人真正感谢他们的鼠标被劫持,无论开发人员的意图多么无辜。

起初我认为.focus()可以使用,但不适用于Firefox,因此在尝试找到简单的跨浏览器行为的解决方案之后,它归结为完全删除控件并创建自定义控件。它涉及大量特定于每个浏览器的特殊样式。请参阅此文章:Creating a Custom HTML5 Video Player and the Shadow DOM。以下演示是该文章demo的多玩家jQuery版本。有关详细信息和参考,请参阅README.md

Plunker

演示 - 自定义控件 - 注意:Firefox和Chrome采用全屏模式,请参阅Plunker

&#13;
&#13;
<!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>
&#13;
&#13;
&#13;

如果仍然无法正常工作,则将整个代码复制并粘贴到任何文本编辑器上,并使用.html扩展名保存,然后在Firefox中打开该文件和/或铬。那将是100%的工作。

答案 5 :(得分:1)

属性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.

我希望有所帮助。

答案 6 :(得分:1)

查看my 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>

答案 7 :(得分:0)

<style>
    video::-webkit-media-controls {
        opacity: 0
    }
    video::-webkit-media-controls:hover {
        opacity: 1
    }
</style>