הפעלת סרטון באינטרנט לנייד

François Beaufort
François Beaufort

איך יוצרים את חוויית המדיה לנייד הטובה ביותר באינטרנט? קל! הכול תלויה במעורבות המשתמשים ובחשיבות שתוענק למדיה באינטרנט הדף הזה. אני חושב שכולנו מסכימים שאם סרטון הוא הסיבה לביקור של משתמש, חוויית המשתמש צריכה להיות סוחפת ומעוררת עניין מחדש.

הפעלת סרטון באינטרנט לנייד

במאמר הזה נראה לכם איך אפשר לשפר את המדיה והופכים אותה לסוחפת יותר בעזרת שפע של ממשקי API באינטרנט. כאן למשל למה אנחנו הולכים ליצור חוויה פשוטה לנגן בנייד עם פקדים, מסך מלא והפעלה ברקע. אפשר לנסות את הדוגמה עכשיו ולמצוא את הקוד במאגר שלנו ב-GitHub.

אמצעי בקרה בהתאמה אישית

פריסת HTML
איור 1.פריסת HTML

כפי שאפשר לראות, פריסת ה-HTML שבה נשתמש לנגן המדיה שלנו היא די פשוט: רכיב ברמה הבסיסית (root) <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();
  }
});

במקום להתאים את פקדי הסרטונים שלנו ב-event listener של click, אנחנו משתמשים אירועי הסרטון play ו-pause. הפיכת אירועי הבקרה שלנו למבוססים עם גמישות (כפי שנראה מאוחר יותר עם ה-Media Session API), והוא יאפשר כדי שנוכל לסנכרן את אמצעי הבקרה שלנו אם הדפדפן מתערב בהפעלה. כשסרטון מתחיל לפעול, אנחנו משנים את הלחצן למצב "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 חזרה לאפס ומוצגות פקדי הסרטונים בינתיים. שימו לב שיכול להיות גם לבחור לטעון באופן אוטומטי סרטון אחר אם המשתמש הפעיל סוג של "הפעלה אוטומטית" .

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

בהמשך מפורטים קטעים שיצרנו עד כה. בקטע הבא ניישם לחצן המסך המלא.

מסך מלא

כאן אנחנו עומדים לנצל מספר ממשקי API לאינטרנט כדי ליצור וחוויית שימוש חלקה במסך מלא. כדי לראות אותה בפעולה, אפשר להיכנס דוגמה.

כמובן שאתם לא צריכים להשתמש בכולם. רק צריך לבחור את אלה שבזכותם ולהתאים אותם אישית כדי ליצור תהליך מותאם אישית.

מניעת הצגה במסך מלא באופן אוטומטי

ב-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>

החלפת מצב של מסך מלא בלחיצה על לחצן

עכשיו, כשנמנעים מהפעלה של מסך מלא אוטומטי, אנחנו צריכים לטפל בעצמנו להציג את הסרטון במסך מלא באמצעות Fullscreen API. כשהמשתמש לוחץ על "לחצן מסך מלא", בואו נצא ממצב מסך מלא עם document.exitFullscreen() אם מצב מסך מלא נמצא כרגע בשימוש על ידי מהמסמך. אם לא, מבקשים מסך מלא במאגר הסרטונים באמצעות ה-method 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);
});

החלפת מצב של מסך מלא לשינוי כיוון המסך

כשהמשתמש מסובב את המכשיר בפריסה לרוחב, עדיף לנהוג בחוכמה לבקש אוטומטית מסך מלא כדי ליצור חוויה סוחפת. בשביל זה, אנחנו זקוקים ל-Screen Orientation API שעדיין לא נתמך. בכל מקום ועדיין בקידומת מסוימת בדפדפנים מסוימים באותו זמן. לכן, התוצאה את השיפור ההדרגתי הראשון שלנו.

איך זה עובד? ברגע שנזהה שינוי בכיוון המסך, בקשה למסך מלא אם חלון הדפדפן בפריסה לרוחב (כלומר הרוחב גדול מהגובה שלו). אם לא, נצא ממסך מלא. זה הכול.

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 API ובמדיה מסוימת כדי לוודא שהחוויה הזו היא הטובה ביותר.

אפשר לנעול את המסך לרוחב ממש בקלות 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 אם זמינים. ממשק ה-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,
          );
        }
      }
    },
  );
}

כמו שאפשר לראות, זו חוויית השימוש חלקה במסך המלא שחיפשנו. כדי לראות איך אפשר לעשות את זה, אפשר לעיין בדוגמה.

הפעלה ברקע

