Mobil Web Video Oynatma

François Beaufort
François Beaufort

Web'de en iyi mobil medya deneyimini nasıl oluşturabilirsiniz? Kolay! Bu tümüyle kullanıcı etkileşimine ve bir web sayfasındaki medyaya verdiğiniz öneme bağlıdır. Sanırım hepimiz, videoyu ziyaret etmenin TEKNİNİn video olduğu durumlarda, kullanıcı deneyiminin sürükleyici ve yeniden ilgi çekici olması gerektiği konusunda hemfikir olduğumuzu düşünüyorum.

mobil web video oynatma

Bu makalede, çok sayıda Web API'si sayesinde medya deneyiminizi aşamalı bir şekilde nasıl geliştirebileceğinizi ve sürükleyici hale nasıl getirebileceğinizi göstereceğim. Bu nedenle özel kontroller, tam ekran ve arka planda oynatma özelliklerine sahip basit bir mobil oynatıcı deneyimi oluşturacağız. Örnek bölümünü hemen deneyip kodu GitHub depomuzda bulabilirsiniz.

Özel denetimler

HTML Düzeni
Şekil 1.HTML Düzeni

Gördüğünüz gibi, medya oynatıcımız için kullanacağımız HTML düzeni oldukça basittir: <div> kök öğesi, <video> medya öğesi ve video kontrollerine ayrılmış bir <div> alt öğesi içerir.

Daha sonra ele alacağımız video kontrolleri şunları içerir: oynatma/duraklatma düğmesi, tam ekran düğmesi, geri ve ileri sarma düğmeleri ve geçerli zaman, süre ve süre takibi için bazı öğeler.

<div id="videoContainer">
  <video id="video" src="file.mp4"></video>
  <div id="videoControls"></div>
</div>

Video meta verilerini okuma

Öncelikle video süresini ve geçerli zamanı ayarlamak ve ilerleme çubuğunu başlatmak için video meta verilerinin yüklenmesini bekleyelim. secondsToTimeCode() işlevinin, birkaç saniyeyi bizim örneğimizde daha uygun olan "ss:dd:ss" biçimindeki bir dizeye dönüştüren, daha önce yazdığım bir özel yardımcı program işlevi olduğunu unutmayın.

<div id="videoContainer">
  <video id="video" src="file.mp4"></video>
  <div id="videoControls">
    <strong>
      <div id="videoCurrentTime"></div>
      <div id="videoDuration"></div>
      <div id="videoProgressBar"></div>
    </strong>
  </div>
</div>
video.addEventListener('loadedmetadata', function () {
  videoDuration.textContent = secondsToTimeCode(video.duration);
  videoCurrentTime.textContent = secondsToTimeCode(video.currentTime);
  videoProgressBar.style.transform = `scaleX(${
    video.currentTime / video.duration
  })`;
});
yalnızca video meta verileri
Şekil 2. Video meta verilerini gösteren Media Player

Videoyu oynat/duraklat

Video meta verileri yüklendikten sonra, oynatma durumuna bağlı olarak kullanıcının videoyu video.play() ve video.pause() ile oynatmasını ve duraklatmasını sağlayan ilk düğmemizi ekleyelim.

<div id="videoContainer">
  <video id="video" src="file.mp4"></video>
  <div id="videoControls">
    <strong><button id="playPauseButton"></button></strong>
    <div id="videoCurrentTime"></div>
    <div id="videoDuration"></div>
    <div id="videoProgressBar"></div>
  </div>
</div>
playPauseButton.addEventListener('click', function (event) {
  event.stopPropagation();
  if (video.paused) {
    video.play();
  } else {
    video.pause();
  }
});

