-
Improvement Suggestion: Video Autoplay Single-Session Handling in Timeline
We’ve improved the video autoplay logic in the
BxTimelineView
class to ensure a better user experience. In the original implementation, multiple videos could start playing simultaneously when scrolled into view, causing audio overlap and performance issues.What we changed:
- Added playback control in
initVideosAutoplay
: Now, when a video starts playing via playerjs, it pauses all other initialized players before continuing. - Enhanced
autoplayVideos
logic: Instead of attempting to play/pause all videos on scroll, it now calculates which video is most visible in the viewport and ensures only that one plays, while pausing any others. - Tracked the currently playing video using
_sCurrentPlayerKey
to avoid redundant operations and maintain consistent state. - Kept backward compatibility with the original functions and admin options (
on
,on_mute
,off
).
This solution works well on the homepage timeline but in the the organization timeline however, I encounter an issue when transitioning from one video to another. It appears that for a brief moment, the videos still play simultaneously.
Any suggestion is welcome:
https://github.com/kabballa/timeline/commit/ee11eb89107b81c199ce9c9fcf8b5b42a463be5a
Why it matters:
This ensures a clean, intuitive browsing experience only one video plays at a time, without sound overlaps or excessive system resource use.
NOTE this is a test not a final solution
/** * Initialize iframe players and enforce single-session playback: only one video plays at a time. * Ensures that before any video plays, all others are paused. */ BxTimelineView.prototype.initVideosAutoplay = function(oParent) { var $this = this; if (this._sVideosAutoplay === 'off') return; // Preserve original initializationthis.initVideos(oParent);var sPrefix = oParent.hasClass(this.sClassView) ? oParent.attr('id') : oParent.parents('.' + this.sClassView + ':first').attr('id'); // Register each iframe player once oParent.find('iframe[id]').each(function() { var frame = this; var key = sPrefix + '_' + frame.id; if (window.glBxTimelineVapPlayers[key]) return; var player = new playerjs.Player(frame); if ($this._sVideosAutoplay === 'on_mute') player.mute(); // Sync container height on ready/playvar fFixHeight = function() {var video = $('#' + key).contents().find('video');if (video.length) $('#' + key).height(video.height() + 'px'); }; player.on('ready', fFixHeight); player.on('play', fFixHeight); // When this player starts, pause all others player.on('play', function() { for (var k in window.glBxTimelineVapPlayers) { if (k !== key) { try { window.glBxTimelineVapPlayers[k].pause(); } catch (e) { // ignore } } } }); window.glBxTimelineVapPlayers[key] = player; }); // Reset current session trackingthis._sCurrentPlayerKey = null; // Bind original scroll autoplay $(window).off('scroll.timelineVap').on('scroll.timelineVap', function() { if (!$this.oView.is(':visible')) return; if (!window.requestAnimationFrame) { setTimeout(function() { $this.autoplayVideos($this.oView, $this._fVapOffsetStart, $this._fVapOffsetStop); }, 100); } else { window.requestAnimationFrame(function() { $this.autoplayVideos($this.oView, $this._fVapOffsetStart, $this._fVapOffsetStop); }); } }); }; /** * Autoplay the single most visible video on scroll: pause the previous and play the new one. */ BxTimelineView.prototype.autoplayVideos = function(oView, fOffsetStart, fOffsetStop) { var sPrefix = oView.attr('id') + '_'; var winTop = $(window).scrollTop(); var winH = $(window).height(); var bestKey = null; var bestRatio = 0; oView.find('.' + this.sClassItem + ' iframe[id]').each(function() { var $f = $(this); var top = $f.offset().top; var bottom = top + $f.height(); var visTop = Math.max(top, winTop); var visBottom = Math.min(bottom, winTop + winH); var ratio = Math.max(0, visBottom - visTop) / $f.height(); var key = sPrefix + this.id; if (ratio > bestRatio) { bestRatio = ratio; bestKey = key; } }); // If a new best: pause old, play newif (bestKey !== this._sCurrentPlayerKey) {if (this._sCurrentPlayerKey && window.glBxTimelineVapPlayers[this._sCurrentPlayerKey]) {try { window.glBxTimelineVapPlayers[this._sCurrentPlayerKey].pause(); } catch (e) { // ignore } } if (bestKey && window.glBxTimelineVapPlayers[bestKey]) { try { window.glBxTimelineVapPlayers[bestKey].play(); } catch (e) { // ignore } } this._sCurrentPlayerKey = bestKey; } }; /** * Manually trigger autoplay logic. */ BxTimelineView.prototype.playVideos = function(oView) { if (this._sVideosAutoplay === 'off') return; this.autoplayVideos(oView, this._fVapOffsetStart, this._fVapOffsetStop); }; /** * Immediately pause all videos and reset the current key. */ BxTimelineView.prototype.pauseVideos = function(oView) { for (var k in window.glBxTimelineVapPlayers) { try { window.glBxTimelineVapPlayers[k].pause(); } catch (e) { // ignore } } this._sCurrentPlayerKey = null; };
- Added playback control in