Построим: с помощью JavaScript - видеопроигрыватель HTML5

Первоначально опубликовано на сайте web-crunch.com 4 октября 2018 г.

Добро пожаловать в следующий выпуск моей серии статей «Построим: с помощью JavaScript». В этом руководстве вы узнаете, как создавать, настраивать видеопроигрыватель HTML5 и управлять им с помощью стандартного JavaScript.

Я буду использовать комбинацию HTML, CSS и JavaScript для выполнения этой задачи, которая немного сложнее, чем в предыдущих частях этой серии.

Первоначальную идею по манипулированию элементом HTML5 Video я получил от Wes Bos, который сделал очень похожий видеоурок в курсе JavaScript 30. Я настоятельно рекомендую пройти его курс, поскольку он учит вас современным советам и приемам для рабочего процесса JavaScript.

Большая заслуга в концепции и структуре кода принадлежит Уэсу. Я хотел попробовать сделать что-то подобное со своим чутьем. Я использовал разные стили, соглашения об именах и многое другое, чтобы добиться того, что вы видите на финальном CodePen.

Посмотреть исходный код

Приступим к кодированию:

Самый первый шаг к размещению видеоэлемента на странице - это его визуализация с использованием тега newish video.

HTML

Мой последний HTML выглядит следующим образом:

<div class="player">
  <video class="player-video" src="https://staging.coverr.co/s3/mp4/Down_by_the_River.mp4"></video>

  <div class="player-controls">

    <div class="progress">
      <div class="filled-progress"></div>
    </div>

        <div class="ply-btn">
        <button class="player-btn toggle-play" title="Toggle Play">
        <svg class="" width="16" height="16" viewBox="0 0 16 16"><title>play</title><path d="M3 2l10 6-10 6z"></path></svg>
        </button>
    </div>

    <div class="sliders">
    <input type="range" name="volume" class="player-slider" min="0" max="1" step="0.05" value="1">

    <input type="range" name="playbackRate" class="player-slider" min="0.5" max="2" step="0.1" value="1">
    </div>

    <button data-skip="-10" class="player-btn">« 10s</button>

    <button data-skip="10" class="player-btn">10s »</button>

  </div>
</div>

Нам нужны различные элементы управления для управления некоторыми частями API элемента video. В каждом элементе video есть огромное количество свойств, которыми мы можем управлять с помощью JavaScript. Все, от громкости до скорости воспроизведения, можно настроить с помощью нескольких строк кода.

HTML выше содержит

  • содержащий .player div, который мы будем использовать для стилизации.
  • элемент video с указанным атрибутом src
  • .player-controls div, в котором будут все элементы управления, которые мы хотим
  • .play-btn div для упаковки значков воспроизведения / паузы, которые мы будем использовать
  • ползунки диапазона ввода для управления громкостью и скоростью воспроизведения
  • кнопки для перехода вперед и назад

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

CSS

Хотя я назвал его CSS выше, на самом деле я буду использовать SCSS для написания своих стилей. Чтобы написать что-то подобное, вам понадобится прекомпилятор. Загляните в codepen для скомпилированных стилей, если вам нужно.

$accent-color: #FFEC41;

body {
  align-items: center;
  background: #000046;
  background: linear-gradient(to right, #1CB5E0, #000046);
  display: flex;
  height: 100vh;
  justify-content: center;
  margin: 0;
  padding: 0;
}

.player {
  max-width: 800px;
  border: 6px solid rgba(255, 255, 255, 0.2);
  box-shadow: 0 0 25px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.1);
  position: relative;
  overflow: hidden;

  &:hover {
    .progress {
      height: 10px;
    }

    .player-controls {
      transform: translateY(0);
    }
  }
}

.player:-webkit-full-screen,
.player:fullscreen {
  max-width: none;
  width: 100%;
}

.play-btn {
  flex: 1;
}

.player-video {
  width: 100%;
  display: block;
}

.player-btn {
  background: none;
  border: 0;
  color: white;
  text-align: center;
  max-width: 60px;
  padding: 5px 8px;

  svg {
    fill: #FFFFFF;
  }

  &:hover,
  &:focus {
    border-color: $accent-color;
    background: rgba(255, 255, 255, .2);
  }
}

.player-slider {
  width: 10px;
  height: 30px;
}

.player-controls {
  align-items: center;
  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.3);
}

.player-controls > * {
  flex: 1;
}

.progress {
  position: relative;
  display: flex;
  flex: 10;
  flex-basis: 100%;
  height: 4px;
  transition: height 0.3s;
  background: rgba(0, 0, 0, 0.5);
}