click etkinlik işleyicide video kontrollerimizi ayarlamak yerine play ve pause video etkinliklerini kullanırız. Denetim etkinliklerimizi esnek hale getirmek (daha sonra Media Session API'de görüleceği gibi) esnekliğe yardımcı olur ve tarayıcı oynatmaya müdahale ederse denetimlerimizi senkronize durumda tutmamıza olanak tanır. Video oynatılmaya başladığında düğme durumunu "duraklat" olarak değiştirir ve video kontrollerini gizleriz. Video duraklatıldığında düğmenin durumunu "oynat" olarak değiştirmemiz ve video kontrollerini göstermemiz yeterlidir.

video.addEventListener('play', function () {
  playPauseButton.classList.add('playing');
});

video.addEventListener('pause', function () {
  playPauseButton.classList.remove('playing');
});

Video currentTime özelliğinin belirttiği zaman timeupdate video etkinliği aracılığıyla değiştiğinde, görünür olmaları durumunda özel denetimlerimizi de güncelleriz.

video.addEventListener('timeupdate', function () {
  if (videoControls.classList.contains('visible')) {
    videoCurrentTime.textContent = secondsToTimeCode(video.currentTime);
    videoProgressBar.style.transform = `scaleX(${
      video.currentTime / video.duration
    })`;
  }
});

Video sona erdiğinde düğmenin durumunu "oynat" olarak değiştiririz, video currentTime ayarını tekrar 0'a getiririz ve şimdilik video kontrollerini gösteririz. Kullanıcı bir tür "Otomatik Oynatma" özelliğini etkinleştirmişse başka bir videoyu otomatik olarak yüklemeyi de seçebileceğimizi unutmayın.

video.addEventListener('ended', function () {
  playPauseButton.classList.remove('playing');
  video.currentTime = 0;
});

Geri ve ileri sar

Devam edelim ve "geri sar" ve "ileri sar" düğmelerini ekleyelim. Böylece kullanıcının bazı içerikleri kolayca atlayabilmesi sağlanır.

<div id="videoContainer">
  <video id="video" src="file.mp4"></video>
  <div id="videoControls">
    <button id="playPauseButton"></button>
    <strong
      ><button id="seekForwardButton"></button>
      <button id="seekBackwardButton"></button
    ></strong>
    <div id="videoCurrentTime"></div>
    <div id="videoDuration"></div>
    <div id="videoProgressBar"></div>
  </div>
</div>
var skipTime = 10; // Time to skip in seconds

seekForwardButton.addEventListener('click', function (event) {
  event.stopPropagation();
  video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
});

seekBackwardButton.addEventListener('click', function (event) {
  event.stopPropagation();
  video.currentTime = Math.max(video.currentTime - skipTime, 0);
});

Daha önce olduğu gibi, video parlaklığını ayarlamak için bu düğmelerin click etkinlik işleyicilerinde video stilini ayarlamak yerine etkinleşen seeking ve seeked video etkinliklerini kullanacağız. Özel seeking CSS sınıfım filter: brightness(0); kadar basit.

video.addEventListener('seeking', function () {
  video.classList.add('seeking');
});

video.addEventListener('seeked', function () {
  video.classList.remove('seeking');
});

Şu ana kadar oluşturduklarımız aşağıda belirtilmiştir. Bir sonraki bölümde tam ekran düğmesini uygulayacağız.

Fullscreen

Bu etkinlikte, mükemmel ve sorunsuz bir tam ekran deneyimi oluşturmak için çeşitli web API'larından yararlanacağız. Nasıl çalıştığını görmek için örneği inceleyin.

Elbette, bunların tümünü kullanmanız gerekmez. Sizin için anlamlı olanları seçin ve bunları birleştirerek özel akışınızı oluşturun.

Otomatik tam ekranı engelle

iOS'te, medya oynatma başladığında video öğeleri otomatik olarak tam ekran moduna girer. Mobil tarayıcılardaki medya deneyimimizi mümkün olduğunca uyarlamaya ve kontrol etmeye çalıştığımızdan, video öğesinin playsinline özelliğini, öğeyi iPhone'da satır içi oynatmaya zorlamak ve oynatma başladığında tam ekran moduna girmeyecek şekilde ayarlamanızı öneririz. Bunun diğer tarayıcılarda yan etkisi olmadığını unutmayın.

<div id="videoContainer"></div>
  <video id="video" src="file.mp4"></video><strong>playsinline</strong></video>
  <div id="videoControls">...</div>
</div>

Düğme tıklamasında tam ekranı aç/kapat

Artık otomatik tam ekranı önlediğimize göre, Tam Ekran API'si ile videonun tam ekran modunu kendimiz yönetmemiz gerekiyor. Kullanıcı "tam ekran düğmesini" tıkladığında, doküman tarafından şu anda tam ekran modu kullanılıyorsa document.exitFullscreen() ile tam ekran modundan çıkalım. Aksi takdirde, varsa requestFullscreen() yöntemiyle video kapsayıcıda tam ekran isteyin veya yalnızca iOS'te video öğesinde webkitEnterFullscreen() öğesine geri dönün.

<div id="videoContainer">
  <video id="video" src="file.mp4"></video>
  <div id="videoControls">
    <button id="playPauseButton"></button>
    <button id="seekForwardButton"></button>
    <button id="seekBackwardButton"></button>
    <strong><button id="fullscreenButton"></button></strong>
    <div id="videoCurrentTime"></div>
    <div id="videoDuration"></div>
    <div id="videoProgressBar"></div>
  </div>
</div>
fullscreenButton.addEventListener('click', function (event) {
  event.stopPropagation();
  if (document.fullscreenElement) {
    document.exitFullscreen();
  } else {
    requestFullscreenVideo();
  }
});

function requestFullscreenVideo() {
  if (videoContainer.requestFullscreen) {
    videoContainer.requestFullscreen();
  } else {
    video.webkitEnterFullscreen();
  }
}

document.addEventListener('fullscreenchange', function () {
  fullscreenButton.classList.toggle('active', document.fullscreenElement);
});

Ekran yönü değiştiğinde tam ekranı aç/kapat

Kullanıcı cihazı yatay modda döndürdüğünde bu konuda dikkatli olalım ve etkileyici bir deneyim oluşturmak için otomatik olarak tam ekran isteğinde bulunalım. Bunun için, henüz her yerde desteklenmeyen ve şu anda bazı tarayıcılarda öneke eklenmiş olan Screen Orientation API'ye ihtiyacımız olacaktır. Böylece, bu, ilk aşamalı geliştirmemiz olacak.

İşleyiş şekli nasıldır? Ekran yönünün değiştiğini tespit edersek tarayıcı penceresi yatay moddaysa (yani genişliği yüksekliğinden fazlaysa) tam ekran isteğinde bulunalım. Öyle değilse tam ekrandan çıkalım. Hepsi bu kadar.

if ('orientation' in screen) {
  screen.orientation.addEventListener('change', function () {
    // Let's request fullscreen if user switches device in landscape mode.
    if (screen.orientation.type.startsWith('landscape')) {
      requestFullscreenVideo();
    } else if (document.fullscreenElement) {
      document.exitFullscreen();
    }
  });
}

Düğme tıklandığında ekranı yatay olarak kilitle

Video yatay modda daha iyi görüntülenebileceğinden, kullanıcı "tam ekran düğmesini" tıkladığında ekranı yatay modda kilitlemek isteyebiliriz. En iyi deneyimi sunmak için daha önce kullanılan Screen Orientation API'yi ve bazı medya sorgularını birleştireceğiz.

Ekranı yatay modda kilitlemek, screen.orientation.lock('landscape') aramak kadar kolay. Ancak bunu yalnızca cihaz matchMedia('(orientation: portrait)') ile dikey moddayken ve matchMedia('(max-device-width: 768px)') ile tek elde tutulabilirken yapmalıyız. Bu durum tablet kullanıcıları için mükemmel bir deneyim teşkil etmez.

fullscreenButton.addEventListener('click', function (event) {
  event.stopPropagation();
  if (document.fullscreenElement) {
    document.exitFullscreen();
  } else {
    requestFullscreenVideo();
    <strong>lockScreenInLandscape();</strong>;
  }
});
function lockScreenInLandscape() {
  if (!('orientation' in screen)) {
    return;
  }
  // Let's force landscape mode only if device is in portrait mode and can be held in one hand.
  if (
    matchMedia('(orientation: portrait) and (max-device-width: 768px)').matches
  ) {
    screen.orientation.lock('landscape');
  }
}

Cihaz yönü değiştirildiğinde ekranın kilidini aç

Ekran kilitliyken ekran yönü değişiklikleri almadığımız için az önce oluşturduğumuz kilit ekranı deneyiminin mükemmel olmadığını fark etmiş olabilirsiniz.

Bu sorunu düzeltmek için varsa Device Orientation API'yi kullanalım. Bu API, bir cihazın uzaydaki konumunu ve hareketini ölçen donanımdan bilgiler sağlar: jiroskop ile cihazın yönü için dijital pusula ve hızı için ivme ölçer. Bir cihaz yönü değişikliği tespit ettiğimizde, kullanıcı cihazı dikey modda tutuyorsa ve ekran yatay modda kilitlenmişse ekranın kilidini screen.orientation.unlock() ile açalım.

function lockScreenInLandscape() {
  if (!('orientation' in screen)) {
    return;
  }
  // Let's force landscape mode only if device is in portrait mode and can be held in one hand.
  if (matchMedia('(orientation: portrait) and (max-device-width: 768px)').matches) {
    screen.orientation.lock('landscape')
    <strong>.then(function() {
      listenToDeviceOrientationChanges();
    })</strong>;
  }
}
function listenToDeviceOrientationChanges() {
  if (!('DeviceOrientationEvent' in window)) {
    return;
  }
  var previousDeviceOrientation, currentDeviceOrientation;
  window.addEventListener(
    'deviceorientation',
    function onDeviceOrientationChange(event) {
      // event.beta represents a front to back motion of the device and
      // event.gamma a left to right motion.
      if (Math.abs(event.gamma) > 10 || Math.abs(event.beta) < 10) {
        previousDeviceOrientation = currentDeviceOrientation;
        currentDeviceOrientation = 'landscape';
        return;
      }
      if (Math.abs(event.gamma) < 10 || Math.abs(event.beta) > 10) {
        previousDeviceOrientation = currentDeviceOrientation;
        // When device is rotated back to portrait, let's unlock screen orientation.
        if (previousDeviceOrientation == 'landscape') {
          screen.orientation.unlock();
          window.removeEventListener(
            'deviceorientation',
            onDeviceOrientationChange,
          );
        }
      }
    },
  );
}

Gördüğünüz gibi bu, beklediğimiz kusursuz tam ekran deneyimi. Bunun nasıl çalıştığını görmek için örneği inceleyin.

Arka planda oynatma

Bir web sayfasının veya web sayfasındaki bir videonun artık görünür olmadığını gördüğünüzde analizlerinizi bunu yansıtacak şekilde güncelleyebilirsiniz. Bu durum, geçerli oynatma süresini de etkileyebilir. Örneğin, farklı bir parça seçebilir, parçayı duraklatabilir, hatta kullanıcıya özel düğmeler bile gösterebilir.

Sayfadaki video görünürlüğü değişikliğini duraklat

Sayfa Görünürlük API'si ile bir sayfanın mevcut görünürlüğünü belirleyebilir ve görünürlük değişiklikleri hakkında bildirim alabiliyoruz. Aşağıdaki kod, sayfa gizlendiğinde videoyu duraklatır. Bu durum, ekran kilidi etkin olduğunda veya örneğin, sekmeler arasında geçiş yaptığınızda meydana gelir.

Çoğu mobil tarayıcı, artık tarayıcının dışında duraklatılmış bir videoyu devam ettirmeye olanak tanıyan kontroller sunduğundan, bu davranışı yalnızca kullanıcının arka planda oynatmasına izin veriliyorsa ayarlamanızı öneririz.

document.addEventListener('visibilitychange', function () {
  // Pause video when page is hidden.
  if (document.hidden) {
    video.pause();
  }
});

Video görünürlüğü değiştiğinde sesi kapatma düğmesini göster/gizle

Yeni Intersection Observer API'yi kullanırsanız ücretsiz olarak daha da ayrıntılı işlem yapabilirsiniz. Bu API, gözlemlenen bir öğe tarayıcının görüntü alanına girdiğinde veya bu görüntüden çıktığında sizi bilgilendirir.

Sayfadaki video görünürlüğüne göre sesi kapatma düğmesini gösterelim/gizleyelim. Video oynatılıyor ancak o anda görünmüyorsa sayfanın sağ alt köşesinde kullanıcının video sesini kontrol edebilmesi için küçük bir ses kapatma düğmesi gösterilir. volumechange video etkinliği, sesi kapatma düğmesinin stilini güncellemek için kullanılır.

<button id="muteButton"></button>
if ('IntersectionObserver' in window) {
  // Show/hide mute button based on video visibility in the page.
  function onIntersection(entries) {
    entries.forEach(function (entry) {
      muteButton.hidden = video.paused || entry.isIntersecting;
    });
  }
  var observer = new IntersectionObserver(onIntersection);
  observer.observe(video);
}

muteButton.addEventListener('click', function () {
  // Mute/unmute video on button click.
  video.muted = !video.muted;
});

video.addEventListener('volumechange', function () {
  muteButton.classList.toggle('active', video.muted);
});

Tek seferde yalnızca bir video oynatma

Bir sayfada birden fazla video varsa, kullanıcının aynı anda birden fazla ses parçasını dinlemek zorunda kalmaması için yalnızca birini oynatmanızı ve diğerlerini otomatik olarak duraklatmanızı öneririz.

// This array should be initialized once all videos have been added.
var videos = Array.from(document.querySelectorAll('video'));

videos.forEach(function (video) {
  video.addEventListener('play', pauseOtherVideosPlaying);
});

function pauseOtherVideosPlaying(event) {
  var videosToPause = videos.filter(function (video) {
    return !video.paused && video != event.target;
  });
  // Pause all other videos currently playing.
  videosToPause.forEach(function (video) {
    video.pause();
  });
}

Medya Bildirimlerini Özelleştir

Media Session API ile şu anda oynatılan video için meta veriler sağlayarak medya bildirimlerini de özelleştirebilirsiniz. Arama veya medya tuşlarından gelen değişiklikleri izleme gibi medyayla ilgili etkinlikleri de yönetmenizi sağlar. Bunun örneğini görmek için örneği inceleyin.

Web uygulamanız ses veya video oynatırken bildirim tepsisinde bir medya bildirimi görebilirsiniz. Android'de Chrome, dokümanın başlığını ve bulabildiği en büyük simge resmini kullanarak uygun bilgileri göstermek için elinden geleni yapar.

Media Session API ile başlık, sanatçı, albüm adı ve poster gibi bazı medya oturumu meta verilerini ayarlayarak bu medya bildirimini nasıl özelleştireceğimize göz atalım.

playPauseButton.addEventListener('click', function(event) {
  event.stopPropagation();
  if (video.paused) {
    video.play()
    <strong>.then(function() {
      setMediaSession();
    });</strong>
  } else {
    video.pause();
  }
});
function setMediaSession() {
  if (!('mediaSession' in navigator)) {
    return;
  }
  navigator.mediaSession.metadata = new MediaMetadata({
    title: 'Never Gonna Give You Up',
    artist: 'Rick Astley',
    album: 'Whenever You Need Somebody',
    artwork: [
      {src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png'},
      {
        src: 'https://dummyimage.com/128x128',
        sizes: '128x128',
        type: 'image/png',
      },
      {
        src: 'https://dummyimage.com/192x192',
        sizes: '192x192',
        type: 'image/png',
      },
      {
        src: 'https://dummyimage.com/256x256',
        sizes: '256x256',
        type: 'image/png',
      },
      {
        src: 'https://dummyimage.com/384x384',
        sizes: '384x384',
        type: 'image/png',
      },
      {
        src: 'https://dummyimage.com/512x512',
        sizes: '512x512',
        type: 'image/png',
      },
    ],
  });
}

Oynatma işlemi tamamlandığında bildirim otomatik olarak kaybolacağı için medya oturumunu "bırakmanız" gerekmez. Herhangi bir oynatma başladığında mevcut navigator.mediaSession.metadata kullanılacağını unutmayın. Bu nedenle, medya bildiriminde her zaman alakalı bilgiler gösterdiğinizden emin olmak için bildirimi güncellemeniz gerekir.

Web uygulamanız bir şarkı listesi sunuyorsa bazı "Önceki Parça" ve "Sonraki Parça" simgeleriyle, kullanıcının doğrudan medya bildiriminden oynatma listenizde gezinmesine izin vermek isteyebilirsiniz.

if ('mediaSession' in navigator) {
  navigator.mediaSession.setActionHandler('previoustrack', function () {
    // User clicked "Previous Track" media notification icon.
    playPreviousVideo(); // load and play previous video
  });
  navigator.mediaSession.setActionHandler('nexttrack', function () {
    // User clicked "Next Track" media notification icon.
    playNextVideo(); // load and play next video
  });
}

Medya işlemi işleyicilerinin kullanılmaya devam edeceğini unutmayın. Bu, etkinlik işleyici kalıbına çok benzer. Tek fark, bir etkinliğin işlenmesi tarayıcının herhangi bir varsayılan davranışı yapmayı durdurması ve bunu web uygulamanızın medya işlemini desteklediğine dair bir sinyal olarak kullanması anlamına gelir. Bu nedenle, uygun işlem işleyiciyi ayarlamadığınız sürece medya işlemi kontrolleri gösterilmez.

Bu arada, medya işlemi işleyicinin ayarını kaldırmak onu null adlı kullanıcıya atamak kadar kolay.

Media Session API'si, atlanan süreyi kontrol etmek isterseniz "Geriye sar" ve "İleriye sar" medya bildirimi simgelerini göstermenizi sağlar.

if ('mediaSession' in navigator) {
  let skipTime = 10; // Time to skip in seconds

  navigator.mediaSession.setActionHandler('seekbackward', function () {
    // User clicked "Seek Backward" media notification icon.
    video.currentTime = Math.max(video.currentTime - skipTime, 0);
  });
  navigator.mediaSession.setActionHandler('seekforward', function () {
    // User clicked "Seek Forward" media notification icon.
    video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
  });
}

"Oynat/Duraklat" simgesi medya bildiriminde her zaman gösterilir ve ilgili etkinlikler tarayıcı tarafından otomatik olarak işlenir. Herhangi bir nedenle varsayılan davranış işe yaramazsa "Oynat" ve "Duraklat" medya etkinliklerini işlemeye devam edebilirsiniz.

Media Session API'nin en güzel yanı, medya meta verilerinin ve kontrollerin göründüğü tek yerin bildirim tepsisi olmamasıdır. Medya bildirimi, eşlenen herhangi bir giyilebilir cihazla otomatik olarak senkronize edilir. Ayrıca kilit ekranlarında da görünür.

Geri bildirim