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);
}
Related
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>
So I've created a smooth scroll effect on my website, works nice, but I'm having an issue with Scrollmagic. When scrolling down, I'm translating elements, but it stutters. A lot. When I disable my smooth scroll script, everything works fine again.
BTW : I'm using Webpack and GSAP for the animations.
My guess is Scrollmagic isn't aware of the animation so it's using the end value, not the current one. But I can't find out how to fix this
Here is my smooth scroll :
import { TweenLite } from 'gsap';
const html = document.documentElement;
const body = document.body;
const scroller = {
target: document.querySelector('.scroll-container'),
ease: 0.1, // <= scroll speed
endY: 0,
y: 0,
resizeRequest: 1,
scrollRequest: 0,
};
let requestId = null;
TweenLite.set(scroller.target, {
rotation: 0.01,
force3D: true,
});
window.addEventListener('load', onLoad);
function onLoad() {
updateScroller();
window.focus();
window.addEventListener('resize', onResize);
document.addEventListener('scroll', onScroll);
}
function updateScroller() {
const resized = scroller.resizeRequest > 0;
if (resized) {
const height = scroller.target.clientHeight;
body.style.height = `${height}px`;
scroller.resizeRequest = 0;
}
const scrollY = window.pageYOffset || html.scrollTop || body.scrollTop || 0;
scroller.endY = scrollY;
scroller.y += (scrollY - scroller.y) * scroller.ease;
if (Math.abs(scrollY - scroller.y) < 0.05 || resized) {
scroller.y = scrollY;
scroller.scrollRequest = 0;
}
TweenLite.set(scroller.target, {
y: -scroller.y,
});
requestId = scroller.scrollRequest > 0 ? requestAnimationFrame(updateScroller) : null;
}
function onScroll() {
scroller.scrollRequest += 1;
if (!requestId) {
requestId = requestAnimationFrame(updateScroller);
}
}
function onResize() {
scroller.resizeRequest += 1;
if (!requestId) {
requestId = requestAnimationFrame(updateScroller);
}
}
And the Scrollmagic part :
import $ from 'jquery';
import * as ScrollMagic from 'scrollmagic';
import { TweenMax, TimelineMax, Power0 } from 'gsap';
import { ScrollMagicPluginGsap } from 'scrollmagic-plugin-gsap';
ScrollMagicPluginGsap(ScrollMagic, TweenMax, TimelineMax);
const controller = new ScrollMagic.Controller();
$('.big-outline-text').each(function() {
const tl = new TimelineMax();
const child = $(this);
if ($(this).hasClass('right-to-left')) {
tl.to(child, 2, { x: -300, ease: Power0.easeInOut });
} else if ($(this).hasClass('left-to-right')) {
tl.fromTo(child, 2, { x: -300 }, { x: 0, ease: Power0.easeInOut }, '+=1');
}
const scene = new ScrollMagic.Scene({
triggerElement: this,
triggerHook: 0.9,
duration: '110%',
})
.setTween(tl)
.addTo(controller);
});
$('.bottom-to-top').each(function() {
const tl2 = new TimelineMax();
const child = $(this);
if ($(this).hasClass('bottom-to-top')) {
tl2.fromTo(child, 2, { y: -300 }, { y: 100, ease: Power0.easeInOut });
}
const scene = new ScrollMagic.Scene({
triggerElement: this,
triggerHook: 0.9,
duration: '220%',
})
.setTween(tl2)
.addTo(controller);
});
I'm sure i'm not the first one having this problem, but i couldn't find any answer.
I managed to solve my issue with a refresh function for the scrollbar. Like in this codepen https://codepen.io/emraher/pen/GPRJEZ?editors=1010
They set the scrollbar and the scrollmagic scene as vars, and then this little gem
var elem = document.querySelector(".content");
var scrollbar = Scrollbar.init(elem)
scrollbar.addListener(() => {
scene.refresh()
})
https://jsfiddle.net/fnethLxm/10/
$(document).ready(function() {
parallaxAuto()
});
function parallaxAuto() {
var viewer = document.querySelector('.viewer.active'),
frame_count = 5,
offset_value = 500;
// init controller
var controller = new ScrollMagic.Controller({
globalSceneOptions: {
triggerHook: 0,
reverse: true
}
});
// build pinned scene
var scene = new ScrollMagic.Scene({
triggerElement: '#sticky',
duration: (frame_count * offset_value) + 'px',
reverse: true
})
.setPin('#sticky')
//.addIndicators()
.addTo(controller);
// build step frame scene
for (var i = 1, l = frame_count; i <= l; i++) {
new ScrollMagic.Scene({
triggerElement: '#sticky',
offset: i * offset_value
})
.setClassToggle(viewer, 'frame' + i)
//.addIndicators()
.addTo(controller);
}
$(".right_arrr").click(function(){
var block = $(this).siblings('.secondSlider');
el = block.find(".active");
elNum = el.attr("data-num");
if(elNum < block.find('.slide').length) {
elNum++;
} else {
elNum=1;
}
hideShow(elNum, block);
alert('slide №' + elNum)
scene = scene.destroy(true);
scene = null;
controller.destroy(true);
controller = null;
parallaxAuto();
});
$(".left_arrr").click(function(){
var block = $(this).siblings('.secondSlider');
el = block.find(".active");
elNum = el.attr("data-num");
if(elNum > 1) {
elNum--;
} else {
elNum=block.find('.slide').length;
}
hideShow(elNum, block);
scene = scene.destroy(true);
scene = null;
controller.destroy(true);
controller = null;
parallaxAuto();
});
function hideShow(num, block) {
block.find("div.active").removeClass("active").animate({ opacity: 0,},300);
block.find("div.slide"+num).addClass("active").animate({ opacity: 1,},300);
}
};
You can see that on 1 and 2 slide plugin work out fine, but on slide 3 it does not work. and error "Cannot read property 'destroy' of null"
A few days longer I sit and I can not understand
how to fix this?
I see 2 issues:
you're setting the scene and the controller to null on every right/left click, and require re-init of it re-calling parallaxAuto;
every time you call parallaxAuto you re-bind the listeners.
I took the freedom to rewrite it for you so the listeners will be bound only once: https://jsfiddle.net/j6u6wp7x/. I just isolated the part in which you re-init the controller and the scene so you can call it at the end of the click without re-binding the events.
How to reset values when switching slides?
https://jsfiddle.net/j6u6wp7x/
var scene;
var controller;
$(document).ready(function() {
parallaxAuto();
$(".right_arrr").click(function(){
var block = $(this).siblings('.secondSlider');
el = block.find(".active");
elNum = el.attr("data-num");
if(elNum < block.find('.slide').length) {
elNum++;
} else {
elNum=1;
}
hideShow(elNum, block);
alert('slide №' + elNum)
scene = scene.destroy(true);
scene = null;
controller.destroy(true);
controller = null;
parallaxAuto();
});
$(".left_arrr").click(function(){
var block = $(this).siblings('.secondSlider');
el = block.find(".active");
elNum = el.attr("data-num");
if(elNum > 1) {
elNum--;
} else {
elNum=block.find('.slide').length;
}
hideShow(elNum, block);
scene = scene.destroy(true);
scene = null;
controller.destroy(true);
controller = null;
parallaxAuto();
});
});
function hideShow(num, block) {
block.find("div.active").removeClass("active").animate({ opacity: 0,},300);
block.find("div.slide"+num).addClass("active").animate({ opacity: 1,},300);
}
// init variables
function parallaxAuto() {
var viewer = document.querySelector('.viewer.active'),
frame_count = 5,
offset_value = 500;
// init controller
controller = new ScrollMagic.Controller({
globalSceneOptions: {
triggerHook: 0,
reverse: true
}
});
// build pinned scene
scene = new ScrollMagic.Scene({
triggerElement: '#sticky',
duration: (frame_count * offset_value) + 'px',
reverse: true
})
.setPin('#sticky')
//.addIndicators()
.addTo(controller);
// build step frame scene
for (var i = 1, l = frame_count; i <= l; i++) {
new ScrollMagic.Scene({
triggerElement: '#sticky',
offset: i * offset_value
})
.setClassToggle(viewer, 'frame' + i)
//.addIndicators()
.addTo(controller);
}
}
You can see in the example that the transition to the next slide stored value
And it is necessary that when we switch the values updated. I can not achieve this effect
I have a clock hand that rotates as a timer. If the user completed activity before the time runs out I need to stop that tween.
I tried remove tween with no luck. What am I doing wrong?
I get into my levelup function but the remove tween does not work.
function Clock() {
// this.board = board;
clockContainer = new createjs.Container();
contain = new createjs.Container();
var clockBack = new createjs.Bitmap(queue.getResult("clockBack"));
clockHand = new createjs.Bitmap(queue.getResult("clockHand"));
clockBack.x = 40;
clockBack.y = 480;
clockHand.x = 95;
clockHand.y = 539;
clockHand.regX = 20
clockHand.regY = 105;
clockHand.scaleX = clockHand.scaleY = 0.50;
clockBack.scaleX = clockBack.scaleY = 0.50;
clockContainer.addChild(clockBack, clockHand);
TimerLength = 30000;
stage.addChild(clockContainer)
mytweentodisable = createjs.Tween.get(clockHand, { loop: false }).to({ rotation: 360 }, TimerLength).call(function () {
//this will trigger the timer is up
GamehasEnded = true;
checkWrongAndRight();
});
}
function levelUp() {
createjs.Tween.removeTweens(mytweentodisable)
console.log("adding Level up button");
levelUpContainer = new createjs.Container();
levelUpIcon = new createjs.Bitmap(queue.getResult("levelUp"));
levelUpContainer.addChild(levelUpIcon);
stage.addChild(levelUpContainer)
levelUpContainer.x = 350
levelUpContainer.y = 500
levelUpContainer.addEventListener("click", function () {
console.log("clicked it");
});
}
This should do the trick:
mytweentodisable.setPaused(true);