How can you smooth scrubbing a video while scrolling? - javascript

I'm using scrollmagic and gsap on my page to scroll through a video, kinda like Apple on this page here: https://www.apple.com//homepod/
Not sure if they use a video as well but it's the same effect I'm trying to achieve.
Anyway, my script works great on Macbook/Safari but Macbook/Chrome stutters a little bit, on PC its even worse with a regular mouse instead of the trackpad.
Is there any way to achieve a smooth scrolling experience, like somehow make the video "catch up" if someone scrolls in "larger" steps with a mouse.
Below you can find the code I have so far:
const video = document.getElementById('video');
const long = document.getElementById('long');
let scrollpos = 0;
let lastpos;
const controller = new ScrollMagic.Controller();
const scene = new ScrollMagic.Scene({
triggerElement: long,
triggerHook: "onEnter"
});
const startScrollAnimation = () => {
scene
.addTo(controller)
.duration(long.clientHeight)
.on("progress", (e) => {
scrollpos = e.progress;
});
setInterval(() => {
if (lastpos === scrollpos) return;
requestAnimationFrame(() => {
video.currentTime = video.duration * scrollpos;
video.pause();
lastpos = scrollpos;
});
}, 50);
};
const preloadVideo = (v, callback) => {
const ready = () => {
v.removeEventListener('canplaythrough', ready);
video.pause();
var i = setInterval(function() {
if (v.readyState > 3) {
clearInterval(i);
video.currentTime = 0;
callback();
}
}, 50);
};
v.addEventListener('canplaythrough', ready, false);
v.play();
};
preloadVideo(video, startScrollAnimation);
startScrollAnimation();
#video {
position: fixed;
}
#long {
height: 20000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.19.1/TweenMax.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/ScrollMagic.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.5/plugins/animation.gsap.min.js"></script>
<video id="video" src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">
</video>
<div id="long"></div>

Related

Scrollmagic video animation in webpack (BigCommerce)

I am trying to achieve a video animation with scrollmagic.
Desired outcome is that, the video plays based on the user scroll position.
export default function magic() {
//MAIN
var controller = new ScrollMagic.Controller();
// SceneOne animation
const $video = $('#soVideo');
let sceneOne = new ScrollMagic.Scene({
duration: 9000,
triggerElement: '#trigger1',
triggerHook: 0
})
.setPin('#trigger1')
.addIndicators()
.addTo(controller);
// SceneOne animation
let accelamount = 0.1;
let scrollpos = 0;
let delay = 0;
sceneOne.on('update', e => {
scrollpos = e.scrollPos / 1000;
});
setInterval(() => {
delay += (scrollpos - delay) * accelamount;
$video.currentTime = delay;
console.log(delay);
}, 33.36);
}
The problem is, I cannot get it working, the video stays static at scroll.
I am trying to do this for a product page, not sure what am I doing wrong.
Thanks for any tips!
Ok figured it out. Works now:
export default function magic() {
const intro = document.querySelector(".video-section");
const video = intro.querySelector('.video_zero');
const text = intro.querySelector('.intro-text');
var controller = new ScrollMagic.Controller();
// SceneOne animation
let sceneOne = new ScrollMagic.Scene({
duration: 9500,
triggerElement: intro,
triggerHook: 0
})
.setPin(intro)
.addIndicators()
.addTo(controller);
// SceneOne animation
let accelamount = 0.1;
let scrollpos = 0;
let delay = 0;
sceneOne.on('update', e => {
scrollpos = e.scrollPos / 1000;
});
setInterval(() => {
delay += (scrollpos - delay) * accelamount;
video.currentTime = delay;
}, 33.3);
}

Javascript: Saving getUserMedia() images

I want to load the user's webcam to a canvas, and then save an image from that canvas as a blob. Hopefully, I will get 1 image every 1 second.
Unfortunately, I only get 1 image saved 5 times, rather than 5 different images. My code is fairly straightforward, but I suspect the issue is with my takeASnap() function. What is going wrong?
Edit: This is observed on Safari on iPad/iPhone, but not in desktop chrome.
var NUM_IMAGES = 5;
const vid = document.querySelector('video');
navigator.mediaDevices.getUserMedia({ video: true }) // request cam
.then(stream => {
vid.srcObject = stream; // don't use createObjectURL(MediaStream)
return vid.play(); // returns a Promise
})
.then(() => { // enable the button
const btn = document.getElementById('download-button');
btn.disabled = false;
btn.onclick = e => {
imageId = 0;
userId = Math.floor(Math.random() * 10000);
recursiveDelay(kickOff, NUM_IMAGES, 1000)
};
});
function kickOff() {
takeASnap().then(saveBlob); // saveBlob out of scope of this question.
}
function recursiveDelay(functionToCall, executionsNumber, timeoutInMilliseconds) {
if (executionsNumber) { //exit condition
functionToCall(); // external function execution
setTimeout(
() => {
recursiveDelay(functionToCall, executionsNumber - 1, timeoutInMilliseconds); //recursive call
}, timeoutInMilliseconds);
}
}
function takeASnap() {
const canvas = document.createElement('canvas'); // create a canvas
const ctx = canvas.getContext('2d'); // get its context
canvas.width = vid.videoWidth; // set its size to the one of the video
canvas.height = vid.videoHeight;
ctx.drawImage(vid, 0, 0); // the video
return new Promise((res, rej) => {
canvas.toBlob(res, 'image/jpeg'); // request a Blob from the canvas
});
}

Multiple audio players but only first working

