web-dev-qa-db-ja.com

HoverまたはPlayで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>
11
Cofey

この動作はブラウザによって内部的に管理されているため、実際に制御することはできません。できることは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>
10
user1693593

このコードを確認してください。あなたの要件に応じて動作します。 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>
3
Khan M

これは、Webkitベースのブラウザであれば簡単に実行できます。キーボードアクセスのサポートを含む、インラインjsを含む自己完結型の例については、以下を参照してください。

プレイ中にコントロールを開いたままにしておくことは、最も難しい面です。 CSS擬似セレクターまたはメディアプレーヤーのシャドウdomへのアクセスが必要です。 Firefoxをサポートする必要がある場合は、シャドウ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/[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>

独自のコードの更新(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/[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

onplayingハンドラーに何を設定しても、show()が実行されるたびにisPlayingをfalseに設定しているため、常にコントロールが削除されます。ロジックを少し変更してみてください。ビデオが停止したり、一時停止したり、状態が変化したりするときのハンドルのように。これらを使用して、コントロールを表示するロジックを変更します。

1
Branco

更新-唯一のクロスブラウザソリューションはカスタムコントロールです

ChromeおよびFirefoxのコントロールは、ホバーおよび再生時に表示されます。副ボーナスとして、反応が良いです。注:Firefoxで正常に機能し、フルスクリーンでChromeと表示される場合は、 Plunker????

Mousemoveイベントが発生しない限り、ビデオの再生時にコントロールを表示することは完全に実行可能ではないように見えます。最初は、マウスを1px前後に動かすことができれば、それはハッキングとリソースの消費にもかかわらず解決策になると考えました。残念ながら、プログラムでマウスカーソルを移動することは不可能です。なぜなら、開発者の意図がいかに無害であっても、マウスがハイジャックされることを本当に評価する人はいないからです。

最初は.focus()が機能すると思ったが、Firefoxでは機能しなかったので、単純なクロスブラウザ動作の解決策を見つけようとすると、コントロールを完全に削除してカスタムコントロールを作成することになりました。それには、各ブラウザに固有の特別なスタイルがたくさん含まれています。この記事を参照してください: カスタムHTML5ビデオプレーヤーとシャドウDOM の作成。次のデモは、記事の demo のマルチプレイヤーjQueryバージョンです。詳細および参照については、 README.md を参照してください。

プランカー????

デモ-カスタムコントロール-注: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%動作します。

1
zer00ne

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>
1
user9178142

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

それがお役に立てば幸いです。

1
toto