تشغيل الفيديو على الويب على الأجهزة الجوّالة

François Beaufort
François Beaufort

كيف يمكنك إنشاء أفضل تجربة وسائط للأجهزة الجوّالة على الويب؟ الأمر سهل. كل شيء تعتمد على تفاعل المستخدمين والأهمية التي تمنحها لوسائل الإعلام على الويب . أعتقد أننا نتفق على أنه إذا كان الفيديو هو سبب زيارة المستخدم، وينبغي أن تكون تجربة المستخدم غامرة وتفاعلية.

تشغيل فيديو على ويب على الأجهزة الجوّالة

سأوضّح لكم في هذه المقالة كيفية تحسين أداء الوسائط بطريقة تدريجية. التجربة وتجعله أكثر شمولية بفضل عدد كبير من واجهات برمجة تطبيقات الويب. وهي تعني عن السبب الذي دفعنا إلى إنشاء تجربة مشغِّل بسيطة على الأجهزة الجوّالة باستخدام عناصر التحكم، وملء الشاشة، والتشغيل في الخلفية. يمكنك تجربة النموذج الآن والعثور على الرمز في مستودع جيت هب الخاص بنا.

عناصر التحكّم المخصّصة

تنسيق HTML
الشكل 1.تنسيق HTML

كما ترى، فإن تخطيط HTML الذي سنستخدمه لمشغِّل الوسائط لدينا هو طريقة سهلة جدًا: يحتوي العنصر الجذر <div> على عنصر وسائط <video> عنصر ثانوي واحد (<div>) مخصّص لعناصر التحكّم في الفيديو

عناصر التحكم في الفيديو التي سنتناولها لاحقًا، وتشمل: زر تشغيل/إيقاف مؤقت، وملء الشاشة وأزرار الانتقال للخلف وللأمام وبعض العناصر للوقت الحالي، بتتبع المدة والوقت.

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

قراءة البيانات الوصفية للفيديو

لننتظر أولاً تحميل البيانات الوصفية للفيديو والوقت والوقت الحالي وتهيئة شريط التقدم. لاحظ أن secondsToTimeCode() هي دالة أداة مساعدة مخصصة كتبتها تحوِّل عدد الثواني إلى سلسلة بالصيغة "hh:mm:ss" وهو التنسيق الأفضل تناسب حالتنا.