The problem is that when i have 2 or more players on html page i can only play music in the first player and the rest wont work.
my customized HTML5 audio player:
<audio id="music" preload="true">
<source src="music/interlude.mp3">
</audio>
<div id="wrapper">
<div id="audioplayer">
<button id="pButton" class="play"></button>
<div id="timeline">
<div id="playhead"></div>
</div>
</div>
</div>
and js for this:
document.addEventListener("DOMContentLoaded", function(event) {
var music = document.getElementById('music'); // id for audio element
var duration; // Duration of audio clip
var pButton = document.getElementById('pButton'); // play button
var playhead = document.getElementById('playhead'); // playhead
var timeline = document.getElementById('timeline'); // timeline
// timeline width adjusted for playhead
var timelineWidth = timeline.offsetWidth - playhead.offsetWidth;
// play button event listenter
pButton.addEventListener("click", play);
// timeupdate event listener
music.addEventListener("timeupdate", timeUpdate, false);
// makes timeline clickable
timeline.addEventListener("click", function(event) {
moveplayhead(event);
music.currentTime = duration * clickPercent(event);
}, false);
// returns click as decimal (.77) of the total timelineWidth
function clickPercent(event) {
return (event.clientX - getPosition(timeline)) / timelineWidth;
}
// makes playhead draggable
playhead.addEventListener('mousedown', mouseDown, false);
window.addEventListener('mouseup', mouseUp, false);
// Boolean value so that audio position is updated only when the playhead is released
var onplayhead = false;
// mouseDown EventListener
function mouseDown() {
onplayhead = true;
window.addEventListener('mousemove', moveplayhead, true);
music.removeEventListener('timeupdate', timeUpdate, false);
}
// mouseUp EventListener
// getting input from all mouse clicks
function mouseUp(event) {
if (onplayhead == true) {
moveplayhead(event);
window.removeEventListener('mousemove', moveplayhead, true);
// change current time
music.currentTime = duration * clickPercent(event);
music.addEventListener('timeupdate', timeUpdate, false);
}
onplayhead = false;
}
// mousemove EventListener
// Moves playhead as user drags
function moveplayhead(event) {
var newMargLeft = event.clientX - getPosition(timeline);
if (newMargLeft >= 0 && newMargLeft <= timelineWidth) {
playhead.style.marginLeft = newMargLeft + "px";
}
if (newMargLeft < 0) {
playhead.style.marginLeft = "0px";
}
if (newMargLeft > timelineWidth) {
playhead.style.marginLeft = timelineWidth + "px";
}
}
// timeUpdate
// Synchronizes playhead position with current point in audio
function timeUpdate() {
var playPercent = timelineWidth * (music.currentTime / duration);
playhead.style.marginLeft = playPercent + "px";
if (music.currentTime == duration) {
pButton.className = "";
pButton.className = "play";
}
}
//Play and Pause
function play() {
// start music
if (music.paused) {
music.play();
// remove play, add pause
pButton.className = "";
pButton.className = "pause";
} else { // pause music
music.pause();
// remove pause, add play
pButton.className = "";
pButton.className = "play";
}
}
// Gets audio file duration
music.addEventListener("canplaythrough", function() {
duration = music.duration;
}, false);
// getPosition
// Returns elements left position relative to top-left of viewport
function getPosition(el) {
return el.getBoundingClientRect().left;
}
/* DOMContentLoaded*/
});
i belive the problem is somewhere in the js code but i cant figure out where.
i took the code form here
I notice that you're using a lot of IDs. When the "first of something" works on a page, usually it's IDs that are causing the issue.
Change all of your IDs for your <audio> players to class attributes. You will also need to update your JavaScript as well, from something like this:
var music = document.getElementById('music');
music.addEventListener(...
To something more like this:
var music = document.querySelectorAll(".music"); // class="music"
for (var i = 0; i < music.length; i++) {
music[i].addEventListener(...
You should find that this will work for you.

Can't set video.currentTime by setting video.currentTime = {value}

In this piece of code I have video.currentTime = {value}
And it works fine.
But when I'm trying to reload the page by pressing Crtl+Shift+R it sets video.currentTime to 0, no matter when on the progress bar I clicked.
Why is that?
window.addEventListener('load', function(){
var video = document.getElementById('video');
var playButton = document.getElementById('play-button');
var pbar = document.getElementById('pbar');
var update;
var pbarContainer = document.getElementById('pbar-container');
video.load();
video.addEventListener('canplay', function(){
playButton.addEventListener('click', playOrPause, false);
pbarContainer.addEventListener('click', skip, false);
}, false);
var skip = function(ev) {
var mouseX = ev.pageX - pbarContainer.offsetLeft;
var width = window.getComputedStyle(pbarContainer).getPropertyValue('width');
width = parseFloat(width.substr(0, width.length - 2));
video.currentTime = (mouseX/width)*video.duration;
};
var updatePlayer = function() {
var percentage = (video.currentTime/video.duration)*100;
pbar.style.width = percentage + '%';
if(video.ended) {
window.clearInterval(update);
playButton.src = 'images/replay.png';
}
};
var playOrPause = function(){
if(video.paused) {
video.play();
playButton.src = 'images/pause.png';
update = setInterval(updatePlayer, 30);
} else {
video.pause();
playButton.src = 'images/play.png';
window.clearInterval(update);
}
};
//video.addEventListener('click', playOrPause(playButton), false);
}, false);
Refreshing your page stops all execution of your code and resets all your variables, basically starting you from the beginning.
If you want variables to remain even after refreshing the page then set the localStorage and your values will be saved on the browser. You can find them if you open your developer tools in the Resources section under localStorage:
localStorage.setItem(video.currentTime,value)
To retrieve it, you'll have to use
localStorage.getItem(video.currentTime,value)

Image Preloading for animation

I have a banner at the top of some of my webpages. The banner is static until the user clicks on the play button which kick starts the animation.
The animation is one long horizontal image that just rolls around and iv got it overlapping perfectly.
But what I want to happen is to have a smaller alternative image to load first, while the large image loads behind then once it is loaded to replace the original small image so there is no delay on the page...
How do I go about this?
This is my js to get the banner to play on click.
$(document).ready(function()
{
var mwbannerfull = $( '#mwbannerfull' );
var playing = false;
var playarrow = $( '.playarrow' );
var speed = 0.145;
var totalDistance = 4350;
playarrow.click(function()
{
if (playing)
pause();
else
play();
playing = !playing
playarrow.toggleClass('playing');
});
function play()
{
var distance = parseInt(mwbannerfull.css('left'), 10) * -1;
if (isNaN(distance)) distance = 0;
distance = totalDistance - distance;
var time = distance / speed;
mwbannerfull.animate({left: '-' + totalDistance + 'px'}, time, 'linear', function()
{
$(this).css(
{
left: 0
});
play();
});
}
function pause()
{
mwbannerfull.stop();
}
});
HTML:
<div id="mwprobanner">
<a href="#!" class="playarrow">
<i class="icon-play"></i>
<i class="icon-pause"></i>
</a>
<div id="mwbannerfull"></div>
</div>
data-l will store your large image
<img data-l='https://www.google.com.hk/images/srpr/logo11w.png' src='http://www.microsoft.com/global/learning/en-us/PublishingImages/ms-logo-site-share.png' />
 
function startLoading() {
$("img").each(function () {
var _this = $(this);
var src = $(this).attr('data-l');
if (src != "") {
var tmp = $(new Image());
//When loaded
tmp.one('load', function () {
_this.replaceWith(tmp);
});
tmp.attr('src', src);
}
});
}
Image will start loading after 2 seconds.
setTimeout(function(){
startLoading();
},2000);
http://jsfiddle.net/netorz04/

Categories