I'm moving a very long image using -webkit-transform: translate(-958px, 0); animation
The image gets cut off very soon after it starts to move horizontally, but if I slightly move the tablet screen, it redraws the screen and it displays the whole image while panning across and very smoothly too.
But how do I simulate this in code?
Some suggestion:
Provide the tablet model and android version may help
Information like image dimension and size, animation duration, or a demo page on jsfiddle/jsbin will help a lot
Back to the question:
in the performance term, using translate3d will get better performance, since it doesn't work, the main bottleneck is elsewhere.
from my experience with mobile webkit, when there is large image (in term of size or dimension), you may have trouble:
Ram problem
High Network Delay
Long enough loading and rendering time
If your UI-triggered redraw will smooth everything, the lag may be caused by image loading & rendering
Solution:
Set a reasonable delay on your animation by animation-delay or setTimeout
More precise: preload the image, and then trigger the animation when it is done by listening its onload event: jQuery .load explanation on image load event behaviour
If the above not work for you, try it: Force-redraw DOM technique for WebKit-based browsers
For 2 & 3, the code will be like this:
$("<img>")
.attr({ src: " /* image url */ " })
.load(function(){
/* i. use class or animationName to set animation */
/* ii. force redraw go there if needed */
/* wrap i & ii into a setTimeout function inside this callback
if more delay is needed */
})
good luck.
This little snippet of code and blog post from Paul Irish might be helpful to you:
http://paulirish.com/2011/requestanimationframe-for-smart-animating/
And it's nearly cross browser if that floats your boat.
Forgive me if I'm not reading your question right.
Are you planning on using the tablet's gyro to pan about a large image? That should be possible in code.
However, forcing a transition to update faster is out of the developer's control, to my knowledge. The best you can do is make sure that that is all the tablet has to worry about.
<!DOCTYPE html>
<html>
<head>
<style>
body {
padding:0px;
overflow:hidden;
}
#img {
position:absolute;
left:0px;
top:20px;
-webkit-transition:left 0.5s;
}
</style>
</head>
<body>
<img id="img" src="img.png" />
</body>
<script>
function onOrientationChange(e) {
var img = document.getElementById("img");
var maxWidth = img.clientWidth - document.body.clientWidth;
img.style.left = ((e.gamma-90) * maxWidth / 180 ) + "px";
}
window.addEventListener('deviceorientation', function(e){onOrientationChange(e)}, true);
</script>
</html>
Have you tried using translate 3d? GPU kicks in when it's used.
HTML5 App/Animation Performance
Related
I'm trying to build a slideshow script will work with images of any width. Not too surprisingly, I'm having some centering issues that cause the portrait mode images to start off on the left when they initially display and then immediately move to the center after a short delay (giving it a bit of a "shooting ducks at a carnival" feel) .
I think that the solution is to get the image width right before it displays and then use that to center it, but I've been having some trouble finding reliable code that does that correctly. I've seen some examples that get the dimensions on load, but since the page (obviously) only loads once before the slideshow starts, that doesn't help much. I put it into CodePen for anyone to view that is kind enough to try and assist me:
http://codepen.io/Realto619/pen/fhdwK
I'm also having a problem with the getPrev() and getNext() functions not working on the first click, They work fine after that, and they seem to be firing on those first clicks, but they don't do what they're designed to until the second click.
Thanks in advance...
As I suspected, the problem was due to the image dimensions / image container not changing for each slide, so the css couldn't center it without having an accurate width for margin:0 auto; to work properly.
I created the following function and then called it in each of the other functions:
function getDim() {
iW = $(window).innerWidth();
iH = $(window).innerHeight();
natW = $(".fadein img").get(0).naturalWidth;
natH = $(".fadein img").get(0).naturalHeight;
natR = natW/natH;
adjH = iH*0.98;
adjW = adjH * natR;
$(".fadein").css('width',adjW);
$(".fadein img").css('width',adjW);
$(".fadein img").css('height',adjH);
}
Hopefully this will help someone else with a similar issue that comes here.
I have the following function that checks the scrolling position of a user so that the menu becomes fixed once they scroll past the masthead
function checkScrolling() {
if( $(window).scrollTop() > $('.masthead').height() ) { // we check against ACTUAL height
$('.menu').addClass('fixed');
}else {
$('.menu').removeClass('fixed');
}
}
and here the desktop and touch screen event listeners:
$(document).bind('touchstart touchend touchcancel touchleave touchmove', function(e){
checkScrolling();
});
$(window).scroll(function(){
checkScrolling();
});
However the touch events only make the menu fixed during the touchmove reliably. If I scroll really fast with a swipe up or down, there is a delay before the menu becomes fixed or unfixed.
Any ideas on how to fix this? See a code example here: http://dev.driz.co.uk/mobileMasthead.html (has been amended based on some answers below, but still does not work correctly on an iPad or iPhone)
Update:
And reading up on the topic reveals that JS like checking scroll position don't happen during the scroll... BUT... I've noticed that http://www.facebook.com/home/ doesn't have this issue when testing it on my iPad. So it's definitely possible to achieve this effect without using any custom JavaScript scrollbars like iScroll or similar.
Maybe I understand the question wrong, but to ensure functionality with high speed scrolling, why don't you tackle it the pure CSS way (aka faking the 'fancy' effect):
Old School (fast but primitive)
HTML
<div id="menu2"></div>
<div class="scroll" id="scroller">
<div id="overlay"></div>
<div id="menu"></div>
</div>
With the simple CSS:
html, body { overflow: hidden; height: 100% }
body { margin:0; padding:0; }
.scroll {
-webkit-overflow-scrolling: touch;
height:960px;
width:640px;
}
#menu2 {
width:640px;
height:20px;
background:red;
position:absolute;
z-index:-1;
}
#menu {
width:100%;
height:20px;
background:red;
z-index:0;
}
You can see a working example HERE.
It may be a bit primitive: but hey! It works! :)
New School (fancy but slow)
Check all of the other answers supplied.
But note that it 'is known' that the usage of JavaScript in combination with scrolling on mobile devices is causing a lot of trouble regarding speed.
That's why I think the simple CSS approach may be better.
If you want to learn about JavaScript and scrolling on mobile devices (and other functions), then there are two articles which I highly recommend reading:
Fast animations with iOS Webkit
Native scrolling: Grins and Gotchas
Facebook doesn't use JavaScript but pure css:
position: -webkit-sticky;
If i remember it correctly this makes the element stick at the top of its parent container when scrolled.
You just needed to attach your scroll events, not to window, document or body, but to a custom container.
On iOS you can't programatically react during these hardware-accelerated window scrolling behaviour.
Here's a fiddle:
a wrapper:
<div id="content">
some not-so-relevant css:
html,body,#content {
width:100%;
height:100%;
}
#content {
background-color: lightblue;
overflow:scroll;
}
attaching the listeners to the container:
function checkScrolling() {
if ($('#content').scrollTop() > mastheadHeight) {
menu.addClass('fixed');
} else {
menu.removeClass('fixed');
}
}
$('#content').scroll(function () {
checkScrolling();
});
You can see it working here for the JS-only fallback:
http://jsfiddle.net/jaibuu/YqPzS/
direct URL: http://fiddle.jshell.net/jaibuu/YqPzS/show/
I once tried to implement sticky headers on mobile version of a site but had encountered whole set of problems.
First and most important is that scroll event does not fire on mobile devices as often as it does on desktop. Usually it does fire when page stops. I don't know the exact reason for that but I can suggest it's because browsers on mobile "send" such tasks to GPU meantime CPU can not keep JS objects up to date with what happens to the canvas (but that's just my suggestion). Newer version of iOSes are making it better for us and probably scroll will work on iPhones.
You should use touch events. This makes you write a separate version of code for devices that support touch input. So have to look for reliable ways of testing for platform.
But touch events are also tricky especially on Android. I thought that in the callback for touchmove event I will be able to figure out current coordinates and go further from that point.
But There this issue https://code.google.com/p/android/issues/detail?id=5491 on Android, in summary touchmove fires once or twice at the very beginning of the touch action and does not fire again. Which makes it totally useless. People are suggesting preventDefault() but you no longer can scroll with that.
So I ended up with idea to reimplement scrolling from scratch using CSS transforms. And here is my results so far:
http://jsbin.com/elaker/23
Open that link on your device and http://jsbin.com/elaker/23/edit on your desktop: you'll be able to edit code and it will live update on you device, very convenient.
NOTE:
I'd like to warn you that this CSS scrolling thing is raw and there are some known problems that are not resolved yet: like you can sometimes scroll beyond top or bottom boundaries or if you just touch (not move) it still will scroll. Also the notorious URL bar will not hide. So there is work to do.
Do you need the touch events to fire this at all? Modern devices should return $(window).scroll() events and scrollTop values. On older Android and and pre-ios5 (I think), position:fixed: didn't work as expected because the of how the viewport worked. But it has been corrected in all new releases.
To further debug devices, I highly recommend Adobe Edge Inspect. You could console.log the scrollTop and see if the devices you care about actually work correctly with out any trickery. You'll get everything you need with their free version.
A great way to dealing with scroll events is not to attach your checks to the scroll event
takes a lot of resources and doesn't work very well with older browsers.
fortunately you can have a lot more control if you just perform a time loop to do that.
codewise that looks like that:
(It's used by twitter)
var MyMenu = $('#menu'),
didScroll = false;
$(window).scroll(function() {
didScroll = true;
});
setInterval(function() {
if ( didScroll ) {
didScroll = false;
//Do your check here
checkScrolling();
}
}, 180);
You should put your $('.masthead').height() outside this checkScrolling function of yours (in a higher scope) as this kind of operations takes a lot of resources and always ask jquery to "select your element" and calculate its size will eventually make your application laggy:
var headerHeight = $('.masthead').height()
function checkScrolling()
.....
Last thing , you can change the value of the interval attribute (right now it's 180 (a bit more that 60hz -. refresh time of the screen) you can make this value bigger, if you want your app to be more performant but a bit less accurate)
Thanks John Resig and twitter:
http://ejohn.org/blog/learning-from-twitter/
Hope that helped
Ajios !
I have just tested your functions for efficiency. You should try this
function checkScrolling() {
if( $(window).scrollTop() > mastheadHeight )menu.css('position','fixed');
else menu.css('position','');
}
This will reduce function call of addClass and RemoveClass and your execution time will take effect. If you want to reduce more execution time, then better to use pure JavaScript
$(document).ready(function(){
var p = $("#stop").offset().top;
$(window).scroll(function(){
if(p<$(window).scrollTop()){
console.log("div reached");
$("#stop").css({position:"fixed",top:0});
}
else{
console.log("div out");
$("#stop").css({position:"static"});
}
})
});
I think this will help you.
The total code is here in jsfiddle.net.
I have tested it for ipad using safari of online ipad2 simulator in http://alexw.me/ipad2/ and it has worked there. So, I think it will work on real ipad too.
setScrollTop: function(inTop) {
var top = Math.max(0,inTop);
if (wm.isMobile) { // should always be true for touch events
top = Math.min(top, this.listNode.clientHeight - this.listNodeWrapper.clientHeight);
if (dojo.isWebKit) {
this.listNode.style.WebkitTransform = "translate(0,-" + top + "px)";
} else if (dojo.isMoz) {
this.listNode.style.MozTransform = "translate(0,-" + top + "px)";
} else if (dojo.isOpera) {
this.listNode.style.OTransform = "translate(0,-" + top + "px)";
} else if (dojo.isIE) {
this.listNode.style.MsTransform = "translate(0,-" + top + "px)";
}
this.listNode.style.transform = "translate(0,-" + top + "px)";
this._scrollTop = top;
this._onScroll();
} else {
this.listNode.scrollTop = top + "px";
}
},
I like to think I'm not a dummy, but I can't get my jQuery horizontal slideshow to animate smoothly especially in FireFox (on a Mac). Anyone have advice?
Animation is being done like so:
$('#lookbook').stop().animate({left: -((lookbook-1)*825)+'px'}, { duration: 800, complete: cap_fade(1)});
Example link:
http://mayfourteenth.com/w/lookbook?preview=1
I've tested in Firefox, Chrome(dev) and Safari on windows and the animation stutters in all browsers(but more in FF though).
To increase JavaScript performance you could get rid of all the getElementById or $("div#mydividentyfier") calls.
If you store them in variables instead they will be cached.
Example:
It could increase performance quite a bit to do this:
var lookbook = $('#lookbook');
var look_caption = $('#look_caption');
if (lookbook.length) {
lookbook.width(lookbook).width()*$('#lookbook img').length)
if (look_caption) {
look_caption.html(lookcaps[0]);
look_caption.fadeIn();
}
Instead of:
if ($('#lookbook').length) {
$('#lookbook').width($('#lookbook').width()*$('#lookbook img').length)
if ($('#look_caption')) {
$('#look_caption').html(lookcaps[0]);
$('#look_caption').fadeIn();
}
I would also recommend using data URIs for the images as it reduces the amount of httpRequests you have to make to get the page loaded.
The animation looks smooth for me in Chrome. However, I believe there are several things you can do to improve smoothness:
First, it's fine to preload all of the images in advance as you do here (at the top). However, displaying them all at once, as in the "Example link", hurts performance, as they are all animating at once:
<div id="lookbook">
<div><img src="/q_images/lib/lookbook/1.jpg"></div>
<div><img src="/q_images/lib/lookbook/2.jpg"></div>
...
<div><img src="/q_images/lib/lookbook/15.jpg"></div>
</div>
Instead of doing this, you can simply cue up the next and previous image on either side of the current image, but then don't have the rest of the images in the page until they're needed. (Preloading them is still fine though.)
Other things which can improve performance slightly are things like the following:
Use smaller (by pixels and/or file size) images.
Make minor code optimizations by computing things in advance.
Use a stand-alone animation library instead of jQuery.
You may also want to use this
.animate({left:'-=825'}); //next
//and
.animate({left:'+=825'}); //previous
Instead of
.animate({left: -((lookbook-1)*825)+'px'});
I would like to do something like this: http://javascript.about.com/library/blcmarquee1.htm
The script I referenced however seems to be a bit laggy (outdated?), so I was wondering if anyone knew of a better solution. (jQuery solutions welcome.)
Just found this — jQuery-driven, and has images. I’m intending to use it for a current project.
http://logicbox.net/jquery/simplyscroll/
UPDATE: I have now used this in production code. The plugin is capable of looping 70+ 150×65px images pretty smoothly - which a number of another plugin I tried similar to this were failing on.
NOTE it reeked havoc with z-index issues in IE 6 / 7 and was not showing up etc. - But this might also have been partly due to my CSS. To anyone having trouble with it not showing up at all in IE check out the standard IE z-index fixes: http://www.google.com/search?q=ie+z+index+issues
LATEST UPDATE:
Addition things to consider when implementing plug-ins like these:
The number of items and type of content to scroll. I found a number that would start to glitch as soon as you had more than say 15 images to scroll.
I found a number of these plugins that were tied to old versions of jquery
If scrolling images ARE THEY ALL THE SAME SIZE again a number of the plug-ins I experimented with only worked if all the images were the same size but did not make this clear in the tutorials. I believe then the plugins run then set a string of li tags that are all x wide then calculate the total distance of them all chained together to manage the scrolling.
Effects - some would continuously scroll others would move one image pause for a second then move another image
I have now also found these two scroller plugins to be very good as well.
http://caroufredsel.frebsite.nl/
http://sorgalla.com/jcarousel/
The Silky-Smooth jQuery Marquee and Giva Labs' Marquee
Just a thought. Could you do something like this.
<style type="text/css">
.imgwindow{
width:500px; //or whatever
height:65px; //or whatever
position:relative;
overflow:hidden;
}
.imgholder{
min-width:2000px;
height:65px;
position:absolute;
left:-200px;
}
.inline-image{
display:inline-block;
}
</style>
<script type="text/javascript">
var img;
function imgScroll(){
img = $(".inline-image").first();
img.animate({width:0},2500,'linear',function(){
img.remove();
$(".imgholder").append(img);
imgScroll();
});
}
$(document).ready(function(){
imgScroll();
});
</script>
and the html
<div class="imgwindow">
<div class="imgholder">
<img class="inline-image" src="image1" /><img class="inline-image" src="image2" />...
</div>
</div>
Here is a link: http://www.avineon.com/
Open this link see on the top. Four images are rotating.
I need something similiar using Javascript.
Is it possible by using Javascript.
I don't think you'll have much luck if you try to do that in pure javascript. It might be possible using the emerging canvas and SVG libraries such as Raphael, but you'll still have cross-browser issues. That site used Flash, and I'd recommend using that if you wanted such an effect.
...why you'd want that on your website is another story though...
You could so something similar, but not exact.
Transparency = Supported in FF, Safari, IE7+
Changing image width = Place image in div with this Css
.class img {
display: block;
width: 100%;
height: 100%
}
This will make the image stretch to fill the .class div. You can then use JS to make this div narrower like the carousel does, and the image contained will animate within the div.
You would then need to track the mouse locations to determine how fast it spins.
You can use an equation using cosine for smooth acceleration from the far ends (IIRC)
You will not however be able to get the images in reverse, unless you create a copy in a server side language or use canvas.
Your best bet would not be to attempt to render something in actual 3D, but rather to use visual tricks to approximate a 3D effect. That is, use perspective / image deformation to make it look like a cube is rotating, similar to what is implemented at this page, which has a better explanation of the math involved.
Really, though, you're probably better off just using Flash.
That effect is possible in JavaScript simply by modifying each of the images width, height, and left styles over time. It's an involved script, but only needs to interpolate those three styles on the each of the image elements.
To get the rotation effect, decrement the width style of the image in a setInterval function while moving the left style property. There is a slight decrement on the height also.
You'll need two images for each side, a front and reverse. When the width decrements to zero, swap the image with it's flipped version and start incrementing the width.
Alternatively use Webkit's, and Firefox's transform css properties.
Or try one of these coverflow components that look similar:
Protoflow,
ImageFlow
<html>
<head>
<title></title>
</head>
<body>
<script type="text/javascript">
if (document.all || document.getElementById){ //if IE4 or NS6+
document.write('<style type="text/css">\n');
document.write('.dyncontent{display: none; width: 728px; height: 90px;}\n');
document.write('</style>');
}
var curcontentindex=0;
var messages=new Array();
function getElementByClass(classname){
var inc=0;
var alltags=document.all? document.all : document.getElementsByTagName("*");
for (i=0; i<alltags.length; i++){
if (alltags[i].className==classname)
messages[inc++]=alltags[i];
}
}
function rotatecontent(){
//get current message index (to show it):
curcontentindex=(curcontentindex<messages.length-1)? curcontentindex+1 : 0;
//get previous message index (to hide it):
prevcontentindex=(curcontentindex==0)? messages.length-1 : curcontentindex-1;
messages[prevcontentindex].style.display="none"; //hide previous message
messages[curcontentindex].style.display="block"; //show current message
}
window.onload=function(){
if (document.all || document.getElementById){
getElementByClass("dyncontent");
setInterval("rotatecontent()", 5000);
}
}
</script>
<table width="100%">
<tr align="center">
<td>
<div class="dyncontent" style="display: block">
first
</div>
<div class="dyncontent">
second
</div>
<div class="dyncontent">
Third
</div>
</td>
</tr>
</table>
</body>
</html>