I've got a series of rectangles drawn on a canvas and use a scroll event listener to move the boxes up and down.
I'm trying to add in some validation so that the boxes cannot be scrolled past a certain point.
Due to the acceleration, the scroll values don't always increment by 1, so when scrolling quickly, sometimes my validation kicks in too early.
Any ideas how to solve this?
So in my event listener I have:
lScroll += e.deltaY;
if (lScroll > 0) {
canScroll = false;
lScroll = 0;
} else {
canScroll = true;
}
https://jsfiddle.net/kr85k3us/3/
Please check if this is working for you: https://jsfiddle.net/kr85k3us/4/
I tested it and it should work, but perhaps you can move your mousewheel faster :)
if (!canScroll && lScroll == 0) {
var first = Object.keys(boxes)[0];
if (boxes[first]['y'] < 10) {
var delta = 10 - boxes[first]['y'];
Object.keys(boxes).forEach(function(k){ boxes[k]['y'] += delta; });
}
}
This is the piece of code I added. If you can't scroll and lScroll is 0, it means that we reached the top. Next, I check if the first box is where it should be. If not (i.e. boxes[first]['y'] < 10) then it adjusts all the boxes' y position.
Related
I have looked around to see if this has been covered, but no dice.
I'm building a website using HTML5, CSS3 (+ animations), bootstrap, Vanilla JS and jQuery. The behavior I'm looking to induce for the site is one where a user visits a landing page and the scroll bar is situated at the top. Then, upon scrolling down past a certain point, a completely different screen takes over.
Here's the (maybe) tricky part:
I want for a visitor to be able to scroll all the way up on this 2nd screen as high as the scrollbar can go. Only once the scroll bar is at the top and they try try to scroll up past the top of the current window, that the original, first screen comes back in to play (while still persisting the progress they made on the 2nd screen if they decide to scroll back down).
Negative scrollTop/browser/window heights to trigger an event in which a user can navigate between pages using a maxed out up-top scrollbar? (& should I use a framework?) Much appreciated!
You could duplicate elements while scrolling,
I made a plunker to give you an idea
jQDocument.on("scroll", () => {
if(jQDocument.scrollTop() < 200) {
//Duplicate elements on top while scrolling up
let topScreen = detectTopScreen()
let indexTopScreen = getIndex(topScreen)
let screenIndexToDuplicate
if(indexTopScreen > 0) {
screenIndexToDuplicate = indexTopScreen - 1
} else {
screenIndexToDuplicate = maxIndex
}
let screenToPrepend = originalLoopDiv.children().eq(screenIndexToDuplicate).clone()
loopDiv.prepend(screenToPrepend)
if(loopDiv.children().length > 6) {
loopDiv.children().eq(loopDiv.children().length - 1).remove()
}
}
if(jQDocument.scrollTop() + jQWindow.outerHeight() > jQBody.outerHeight() - 200) {
//Duplicate elements on bottom while scrolling down
let bottomScreen = detectBottomScreen()
let indexBottomScreen = getIndex(bottomScreen)
let screenIndexToDuplicate
if(indexBottomScreen < maxIndex) {
screenIndexToDuplicate = indexBottomScreen + 1
} else {
screenIndexToDuplicate = 0
}
let screenToAppend = originalLoopDiv.children().eq(screenIndexToDuplicate).clone()
loopDiv.append(screenToAppend)
if(loopDiv.children().length > 6) {
loopDiv.children().eq(0).remove()
}
}
})
I currently have a slick slider on top of my page that should change slides on scroll, and after a certain point it should go back to normal scroll behaviour.
What I want to do is to initially lock the scroll (yeah, I know that changing scroll behaviour is a terrible thing for UX, but this was a mandatory request that came directly from the client), check the amount of times the scroll event was fired, check if it was up or down, and then switch slides accordingly.
My code currently looks like this:
$('body').on('DOMMouseScroll mousewheel', function(e) {
var viewportHeight = $(window).height(),
firstWaypoint = viewportHeight / 2,
secondWaypoint = viewportHeight,
thirdWaypoint = 3/2 * viewportHeight,
unlockPoint = viewportHeight * 2,
$slider = $('.home-hero__inner__text');
if (/* Y scroll position < thirdWaypoint lock scroll */) {
e.preventDefault();
} else if (/* If Y scroll position < secondWaypoint show first slide */) {
$slider.slick('slickGoTo', 0);
} else if (/* If Y scroll position > secondWaypoint show second slide */) {
$slider.slick('slickGoTo', 1);
} else if (/* If Y scroll position > thirdWaypoint show third slide */) {
$slider.slick('slickGoTo', 2);
} else {
// Unlock scroll
}
});
The thing is that as I currently lock scroll capabilities using preventDefault(), I can no longer check my page scrollTop() position since it is always at the top, returning a 0 value.
Is there a way to check how many pixels should've been scrolled on normal behaviour, and whether it was up or down? 'cause I need to keep a track of the assumed scroll position to trigger each step of the if statement.
You could just check if the wheelDelta is positive or negative
$(document).on('DOMMouseScroll mousewheel', function(e) {
var moved = e.originalEvent.wheelDelta || e.originalEvent.detail * -1 || 0;
if (moved > 0) {
// scrolled up
} else if (moved < 0) {
// scrolled down
}
});
FIDDLE
I would like to use Bootstrap slider to represent 3 lengths of axes whose sum doesn't change (the max value of the slider corresponds to this sum).
So I have 2 cursors on bootstrap slider and the 3 intervals represent these lengths.
Here's an example: bootstrap with two cursors
My issue is that I would like to stop dragging the second cursor (on the right) when it is equal (or nearly with a fixed step) to the first one (on the left) and inversely for the first cursor.
I saw there's an slide stop event but this doesn't seem to be the same thing.
I have surely to modify the bootstrap-slider.js source but I don't know how to do for implementing this specific functionality.
It would be like:
slider.on("slide", function(slideEvt) {
if ((cursor2.value - cursor1.value) < step)
{ this.stopSlide();}
});
Using slides events it's possible. The idea is to see which cursor is fixed, and when its value change, block the slide movement, by setting value.
Didn't find a cleaner way to block slide event...
Working code :
var slider = new Slider("#slider1");
var initPos,
fixedCursor,
fixedCursorPos;
slider.on("slideStart", function(slideEvt) {
initPos = slideEvt;
fixedCursor = null;
fixedCursorPos = null;
});
slider.on("slide", function(slideEvt) {
if (initPos[0] !== initPos[1] && (slideEvt[0] !== initPos[0] || slideEvt[1] !== initPos[1])) {
if (fixedCursor == null) {
fixedCursor = (slideEvt[0] === initPos[0] ? 0 : 1);
fixedCursorPos = slideEvt[fixedCursor];
}
if (fixedCursorPos !== slideEvt[fixedCursor]) {
slider.setValue([fixedCursorPos, fixedCursorPos], false, false);
}
}
});
Working JS Bin : http://jsbin.com/kopakiciti/1/edit?html,js,output
I'm having an issue getting a value from my script make a change on my website. What I'm trying to accomplish, is to allow users to scroll through some elements on my page, going left or right. Keep in mind I'm not very good with JavaScript, so here is what I have so far.
window.onload = makeActive;
function makeActive() {
var showCase = document.getElementById("showcase");
var slides = showCase.getElementsByTagName("div");
var mouseDown = false;
if(window.addEventListener) {
showCase.addEventListener('mousedown',startDrag,false);
document.body.addEventListener('mousemove',drag,false);
document.body.addEventListener('mouseup',stopDrag,false);
}
else if(window.attachEvent) {
showCase.attachEvent('onmousedown',startDrag);
document.body.attachEvent('onmousemove',drag);
document.body.attachEvent('onmouseup',stopDrag);
}
function startDrag(e) {
mouseDown = true;
}
function drag(e) {
if(mouseDown == true) {
for(var i=0; i < slides.length; i += 1) {
slides[i].style.left -= e.clientX + "px";
}
}
}
function stopDrag(e) {
mouseDown = false;
}
}
So basically what this does, is it selects all of the div elements within my showCase element. It proceeds to check whether or not my mouse is hovering over it, and if I am, it will check to see if my mouse is down, if my mouse button is down, it will check for my mouse to move left or right. This all works fine, but the issue comes into play when I try to cycle through my slides, when I try to do any form of an equation to the slides, it gives me a parse error. I think this is because when i try to reference slides[i].style.left comes out as a string. I've tried parsing it to a numeric form, and
stripping it of characters, but that doesn't seem to work. I've also tried using "leftoffset" to grab the value, but no matter what I do, it either gives me an error that is "style.left is undefined", "NaN", or "parse error: declaration dropped". Is there an easier way to do this?
I believe it's this line:
slides[i].style.left -= e.clientX + "px";
If slides[i].style.left has a value like 10px you can't substract an integer value. You need to remove the "px" first:
var leftPos = 0;
if(slides[i].style.left) {
leftPos = slides[i].style.left.toString().replace(/(px)/i, "");
}
slides[i].style.left = (leftPos - e.clientX) + "px";
Is there a way to disable or detect that wheel events are from the "inertia" setting on a Mac?
I'd like to be able to tell the difference between real events and the others...or disable that kind of scrolling for a particular page.
I found a solution that works really well for this. Below is some pasted code from my project. It basically comes down to this logic:
A scroll event is from a human when ANY ONE of these conditions are true:
The direction is the other way around than the last one
More than 50 milliseconds passed since the last scroll event (picked 100ms to be sure)
The delta value is at least as high as the previous one
Since Mac spams scroll events with descreasing delta's to the browser every 20ms when inertial scrolling is enabled, this is a pretty failsafe way. I've never had it fail on me at least. Just checking the time since the last scroll won't work because a user won't be able to scroll again if the "virtual freewheel" is still running even though they haven't scrolled for 3 seconds.
this.minScrollWheelInterval = 100; // minimum milliseconds between scrolls
this.animSpeed = 300;
this.lastScrollWheelTimestamp = 0;
this.lastScrollWheelDelta = 0;
this.animating = false;
document.addEventListener('wheel',
(e) => {
const now = Date.now();
const rapidSuccession = now - this.lastScrollWheelTimestamp < this.minScrollWheelInterval;
const otherDirection = (this.lastScrollWheelDelta > 0) !== (e.deltaY > 0);
const speedDecrease = Math.abs(e.deltaY) < Math.abs(this.lastScrollWheelDelta);
const isHuman = otherDirection || !rapidSuccession || !speedDecrease;
if (isHuman && !this.animating) {
this.animating = true; // current animation starting: future animations blocked
$('.something').stop().animate( // perform some animation like moving to the next/previous page
{property: value},
this.animSpeed,
() => {this.animating = false} // animation finished: ready for next animation
)
}
this.lastScrollWheelTimestamp = now;
this.lastScrollWheelDelta = e.deltaY;
},
{passive: true}
);
There's one caveat by the way: Mac also has acceleration on the scrolling, i.e.: at first, the delta value is higher for each successive event. It seems like this does not last more than 100ms or so though. So if whatever action/animation you are firing as a result of the scroll event lasts at least 100ms and blocks all other actions/animations in the meantime, this is never a problem.
Yes and no.
You can use touchdown/up, and scroll as events to look for the page moving about but those won't trigger if the OS is doing an inertial scroll. Fun, right?
One thing that you can continually detect, however, is window.pageYOffset. That value will keep changing while an inertial scroll is happening but won't throw an event. So you can come up with a set of timers to keep checking for an inertial scroll and keep running itself until the page has stopped moving.
Tricky stuff, but it should work.
Oh how is this issue killing me :/
I'm in the process of creating "endless" scrolling large file viewer.
To make situation worse, this editor is embedded in page that has its own scroll bar, because its bigger than one screen.
U use overflow-x scroll for horizontal scroll, but for vertical scroll i need current line highlighter (as seen in most modern IDEs) so i'm using jquery mousewheel plugin, and scrolling moving content for line height up or down.
It works perfectly on ubuntu/chrome but on MacOS Lion/Chrome sometimes, and just sometimes,
when you scroll, it doesn't prevent default scroll on the editor element, and event propagates "up" and page it self starts to scroll.
I cant even describe how much annoying that is.
As for inertial scroll it self, i successfully reduced it with two timers
var self = this;
// mouse wheel events
$('#editorWrapper').mousewheel(function (event, delta, deltax, deltay) {
self._thisScroll = new Date().getTime();
//
//this is entirely because of Inertial scrolling feature on mac os :(
//
if((self._thisScroll - self._lastScroll) > 5){
//
//
// NOW i do actual moving of content
//
//
self._lastScroll = new Date().getTime();
}
5ms is value i found to have most natural feel on my MacBook Pro, and you have to scroll mouse wheel really fast to catch one of those..
Even still, sometimes on Mac listener on mac wrapper doesn't prevent default, and page scrolls down.
Well, (I might be wrong), I think that the "inertia" settings on the Mac are all computed by the system itself, the browser, or any program for that matter would just think that the user is scrolling quickly, rather than slowing down.
I'm not sure about other browsers, but the following event fires during inertial scroll on my Chrome, FF, Safari (mac):
var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel";
function scrollEE (e) {
console.log(e);
}
window.addEventListener(mousewheelevt, scrollEE, false);
I had a big problem with an object animating based on scroll position after the scroll had completed, and the inertial scroll was really messing me around. I ended up calculating the velocity to determine how long the inertial scroll would last and used that to wait before animating.
var currentY = 0;
var previousY = 0;
var lastKnownVelocity = 0;
var velocityRating = 1;
function calculateVelocity() {
previousY = currentY;
currentY = $('#content').scrollTop();
lastKnownVelocity = previousY - currentY;
if (lastKnownVelocity > 20 || lastKnownVelocity < -20) {
velocityRating = 5;
} else {
velocityRating = 1;
}
}
$('#content').scroll(function () {
// get velocity while scrolling...
calculateVelocity();
// wait until finished scrolling...
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
// do your animation
$('#content').animate({scrollTop: snapPoint}, 300);
}, 300*velocityRating)); // short or long duration...
});
There's a library that solves this problem.
https://github.com/d4nyll/lethargy
After installing it, use it like this:
var lethargy = new Lethargy();
$(window).bind('mousewheel DOMMouseScroll wheel MozMousePixelScroll', function(e){
if(lethargy.check(e) === false) {
console.log('stopping zoom event from inertia')
e.preventDefault()
e.stopPropagation();
}
console.log('Continue with zoom event from intent')
});
Following your instructions with my type of code, I had to set timeout of 270ms on each action activated by scroll to get it all smooth, so if anyone is using something similar to me here is my example if not ignore it, hope it will help you.
//Event action
function scrollOnClick(height) {
$('html').animate({
scrollTop: height
}, 'fast');
return false;
};
// Scroll on PC
let timer = false;
$(window).on('mousewheel', function (event) {
if(timer != true) {
var heightWindow = $(window).height();
var heightCurrent = $(window).scrollTop();
if (event.originalEvent.wheelDelta >= 1) {
if (heightWindow >= heightCurrent) {
timer= true;
scrollOnClick(0)
setTimeout(function (){
timer = false;
},270);
}
} else {
if (heightCurrent < heightWindow) {
timer= true;
scrollOnClick(heightWindow)
setTimeout(function (){
timer = false;
},270);
}
}
}
});