play video when user scrolls specifically using video.js - javascript

I realise variants of this question have been asked before, but none specifically relating to how to do it on video.js
I'm using video.js for my videos. I need the videos to play when the user scrolls to their view. Many people have asked about html5 videos but I would like to know on video.js specifically. I've done the following but no luck -
var videos = videojs('movie-id_html5_api');
videojs("movie-id_html5_api").ready(function(){
var videos = this;
fraction = 0.8;
function checkScroll() {
for(var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
videos.play();
} else {
videos.pause();
}
}
}
});
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
I should also mention I have multiple videos with the same ID 'movie-id_html5_api' and I would all like to get them to play when scrolled.
thank you in advance!!

I updated the jsFiddle to only play each video if it is scrolled into view. If the video is not in view, it is paused. Credit to this Stack Overflow answer for the playVideos function and also for storing each video player id in an array. Also credit to this Stack Overflow answer for the isOnScreen() function.
videojs.options.flash.swf = "http://vjs.zencdn.net/c/video-js.swf";
var players = ['example_video_1', 'example_video_2'];
window.addEventListener("resize", playVideos, false);
window.addEventListener("scroll", playVideos, false);
// Loop through all the players, check if video player is visible in the viewport. If it is visible, play it. If not, do not play it.
function playVideos() {
for (var i = 0; i < players.length; i++)
{
var videoPlayer = $('#' + players[i]);
var videoPlayerElem = _V_('#' + players[i]);
if (isOnScreen(videoPlayer))
{
videoPlayerElem.play();
}
else
{
videoPlayerElem.pause();
}
}
}
function isOnScreen(element) {
var elementOffsetTop = element.offset().top;
var elementHeight = element.height();
var screenScrollTop = $(window).scrollTop();
var screenHeight = $(window).height();
var scrollIsAboveElement = elementOffsetTop + elementHeight - screenScrollTop >= 0;
var elementIsVisibleOnScreen = screenScrollTop + screenHeight - elementOffsetTop >= 0;
return scrollIsAboveElement && elementIsVisibleOnScreen;
}

Related

How to play and pause a Youtube video in a webpage while scrolling(using javascript or jquery)?

