Skip to content

Instantly share code, notes, and snippets.

@markteater
Last active August 11, 2021 13:37
Show Gist options
  • Save markteater/58c0779f044d54d912d4 to your computer and use it in GitHub Desktop.
Save markteater/58c0779f044d54d912d4 to your computer and use it in GitHub Desktop.

Scrolleo

Apple has been creating some amazing scrolling video sites lately and I've been trying to create a simplified plugin to recreate this effect. This is the alpha version of this plugin. You can see an example of the effect I'm recreating here: http://www.apple.com/macbook/

This uses requestAnimationFrame and allows you to create multiple custom scrolling videos easily.

A Pen by Mark Teater on CodePen.

License.

<div style="margin-top: 20px">
<!-- Intro -->
<h1>Scrolleo</h1>
<p>This is a test of videos reacting to scrolling.</p>
<p>Created by <a href="http://markteater.com">Mark Teater</a>, based on a <a href="http://codepen.io/anon/pen/mJwbK">CodePen</a> found on <a href="http://www.reddit.com/r/webdev/comments/2krge1/codepens_killer_html5_video_scrolling_controls_w/">Reddit</a></p>
<p>Inspired by Apple's scrolling videos: <a href="http://www.apple.com/macbook/">MacBook</a></p>
<p>Scroll down to test</p>
</div>
<div style="margin-top: 100vh">
<!-- Video 1 -->
<video id="scrolleo-1" width="100%" height="100%" autobuffer="autobuffer" preload="preload">
<source src="http://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.mp4">
</video>
</div>
<div style="margin-top: 800px">
<!-- Video 2 -->
<video id="scrolleo-2" width="100%" height="100%" autobuffer="autobuffer" preload="preload">
<source src="http://www.html5rocks.com/tutorials/video/basics/Chrome_ImF.mp4">
</video>
</div>
<!-- Spacer -->
<div style="margin-top: 800px"></div>
/* Scrolleo - make your video scroll with inertia
* MIT License - by Mark Teater
*/
(function(window, document, undefined) {
"use strict";
var _Scrolleo = function(opts) {
// Defaults
this.acceleration = 0.08; //1 is fastest, 0 is slowest, 0.08 is default
this.secondsPerScreen = null; //Set this to the length of the video. "1" is 1 second.
this.additionalOffset = 0; //Add or subtract pixels to when the video will start. "10" means that the video will start 10px earlier.
this.wrapperEl = null;
// Override defaults
if (opts) {
for (var opt in opts) {
this[opt] = opts[opt];
}
}
};
var targetscrollpos;
_Scrolleo.prototype = {
init: function() {
var self = this;
this.wrapper = document.querySelectorAll(this.wrapperEl);
// get the location of the top of the page
targetscrollpos = window.pageYOffset;
Array.prototype.forEach.call(this.wrapper, function(wr) {
// Set the pixelsPerSecond to the full duration of the video if nothing was set in the options
if (self.secondsPerScreen === null) {
self.wrapper[0].addEventListener('loadedmetadata', function() {
self.secondsPerScreen = self.wrapper[0].duration;
//recalculate values on video with new pixelsPerSecond
self.distanceToTop = getElemDistanceToTop(elem);
self.offsetFromTop = getOffsetFromTop(self.distanceToTop);
self.pixelsPerSecond = getPixelsPerSecond();
});
}
self.pixelsPerSecond = null;
self.scrollpos = null;
self.currentTime = null;
self.offsetFromTop = null;
self.distanceToTop = null;
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
},
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
// requestAnim shim layer by Paul Irish
window.requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( /* function */ callback, /* DOMElement */ element) {
window.setTimeout(callback, 1000 / 60);
};
})();
// Define functions to be used by Scrolleo
var getElemDistanceToTop = function(elem) {
//http://gomakethings.com/get-distances-to-the-top-of-the-document-with-native-javascript/
var location = 0;
if (elem.offsetParent) {
do {
location += elem.offsetTop;
elem = elem.offsetParent;
} while (elem);
}
return location >= 0 ? location : 0;
},
getOffsetFromTop = function(distanceToTop) {
var offset = distanceToTop - window.innerHeight + self.additionalOffset;
return offset >= 0 ? offset : 0;
},
getPixelsPerSecond = function() {
var pixelsPerSecond = (window.innerHeight + self.wrapper[0].clientHeight) / self.secondsPerScreen;
return pixelsPerSecond >= 0 ? pixelsPerSecond : 0;
},
scrollHandler = function() {
targetscrollpos = window.pageYOffset;
},
resizeHandler = function() {
//recalculate values on resize
self.distanceToTop = getElemDistanceToTop(elem);
self.offsetFromTop = getOffsetFromTop(self.distanceToTop);
self.pixelsPerSecond = getPixelsPerSecond();
},
scrollControl = function() {
//what to do when scrolling
self.scrollpos += ((targetscrollpos - self.offsetFromTop) - self.scrollpos) * self.acceleration;
self.currentTime = self.scrollpos / self.pixelsPerSecond; //convert scrollpos from pixels to seconds to set self.currentTime
self.wrapper[0].currentTime = self.currentTime;
self.wrapper[0].pause();
};
// Get an element's distance from the top of the page
var elem = self.wrapper[0];
// Calulate the initial size, distance, and offset of each scrolleo video
self.distanceToTop = getElemDistanceToTop(elem);
self.offsetFromTop = getOffsetFromTop(self.distanceToTop);
self.pixelsPerSecond = getPixelsPerSecond();
self.scrollpos = targetscrollpos - self.offsetFromTop;
wr.pause();
// Use requestAnimationFrame to ensure the video is updating when the browser is ready
window.requestAnimFrame(function render() {
window.requestAnimFrame(render);
scrollControl();
});
window.addEventListener('scroll', scrollHandler, false);
window.addEventListener('resize', resizeHandler, false);
});
}
};
window.Scrolleo = _Scrolleo;
})(window, document);
@jansemka
Copy link

jansemka commented Oct 1, 2015

Nice Script but there seems to be some issues regarding to safari, sometimes the videos does appear but will not play and stay on pause. i can not reproduce the issue sometimes it works sometimes not.

@Mofect
Copy link

Mofect commented Aug 11, 2021

why I changed it to my video, it's playing not smoothly? is there any requirement for the video?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment