I'm trying to make something happen when my element has moved a certain left amount.
var box = $('#box');
box.css('left', '500px'); //This is moving the box. I have a transition set in my css
if (box.offset().left == 200){
alert("yes");
}
It doesn't alert yes, though. I tried outputting the offset().left position into the console, and it DOES reach 200. Am I just using inproper syntax?
Using elem.css('left','500px') will set the left of the element to 500px. As you are comparing with 200, it will never meet that condition. Even in the soultion provided below, you still con not reallt compare it with single value as the progress will never be one pixle at a time. In provided solution, range of 200 - 250 is given.
Use progress option of jQuery animate
Try this:
var box = $('#box');
var datatDiv = $('#data');
box.animate({
'left': '500px'
}, {
duration: 5000,
progress: function() {
var leftVal = box.offset().left
if (leftVal > 200 && leftVal < 250) {
datatDiv.text("YES");
} else {
datatDiv.text("NO");
}
}
});
#box {
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="box">BOX</div>
<br>
<div id='data'></div>
Fiddle here
I was able to accomplish what I needed through the position() function. I have a side scrolling image that moves to the left, and you control a snowball that must hop over trees. I needed to detect when the image had moved a certain distance to the left, which meant it collided with a tree in the image.
if (
(sideScroller.position().left < -960 && sideScroller.position().left > -1200 + mobileDelay) && (snowball.css('bottom') == '75px')
) {
hitTree()
}
mobileDelay was just some additional value for mobile devices, and hit tree just caused the snowball to evaporate when it hits a tree, lol. Position() works like a charm here.
I would just like to point out that Rayon's answer works as well, however I personally prefer to leave as much animation to CSS as possible, since in my experience it always runs smoother. Due to the large image, and the timed interval involved, using jQuery animation was not ideal in this situation.
Related
So I have two sections of content near the top of my page and I’d like for users who have scrolled down to near the top of the second section to get “scroll snapped” to the top of the second one once they have stopped scrolling.
I think it should be possible using jQuery but I haven’t been able to figure it out. Here are my examples:
Without my attempt: http://codepen.io/jifarris/pen/gaVgBp
With my broken attempt: http://codepen.io/jifarris/pen/gaVgQp
Basically I can’t figure out how to make it try scrolling to the spot only once, after scrolling has stopped. It’s kind of just freaking out.
I love how the recently introduced scroll snap points CSS feature handles scroll snapping and I’d almost prefer to use it – for the browsers that support it, at least – but it seems like it only works for items that take up 100% of the viewport height or width, and it seems like it’s for scrolling within an element, not the page itself.
The top section has a fixed height, so this really can be handled with pixel numbers.
And for reference, here’s the heart of the code from my attempt:
$(function() {
$(document).on('scroll', function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('html, body').animate({scrollTop: '356'}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
});
});
KQI's answer contains most of the steps required to create a well functioning section-scroll for use in your application/webpage.
However, if you'd just want to experiment yourself, developing your script further, the first thing you'll have to do is add a timeout handler. Otherwise your logic, and therefor scrollAnimation, will trigger every single pixel scrolled and create a buggy bouncing effect.
I have provided a working example based on your script here:
http://codepen.io/anon/pen/QjepRZ?editors=001
$(function() {
var timeout;
$(document).on('scroll', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
var top = $(document).scrollTop();
if (top > 255 && top < 455) {
$('body').animate({
scrollTop: '356'
}, 500);
$('body').addClass('hotzone');
} else {
$('body').removeClass('hotzone');
}
}, 50);
});
});
Good luck!
All right, there are couple of things you gonna have to deal with to get a good result: which are performance, call stack queue, easing.
Performance wise you should drop jQuery animate and use VelocityJs which gives a smoother transition, better frame per second (fps) to avoid screen glitches especially on mobiles.
Call stack: you should wrap whatever logic you have to animate the scrolltop with 'debounce' function, set the delay for let say 500mm and check the scrolling behavior. Just so you know, the 'scroll' listener your using is firing on each pixel change and your script will go crazy and erratic. (It is just gonna be a moment of so many calc at the same time. Debounce will fix that for you)
Easing: make the transition looks cool not just dry snappy movement.
Remember, 'easing' with Velocity starts with 'mina.' i.e.
'Mina.easingFnName'
Finally, your logic could be right, i am in my phone now cannot debug it but try to simplify it and work with a single problem at once, be like i.e.
If ( top > 380 ) // debounce(...)
Is it possible to slow down the speed of a draggable element?
I have build a simple slider with jQuery drag and drop. When the slider element (the draggable element) moves to certain positions a picture fades in. So if you move the draggable element not too fast it looks like you can handle a "picture animation" with the slider. Now, I want to slow down the draggable element. So the user never can drag the element too fast.
This is an example of my code.
$('.slider').mousemove(function(){
if($(this).position().left >= 0 && $(this).position().left <= 2 ) {
$('.slider_1').fadeIn();
$('.slider_2').fadeOut();
}
...
I hope someone can help me :)
Ah! finally an interesting jQuery question.
This can definitely be achieved. Below I've explained how. If you want to go straight to the demo, click here.
Let's assume your HTML is setup as follows:
<div id="slider">
<div id="bar"></div>
</div>
Where the bar is the actual thing you click and drag.
Now what you need to do is the following:
get the $('#bar').offset().left
explicitly specify the position of #bar when the draggable is dragged, using some extra variable SPEED
For example:
ui.position.left += (ui.offset.left - ui.originalPosition.left - leftOffset)*SPEED;
Then, you can use the $('#bar').offset().left in jQuery's .fadeTo() (or other) function to change the opacity of the image you are talking about.
This all seems rather trivial, but it's not. There are some problems when trying to implement this. For example:
When the slider reaches the maximum sliding distance, it should stop animating or be reset. You can do this in multiple ways but I think the easiest solution is to write a .mousedown / .mouseup listener which updates a variable dragging, that keeps track whether the user is still trying to drag #bar. If it's not, reset #bar. If it is, keep the slider at the maximum distance until .mouseup is fired.
Also, you must be careful with predefined borders.
The code I propose is the following:
// Specify your variables here
var SPEED = -0.6;
var border = 1; // specify border width that is used in CSS
var fadeSpeed = 0; // specify the fading speed when moving the slider
var fadeSpeedBack = 500; // specify the fading speed when the slider reverts back to the left
// Some pre-calculations
var dragging = false;
var slider = $('#slider');
var leftOffset = slider.offset().left + border;
var adjustedSliderWidth = 0.5*slider.width();
// the draggable function
$('#bar').draggable({
axis: "x",
revert : function(event, ui) {
$(this).data("draggable").originalPosition = {
top : 0,
left : 0
};
$('#image').fadeTo(fadeSpeedBack, 0);
return !event;
},
drag: function (event, ui) {
var barOffset = $('#bar').offset().left - leftOffset;
if (barOffset >= 0) {
if (barOffset < adjustedSliderWidth) {
ui.position.left += (ui.offset.left - ui.originalPosition.left - leftOffset)*SPEED;
} else {
if (!dragging) { return false; }
else { ui.position.left = adjustedSliderWidth; }
}
}
// fading while moving:
$('#image').fadeTo(fadeSpeed, (ui.position.left/adjustedSliderWidth));
// remove this if you don't want the information to show up:
$('#image').html(ui.position.left/adjustedSliderWidth
+"<br \><br \>"
+ui.position.left);
}
});
// the mouse listener
$("#bar").mousedown(function(){ dragging = true; });
$("#bar").mouseup(function(){ dragging = false; });
I've also implemented the revert option on draggable so the slider nicely returns to zero when the user releases #bar. Of course you can delete this if you want.
Now the variable that your whole question is about is the variable: SPEED.
You can specify the speed of dragging by specifying a number for this variable.
E.g.:
var SPEED = 0.0; // the normal dragging speed
var SPEED = 0.5; // the dragging speed is 1.5 times faster than normal
var SPEED = -0.5; // the dragging speed is 0.5 times faster than normal
So negative values give a slower dragging speed and positive values give a faster dragging speed.
Unfortunately (or actually: fortunately), it is not possible to change the speed of the mouse pointer. This because only the OS has control over the mouse coordinates and speed. Browsers cannot influence this. Personally I think it doesn't matter: moving the slider slower than normal is what you're trying to achieve, so you can ignore the mouse pointer.
To see all this in action, I've prepared a working jsFiddle for you:
DEMO
I hope this helps you out :)
I have a slight problem with Javascript (or jQuery?).
Its part of a gallery from codrops.com. Its just a small part of it. Its the part Im having trouble with. (see it at kuglerdesign.com/gallery.html - still a site under construction)
// stops slideshow
_stopSlideshow = function( pause ) {
if( Gamma.isAnimating ) {
return false;
}
Gamma.isAnimating = true;
clearTimeout( Gamma.slideshowtimeout );
if( !pause ) {
Gamma.slideshow = false;
Gamma.svplay.removeClass( 'gamma-btn-sspause' );
Gamma.svMargins = Gamma.settings.svMarginsVH;
_toggleControl( Gamma.svclose, 'on' );
_toggleControl( Gamma.svnavprev, 'on', { left : 20 } );
_toggleControl( Gamma.svnavnext, 'on', { right : 60 } );
_svResizeImage( function() {
Gamma.isAnimating = false;
} );
However, there is a problem with the arrows that allow you to see previous and next picture. It happens when you tell the gallery to start the slideshow (which moves the arrows to -60px left and right (not seen here), respectively) and then stop the slideshow. The Javascript reads that it should move the arrows back into position to 60px right and 20px left.
But, on a mobile 60px is too much, and on a desktop 20px(for mobile) is too little (at start it uses CSS class, where it is different for mobile and desktop).
I was hoping to be able to write an if statement that would say if the screen is smaller than 760px, it would move by 20px, otherwise it would take 60px.
I wrote something like this:
_toggleControl( Gamma.svnavnext, 'on', { if (screen.width < 760) {right : 20;} else {right :60 } );
(rest of code still same)
Would that ever have a chancce of working without adding too much more code?
Short answer:
Put in a simple 'if' statement and abstract the values:
if (window.innerWidth < 760) {
var offsetLeft = 8;
var offsetRight = 20;
} else {
var offsetLeft = 20;
var offsetRight = 60;
}
_toggleControl( Gamma.svnavprev, 'on', { left : offsetLeft } );
_toggleControl( Gamma.svnavnext, 'on', { right : offsetRight } );
Long (more correct) answer:
Use a relative unit of space, like em or %. That way it'll always show correctly regardless of screen size. Using the fixed unit px, you'll have to hard-code different amounts for each possible screen size (which, nowadays, varies a lot what with tablets and phones in all shapes and sizes).
It looks like all of your CSS is currently using the px units and made responsive using JavaScript (I see some percentages but those look set using JavaScript as well). If you need an introduction to using scalable units of space (you should always use scalable units unless you have no other option, which rarely happens), I direct you to this excellent beginner's article by Kyle Schaeffer.
Alternatively, detect the device's screen dimensions and use a different CSS file based on that. This depends on your layout, of course, as it is increasingly more common to design in a mobile-first approach and simply scale up to desktop sizes using the aforementioned relative units. This design method removes the need of separate layouts for different devices.
Also, if all you're trying to do is hide the arrows temporarily, I suggest simply setting display: none in CSS using JavaScript to hide them and display: block to make them reappear. A simple way of doing this would be this:
var arrows = document.getElementByClassName('arrow'); // assuming the arrows have a common class name called 'arrow'
for (i=0; i<arrows.length; i++) {
arrows[i].style.display = 'none';
}
You can use this approach: Live Detect Browser Size - jQuery / JavaScript
Followed by updating the _toggleControl function within a check of the browser size.
I'm trying to accomplish the effects on this site, but I only need to move around a few things based on scrolling. My goal is for the basketball to go into the box, then after scrolling down a little bit the box goes onto the truck and the objects all go together horizontally across the page. Also, I want this to happen when you scroll down, not when you scroll up like in the current fiddle.
Here my current Fiddle, originally based on this Fiddle. Below is the js.
var fixedElement = false;
var changingMoment = 150;
$(window).scroll(function() {
// floatingContentMark lets us know where the element shall change from fixed to relative
// and vice versa
var distanceFromTop = $("#floatingContentMark").offset().top - $(this).scrollTop();
if ((distanceFromTop <= changingMoment && !fixedElement) ||
(distanceFromTop >= changingMoment && fixedElement))
{ // either we came from top or bottom, same function is called
fixedElement = !fixedElement;
$('#box').trigger('fixElement');
}
});
$('#box').bind('fixElement', function() {
if ($(this).css('position') != 'fixed') {
$(this).css('position', 'fixed') ;
$(this).css('top', changingMoment) ;
}
else {
$(this).css('position', 'relative') ;
$(this).css('top', 'auto') ;
}
}) ;
I think that your project will be a lot easier if you use a framework or jQuery Plugin for custom/parallax scrolling. A quick google search brings up several that will easily do what you want:
skrollr
scrollorama
scrollpath
sequence.js
stellar.js
scrolly
NikeBetterWorld
Scrolling Parallax
jarallax
superscrollarama
For some inspiration, check out the pages listed here.
My problem
I am making a vertical website for a client who wishes to have the window "snap" to the nearest page when most of the element is visible in the viewport. So, if the page is 85% visible, it should scroll to be 100% visible.
My problem is that occasionally when scrolling all the way to the top or bottom of the viewport, the viewport will "stick" to the first or last element, preventing a few scroll events and causing a highly noticeable flicker.
A working fiddle is here: http://jsfiddle.net/RTzu8/1/
To reproduce the error, use the scrollbar to scroll to the bottom of the page. Then, scroll up with your mousewheel. You should see the flicker. Sometimes it takes a few refreshes or attempts, but the issue is highly reproducible.
I'm at a loss as to what could be causing this issue. See below for a run-down of my code and what I have tried to prevent it so far.
My code
To accomplish my snapping, I needed to detect whether an element was a certain percentage visible. So, I added a jQuery function, isNearScreen, below. I have thoroughly tested this function, and as far as I can tell it returns accurate results.
//Modification of http://upshots.org/javascript/jquery-test-if-element-is-in-viewport-visible-on-screen
//Returns "true" if element is percent visible within the viewport
$.fn.isNearScreen = function(percent){
var offset = 1 - percent;
var win = $(window);
var viewport = {
top : win.scrollTop()
};
viewport.bottom = viewport.top + win.height();
var bounds = this.offset();
bounds.bottom = bounds.top + this.outerHeight();
bounds.top = bounds.top;
//If the element is visible
if(!(viewport.bottom < bounds.top || viewport.top > bounds.bottom)){
//Get the percentage of the element that's visible
var percentage = (viewport.bottom - bounds.top) / this.height();
//If it's greater than percent, but less than 1 + (1 - percent), return true;
return (percentage > (1 - offset) && percentage < (1 + offset));
}
return false;
};
I then created a snap function, which makes use of Underscore.js's _.debounce function, to only fire on the trailing end of continuous scroll events. It fires after a 500ms timeout, and I am fairly (though not 100%) convinced that it is firing correctly. I have not been able to reproduce console logs that would indicate multiple concurrent firings.
//Snaps the screen to a page after scroll input has stopped arriving.
var snap = _.debounce(function(event){
//Check each page view
$.each($('.page-contents'), function(index, element){
//If the page view is 70% of the screen and we are allowed to snap, snap into view
if($(element).isNearScreen(0.7)){
$('html,body').animate({
scrollTop: $(element).offset().top
}, 300);
}
});
}, 500);
Finally, I bind to the window's scroll event
$(window).on('scroll', snap});
The (extremely simplified) HTML:
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
<div class="page">
<div class="page-contents"></div>
</div>
and CSS:
.page{
height: 750px;
width: 100%;
margin: 10px 0;
background: gray;
}
.page-contents{
height: 100%;
width: 100%;
}
What I've tried
I have tried the following, with no success:
Setting a boolean, 'preventSnap', on the window, checking its state, and only firing the animate portion of snap if it is set to false. After animation, set it to true, then set it to false after 500ms (which should in theory prevent double firings).
Calling .stop() on the element before running the snap animation.
Calling event.preventDefault() on the scroll event before running the animation.
Reducing and increasing my _.debounce delay. Interestingly, a lower _.debounce delay (200-300ms) seems to aggravate the problem and a higher _.debounce delay (1000ms) seems to fix it. This is not an acceptable solution, however, as it feels "long" waiting 1sec for the page to "snap".
Changing the heights of the elements
If there is any other information I can provide, please let me know. I'm at a loss!
I think this is a combination of events and how _.debounce works. I noticed in the fiddle (in Chrome) that the elements were 'jitterring' long after the snap finished. If you put a console log in the snap event handler you can see it's constantly being called after a snap even with no scroll inputs.
This must be the scroll animation itself setting off the snap, I tried to set a flag to prevent dual snapping and clearing the flag after the animation was finished -- however that didn't work I think because _.debounce is queuing the event to happen later (after the animation finishes and clears the flag).
So what does work is to add this as the start of the snap handler:
var nosnap = false;
var snap = _.debounce(function(event){
// Don't snap if already animating ...
if (nosnap) { nosnap = false; return; }
nosnap = true;
Fiddle
That prevents the animation directly firing the next snap event -- however that's going to cause issues if you scroll again during the animation.
So, that's a bit of a hack. Ideally you want to be able to tell what's causing the scroll event and react accordingly but there's no easy way to do that.
I absolutely think you need to stop the animation when handling a second scroll event as well.