Javascript to use different value for mobile vs desktop in gallery - javascript

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.

Related

Very simple parallax code has performance issues

For a parallax-effect, I created a simple script in native Javascript, but it seems to fail somewhere I can't see. That's why I already added the requestAnimationFrame-functionality, but it doesn't seem to really help.
My relevant code is as follows:
var $parallax, vh;
$(document).ready(function() {
$parallax = $('.parallax');
vh = $(window).height();
$parallax.parallaxInit();
});
$(window).resize(function() {
vh = $(window).height();
$parallax.parallaxInit();
});
$.fn.parallaxInit = function() {
var _ = this;
_.find('.parallax-bg')
.css('height', vh + (vh * .8) );
}
//call function on scroll
$(window).scroll(function() {
window.requestAnimationFrame(parallax);
});
var parallaxElements = document.getElementsByClassName('parallax'),
parallaxLength = parallaxElements.length;
var el, scrollTop, elOffset, i;
function parallax(){
for( i = 0; i < parallaxLength; i++ ) {
el = parallaxElements[i];
elOffset = el.getBoundingClientRect().top;
// only change if the element is in viewport - save resources
if( elOffset < vh && elOffset + el.offsetHeight > 0) {
el.getElementsByClassName('parallax-bg')[0].style.top = -(elOffset * .8) + 'px';
}
}
}
I think it's weird that this script by Hendry Sadrak runs better than my script (on my phone) while that is not really optimised, as far as I can tell.
Update: I checked if getBoundingClientRect might be slower in some freak of Javascript, but it's about 78% faster: https://jsperf.com/parallax-test
So here is the downlow on JS animations on mobile devices. Dont rely on them.
The reason is that mobile devices have a battery and the software is designed to minimize battery load. One of the tricks that manufacturers use (Apple does this on all their mobile devices) is temporarily pause script execution while scrolling. This is particularly noticeable with doing something like parallax. What you are seeing is the code execution - then you scroll, it pauses execution, you stop scrolling and the animation unpauses and catches up. But that is not all. iOS uses realtime prioritization of the UI thread - which means, your scrolling takes priority over all other actions while scrolling - which will amplify this lag.
Use CSS animation whenever possible if you need smooth animation on mobile devices. The impact is seen less on Android as the prioritization is handled differently, but some lag will likely be noticeable.
Red more here: https://plus.google.com/100838276097451809262/posts/VDkV9XaJRGS
I fixed it! I used transform: translate3d instead, which works with the GPU instead of the CPU. Which makes it much smoother, even on mobile.
http://codepen.io/AartdenBraber/pen/WpaxZg?editors=0010
Creating new jQuery objects is pretty expensive, so ideally you want to store them in a variable if they are used more than once by your script. (A new jQuery object is created every time you call $(window)).
So adding var $window = $(window); at the top of your script and using that instead of calling $(window) again should help a lot.

StickyNav - debug

// STICKY NAVBAR
var num = 816; //number of pixels before modifying styles
$(window).bind('scroll', function () {
if ($(window).scrollTop() > num) {
$('.navbar').addClass('navbar-fixed-top');
} else {
$('.navbar').removeClass('navbar-fixed-top');
}
});
Thisis my code for the sticky navbar (using a custom Bootstrap navbar), and It works fantastic on Desktop browsers...but the var num is measured in pixels instead of em. That doesn't translate well to mobile.
Is there a way to:
Measure the scroll in em instead of px
or...
Detect when the bar hits the top of the window and then make it stick instead of using hard-coded measurements?
The problem you're having is not related to a different measurement on mobile devices, cause it's always measured and calculated in pixels on any device. It's more likely that the position of your navbar is just different on a mobile device.
And yes, a solution would be to get the current offset of your navbar and use that instead of the hard coded 'num', like so:
var navbarOffset = $('.navbar').offset();
var num = navbarOffset.top;
But please keep in mind that this approach assumes that the position of the navbar will not change during the its lifetime. Once your dealing with a responsive design you should recalculate the navbar offset every time the size of a content wrapper element changes.
How elaborate this whole thing can get you can check out here:
https://github.com/zurb/foundation/blob/master/js/foundation/foundation.topbar.js

scrolling on mobile devices

