I am trying to achieve an effect on a site I'm working on, where as the user scrolls vertically, an element slides horizontally the relative amount (i.e. if the user scrolls down by 10% of the document height, the particular element will move to the left by 10% of its own width).
The issue I'm having is that I'm using the touchstart and touchend events to calculate how much the user has scrolled vertically. However, on mobile devices (e.g. iPhone), the document will continue to scroll for a short while after the user finishes swiping and stops touching the screen.
Is there an alternative way to detect the document scrolling on mobile devices that will include the distance scrolled after the touch event ends?
Thanks!
My code:
var wave = $('.front-page__mobile-wave');
var height = $(document).height();
var width = wave.width();
var oneWidth = width/100;
var ts;
$(document).bind('touchstart', function (e){
ts = e.originalEvent.touches[0].clientY;
});
$('.front-page').bind('touchmove', function(e){
var te = e.originalEvent.changedTouches[0].clientY;
var scroll = $(window).scrollTop();
var percentHeight = scroll / height * 100;
var bgSlideForward = oneWidth * percentHeight;
var bgSlideBack = -oneWidth * percentHeight;
if(ts < te){
wave.css('left', bgSlideBack);
} else if (ts > te){
wave.css('left', -bgSlideForward);
}
});
Resolution was to use the scroll event instead of touchstart and touchmove and use variables to keep track of the current vs previous scrollTop, i.e.:
var wave = $('.front-page__mobile-wave');
var height = $(document).height();
var width = wave.width();
var oneWidth = width/100;
var lastScrollTop = 0;
$(window).scroll(function(e){
var scroll = $(window).scrollTop();
var percentHeight = scroll / height * 100;
var bgSlideForward = oneWidth * percentHeight;
var bgSlideBack = -oneWidth * percentHeight;
if(scroll > lastScrollTop){
wave.css('left', bgSlideBack);
} else if (scroll < lastScrollTop){
wave.css('left', -bgSlideForward);
}
lastScrollTop = scroll;
});
Related
I have the following code which works fairly okay to set a video's current time to the page's scroll position:
var vid0 = document.getElementById('v0');
var scrollpos = 0; var targetscrollpos = 0; vid0.pause();
window.onscroll = function(){ targetscrollpos = window.pageYOffset / 1700;};
setInterval(function(){
scrollpos += (targetscrollpos - scrollpos) * 0.25;
vid0.currentTime = scrollpos; vid0.pause();
}, 40);
My issue is that this is independent of the page height - so you set the acceleration with a number, instead of calculating either that number from the page height or connect the page height somehow to the video's length, so when the user scrolls the video ends exactly when the document is scrolled all the way to the end.
The playback speed in this case would depend on the document length, which is absolutely fine and expected, I just have no idea how to go about this issue.
Any ideas?
solved it.
var vid0 = document.getElementById('v0');
var scrollpos = 0; var targetscrollpos = 0; vid0.pause();
window.onscroll = function(){
var vrate = document.body.scrollHeight / (vid0.duration * 1.1);
targetscrollpos = window.pageYOffset / vrate;};
setInterval(function(){
scrollpos += (targetscrollpos - scrollpos) * 0.25;
vid0.currentTime = scrollpos; vid0.pause();
}, 40);
i am currently developing a website where i used some svg technology! However, i have some svg icons that are animated on scroll but this happens as soon as they appear on the screen! The problem is that i need these icons to appear on the screen and instead of getting triggered straight away i need it to wait for the user to scroll i little bit more and then start animating! this is the js i used:
$(window).scroll(function() {
drawLines();
});
function drawLines(){
$.each($(".red-line"), function(i, val){
var line = val;
drawLine($(this), line);
});
}
function drawLine(container, line){
var length = 0;
var pathLength = line.getTotalLength();
var distanceFromTop = container.offset().top - $(window).scrollTop();
var percentDone = 1 - (distanceFromTop / $(window).height());
length = percentDone * pathLength - 500;
line.style.strokeDasharray = [length,pathLength].join(' ');
}
Measure the scroll distance with .scrollTop() and set the drawLines() to start after a specified distance (in the example 200):
$(window).scroll(function (event) {
var sc = $(window).scrollTop();
if (sc>200){drawLines();}
});
I am trying to sync two scrollable DIVS scroll positions.
Methods followed :
Method - 1 : on-scroll event setting the scrollTop of other DIV.
problem : scroll event executed at the end and UI is sluggish in iOS safari.
Method - 2 : used setInterval to sync both scroll positions.
Problem : iOS does not execute timer functions during scroll,
so scroll positions synced at the end. Again this is more sluggish.
Tried, timers fix as mentioned in many blogs but still no grace.
Method -3 : Tried custom scrollbar, so iScroll and tried to sync both on scroll event,
Problem : this seems better but in iOS still it is sluggish!!!
Method -4 : Tried custom scrollbar, so iScroll and tried to sync both on scroll event,
Problem : Used iScroll but using timers rather depending on onScroll event,
But during touchmove, iOS is busy in providing animations
rather executing required timers till touchend.
Below code refers to this method. It is also sluggish.
var active = .., other = ...
// active : active Scrolling element
// other : Element to be in sync with active
window.setInterval(function () {
var y;
if (active) {
y = active.y;
} else {
return;
}
var percentage = -y / (active.scrollerHeight - active.wrapperHeight);
var oscrollTop = percentage * (other.scrollerHeight - other.wrapperHeight);
if (-other.maxScrollY >= toInt(oscrollTop)) {
other.scrollTo(0, -toInt(oscrollTop));
}
}, 20);
How can make syncing scroll positions of two scrollable DIVS smoother. Please suggest me something, it is irritating me.
relying on the scroll events (OPs method 1) is fine for a desktop implementation. the scroll event fires before the screen is updated. on mobile devices, especially iOS this is not the case. due to limited resources the scroll event only fires after the user completed (lifted his finger) the scroll operation.
implementing manual scrolling
to have a scroll event while the user scrolls on iOS requires to implement the scrolling manually.
register the touchstart event. and get the first touch:
var element1 = document.getElementById('content1');
var element2 = document.getElementById('content2');
var activeTouch = null;
var touchStartY = 0;
var element1StartScrollTop = 0;
var element2scrollSyncFactor = 0;
document.addEventListener('touchstart', function(event) {
event.preventDefault();
var touch = event.changedTouches[0];
if ( activeTouch == null ) {
// implement check if touch started on an element you want to be scrollable
// save a reference to the scrolling element for the other functions
activeTouch = touch;
touchStartY = touch.screenY;
// if scroll content does not change do this calculation only once to safe compute and dom access time while animating
calcSyncFactor();
}
});
function calcSyncFactor()
{
// calculate a factor for scroll areas with different height
element2scrollSyncFactor = (element2.scrollHeight - element2.clientHeight) / (element1.scrollHeight - element1.clientHeight);
}
update your scroll position on finger movement:
document.addEventListener('touchmove', function() {
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
var yOffset = touch.screenY - touchStartY;
element1.scrollTop = element1StartScrollTop + (0 - yOffset);
syncScroll();
break;
}
}
});
function syncScroll()
{
element2.scrollTop = Math.round(element1.scrollTop * element2scrollSyncFactor);
}
it is possible to add a check that starts the scrolling only after the user has moved his finger some pixels. this way if the user clicks an element the document will not scroll some pixels.
cleanup after the user lifts the finger:
document.addEventListener('touchend', touchEnd);
document.addEventListener('touchcancel', touchEnd);
function touchEnd(event)
{
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
// calculate inertia and apply animation
activeTouch = null;
break;
}
}
}
to have the scrolling feel more natuaral apply the iOS rubber band effect and inertia. calculate the velocity of the scroll by comparing the last touchMove yOffset with the one before. from this velocity apply an animation (for example css transition) that slowly stops the scrolling
see FIDDLE. see result on iOS. the fiddle only implements the solution for touch devices. for desktop devices use OP's method 1. implement a condition which checks which method to use depending on device.
how to apply inertia with css transitions
it would be possible to animate in javascript with requestAnimationFrame. a probably more performant way on mobile might be the use of css transformations or css animations. although an elements scroll position can not be animated with css.
change the structure of the html to.
div: container with overflow: hidden
div: content with position: absolute
depending on content size use css property -webkit-transform: translateZ(0) on content div. this will create a new layer with its own backing surface, which will be composited on the gpu.
implement the functions described above so that they animate the content's top position instend of scrollTop
var element1 = document.getElementById('content1');
var element2 = document.getElementById('content2');
var activeTouch = null;
var touchStartY = 0;
var element1StartScrollTop = 0;
var element2scrollSyncFactor = 0;
var offsetY = 0;
var lastOffsetY = 0;
document.addEventListener('touchstart', function(event) {
event.preventDefault();
var touch = event.changedTouches[0];
if ( activeTouch == null ) {
activeTouch = touch;
touchStartY = touch.screenY;
// use offsetTop instead of scrollTop
element1StartScrollTop = element1.offsetTop;
// if scroll content does not change do this calculation only once to safe compute time while animating
calcSyncFactor();
// cancel inertia animations
element1.style.webkitTransition = 'none';
element2.style.webkitTransition = 'none';
}
});
function calcSyncFactor()
{
// calculate a factor for scroll areas with different height
// use the div's sizes instead of scrollTop
element2scrollSyncFactor = (element2.clientHeight - element2.parentNode.clientHeight) / (element1.clientHeight - element1.parentNode.clientHeight);
}
document.addEventListener('touchmove', function() {
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
lastOffsetY = offsetY;
offsetY = touch.screenY - touchStartY;
// use offsetTop instead of scrollTop
element1.style.top = (element1StartScrollTop + offsetY) + 'px';
syncScroll();
break;
}
}
});
function syncScroll()
{
element2.style.top = Math.round(element1.offsetTop * element2scrollSyncFactor) + 'px';
}
document.addEventListener('touchend', touchEnd);
document.addEventListener('touchcancel', touchEnd);
function touchEnd(event)
{
for ( var i = 0; i < event.changedTouches.length; i++ ) {
var touch = event.changedTouches[i];
if ( touch === activeTouch ) {
applyInertia();
activeTouch = null;
break;
}
}
}
when the user finishes scrolling and lifts his finger apply the inertia
function applyInertia()
{
var velocity = offsetY - lastOffsetY;
var time = Math.abs(velocity) / 150;
var element1EndPosition = element1.offsetTop + velocity;
element1.style.webkitTransition = 'top ' + time + 's ease-out';
element1.style.top = element1EndPosition + 'px';
element2.style.webkitTransition = 'top ' + time + 's ease-out';
element2.style.top = Math.round(element1EndPosition * element2scrollSyncFactor) + 'px';
}
the inertia is calculated from the velocity when the user lifted the finger. fiddle around with the values to get desired results. a rubberband effect could be implemented in this function aswell. to have no javascript involved applying css animations might be the trick. another way would be to register events for when the transitions finish. if the transition finishes and the scroll position is outside the container apply a new transition that animates the content back.
see FIDDLE. see result on iOS.
I want scroll page content on mouse scroll.
I have 5 images in page, at a time i want to show one image per screen.
If i scroll down second image showed be shown, if i scroll up previous image should be shown. Like wise until the last image.I have tried an example but have no idea how achieve this.
JavaScript:
var winHeight = $(window).height();
var prevHeight = 0;
var scrollCount = 0;
var docHeight = $(document).height();
$(document).ready(function(){
$(window).scroll(function(e){
console.log("in scroll top");
var top = $(window).scrollTop();
console.log("top - "+top);
if(top !=0 && top != docHeight){
if(top > prevHeight){
scrollCount = scrollCount+1;
}else{
scrollCount = scrollCount-1;
}
console.log("scroll count="+scrollCount);
$(window).scrollTop(winHeight*scrollCount);
prevHeight = top;
if(scrollCount < 0){
scrollCount = 0;
}
e.preventDefault();
}
});
My example is here http://jsbin.com/iwOsiFIY/1/
Just set the margins to what you want them to be for each image in CSS. Or a workaround is adding a bunch of "p" tags in HTML without actually adding a paragraph, The first way is the best. You might also need some JavaScript for resizing.
I have a div with overflow set to scroll which essentially streams data line by line off a file. I'd like to scroll automatically to the bottom of the div whenever the stream overflows, but without using a "Click here to scroll to bottom" button.
I already know of the scrollTop = scrollHeight solution, but that requires some kind of event trigger on the client's side. I don't want this element to be interactive; it should scroll by itself.
Is there any way to achieve this?
A lot of the scrollHeight implementations didn't work for me, offsetHeight seemed to do the trick.
Pretty sure that scrollHeight tries to move it to the bottom of the height of the static element, not the height of the scrollable area.
var pane = document.getElementById('pane');
pane.scrollTop = pane.offsetHeight;
There's no way to automatically scroll an element to the bottom. Use element.scrollTop = element.scrollHeight.
If you don't know when the element is going to resize, you could add a poller:
(function(){
var element = document.getElementById("myElement");
var lastHeight = element.scrollHeight;
function detectChange(){
var currentHeight = element.scrollHeight;
if(lastHeight != currentHeight){
element.scrollTop = currentHeight;
lastHeight = currentHeight;
}
}
detectChange();
setInterval(detectChange, 200); //Checks each 200ms = 5 times a second
})();
Some old code of mine with a running example that will stay at the bottom when new content is added, if the user scrolls it will not more it to the bottom.
var chatscroll = new Object();
chatscroll.Pane =
function(scrollContainerId)
{
this.bottomThreshold = 25;
this.scrollContainerId = scrollContainerId;
}
chatscroll.Pane.prototype.activeScroll =
function()
{
var scrollDiv = document.getElementById(this.scrollContainerId);
var currentHeight = 0;
if (scrollDiv.scrollHeight > 0)
currentHeight = scrollDiv.scrollHeight;
else
if (objDiv.offsetHeight > 0)
currentHeight = scrollDiv.offsetHeight;
if (currentHeight - scrollDiv.scrollTop - ((scrollDiv.style.pixelHeight) ? scrollDiv.style.pixelHeight : scrollDiv.offsetHeight) < this.bottomThreshold)
scrollDiv.scrollTop = currentHeight;
scrollDiv = null;
}