需要在悬停或播放时显示 HTML5 视频控件
Posted
技术标签:
【中文标题】需要在悬停或播放时显示 HTML5 视频控件【英文标题】:Need to show HTML5 Video Controls on Hover or Play 【发布时间】:2018-07-19 12:22:31 【问题描述】:我想在一个页面上隐藏多个 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>
【问题讨论】:
不要设置变量isPlaying
,而是考虑检查videoElement的paused
属性($('.video')[0].paused
),当视频尚未开始时,true
被暂停(是的,真的)或当它结束时。此外,就像提到的K3N 一样,(本机)控制栏由浏览器在内部处理。
随着触摸屏的普及,您确定 mouseover 和 mouseleave 会为您的观众服务吗?会映射到 touchenter / touchleave 吗?否则,您的基于触摸的用户将无法访问视频控件。
【参考方案1】:
<style>
video::-webkit-media-controls
opacity: 0
video::-webkit-media-controls:hover
opacity: 1
</style>
【讨论】:
请将您的代码格式化为可读格式。另外,解释你的代码。它有什么作用? 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的答案以添加解释并说明适用的限制和假设。 From Review【参考方案2】:我们无法真正控制这种行为,因为它是由浏览器内部管理的。我们所能做的就是指定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>
【讨论】:
【参考方案3】:无论您在 onplaying
处理程序上设置什么,每次执行 show()
时,您仍然将 isPlaying
设置为 false,因此它始终会删除控件。试着改变你的逻辑。就像视频停止、暂停或以其他方式改变状态时的句柄一样。使用这些来更改您在显示控件时的逻辑。
【讨论】:
【参考方案4】:您可以在任何基于 webkit 的浏览器中轻松完成此操作。请参阅下面的内联 js 的独立示例,包括对键盘访问的支持。
在游戏过程中保持控件打开是最棘手的方面。它需要 CSS 伪选择器或访问媒体播放器的 shadow 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>
【讨论】:
video[controls]::-webkit-media-controls-pane
css 刚刚为我解决了一个巨大的问题,即控件由于某种原因没有显示在 chrome 中。谢谢!【参考方案5】:
查看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>
【讨论】:
在回答之前检查现有的答案,看看您是否提出任何新的建议【参考方案6】:属性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.
希望对你有帮助。
【讨论】:
【参考方案7】:检查此代码。它根据您的要求工作。在if
条件下使用$('.video').get(0).paused
函数设置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>
【讨论】:
感谢您的帮助。我尝试了您的代码并收到错误Uncaught TypeError: Cannot read property 'paused' of undefined
。我将if (!$('.video').get(0).paused)
更改为if (!$(this).get(0).paused)
并且错误消失了,但是当视频播放时,鼠标离开时控件仍然消失。有什么想法吗?
嗯,我无法播放,因为没有找到视频。
我刚刚编辑了您的答案并添加了一个开源 MP4。尝试播放它并注意当鼠标离开视频元素时控件是如何消失的。
你可以只使用 this.paused,因为 this == $(this).get(0)【参考方案8】:
更新 - 唯一的跨浏览器解决方案是自定义控件
Chrome 和 Firefox 控件在悬停和播放时可见。作为附带奖励,它是响应式的。注意:如果您想看到它在 Firefox 和 Chrome 全屏下正常运行,请参阅Plunker?。
除非触发 mousemove 事件,否则在播放视频时显示控件似乎并不完全可行。起初我想如果一个人可以来回移动鼠标 1px 那么这将是一个解决方案,尽管它很笨拙并且消耗资源。不幸的是,以编程方式移动鼠标光标是不可能的,因为无论开发人员的意图多么天真,没有人真正理解他们的鼠标被劫持。
起初我认为.focus()
可以工作,但不适用于 Firefox,因此在尝试为简单的跨浏览器行为找到解决方案后,最终决定完全删除控件并创建自定义控件。它涉及大量特定于每个浏览器的特殊样式。见这篇文章:Creating a Custom HTML5 Video Player and the Shadow DOM。下面的demo是文章demo的多人jQuery版本。有关详细信息和参考,请参阅README.md。
Plunker?
演示 - 自定义控件 - 注意:全屏模式下的 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% 有效。
【讨论】:
抱歉,我不能reproduce in Firefox v59b/60a(Windows,没有附加组件)。你用的是什么版本? @K3N 我的错,请参阅唯一跨浏览器解决方案的更新。以上是关于需要在悬停或播放时显示 HTML5 视频控件的主要内容,如果未能解决你的问题,请参考以下文章