I mean to say that the video should not be playing when the page is loaded.
It should play when the video player comes in the focus of the window screen and it should pause when it is not visible on the screen using the scroll function.
I am not expecting to play videos on separate tabs.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>
<body>
<p>
This is some text
</p>
<div style="margin-top:1000px;margin-bottom:1000px;">
<iframe width="445" height="245" src="https://www.youtube.com/embed/LA5XtlyVILo?rel=0&enablejsapi=1&version=3&playerapiid=ytplayer" frameborder="0" allowfullscreen></iframe>
</div>
<script type="text/javascript">
var videos = document.getElementsByTagName("iframe"), fraction = 0.8;
function checkScroll() {
for(var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = 0,
y = 0,
w = video.width,
h = video.height,
r, //right
b, //bottom
visibleX, visibleY, visible,
parent;
parent = video;
while (parent && parent !== document.body) {
x += parent.offsetLeft;
y += parent.offsetTop;
parent = parent.offsetParent;
}
r = x + parseInt(w);
b = y + parseInt(h);
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
playVideo();
} else if(visible < fraction) {
pauseVideo();
}
}
};
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
//check at least once so you don't have to wait for scrolling for the video to start
window.addEventListener('load', checkScroll, false);
checkScroll();
function playVideo() {
player.playVideo();
}
function pauseVideo() {
player.pauseVideo();
}
</script>
</body>
</html>
Update based on Posters Feedback
After searching around on stackoverflow to expand on my own solution to the problem, I found the answer to your question in another stackoverflow question.
Here's a fiddle slightly modified fiddle based on the original post
/*Credit to original author http://stackoverflow.com/a/7557433/1684970 */
var LoadVideo = function(player_id) {
var Program = {
Init: function() {
this.NewPlayer();
this.EventHandler();
},
NewPlayer: function() {
var _this = this;
this.Player = new YT.Player(player_id, {});
_this.Player.$element = $('#' + player_id);
},
Play: function() {
if (this.Player.getPlayerState() === 1) return;
this.Player.playVideo();
},
Pause: function() {
if (this.Player.getPlayerState() === 2) return;
this.Player.pauseVideo();
},
ScrollControl: function() {
if (Utils.IsElementInViewport(this.Player.$element[0])) this.Play();
else this.Pause();
},
EventHandler: function() {
var _this = this;
$(window).on('scroll', function() {
_this.ScrollControl();
});
}
};
var Utils = {
IsElementInViewport: function(el) {
if (typeof jQuery === "function" && el instanceof jQuery) el = el[0];
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
};
return Program.Init();
};
LoadVideo('playerA');
Original Answer
Since you haven't specified if the video is in a iframe or anything, I will assume it's just embedded and offers the user play/pause/open in youtube options.
The first part is detecting if the element is currently in the viewport, and therefore visible. and then the click event if visible/not-visible.
$(window).scroll(function() {
var top_of_element = $("#element").offset().top;
var bottom_of_element = $("#element").offset().top + $("#element").outerHeight();
var bottom_of_screen = $(window).scrollTop() + $(window).height();
var top_of_screen = $(window).scrollTop();
if((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)){
// The element is visible, trigger play click event
$("#element").playVideo()
}
else {
// The element is not visible, trigger pause click event
$("#element").pauseVideo()
}
});
You can fire the pause event for the player by watching on the scroll event for the page as
$(window).scroll(function() {
if($(window).scrollTop() > height) { //height is the pixels by which if you scroll the player gets hidden
player.pauseVideo(); //player is the YouTube player object you created
}
else
player.playVideo(); //the player is visible.
});
Correct Code for this question.
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//www.youtube.com/player_api"></script>
</head>
<body>
<p>
This is some text
</p>
<div style="margin-top:1000px;margin-bottom:1000px;">
</div>
<div id="player"></div>
<script type="text/javascript">
src="//www.youtube.com/player_api"
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
height: '245',
width: '445',
videoId: 'FSfz0NxzN80',
rel:'0'
});
}
var videos = document.getElementsByTagName("iframe"), fraction = 0.8;
function checkScroll() {
for(var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = 0,
y = 0,
w = video.width,
h = video.height,
r, //right
b, //bottom
visibleX, visibleY, visible,
parent;
parent = video;
while (parent && parent !== document.body) {
x += parent.offsetLeft;
y += parent.offsetTop;
parent = parent.offsetParent;
}
r = x + parseInt(w);
b = y + parseInt(h);
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
player.playVideo();
}
else if(visible < fraction) {
player.pauseVideo();
}
}
};
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
//check at least once so you don't have to wait for scrolling for the video to start
window.addEventListener('load', checkScroll, false);
checkScroll();
</script>
</body>
</html>

trigger animation at the end of debounced requestanimationframe

I followed Paul Lewis's guide to debounce and requestAnimationFrame. I'm translating an image across the screen on scroll when it comes into view.
var bicycles = $('.tandem-bike', context),
lastScrollY = 0,
ticking = false;
function update() {
var windowHeight = window.innerHeight,
windowWidth = $(window).width(),
bikeTop = [];
bicycles.each( function (i, el) {
bikeTop[i] = $(this).offset();
});
bicycles.each(function(i, el) {
var position = bikeTop[i];
var fromTop = position.top - windowHeight;
var imgHeight = $(this).height();
// When this image scrolls into view.
if (lastScrollY > fromTop && lastScrollY < position.top + imgHeight && i == 1 ) { // 375 ~= height of image
var translate = Math.floor((lastScrollY - fromTop) / ((windowHeight + imgHeight + 300) / windowWidth));
console.log('add tp tranlate ', translate);
$(this).css('transform', 'translateX(' + (translate - 275) + 'px)');
}
});
ticking = false;
}
function onScroll() {
lastScrollY = window.scrollY;
requestTick();
}
function requestTick() {
if(!ticking) {
requestAnimationFrame(update);
ticking = true;
}
}
window.addEventListener('scroll', onScroll, false);
This works great and the bicycle-built-for-two slides effortlessly across the screen. However, I want the image to "bounce" when the user stops scrolling. I figure an easy way would be to add a class when the animation ends, and pull it off when the animation starts. The obvious place to do that is within the if block in requestTick().
if(!ticking) {
$('.tandem-bike').removeClass('bounce');
requestAnimationFrame(update);
$('.tandem-bike').addClass('bounce');
ticking = true;
}
or
if(!ticking) {
requestAnimationFrame(update);
$('.tandem-bike').addClass('bounce');
ticking = true;
} else {
$('.tandem-bike').removeClass('bounce');
}
}
Neither works, and I don't love then because I'm whole-sale adding classes to all the animated images on the page. (I would live with that if it worked)