כשאתם מזהים שדף אינטרנט או סרטון בדף האינטרנט לא גלויים יותר, מומלץ לעדכן את ניתוח הנתונים כדי לשקף זאת. הפעולה הזו יכולה להשפיע גם על את ההפעלה הנוכחית, כמו בבחירת רצועה אחרת, בהשהיה או אפילו בהצגה למשל לחצנים מותאמים אישית למשתמש.

שינוי בחשיפה של הסרטון בדף

בעזרת Page Visibility API, אנחנו יכולים לקבוע את החשיפה הנוכחית של ולקבל התראות על שינויים בחשיפה. הקוד שבהמשך משהה את הסרטון כשהדף מוסתר. הפעולה הזו מתרחשת כשנעילת המסך פעילה או כשעוברים בין כרטיסיות מכונה.

רוב הדפדפנים לנייד מציעים כעת פקדים מחוץ לדפדפן, שמאפשרים אם הסרטון מושהה, מומלץ להגדיר את ההתנהגות הזו רק אם המשתמש מורשה לפעול ברקע.

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

הצגה/הסתרה של לחצן ההשתקה בזמן שינוי החשיפה של הסרטון

אם אתם משתמשים ב-Intersection Observer API, אפשר לבצע שינויים מפורטים עוד יותר ללא תשלום. ה-API הזה מיידע אתכם כשרכיב שנמדד נכנס אל אזור התצוגה של הדפדפן.

הצגה/הסתרה של לחצן השתקה בהתאם להיקף החשיפה של הסרטון בדף. אם המיקום הסרטון מופעל אבל אינו גלוי כרגע, יוצג לחצן השתקה קטן בפינה הימנית התחתונה של הדף, כדי שהמשתמש יוכל לשלוט בסאונד של הסרטון. אירוע וידאו אחד (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();
  });
}

התאמה אישית של התראות המדיה

באמצעות Media session API אפשר גם להתאים אישית את המדיה. התראות על ידי הוספת מטא-נתונים עבור הסרטון שמופעל כרגע. כמו כן מאפשר לטפל באירועים שקשורים למדיה, כמו דילוג או שינוי מעקב שעשוי להגיע מהתראות או ממקשי מדיה. כדי לבדוק את זה, אפשר לסמן את הדוגמה.

כשאפליקציית האינטרנט מפעילה אודיו או וידאו, כבר אפשר לראות מדיה הודעה שמופיעה במגש ההתראות. ב-Android, אנחנו עושים כמיטב יכולתנו ב-Chrome כדי להציג מידע מתאים באמצעות כותרת המסמך תמונת הסמל שהוא יכול למצוא.

עכשיו נראה איך להתאים אישית את התראת המדיה על ידי הגדרה של מדיה מטא-נתונים של הסשן, כמו השם, האומן, שם האלבום וגרפיקה עם Media session API.

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 יהיה בשימוש בכל הפעלה ראשונית. הזה זו הסיבה שצריך לעדכן אותו כדי לוודא שהמודעות שלך תמיד מוצגות מידע בהודעה במדיה.

אם אפליקציית האינטרנט מספקת פלייליסט, מומלץ לאפשר למשתמש לנווט דרך הפלייליסט ישירות מההתראה במדיה עם כיתובים Track" ו-"Next Track" סמלים.

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

חשוב לזכור שרכיבי handler של פעולות מדיה יישארו ללא שינוי. האירוע הזה דומה מאוד לאירוע דפוס האזנה, מלבד הטיפול באירוע, פירושו שהדפדפן מפסיק עושה כל פעולת ברירת מחדל ומשתמשת בה כאות לכך שאפליקציית האינטרנט תומך בפעולת המדיה. לכן, אמצעי הבקרה לפעולות במדיה לא יוצגו, אלא אם אתם מגדירים את ה-handler המתאים של הפעולות.

דרך אגב, אפשר לבטל הגדרה של handler של פעולות מדיה בקלות כמו להקצות אותו ל-null.

Media Session API מאפשר לכם להציג את האפשרות 'דילוג אחורה' ו-"Seek Forward" סמלים של התראות מדיה אם רוצים לקבוע את משך הזמן שהמערכת דילגה עליו.

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

האפשרות "הפעלה/השהיה" מוצג תמיד בהתראה של המדיה ובסמל האירועים מטופלים באופן אוטומטי על ידי הדפדפן. אם מסיבה כלשהי ברירת המחדל ההתנהגות לא מתאימה, עדיין אפשר להשתמש ב-"Play" ו"Pause" מדיה אירועים.

הקטע המגניב ב-Media Session API הוא שמגש ההתראות המקום היחיד שבו מוצגים הפקדים והמטא-נתונים של המדיה. המדיה ההתראה מסונכרנת באופן אוטומטי עם כל מכשיר לביש שהותאם. ואפשר גם להשתמש מופיע במסכי הנעילה.

משוב