<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
  })`;
});
البيانات الوصفية للفيديو فقط
الشكل 2. مشغّل وسائط يعرض البيانات الوصفية للفيديو

تشغيل الفيديو أو إيقافه مؤقتًا

الآن بعد أن تم تحميل البيانات الوصفية للفيديو، لنقم بإضافة أول زر يتيح للمستخدمين تشغيل الفيديو وإيقافه مؤقتًا باستخدام video.play() وvideo.pause() بناءً على حالة التشغيل.

<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، نستخدم حدثَي الفيديو play وpause. يساعد جعل عناصر التحكم استنادًا إلى الأحداث مرونة (كما سنرى لاحقًا من خلال واجهة برمجة التطبيقات لجلسات الوسائط) وسنتيح الحفاظ على مزامنة عناصر التحكم في حالة تدخل المتصفح في التشغيل. عندما يبدأ تشغيل الفيديو، نغير حالة الزر على "إيقاف مؤقت" وإخفاء عناصر التحكم في الفيديو. عندما يتوقف الفيديو مؤقتًا، ما عليك سوى تغيير حالة الزر إلى "تشغيل" وعرض عناصر التحكم في الفيديو.

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

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

عندما يتم تغيير الوقت المشار إليه بالفيديو currentTime عبر حدث فيديو timeupdate، سنحدِّث أيضًا عناصر التحكم المخصصة إذا كانت مرئية.

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

عند انتهاء الفيديو، نغيّر حالة الزر إلى "تشغيل" ونضبط الفيديو currentTime رجوع إلى القيمة 0 وعرض عناصر التحكم في الفيديو في الوقت الحالي. لاحظ أنه يمكننا أيضًا تحميل فيديو آخر تلقائيًا إذا مكّن المستخدم بعض مقاطع الفيديو نوع من "التشغيل التلقائي" الجديدة.

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

الترجيع للأمام وللخلف

دعنا نواصل ونضيف "ترجيع" و"التقديم" الأزرار حتى يتمكن يمكنه تخطي بعض المحتوى بسهولة.

<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);
});

كما في السابق، بدلاً من تعديل نمط الفيديو في أدوات معالجة الأحداث في click سنستخدم حدثَي الفيديو seeking وseeked اللذين تم تنشيطهما لضبط مستوى سطوع الفيديو. فئة CSS المخصّصة لديّ seeking بسيطة مثل filter: brightness(0);.

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

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

إليك أدناه ما أنشأناه حتى الآن. في القسم التالي، سننفذ زر ملء الشاشة.

ملء الشاشة

وسنستفيد هنا من العديد من واجهات برمجة تطبيقات الويب لإنشاء واجهة تجربة سلسة بملء الشاشة لكي تظهر لك هذه العملية عمليًا، يمكنك الاطّلاع على نموذج.

بالطبع، لا يتعين عليك استخدامها كلها. ما عليك سوى اختيار الشبكات التي تجعل بالنسبة إليك ودمجها لإنشاء التدفق المخصص الخاص بك.

منع وضع ملء الشاشة التلقائي

على iOS، تدخل عناصر video تلقائيًا في وضع ملء الشاشة عند تشغيل الوسائط بدء التشغيل. ونظرًا لأننا نحاول تصميمها والتحكم فيها قدر الإمكان تجربة الوسائط على متصفّحات الأجهزة الجوّالة، أنصحك بضبط playsinline سمة العنصر video لفرض تشغيله بشكل مضمّن على iPhone وليس الدخول إلى وضع ملء الشاشة عند بدء التشغيل. يُرجى العِلم أنّ هذا الإجراء ليس له آثار جانبية. في المتصفحات الأخرى.

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

تبديل وضع ملء الشاشة عند النقر على الزر

والآن بعد أن منعنا وضع ملء الشاشة التلقائي، نحتاج إلى معالجة أنفسنا وضع ملء الشاشة للفيديو باستخدام واجهة برمجة تطبيقات ملء الشاشة. عندما يكون المستخدم ينقر على "زر ملء الشاشة"، ليخرج من وضع ملء الشاشة مع document.exitFullscreen() إذا كان وضع ملء الشاشة قيد الاستخدام حاليًا من قِبل جلسة المراجعة. في الحالات الأخرى، يمكنك طلب وضع ملء الشاشة في حاوية الفيديو باستخدام الطريقة. requestFullscreen() إذا كان متاحًا أو سيتم الرجوع إلى webkitEnterFullscreen() على عنصر الفيديو على أجهزة iOS فقط.

<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);
});

تبديل وضع ملء الشاشة عند تغيير اتجاه الشاشة

عندما يقوم المستخدم بتدوير الجهاز في الوضع الأفقي، لنكن ذكيين في هذا الأمر طلب وضع ملء الشاشة تلقائيًا لإنشاء تجربة غامرة. لهذا، سنحتاج إلى واجهة برمجة تطبيقات توجيه الشاشة التي لا تتوفّر بعد في كل مكان ولا تزال بادئة في بعض المتصفحات في ذلك الوقت. وبالتالي، سيكون هذا تحسيننا التدريجي الأول.

كيف يتم ذلك؟ وبمجرد أن نكتشف تغير اتجاه الشاشة، طلب وضع ملء الشاشة إذا كانت نافذة المتصفح في الوضع الأفقي (أي العرض أكبر من ارتفاعه). وإذا لم يكن الأمر كذلك، يمكننا الخروج من وضع ملء الشاشة. دِي كُلّْ حَاجَة.

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();
    }
  });
}

قفل الشاشة في الوضع الأفقي عند النقر على الزر

بما أنّه قد يتم عرض الفيديو بشكل أفضل في الوضع الأفقي، قد نحتاج إلى قفل الشاشة أفقي عندما ينقر المستخدم على "زر ملء الشاشة". سنقوم بدمج واجهة برمجة تطبيقات توجيه الشاشة وبعض الوسائط الطلبات لضمان تقديم هذه التجربة الأفضل.

قفل الشاشة في الوضع الأفقي أمر سهل تمامًا كإجراء الاتصال screen.orientation.lock('landscape') ومع ذلك، ينبغي لنا القيام بذلك فقط عندما الجهاز في الوضع العمودي مع matchMedia('(orientation: portrait)') ويمكن أن الإمساك بيد واحدة باستخدام matchMedia('(max-device-width: 768px)') بهذا الشكل تجربة رائعة للمستخدمين على الجهاز اللوحي.

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');
  }
}

فتح قفل الشاشة عند تغيير اتجاه الجهاز

ربما لاحظت أن تجربة شاشة القفل التي أنشأناها للتو ليست تمامًا نظرًا لأننا لا نستلم تغييرات اتجاه الشاشة عندما تكون الشاشة مقفلة.

لإصلاح ذلك، يمكننا استخدام Device Orientation API إذا المتوفرة. توفر واجهة برمجة التطبيقات هذه معلومات من الأجهزة التي تقيس الموضع والحركة في الفضاء: جيروسكوب وبوصلة رقمية واتجاهه ومقياس التسارع لمعرفة سرعته. عندما نكتشف جهازًا تغيير الاتجاه، سيتم فتح قفل الشاشة باستخدام screen.orientation.unlock() إذا يحمل المستخدم الجهاز في الوضع العمودي ويتم قفل الشاشة في الوضع الأفقي.

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,
          );
        }
      }
    },
  );
}

كما ترى، هذه هي تجربة ملء الشاشة السلسة التي كنا نبحث عنها. للاطلاع على هذه العملية عمليًا، يمكنك الاطلاع على النموذج.

التشغيل في الخلفية

عندما ترصد صفحة ويب أو لم يعُد فيديو في صفحة الويب يظهر، ننصحك بتعديل إحصاءات قناتك لتعكس ذلك قد يؤثر هذا أيضًا في التشغيل الحالي عند اختيار مسار مختلف أو إيقافه مؤقتًا أو حتى عرض الأزرار المخصصة للمستخدم مثلاً.

إيقاف الفيديو مؤقتًا عند تغيير مستوى عرض الصفحة

باستخدام واجهة برمجة تطبيقات مستوى رؤية الصفحة، يمكننا تحديد مستوى رؤية وسيتم إشعارك بتغييرات مستوى الرؤية. يؤدي الرمز أدناه إلى إيقاف الفيديو مؤقتًا عندما تكون الصفحة مخفي. يحدث ذلك عندما يكون قفل الشاشة نشطًا أو عند تبديل علامات التبويب مثال.

ونظرًا لأن معظم متصفحات الجوّال تقدم الآن عناصر تحكم خارج المتصفح تسمح استئناف الفيديو المتوقف مؤقتًا، فأوصيك بضبط هذا السلوك فقط إذا كان المستخدم يُسمح لهم بتشغيلها في الخلفية.

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

إظهار أو إخفاء زر كتم الميكروفون عند تغيير مستوى عرض الفيديو

إذا كنت تستخدم واجهة برمجة تطبيقات مراقبة التقاطع الجديدة، يمكنك معرفة المزيد بدون أي تكلفة. وتتيح لك واجهة برمجة التطبيقات هذه معرفة وقت دخول عنصر تمت ملاحظته أو الخروج منه إطار عرض المتصفح.

لِنعرض أو نخفي زر كتم الميكروفون حسب مستوى عرض الفيديو على الصفحة. في حال حذف يتم تشغيل الفيديو ولكنه غير مرئي حاليًا. سيظهر زر صغير لكتم الصوت في أسفل يسار الصفحة لمنح المستخدم إمكانية التحكم في صوت الفيديو. تشير رسالة الأشكال البيانية يُستخدَم حدث فيديو واحد (volumechange) لتعديل تصميم زر كتم الميكروفون.

<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);
});

تشغيل فيديو واحد فقط في كل مرة

إذا كان هناك أكثر من فيديو واحد في صفحة واحدة، أقترح عليك تشغيل فيديو واحد فقط. وإيقاف الإشعارات الأخرى مؤقتًا تلقائيًا حتى لا يضطر المستخدم إلى سماعها يتم تشغيل عدة مقاطع صوتية في الوقت نفسه.

// 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();
  });
}

تخصيص إشعارات الوسائط

باستخدام واجهة برمجة التطبيقات لجلسات الوسائط، يمكنك أيضًا تخصيص الوسائط من خلال توفير بيانات وصفية للفيديو الذي يتم تشغيله حاليًا. وكذلك يسمح لك بالتعامل مع الأحداث ذات الصلة بالوسائط مثل تقديم الطلب أو تتبّع التغييرات. التي قد تأتي من الإشعارات أو مفاتيح الوسائط. للاطلاع على ذلك عمليًا، تحقق من العينة

عندما يشغِّل تطبيق الويب محتوى صوتيًا أو فيديو، ستظهر لك وسائط. إشعار موجود في قائمة الإشعارات. يعمل Chrome على أفضل وجه على أجهزة Android لعرض المعلومات المناسبة باستخدام عنوان المستند وأكبر صورة الرمز التي يمكن أن يعثر عليها.

لِنطّلع على كيفية تخصيص إشعار الوسائط هذا عن طريق ضبط بعض الوسائط البيانات الوصفية للجلسة مثل العنوان والفنان واسم الألبوم والعمل الفني مع واجهة برمجة التطبيقات لجلسات الوسائط.

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',
      },
    ],
  });
}

بعد انتهاء التشغيل، لن تحتاج إلى "طرح التطبيق". جلسة الوسائط على أنها سيختفي تلقائيًا. ضع في اعتبارك أن العناصر الحالية سيتم استخدام "navigator.mediaSession.metadata" عند بدء أي عملية تشغيل. هذا النمط هو سبب حاجتك إلى تعديلها لضمان عرض إعلاناتك دائمًا المعلومات في إشعار الوسائط.

إذا كان تطبيق الويب يوفّر قائمة تشغيل، قد تحتاج إلى السماح للمستخدم بالتنقل فيها. عبر قائمة التشغيل مباشرةً من إشعار الوسائط مع بعض مسار" و"المقطع الصوتي التالي" الأيقونات.

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
  });
}

يُرجى العلم أنّ معالِجات إجراءات الوسائط ستظل موجودة. هذا مشابه جدًا للحدث نمط المستمع باستثناء أن معالجة حدث يعني أن المتصفح يتوقف يؤدي أي سلوك تلقائي ويستخدم ذلك كإشارة إلى أن تطبيق الويب يدعم إجراء الوسائط. وبالتالي، لن يتم عرض عناصر التحكّم في إجراءات الوسائط ما لم وتقوم بتعيين معالج الإجراء المناسب.

وبالمناسبة، فإنّ إلغاء معالِج إجراء الوسائط سيكون سهلاً مثل تعيينه إلى null.

تسمح لك واجهة برمجة التطبيقات لجلسة الوسائط بعرض خيار "ترجيع الفيديو" و"التقديم" وأيقونات إشعارات الوسائط إذا كنت تريد التحكم في مقدار الوقت الذي يتم تخطيه.

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);
  });
}

زر "تشغيل/إيقاف مؤقت" الرمز دائمًا في إشعار الوسائط وفي الفيديو حيث يتم التعامل مع الأحداث تلقائيًا بواسطة المتصفح. إذا كان الإعداد الافتراضي السلوك لا ينجح، فيمكنك التعامل مع "اللعب" و"إيقاف مؤقت" الوسائط الأحداث.

الأمر الرائع في واجهة برمجة التطبيقات لجلسات الوسائط هو أن قائمة الإشعارات ليست وهو المكان الوحيد الذي تكون فيه البيانات الوصفية وعناصر التحكم في الوسائط مرئية. وسائل الإعلام تتم مزامنة الإشعارات تلقائيًا مع أي جهاز مقترن قابل للارتداء. ويمكن أيضًا أن يتم يظهر على شاشات القفل.

ملاحظات