I am writing a script that uses the Wipe animation from the scrollorama.js script. I am hoping to be able to implement a video to autoplay at certain markers in the scroll depth: ie, when a video page has wiped out another, and is now fully viewable. I have figured out how to measure scroll depth, i am successfully logging it in my console. I have figured out how to measure how deep i have scrolled, but maybe i am so tired, i don't know how to ask the video to play automatically at the scroll depth. I hope this is a legal question, and that I can get some assistance. Has anyone out there tried this before? Here is the code thus far.
enter code here $(document).ready(function() {
$(window).scroll(function(e) {
var scrollAmount = $('body').scrollTop();
console.log(scrollAmount);
});
var controller = $.superscrollorama();
var pinDur = 800;
// create animation timeline for pinned element
var pinAnimations = new TimelineLite();
//pinAnimations.append([TweenMax.to($('#green'), .5, {css:{top:0}})], .5)
pinAnimations.append([TweenMax.to($('#intromovie'), .5, {css:{top:0}})], .5 )
pinAnimations.append([TweenMax.to($('#red'), .5, {css:{top:0}})], .5)
pinAnimations.append([TweenMax.to($('#blue'), .5, {css:{top:0}})], .5 )
pinAnimations.append([TweenMax.to($('#movie1'), .5, {css:{top:0}})], .5);
pinAnimations.append([TweenMax.to($('#history1'), .5, {css:{top:0}})], .5);
//pinAnimations.append(TweenMax.to($('#pin-frame-unpin'), .5, {css:{top:'100px'}}));
controller.pin($('#content_wrapper'), pinDur, {
anim:pinAnimations,
onPin: function() {
$('#content_wrapper').css('height','100%');
},
onUnpin: function() {
$('#content_wrapper').css('height','1000px');
}
});
});
I figured this out, so i answer my own question with the help of a lot of other answers patched together here!
If anyone is interested, the html was simple:
<div id="videoHolder"></div>
Jquery was also simple:
$(function(){
$(window).scroll(function(e) {
var scrollAmount = $('body').scrollTop();
console.log(scrollAmount);
if(scrollAmount >="theamountyouwant" && scrollAmount <= "theotheramountyouwant") {
$("#videoHolder").html(
'<video width="1200" height="700" autoplay>' +
'<source src="http://itp.nyu.edu/~rnr217/HTML5/Week3/video/testopen.webm" type="video/webm"></source>' +
'<source src="http://itp.nyu.edu/~rnr217/HTML5/Week3/video/testopen.mp4" type="video/mp4"></source>' +
'</video>');
So in this case we can use an integrated JavaScript API namely Intersection Observer. Now our main task is to play the video at an particular position, so for this task we will set up a trigger on the intersectionRatio value.
const images = document.querySelectorAll(".mydivTriggerClass");
vid = document.getElementById("myVideoId");
observer = new IntersectionObserver((entries) => {
console.log(entries);
if (entry.intersectionRatio > 0.34) {
vid.play();
} else {
entry.target.style.animation = "none";
}
});
observer.observe(image);
NOTE: Please note that the console log entries are optional - its just so that when you inspect you get the info showing where this intersection ratio came from.
For a perfect working example please visit this link.
just add the script from below and add playonscroll param to your video tag anywhere on a page.
As well some times it's required to use different scroll container than body, sometimes its not obvious, so the following code works like a charm for me:
setInterval(function () {
$('video[playonscroll]').each(function () {
var videoElement = $(this)[0];
var eTop = $(this).offset().top;
var elementOffestY = (eTop - $(window).scrollTop());
var videoOffset = [elementOffestY, elementOffestY+$(this).height()];
if ((videoOffset[0] < 100) && (videoOffset[1] > 350)) {
console.log('play');
if (!videoElement.playing) {
videoElement.play();
}
} else {
if (videoElement.playing) {
videoElement.pause();
}
}
});
},300);
in case if you always use body container for scroll just change setInterval to $(window).scroll
And don't forget to define property for Video tag element:
Object.defineProperty(HTMLMediaElement.prototype, 'playing', {
get: function(){
return (this.currentTime > 0 && !this.paused && !this.ended && this.readyState > 2);
}
})
Related
I have a smooth scroll in vanilla js for my one page website which i try to implement without jquery, and I want to add a timing function like cubic bezier. Is there any way to do that in javascript? Here is the code:
{
'use strict';
let currentY = 0;
let destination = 0;
let speed = 40;
let scroller = null;
function smoothScroll(id) {
destination = document.getElementById(id).offsetTop;
//if the user scrolls down
if (window.pageYOffset < destination) {
scroller = setTimeout(function () {
smoothScroll(id);
}, 1);
currentY = currentY + speed;
if (currentY >= destination) {
clearTimeout(scroller);
}
//if the user scrolls up
} else {
scroller = setTimeout(function () {
smoothScroll(id);
}, 1);
currentY = currentY - speed;
if (currentY <= destination) {
clearTimeout(scroller);
}
}
window.scroll(0, currentY);
}
window.onscroll = function () {
currentY = this.pageYOffset;
};
Array.from(document.querySelectorAll(".scroll")).forEach(e => {
e.addEventListener('click', () => {
smoothScroll(e.href.split('#')[1]);
});
});
}
And here is a codepen to watch it in action : https://codepen.io/anon/pen/NYNQym
Thanks in advance.
First, you should use requestAnimationFrame(fn) instead of setTimeout(fn,1).
Your animation system is incremental - it says 'am I there yet? if no, go closer; if yes, stop.' This is OK but the only information it gives you about the animation is whether it's finished or not finished.
Easing would be something like 'when it's close to the end, slow down', but you don't know when you are close to the end.
Let's say we want to move from scroll position 100 to scroll position 200, starting at time 0 and ending at time 500. It's a mapping of time to position. If it's time 250, we should be at position 150 - they're both halfway. The same thing works for any other time. This is called tweening and it's the most common way to do animation.
Once we're working this way, we can do easing. The easing functions themselves are really simple - here are all the classic ones.
I can post code if you want but it sounds like you're trying to figure this out yourself, hope this was helpful and good luck.
I'm playing around with pure JavaScript, so I created a small fade in/out object, to adjust images opacity onmouseover and onmouseout. Fading works fine when the mouseover and mouseout actions are precise:
Start moving the cursor from the white background
Hover over an image
Hover back over the white background
The problem is, as soon as I start to move the mouse "naturally" from one image to another, the fading (or rather the script itself) freezes.
I'm not sure whether it's a animation-speed problem, or there's something I'm missing in the implementation.
If someone has the time to take a look, I would appreciate a peer check, so I can crack the issue and learn new stuff.
Here's a fiddle: http://jsfiddle.net/6bd3xepe/
Thanks!
As I see it, you have one INTERVAL for you FADER, you need one for each IMG.
My jsfiddle fixes this. I added an ALT-attribute to each IMG with "dome" content, so as to circumvent the jsfiddle working on non-cat-images .. ignore that part - commented out below.
There are some fundamental things wrong with the design - keeping track of objects & references is key. Usage of "this" & "that" aren't helping in the current implementation (see comments to OP). Also, on another note, the usage of "toFixed(2)" is not really required IMHO and you can shorten "o = o + 0.1" to "o += 0.1".
JS:
var fader = {
target: document.getElementsByTagName('img'),
interval: [],
speed: 25,
default_opacity: 1,
init: function() {
this.bindEvents();
},
// Get element's opacity and increase it up to 1
fadeIn: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fI: "+idx+" "+element_opacity);
this.default_opacity = element_opacity.toFixed(2);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) < 1) {
element_opacity = element_opacity + 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
}
}, that.speed);
},
// Get current opacity and decrease it back to the default one
fadeOut: function(element) {
var element_opacity = this.getOpacity(element),
that = this,
idx = element.getAttribute('data-idx');
console.log("fO: "+idx+" "+element_opacity);
this.interval[idx] = setInterval(function() {
if (element_opacity.toFixed(2) > that.default_opacity) {
element_opacity = element_opacity - 0.1;
element.style.opacity = element_opacity.toFixed(2);
} else {
clearInterval(that.interval[idx]);
element.removeAttribute('style');
}
}, that.speed);
},
// Get opacity of an element using computed styles
getOpacity: function(element) {
var styles = window.getComputedStyle(element),
opacity = parseFloat(styles.getPropertyValue('opacity'));
return opacity;
},
bindEvents: function() {
var that = this, count = 0;
for (var i in this.target) {
// the whole "dome" is just a fsfiddle hack - otherwise it sees 7 images instead of 4!
//if( this.target[i].alt == "dome" ){
console.log("COUNT: "+count);
this.target[i].setAttribute('data-idx',count);
this.target[i].onmouseover = function() {
that.fadeIn(this);
}
this.target[i].onmouseout = function() {
that.fadeOut(this);
}
count++;
//}
}
}
};
fader.init();
I'm using a simple chunk of code (based of 'ScrollTo Posts with jQuery' which allows you to click a next/previous link and it'll jump through to the top of each post.
I have my HTML structure so it goes post > image > post > image etc.
I'm wondering if it's possible that if you click the next/previous button, it scrolls to the next post as normal, but it hangs/hovers over the images/div inbetween? So it eventually completes it's scroll, but slows down over the divs inbetween.
Here's my jQuery code:
$(function () {
function a(f) {
var b, e, c = [],
d = $(window).scrollTop(),
g = $('.section-slide');
g.each(function () {
c.push(parseInt($(this).offset()['top'], 10))
});
for (e = 0; e < c.length; e++) {
if (f == 'next' && c[e] > d) {
b = g.get(e);
break
}
if (f == 'prev' && e > 0 && c[e] >= d) {
b = g.get(e - 1);
break
}
}
if (b) {
$.scrollTo(b, {
duration: 1400
})
}
return false
}
$('#next,#prev').click(function () {
return a($(this).attr('id'))
});
$('.scrolltoanchor').click(function () {
$.scrollTo($($(this).attr('href')), {
duration: 1400
});
return false
})
});
Assuming your structure will remain static: post -> image -> post -> image etc. you can accomplish this by finding the previous / next image to the post you will be scrolling to, and scrolling to it first, then use the onAfter callback/setting from the $.scrollTo plugin to fire a secondary scroll after a predefined setTimeout like this:
$(function () {
function scroll(direction) {
var scroll, scrollImage, i,
positions = [],
here = $(window).scrollTop(),
collection = $('.post');
collection.each(function () {
positions.push(parseInt($(this).offset()['top'], 10));
});
for (i = 0; i < positions.length; i++) {
if (direction == 'next' && positions[i] > here) {
scroll = collection.get(i);
// Find Image Before Post
scrollImage = $(scroll).prev('.image').get(0);
break;
}
if (direction == 'prev' && i > 0 && positions[i] >= here) {
scroll = collection.get(i - 1);
// Find Image After Post
scrollImage = $(scroll).next('.image').get(0);
break;
}
}
if (scroll) {
// Check if Scroll Image Exists
if (scrollImage){
// Scroll with Image Delay
$.scrollTo(scrollImage, {
duration: 750,
onAfter: function(){
setTimeout(function(){
$.scrollTo(scroll, {
duration: 750
});
}, 1000); // Change the Delay to Increase / Decrease the Hover
}
});
} else {
$.scrollTo(scroll, {
duration: 750
});
}
}
return false;
}
$("#next,#prev").click(function () {
return scroll($(this).attr('id'));
});
$(".scrolltoanchor").click(function () {
$.scrollTo($($(this).attr("href")), {
duration: 750
});
return false;
});
});
You can find an updated fiddle here: http://jsfiddle.net/hfg2v/2/
I hope this helps.
This is happening because you're using a parallax scrolling library (Stellar.js), which makes different elements scroll at different speeds.
A possible fix would be to scroll at a higher speed when no element is in the current viewport until the edge of the next element is just off the screen, then immediately scroll at the original scrolling speed until there are no elements in the viewport again, and keep repeating this until you reach the desired scroll offset.
Edit:
Sorry, something came up while I was writing my answer and I didn't have time to finish the code.
However, after working on it for some time I'm starting to think that my proposed solution wouldn't work. I was thinking something along those lines:
$(window).scrollTo(640, {onAfter: function () {
var scrollRatio = 3;
var distance = 855 - 640;
$(window).scrollTo(855, {
easing: 'linear',
duration: distance * scrollRatio / speed,
onAfter: function () {
var scrollRatio = 1;
var distance = 1200 - 855;
$(window).scrollTo(1200, {
easing: 'linear',
duration: distance * scrollRatio / speed,
onAfter: function () {
var scrollRatio = 3;
var distance = 1280 - 1200;
$(window).scrollTo(1280, {
easing: 'linear',
duration: distance * scrollRatio / speed
});
}
});
}
});
}});
If you paste the previous code in the website provided in the question (http://dev.du.st/field-station/), you'll be taken to the first element, and it will attempt to scroll you to the next one using the method I described. I hardcoded the offset values because I was still experimenting with it. However, I don't think this approach would work since it still feels off. This is because changing instantly speed in the middle of the animation will always be noticeable.
Right now, I think the best way you can mitigate the slow-scrolling feel that parallax scrolling is causing is by using a different easing function. After all, making the background pictures slower, is exactly what you're using parallax scrolling for.
The following code, when ran in your website, would make all animations use 'easeOutCirc' for their easing function by default, after some experimenting, I found it to be the one that makes the scrolling feel least odd:
// Add the jQuery-easing plugin, needed for the more sophisticated easing functions.
$.getScript('//cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js');
// Set easeOutCirc as the default easing.
jQuery.easing.def = 'easeOutCirc';
You can find more easing functions at this website
Once you're done experimenting, if you do decide on using an easing (you can use different ones for scrolling up and down), then you should probably keep the default easing as is, and just change the easing in the scroll animation by adding {easing: EASING_NAME} to your options hash in the scrollTo function. So your code will look something like this:
$.scrollTo($($(this).attr("href")), {
duration: 750,
easing: 'easeOutCirc'
});
I had an idea for like a bus window as a fixed frame, about 800px wide, with a parallax city with the content on billboards spaced out so when you scroll between them it allows the parallax to look like bus is moving. The content will be much bigger than the window like a sprite and I'll put forward and back buttons that will scrollBy (x amount, 0). I have a working parallax script and a rough cityscape of 3 layers that all work fine.
I have hit a wall. I am trying to clear a scrollBy animation after it scrolls 1000px. Then you click it again and it goes another 1000px. This is my function.
function scrollForward() {
window.scrollBy(5,0);
scrollLoop = setInterval('scrollForward()',10);
}
So far I can only clear it when it gets to 1000. I tried doing 1000 || 2000 ect but after the first one it goes really fast and won't clear.
Excelsior https://stackoverflow.com/users/66580/majid-fouladpour wrote a great piece of code for someone else with a different question. It wasn't quite right for what the other guy wanted but it is perfect for me.
function scrollForward() {
var scrolledSoFar = 0;
var scrollStep = 75;
var scrollEnd = 1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar >= scrollEnd ) clearInterval(timerID);
}, 10);
}
function scrollBack() {
var scrolledSoFar = 0;
var scrollStep = -75;
var scrollEnd = -1000;
var timerID = setInterval(function() {
window.scrollBy(scrollStep, 0);
scrolledSoFar += scrollStep;
if( scrolledSoFar <= scrollEnd ) clearInterval(timerID);
}, 10);
}
Now for step two figuring out how to put this content animation behind a frame.
Not quite sure what your asking here. Perhaps you could provide more relevant code?
I do see a potential issue with your code. You call setInterval('scrollForward()', 10) which will cause scrollForward to be called every 10ms. However, each of those calls to scrollForward will create more intervals to scrollForward creating a sort of explosion of recursion. You probably want to use setTimeout or create your interval outside of this function.
Also, as an aside you can change your code to simply: setInterval(scrollForward, 10). Removing the quotes and the parens makes it a littler easier to read and manager. You can even put complex, lambda functions like:
setInterval(function() {
scrollForward();
// do something else
}, 10);
edit:
So if you know that scrollForward moves the item 10px, and you want it to stop after it moves the item 1000px, then you simply need to stop it has moved that much. I still don't know how your code is actually structured, but it might look something like the following:
(function() {
var moved_by = 0;
var interval = null;
var scrollForward = function() {
// move by 10px
moved_by += 10;
if (moved_by === 1000 && interval !== null) {
clearInterval(interval);
}
};
var interval = setInterval(scrollForward, 10);
})();
If you want to clear it after 1000 or 2000, you simply adjust the if statement accordingly. I hope that helps.
I'm kinda new to jQuery and JS and i'm trying to make a text-size adjustable to the screen width on a mobile site
I search the web for an answer and came up with this solution
(function($) {
$.fn.textfill = function(maxFontSize) {
maxFontSize = parseInt(maxFontSize, 10);
return this.each(function(){
var ourText = $("span", this),
parent = ourText.parent(),
maxHeight = parent.height(),
maxWidth = parent.width(),
fontSize = parseInt(ourText.css("fontSize"), 10),
multiplier = maxWidth/ourText.width(),
newSize = (fontSize*(multiplier-0.1)),
textHeight = ourText.height();
if(newSize > 35){
ourText.css("fontSize",35);
}
else{
ourText.css(
"fontSize",
(maxFontSize > 0 && newSize > maxFontSize) ?
maxFontSize :
newSize
);
}
});
};
})(jQuery);
$(document).ready(function() {
$('.jtextfill').textfill({ maxFontPixels: 36 });
});
but then it was only when the page first reloads and not on windows resizes, so I entered it all into a function and added
$(document).ready(function () {
resizeTextHeb();
$(window).resize(function() {
resizeTextHeb();
});
});
but now on the iPhone/iPad/Android etc. it doesnt reload the function when the screen rotates.
doesnt the screen rotation acts as a window resize? what can I do to make it work?
thanks.
thanks
You may use media queries for that, without using js.
your function name was "resizeTextHeb()" on the main question, any function name issues!?
i suggest u to use google chrome js console for error reports or the error console on the FF browser. , anyway, if it does alert u, so the main issue is with ur function, not the orientation method.