JavaScript Css Animation

I have a Javascript animation at http://dev17.edreamz3.com/css/
All code works, however, there are performance problems. on Desktop, its good, On mobile things are so slow that it's unusable. I want to optimize the animation so that it runs smoothly on mobile. It can take 20 seconds or more for the animation to render.
Right now the way the code is designed is in js/anim.js there is a render() function that gets executed every time a scroll event happens. The problem is that this routine is not efficient, that's what I think of. Each time render() executes it loops through all the paths and sections of the maze and redraws them, is there any alternative way or a strategy to get it working both on mobile as well as desktop.
var offPathTime = 1000;
window.offSection = -1;
function render() {
// var top = ($window.scrollTop() + (0.4 * $window.height())) / window.scale;
var top = ($('.parent-div').scrollTop() + (0.4 * $('.parent-div').height())) / window.scale;
top -= 660;
top /= mazeSize.h;
if (window.offSection != -1) {
$body.addClass("blockScroll");
$('.parent-div').addClass("blockScroll");
// var wtop = $window.scrollTop() / window.scale;
var wtop = $('.parent-div').scrollTop() / window.scale;
wtop -= 660;
wtop /= mazeSize.h;
var $offSection = $("#offSection" + window.offSection);
var $section = $("#section" + window.offSection);
$(".section").removeClass("sectionActive");
$offSection.addClass("sectionActive");
$section.addClass("sectionActive");
var sTop = 200 -(mazeSize.h * (window.offSections[window.offSection].cy - wtop));
$container.animate({
left: 290 -(mazeSize.w * window.offSections[window.offSection].cx) + "px",
top: sTop + "px"
}, offPathTime);
// Path
var lr = offPaths[window.offSection].x1 > offPaths[window.offSection].x0;
var dx = Math.abs(offPaths[window.offSection].x1 - offPaths[window.offSection].x0);
var dashw = (dx * mazeSize.w) | 0;
$offPaths[window.offSection].css("width", "0px");
$offPaths[window.offSection].show();
if (lr) {
$offPaths[window.offSection].animate({
width: dashw + "px"
}, offPathTime);
} else {
var x0 = offPaths[window.offSection].x0 * mazeSize.w;
var x1 = offPaths[window.offSection].x1 * mazeSize.w;
$offPaths[window.offSection].css("left", x0 + "px");
$offPaths[window.offSection].animate({
width: dashw + "px",
left: x1 + "px"
}, offPathTime);
}
return;
}
$body.removeClass("blockScroll");
$('.parent-div').removeClass("blockScroll");
$(".offPath").hide();
if ($container.css("top") != "0px") {
$container.animate({
left: "-1550px",
top: "0px"
}, 500);
}
var pathIdx = -1;
var path0 = paths[0];
var path1;
var inPath = 0;
var i;
var curTop = 0;
var found = false;
for (i=0; i<paths.length; i++) {
var top0 = (i == 0) ? 0 : paths[i-1].y;
var top1 = paths[i].y;
if (top >= top0 && top < top1) {
pathIdx = i;
path1 = paths[i];
inPath = (top - top0) / (top1 - top0);
found = true;
if (i > 0) {
var dy = paths[i].y - paths[i-1].y;
var dx = paths[i].x - paths[i-1].x;
var vert = dx == 0;
if (vert)
$paths[i-1].css("height", (dy * mazeSize.h * inPath) + "px");
$paths[i-1].show();
}
} else if (top >= top0) {
path0 = paths[i];
var dy = paths[i].y - top0;
var vert = dy != 0;
if (i > 0) {
if (vert)
$paths[i-1].css("height", (dy * mazeSize.h) + "px");
$paths[i-1].show();
}
} else {
if (i > 0) {
$paths[i-1].hide();
}
}
curTop = top1;
}
// Check for an active section
$(".section").removeClass("sectionActive");
var section;
for (i=0; i<sections.length; i++) {
var d = Math.abs(sections[i].cy - (top - 0.05));
if (d < 0.07) {
var $section = $("#section" + i);
$section.addClass("sectionActive");
}
}
}
1) At the very least - assign all DOM objects to variables outside of the function scope. Like this:
var $parentDiv = $('.parent-div');
var $sections = $(".section");
...
function render() {
...
2) Also you should probably stop animation before executing it again, like this:
$container.stop(true).animate({
...
If you are running render() function on scroll - it will run many times per second. stop() helps to prevent it somewhat.
3) If it will not be sufficient - you can switch from jQuery to Zepto(jQuery-like api, but much faster and uses css transitions for animations) or to Velocity(basically drop-in replacement for jQuery $.animate and much faster than original) or even to GSAP - much more work obviously, but it is very fast and featured animation library.

select specific area of a div

I made a carousel using 2 divs named "left" and "right" putting mousemove events on them. I wanted to make it go up and down as well so I created a "top" and "bottom" and noticed that I couldn't make them combine to go the way the cursor goes.
I thus thought of targeting a specific area in the container (i.e top half of my container div) instead of creating divs inside triggering a specific direction, this way (I think) I can trigger all these event altogether. However after now hours of research I couldn't find a way to do so.
How should I proceed ? here is the code : http://jsfiddle.net/pool4/vL5g3/3/
var x=0,
y=0,
rateX=0,
rateY=0,
maxspeed=10;
var backdrop = $('.backdrop');
$('.directionx', backdrop).mousemove(function(e){
var $this = $(this);
var left = $this.is('.left');
var right = $this.is('.right');
if (left){
var w = $this.width();
rateX = (w - e.pageX - $this.offset().left + 1)/w;
}
else if (right){
var w = $this.width();
rateX = -(e.pageX - $this.offset().left + 1)/w;
}
});
$('.directiony', backdrop).mousemove(function(e){
var $this = $(this);
var top = $this.is('.top');
var bottom = $this.is('.bottom');
if (top){
var h = $this.height();
rateY = (h - e.pageY - $this.offset().top + 1)/h;
}
else if (bottom) {
var h = $this.height();
rateY = -(e.pageY - $this.offset().top + 1)/h;
}
});
backdrop.hover(
function(){
var scroller = setInterval( moveBackdrop, 30 );
$(this).data('scroller', scroller);
},
function(){
var scroller = $(this).data('scroller');
clearInterval( scroller );
}
);
function moveBackdrop(){
x += maxspeed * rateX;
y += maxspeed * rateY;
var newpos = x+'px '+y+'px';
backdrop.css('background-position',newpos);
}
Your problem is that the divs that control movement up and down are placed over the ones that control left and right, so the latter do not receive the mousemove event ever. Mouse events do not propagate through layers, even if they're transparent. I changed your code and CSS, so each div is in one of the corners. To make things easier, I've used data-* attributes so the direction controlled by each div is set in a declarative way, without the need to change the code. You'll see that the code is much simpler (and it could be simplified even more).
By the way, you could achieve this witout extra divs, just controlling where the cursor is (to the top, right, left or bottom of the center of the div).
backdrop.on('mousemove', '.dir', function(e){
var $this = $(this);
var direction = $(e.target).attr('data-direction');
var left = direction.indexOf('left') > - 1;
var right = direction.indexOf('right') > - 1;
var top = direction.indexOf('up') > - 1;
var bottom = direction.indexOf('down') > - 1;
if (left){
var w = $this.width();
rateX = (w - e.pageX - $this.offset().left + 1)/w;
}
else if (right){
var w = $this.width();
rateX = -(e.pageX - $this.offset().left + 1)/w;
}
if (top){
var h = $this.height();
rateY = (h - e.pageY - $this.offset().top + 1)/h;
}
else if (bottom) {
var h = $this.height();
rateY = -(e.pageY - $this.offset().top + 1)/h;
}
});
I've updated your fiddle.
EDIT In this new fiddle I do it without extra divs:
var w = backdrop.width() / 2;
var h = backdrop.height() / 2;
var center = {
x: backdrop.offset().left + backdrop.width() / 2,
y: backdrop.offset().top + backdrop.height() / 2
};
backdrop.on('mousemove', function(e){
var offsetX = e.pageX - center.x;
var offsetY = e.pageY - center.y;
rateX = -offsetX / w;
rateY = -offsetY / h;
});
backdrop.hover(
function(){
var scroller = $(this).data('scroller');
if (!scroller) {
scroller = setInterval( moveBackdrop, 30 );
$(this).data('scroller', scroller);
}
},
function(){
var scroller = $(this).data('scroller');
if (scroller) {
clearInterval( scroller );
$(this).data('scroller', null);
}
}
);
As you see, the mousmove handler is considerably simpler.
To avoid issue of children losing event could use just the one.
First HTML from 4 child divs to just one
<div class="backdrop">
<div class="direction"></div>
</div>
<div id="pos"></div>
Next Inside the mousemove find your relative position
//Get Relative Position
var relX = e.pageX - $this.offset().left;
var relY = e.pageY - $this.offset().top;
Get Relative Position as a percentage of width and put 50% of it in negative for direction
var w = $this.width();
rateX = ((relX / w) - 0.5) * -1;
var h = $this.height();
rateY = ((relY / h) - 0.5) * -1;
Fiddle

HTML5 and Javascript to play videos only when visible

I am interested in setting up an HTML page with multiple video clips such that each video clip plays only while visible and then pauses when out of view.
I have found this great example of how this can be implemented with one clip, but I have been unable to modify the code to work with multiple clips. Perhaps I need to convert this code into a function for easy re-usability?
Here is what I have so far (JS Bin linked above modified for 2 clips instead of one).
This code seems to work for only one of the two clips.
<!DOCTYPE html>
<html>
<!-- Created using jsbin.com Source can be edited via http://jsbin.com/ocupor/1/edit
-->
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<style>
#right {
position: absolute;
top: 2000px;
}
#video1 {
position: absolute;
left: 2000px;
top: 2000px;
}
#video2 {
position: absolute;
left: 2000px;
top: 3000px;
}
</style>
<style id="jsbin-css">
</style>
</head>
#
<body style="width: 4000px; height: 4000px;">
<div id="info"></div>
<div id="down">
scroll down please...
</div>
<div id="right">
scroll right please...
</div>
<video id="video1">
<source src="http://video-js.zencoder.com/oceans-clip.mp4"/>
</video>
<script>
var video = document.getElementById('video1'), fraction = 0.8;
function checkScroll() {
var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
video.play();
} else {
video.pause();
}
}
checkScroll();
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
</script>
<video id="video2">
<source src="http://video-js.zencoder.com/oceans-clip.mp4"/>
</video>
<script>
var video = document.getElementById('video2'), fraction = 0.8;
function checkScroll() {
var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
video.play();
} else {
video.pause();
}
} checkScroll();
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
</script>
</body>
</html>
Using the isInViewport plugin and jQuery, here's my code for the task
$('video').each(function(){
if ($(this).is(":in-viewport")) {
$(this)[0].play();
} else {
$(this)[0].pause();
}
})
OK, I think, it must be something like this:
var videos = document.getElementsByTagName("video");
function checkScroll() {
var fraction = 0.8; // Play when 80% of the player is visible.
for(var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = video.offsetLeft, y = video.offsetTop, w = video.offsetWidth, h = video.offsetHeight, r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
video.play();
} else {
video.pause();
}
}
}
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
None of the above seemed to work for me, but I finally found a way: you'll need the visible plugin, and this little piece of code right here:
$(window).scroll(function() {
$('video').each(function() {
if ($(this).visible(true)) {
$(this)[0].play();
} else {
$(this)[0].pause();
}
})
});
This will allow any video to play only when it gets into viewport. By replacing visible( true ) by visible()
, you can set it to play only when the entire video DOM element is in viewport.
Y'all need to get with the times and use IntersectionObserver (and the appropriate polyfill or babeifyl). This script will play/pause all videos on a page when they scroll in/out of view. Boom.
<script crossorigin="anonymous" src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver%2CIntersectionObserverEntry"></script>
<script>
let video = document.querySelector('video');
let isPaused = false;
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio!=1 && !video.paused){
video.pause();
isPaused = true;
}
else if(isPaused) {
video.play();
isPaused=false}
});
}, {threshold: 1});
observer.observe(video);
</script>
source
Need to check if the video is visible during the scrolling.
$(window).scroll(function() {
$('video').each(function(){
if ($(this).is(":in-viewport")) {
$(this)[0].play();
} else {
$(this)[0].pause();
}
})
});
Using jQuery, isInViewport, and Coffeescript, the complete solution for me looked like this:
$(window).scroll ->
$('video:in-viewport').each -> $(#)[0].play()
$('video:not(:in-viewport)').each -> $(#)[0].pause()
Old question, but just wanted to add my two cents, I initially started with the jQuery code above, but ran into some issues with the implementation. This solution should work with multiple videos, and also prevents a problem where the user pauses a video and tries to scroll away and it just starts again:
<script>
var videoList = [];
var scrollPauseList = [];
var clickedPauseList = [];
</script>
<script>
var myScrollFunc = function() {
$(".video-js").each(function(){
var inView = $(this).is(":in-viewport");
var isPaused = $(this)[0].player.paused();
var playerIdx = videoList.indexOf(this.id);
var scrollPaused = scrollPauseList[playerIdx];
var clickPaused = clickedPauseList[playerIdx];
if (inView) {
var hasEnded = $(this)[0].player.ended();
var curTime = $(this)[0].player.currentTime();
var hasStarted = curTime > 0;
if(hasStarted && !hasEnded && !clickPaused)
{
scrollPauseList[playerIdx] = false;
$(this)[0].player.play();
}
} else if(!isPaused) {
scrollPauseList[playerIdx] = true;
$(this)[0].player.pause();
}
});
};
$(window).scroll(myScrollFunc);
</script>
<video
class="video-js" controls></video>
<script>
$(".video-js").each(function(){
videoList[videoList.length] = this.id;
scrollPauseList[scrollPauseList.length] = false;
clickedPauseList[scrollPauseList.length] = false;
});
for(var i = 0; i < videoList.length; i++)
{
var playerID = videoList[i];
var player = videojs(playerID);
player.on('pause', function() {
var pID = videoList.indexOf(this.id());
if(!scrollPauseList[pID])
{
clickedPauseList[pID] = true;
scrollPauseList[pID] = false;
}
else
{
clickedPauseList[pID] = false;
scrollPauseList[pID] = false;
}
});
}
</script>
I've scrubbed some stuff, and i'm using video-js, so you may need to modify it a bit to get your implementation to work.
Tried many solutions, the only one partially working is the one posted below. The problem is that having 3 videos on the page, the second one and the third one are basically controlled by the first one.
So they start playing when the page is loaded (while they are supposed to play when in viewport) and they get paused when the first get paused, any suggestion on having this working with multiple videos?
Tried using getElementById but didn't work, tried also jquery plugins but no good results.
Here you have the www page where you can see what happen and all source code of course.
http://185.197.128.183/~monompro/
window.onload = function() {
var videos = document.getElementsByTagName("video"),
fraction = 0.8;
function checkScroll() {
for (var i = 0; i < videos.length; i++) {
var video = videos[i];
var x = video.offsetLeft,
y = video.offsetTop,
w = video.offsetWidth,
h = video.offsetHeight,
r = x + w, //right
b = y + h, //bottom
visibleX, visibleY, visible;
visibleX = Math.max(0, Math.min(w, window.pageXOffset + window.innerWidth - x, r - window.pageXOffset));
visibleY = Math.max(0, Math.min(h, window.pageYOffset + window.innerHeight - y, b - window.pageYOffset));
visible = visibleX * visibleY / (w * h);
if (visible > fraction) {
video.play();
} else {
video.pause();
}
}
}
window.addEventListener('scroll', checkScroll, false);
window.addEventListener('resize', checkScroll, false);
}
As explained here, the offsetTop/offsetLeft/etc. approaches are slower and more error prone than the newer getBoundingClientRect approach. Here's some working code to play any videos that are even partially visible in the viewport:
function playVisibleVideos() {
document.querySelectorAll("video").forEach(video => elementIsVisible(video) ? video.play() : video.pause());
}
function elementIsVisible(el) {
let rect = el.getBoundingClientRect();
return (rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth));
}
let playVisibleVideosTimeout;
window.addEventListener("scroll", () => {
clearTimeout(playVisibleVideosTimeout);
playVisibleVideosTimeout = setTimeout(playVisibleVideos, 100);
}, {passive: true});
window.addEventListener("resize", playVisibleVideos);
window.addEventListener("DOMContentLoaded", playVisibleVideos);
The setTimeout stuff ensures that the playVisibleVideos() function isn't called any more often than once every 100ms as the user scrolls (so it doesn't cause lag). The {passive: true} ensures the scroll handler function doesn't "block" scrolling (which can cause scroll lag).
Note that it seems like #Tristanisginger's answer using the more modern IntersectionObserver approach may be a better choice than this one for most people.
This is how I managed to play a video only when the user scrolls to it.
I used IsInViewport plugin.
Hope you find it useful!
$(window).scroll(function() {
var video = $('.yourvideo');
$(video).each(function(){
if(video.is(':in-viewport')){
video[0].play();
video.removeClass('yourvideo');
//I removed class to stop repeating the action ".play()" when it is scrolled again.
}
});
});
In case anyone else runs into this question, I was unable to use Saike's solution on my WordPress site because of the way the videos were auto embedded (MediaElement player). However, qwazix's solution worked with some modification. Here is the jQuery code that works with the IsInView plugin. Here are my include scripts (placed at the end of footer.php in my theme folder).
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="js/isInViewport.min.js" type="text/javascript"></script>
<script src="js/scrollview.min.js" type="text/javascript"></script>
And the jQuery code (modify 400 to your tolerance liking)
$(function() {
$(window).scroll(function() {
$('.wp-video-shortcode').each(function() {
var str = $(this).attr('id');
var arr = str.split('_');
typecheck = arr[0];
if ($(this).is(":in-viewport( 400 )") && typecheck == "mep") {
mejs.players[$(this).attr('id')].media.play();
} else if (typecheck == "mep") {
mejs.players[$(this).attr('id')].media.pause();
}
});
});
});
Only issue I have with this code is that it does restart a video clip on scroll even if paused by the user. Wasn't a deal-breaking issue on my site. Here is the code in action: Ultrasoundoftheweek.com
If you're looking for a simple solution without any dependencies, here it is:
let video = document.getElementById('video')
function playVideoOnScroll () {
const threshold = 300 //px above the video to start playing
let offset = video.getBoundingClientRect().top
if (offset < threshold) {
demoVideo.play()
} else {
demoVideo.pause()
}
}
window.addEventListener('scroll', playVideoOnScroll, false)
window.addEventListener('resize', playVideoOnScroll, false)
My working solution in vanilla 2023 Javascript using IntersectionObserver:
document.addEventListener('DOMContentLoaded', function() {
const videos = document.querySelectorAll('video');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.play();
} else {
entry.target.pause();
}
});
});
videos.forEach(video => {
observer.observe(video);
});
});

Categories