This question is more of an advice research, I do hope that it will be helpful for others and it won't closed, as I'm not quite sure where to ask for advice on this matter.
I've been developing for mobile for the past 6 months and I had the occasion to deal with all kinds of situations and bugs on various devices.
The most troubling was the scrolling issue, when it comes to scrolling in multiple areas of the website. On three projects that I have been working on I've been building a navigation that behaves the same way that the native iOS Facebook app has, or the Google website on mobile, etc. And for each one I came up with different solutions.
But a few days ago I have just released a new JavaScript library, drawerjs, that can be used to generate such navigation (so called off canvas concept). The difference between the other libs and this one is that is library agnostic, and it acts on touch behavior (the same way that the Facebook app behaves) not just open / close on click.
One of the things that I have left to implement is a solution for scrolling inside the menu and the navigation without affecting one another (most of the time when you scroll in such way, the content tends to scroll together with you menu or after you have reached the end of the menu scrolling).
I have two solutions in mind:
One approach would be to use the same principle I'm using for dragging the content and showing the navigation, on touchmove I prevent the default scrolling on document / content and I start translating the contents with the same amount you scroll. And with the same resistant behavior as a touch slider would have (when you exceed the boundaries and let go, the contents would translate back so it doesn't exceed the boundary anymore, or on swipe with the same behavior).
A second approach would be using the native overflow-scrolling that iOS has and would offer the same feel as described in the first approach. The downside of this would be that only iOS devices would have the nice resistant feature, but it would be, supposedly, less of a hassle the the first approach.
So I'm not quite sure which approach I should take, or if there any better solutions for that. I'm also trying to keep in mind that some users would like to hide the url bar, so scrolling on the body / html would have to be kept (on the y axis).
You could do touchmove . But as far as I understand, you want something like this?
http://jsfiddle.net/2DwyH/
using
var menu = $('#menu')
menu.on('mousewheel', function(e, d) {
if((this.scrollTop === (menu[0].scrollHeight - menu.height()) && d < 0) || (this.scrollTop === 0 && d > 0)) {
e.preventDefault();
}
});
Using this plugin from Brandon Aaron - github : https://github.com/brandonaaron/jquery-mousewheel
And it should work with Android: What DOM events are available to WebKit on Android?
Some more info here: Prevent scrolling of parent element?
Also without using the plugin above , using only jQuery you could do this like it says on the link above - answer from Troy Alford
$('.Scrollable').on('DOMMouseScroll mousewheel', function(ev) {
var $this = $(this),
scrollTop = this.scrollTop,
scrollHeight = this.scrollHeight,
height = $this.height(),
delta = (ev.type == 'DOMMouseScroll' ?
ev.originalEvent.detail * -40 :
ev.originalEvent.wheelDelta),
up = delta > 0;
var prevent = function() {
ev.stopPropagation();
ev.preventDefault();
ev.returnValue = false;
return false;
}
if (!up && -delta > scrollHeight - height - scrollTop) {
// Scrolling down, but this will take us past the bottom.
$this.scrollTop(scrollHeight);
return prevent();
} else if (up && delta > scrollTop) {
// Scrolling up, but this will take us past the top.
$this.scrollTop(0);
return prevent();
}
});
The JS Fiddle he mentions: http://jsfiddle.net/TroyAlford/4wrxq/1/
Why not just provide a fixed height to your widget (min and max will also do). Then define like these -
height: x px;
overflow-y: auto;
This way till the focus is inside the widget, it'll only overflow the widget, once outside the page will scroll without affecting widget content at all.

Improving Performance on Background Parallax Scrolling

Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/

Scrolling into view an element hidden beneath a persistent header

I'm currently working under some tight restrictions with regard to what I can do with JavaScript (no frameworks such as jQuery for example, and nothing post Firefox 2.0).
Here's my problem; I have a persistent header floating at the top of the page. I have input elements scattered throughout (we're replicating a paper form exactly, including the background image). There is a field nearing the bottom of the page that gets tabbed out (using keyboard tab button) and puts the focus on a field at the top of the page. Firefox will automatically scroll the field "into view". However, while the browser believes the field is in view, it's actually hidden beneath the persistent header.
http://imageshack.us/a/img546/5561/scrollintoviewproblem.png
The blue field above is accessed by hitting "tab" from another location on the page. The browser believes the field has been scrolled into view, but it's in fact hidden beneath the floating persistent header.
What I'm looking for is ideas as to how I can detect that the field is beneath this header and scroll the entire page accordingly.
I've tried a few variations of margin & padding (see other considerations at http://code.stephenmorley.org/javascript/detachable-navigation/#considerations) without luck. I've also tried calling the JavaScript function "scrollIntoView(element)" each time we focus on a field, but given the amount of fields on the form (and the fact that we're aligning them to match the background image of a paper form exactly), this was causing some pretty severe "jumping" behavior when tabbing through fields close to each other that were at slightly different heights.
I can change how the persistent header is done, so long as it doesn't require too much effort. Unfortunately, frames are out of the question because we need to interact with the page content from the persistent header.
Ideally the solution would be in CSS, but I'm open to JavaScript if it solves my problem.
Another note, we require that the input elements have a background color, which means that adding padding to them would make the background color stretch, which hides parts of the background image. BUT, the input elements are in a div, so we might be able to use this to our advantage.
So after doing some more searching (thanks to #Kraz for leading on this route with the scrollTo() suggestion) I've found a solution that works for me.
I've added an onFocus call to each element dynamically, so they always call the scrollScreenArea(element) function, which determines if they're hidden beneath the top header or too close to the footer area (this solves another problem entirely, using the same solution).
/* This function finds an element's position relative to the top of the viewable area */
function findPosFromTop(node) {
var curtop = 0;
var curtopscroll = 0;
if (node.offsetParent) {
do {
curtop += node.offsetTop;
curtopscroll = window.scrollY;
} while (node = node.offsetParent);
}
return (curtop - curtopscroll);
}
/* This function scrolls the page to ensure that elements are
always in the viewable area */
function scrollScreenArea(el)
{
var topOffset = 200; //reserved space (in px) for header
var bottomOffset = 30; //amount of space to leave at bottom
var position = findPosFromTop(el);
//if hidden beneath header, scroll into view
if (position < topOffset)
{
// to scroll up use a negative number
scrollTo(el.offsetLeft,position-topOffset);
}
//if too close to bottom of view screen, scroll into view
else if ((position + bottomOffset) > window.innerHeight)
{
scrollTo(0,window.scrollY+bottomOffset);
}
}
Let me know if you have any questions. Thanks #Kraz for sending me onto this solution.
As well, I'd like to reference Can I detect the user viewable area on the browser? since I took some code from there and that partially described my problem (with a neat diagram to boot).
The easiest method for doing this will be listening for each element's focus event and then seeing if it is on the page. In pure JS, it is something like:
var input = Array.prototype.slice.call(document.getElementsByTagName('input'));
for ( var i in input )
input[i].addEventListener( 'focus', function (e) {
var diff = 150 /* Header height */ - e.target.getBoundingClientRect().top;
if ( diff > 0 ) {
document.body.scrollTop += diff;
document.documentElement && document.documentElement.scrollTop += diff;
}
}, false );
I didn't include the IE addEvent method, but it should be pretty easy to make that on your own given this base.

Categories

Resources