.filled-progress {
  width: 50%;
  background: $accent-color;
  flex: 0;
  flex-basis: 50%;
}

.sliders {
  max-width: 200px;
  display: flex;
}

input[type=range] {
  -webkit-appearance: none;
  background: transparent;
  width: 100%;
  margin: 0 5px;
}

input[type=range]:focus {
  outline: none;
}

input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 8px;
  cursor: pointer;
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
  background: rgba(255, 255, 255, 0.5);
  border-radius: 10px;
  border: 0.2px solid rgba(1, 1, 1, 0);
}

input[type=range]::-webkit-slider-thumb {
  height: 15px;
  width: 15px;
  border-radius: 50px;
  background: white;
  cursor: pointer;
  -webkit-appearance: none;
  margin-top: -3.5px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

input[type=range]:focus::-webkit-slider-runnable-track {
  background: rgba(255, 255, 255, 0.8);
}

input[type=range]::-moz-range-track {
  width: 100%;
  height: 8px;
  cursor: pointer;
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
  background: #ffffff;
  border-radius: 10px;
  border: 0.2px solid rgba(1, 1, 1, 0);
}

input[type=range]::-moz-range-thumb {
  box-shadow: 0 0 3px rgba(0, 0, 0, 0), 0 0 1px rgba(13, 13, 13, 0);
  height: 15px;
  width: 15px;
  border-radius: 50px;
  background: white;
  cursor: pointer;
}

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

JavaScript

Наш JavaScript будет ориентирован на объектно-ориентированный подход. Это означает, что мы создадим множество маленьких функций, которые в основном делают одно. В совокупности они сделают видео полнофункциональным компонентом. По отдельности это упростит понимание того, что происходит, любому другому разработчику, который может увидеть код. Большинство итераций в самом видео вызываются прослушивателями событий (список для взаимодействия с пользователем). Мы напишем функции, которые делают что-то, когда фиксируются определенные действия. Заключительная логика здесь:

const player = document.querySelector('.player');
const video = player.querySelector('.player-video');
const progress = player.querySelector('.progress');
const progressFilled = player.querySelector('.filled-progress');
const toggle = player.querySelector('.toggle-play');
const skippers = player.querySelectorAll('[data-skip]');
const ranges = player.querySelectorAll('.player-slider');

// Logic
function togglePlay() {
  const playState = video.paused ? 'play' : 'pause';
  video[playState](); // Call play or paused method
}

function updateButton() {
  const togglePlayBtn = document.querySelector('.toggle-play');

  if(this.paused) {
    togglePlayBtn.innerHTML = `<svg class="" width="16" height="16" viewBox="0 0 16 16"><title>play</title><path d="M3 2l10 6-10 6z"></path></svg>`;
  } else {
    togglePlayBtn.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16"><title>pause</title><path d="M2 2h5v12H2zm7 0h5v12H9z"></path></svg>`;
  }
}

function skip() {
  video.currentTime += parseFloat(this.dataset.skip);
}

function rangeUpdate() {
  video[this.name] = this.value;
}

function progressUpdate() {
  const percent = (video.currentTime / video.duration) * 100;
  progressFilled.style.flexBasis = `${percent}%`;
}

function scrub(e) {
  const scrubTime = (e.offsetX / progress.offsetWidth) * video.duration;
  video.currentTime = scrubTime;
}

// Event listeners
video.addEventListener('click', togglePlay);
video.addEventListener('play', updateButton);
video.addEventListener('pause', updateButton);
video.addEventListener('timeupdate', progressUpdate);

toggle.addEventListener('click', togglePlay);
skippers.forEach(button => button.addEventListener('click', skip));
ranges.forEach(range => range.addEventListener('change', rangeUpdate));
ranges.forEach(range => range.addEventListener('mousemove', rangeUpdate));

let mousedown = false;
progress.addEventListener('click', scrub);
progress.addEventListener('mousemove', (e) => mousedown && scrub(e));
progress.addEventListener('mousedown', () => mousedown = true);
progress.addEventListener('mouseup', () => mousedown = false);

Как видите, количество слушателей событий, которые нам здесь нужны, становится довольно сложным, но, тем не менее, все работает довольно хорошо. Обязательно просмотрите видео, чтобы получить полный контекст. Письменные версии предназначены для контекстных целей, но также и для справки, если вы предпочитаете ссылаться на код напрямую, а не на код.

Большое спасибо за просмотр / чтение. Впереди еще много всего. Если вы новичок здесь, обязательно посмотрите другие видео из этой серии, перечисленные ниже:

Давайте строим: с помощью серии JavaScript

Первоначально опубликовано на сайте web-crunch.com 4 октября 2018 г.