I've written an Ionic app, where nine images are positioned inside a DIV. Here's the relevant snippet from my CSS file:
.mydiv {
position: relative;
overflow: hidden;
}
.mydiv > img {
position: absolute;
box-shadow: 6px 6px 8px #aaaaaa;
}
I'm using $ionicGesture.on('drag', function(e) {}) to move these images around, following the move of my finger. That's the relevant part from my controller.js:
jQuery('.mydiv > img').each(function(i, myImg) {
jQuery(myImg).css('left', initLeft + e.gesture.touches[0].pageX - initX);
jQuery(myImg).css('top', initTop + e.gesture.touches[0].pageY - initY);
}
So, basically, every time the drag event occurs, I move the images by changing their 'left' and 'top' attribute.
The problem is though, that this solutions works wonderfully in a browser, but is horribly lagging as an Ionic app on my iPhone 5. When I put the contents of the Ionic 'www' folder on a web server and access the web app with my iPhone's web browser, the movement is extremely smooth. So, obviously, it's not a performance issue, but Ionic is doing something that makes the app stuttering. Using the Chrome profiler on my Mac, I learned nearly all of the time is used by internal Ionic functions and not by my client code. This seems to be another hint, that this is some kind of Ionic problem.
Why is my app, that runs perfectly smooth in my mobile browser on the same device gets jerky as hell when packed as an Ionic app?
Additional information:
Those images I drag along are scaled with jQuery(myImg).width(). I now replaced them with pre-scaled image files and got rid of the box-shadow style. Now the lagging is reduced but still worse than in the mobile browser. Is it possible, that the mobile Safari on my iPhone uses the GPU for Javascript induced style changes, while for a Cordova app the GPU is not used?
Looks like you have animation issue. Here is an article from Ionic team that explains on how to deal with this.
What’s happening is that as you try to animate the left property of
the items, the web view will have to go back and try to recalculate
that item’s position. This happens for a lot of CSS properties.
Thankfully, we have other ways to animate these items.
Let’s change that left property to transform and set the value to use
translate3d. Translate3d is a special CSS value that will actually
move the animation to the device’s GPU. This is called hardware
accelerated animation.
#-webkit-keyframes slideIn {
0% {
left: -100%;
}
100% {
left: 0;
}
}
#-webkit-keyframes slideInSmooth {
0% {
-webkit-transform: translate3d(-100%,0,0);
}
100% {
-webkit-transform: translate3d(0,0,0);
}
}
.slide-in{
-webkit-animation: slideInSmooth ease-in 1;
animation: slideInSmooth ease-in 1;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
-webkit-animation-duration: 750ms;
animation-duration: 750ms;
}
I haven't seen your animation or your code but my guess is that the device GPU isn't carrying the weight it should.
Obviously without seeing your app, I'm doing some guessing here -- but from what you've described this may be the issue:
There are a couple ways to get the GPU to kick in, but the most basic is not to animate using "top" and "left". Using standard CSS positioning like top/left to animate DOM elements works, but it's the slowest way to go and as you've discovered: performance tends to suck in many environments. (Particularly when you're using complex images or blurs/shadows).
A far better way to go is to use translateX / translateY which engages the GPU. That should give you silky smooth performance. Using translateX and translateY will also allow for subpixel rendering and give a far smoother appearance than using top/left which blips along pixel-by-pixel.
There's an excellent discussion of this by a Chrome team member here:
https://www.youtube.com/watch?v=NZelrwd_iRs
Lastly, if for some reason you must use top/left -- a quick and somewhat hacky method to get the GPU to kick in is to set the parent DIV to translate3d(0, 0, 0);
This forces the parent layer into the hands of the GPU (via 3D transforms) during the browser-paint, and the subsequent standard CSS top/left changes that you're using should now render using the GPU. Needless to say, this second method isn't the best way to go in terms of best-practices, so go with CSS transforms first if that's an option.
Using transform: translate3d() does the trick. Animations are now extremely smooth. Because I had some trouble to convert my code from the CSS left-top-style to using translate3d(), here is what I did as a reference for other users:
jQuery('.mydiv > img').each(function(i, myImg) {
var x = initLeft + e.gesture.touches[0].pageX - initX;
var y = initTop + e.gesture.touches[0].pageY - initY;
jQuery(myImg).css({
'-webkit-transform' : 'translate3d('+x+'px, '+y+'px, 0px)',
'transform' : 'translate3d('+x+'px, '+y+'px, 0px)'
});
}
I even can switch on box-shadow again and the animation stays silky smooth.
Related
As jQuery.fadeIn is not very smooth on mobile devices I try to use CSS but it doesn't work as expected. How to create a smooth CSS animation using Javascript?
In general this is what I'm trying:
$('div')
.css('opacity', 0) // at first, set it transparent
.css('display', 'block') // make it appear
.css('transition', 'opacity 1000ms linear') // set a transition
.css('opacity', 1); // let it fade in
https://jsfiddle.net/8xa89y04/
EDIT1:
I'm not searching a solution using static CSS classes. The point is: I need to set this dynamically in Javascript code - a replacement for jQuerys fadeIn() for example.
Your logic isn't quite right. Firstly you cannot animate display, so to achieve what you require the element has to always be rendered in the DOM (ie. anything but display: none). Secondly, the transition property should be placed within the CSS styling itself. Finally you can make this much more simple by setting all the rules in CSS classes and just turning the class on/off. Try this:
div {
position: absolute;
width: 100px;
height: 100px;
background-color: black;
opacity: 0;
transition: opacity 1000ms linear;
}
.foo {
opacity: 1;
}
$('div').addClass('foo');
Working example
Use this code.
CSS
div {
width: 100px;
height: 100px;
background-color: black;
transition:opacity 2s;
}
JavaScript
$('div').hover(function(){
$(this).css('opacity','0');
})
Without using CSS properly, you are going the long way about it. You'll need to emulate what you would normally do in CSS, using JavaScript, so you'll be setting all your CSS properties, transitions etc, then applying them with js.
I can't personally see any benefit in doing this. Using actual CSS would be cleaner, more efficient, more maintainable, and simply a plain better solution to what you need.
I think this is what you are looking for.
$('div').css({"display":"block", "opacity":"0"}) //Make div visible and opacity as "0"
$('div').animate({opacity :1}, 1000); //Animate div to opacity "1"
Take a look at this Demo
Found the cause here: CSS transitions do not work when assigned trough JavaScript
To give this attention I need to give the browser some time - or better: a working slot to activate the transition as the time seems not to be a problem.
The following code cuts the process in two by using setTimeout()... and it works!
var div = $('div');
// first process
div
.css('opacity', 0) // initial opacity
.css('display', 'block') // make it appear (but still transparent)
.css('transition', 'opacity 1s linear'); // set up a transition for opacity
// break - start the transition in a new "thread" by using setTimeout()
window.setTimeout(function(){
div.css('opacity', 1); // start fade in
}, 1); // on my desktop browser only 1ms is enough but this
// may depend on the device performance
// maybe we need a bigger timeout on mobile devices
I have a background image that has 4 images of a character in different walking positions.
Is it possible on scroll with Skrollr to move the sprite position (let's say by 150px on the x co-ordinate) every 0.5 seconds?
Want to give it a walking effect but only on scroll.
Maybe there's another way to achieve this effect? Quite keen on only using Skrollr as I've used it in the past and LOVE it!
The only way I've managed to achieve this so far is by creating a sprite of images on a completely landscape canvas type so that all of the images are changed upon the X-Axis then using skrollr stylesheets (click here for github) to apply the animation upon scroll.
Start by making a Skrollr.css file. Be sure to include the 'data-skrollr-stylesheet' to the link tag.
<link href="css/skrollr.css" rel="stylesheet" data-skrollr-stylesheet>
Right before the closing body tag, include the skrollr stylesheet min js file followed by the skrollr JS. See below.
<script type = "text/javascript" src="dist/skrollr.stylesheets.min.js"></script>
<script type = "text/javascript" src="js/skrollr.js"></script>
To get you started I've placed a simple demo of what your skrollr stylesheet should look like.
.div-name {
-skrollr-animation-name: animation1;
}
#-skrollr-keyframes animation1 {
500 {
background-position: 0px;
}
600 {
background-position: -100px;
}
}
The numbers in the animation are relative to the top of the browser window. 600px from the top of the window, the background-position will move -100px on the X-axis.
I am currently trying to figure out how to remove the transition/slider effect on scroll. Personally I'd like the images to change on demand rather than scroll. I hope my instruction has given you something to start with and if I figure out how to change the image instantly without the transition, I will expand on this here. Good luck with the project.
I managed to include frame-by-frame sprite animation in skrollr by adding a custom easing that does the trick. Math.round in my intuition would return sort of a square wave and surprisingly it worked (but be aware, I'm not sure about the math behind it):
var s = skrollr.init({
easing: {
frames: function(p) {
return Math.round(Math.sin(p) * 50);
}
}
});
This custom easing will:
loop your animation without any interpolation so that you don't get that infamous "sliding" instead of the animation.
After that you call the animation on css this way (please note I'm using skrollr stylesheet but there's no reason why it shouldn't work directly with html data tags as well):
.animation{
display: block;
width: 208px; /*your sprite width*/
height: 227px; /*you sprite height*/
background-image: url(yourspritesheet.png);
background-position: 0px 0px;
-skrollr-animation-name: youranimation;
}
#-skrollr-keyframes youranimation{
start{background-position-x[frames]:0px;}
end{background-position-x[frames]:208px;}
}
In this example, the spritesheet is horizontal and 208px is the sprite width. The code will go through the spritesheet at steps of 208px and start from the beginning when it reaches the end. Tested on Chrome, Firefox and Safari. You can find an example of the result here (school project): http://www.sartirani.it/daniele/scroll/public_html/
This is a follow up to my question here: jquery UI add class with animation does't work
See the new jsfiddle and try this in Firefox: http://jsfiddle.net/40mga4vy/3/
-webkit-transition: all 2.0s ease;
-moz-transition: all 2.0s ease;
-o-transition: all 2.0s ease;
transition: all 2.0s ease;
This code in combination with some jquery animates a background image change when selecting a new background image from a select-element. It works in all browsers except Firefox (tested in MacOS 35.0.1).
While animating a change in the background color and width/height properties works like a charme in FF: http://jsfiddle.net/tw16/JfK6N/ - animating a background image does not work.
Researching showed that a "left" property has to be set but it turned out to not have any impact. I also tried various notations but with no success, I cannot make it work.
There is a workaround shown in this fiddle:
http://jsfiddle.net/40mga4vy/1/
function changeBackground() {
$('#wallpaper').removeClass();
$("#wallpaper").addClass("wallpaper_" + $("#select_category").val()).css('opacity','0').animate({opacity:'1'});
};
This works in FF but its a bit ugly as the class is removed and then opacity raises afterwards (doesn't look as smooth as the css solution).
Any hints/tricks or is this simply not supported?
As far as I know, there is no suport in any browser to swap images smoothly in one single element in css.
After you do what you need, make sure you take a look into performance, your workaround is not as much as efficient as it could. In this code
$("#wallpaper").addClass("wallpaper_" + $("#select_category").val()).css('opacity','0').animate({opacity:'1'});,
the browser will take every single step until
.animate({opacity:'1'}).
For instance, the browser first has to find $("#wallpaper") then, it will call for .addClass("wallpaper_" + ...);
and concatenate the result from finding $("#select_category") then getting .val() and so on. everytime this function is called, it will iterate through every single of these objects, so it is not as efficient as probably could and with two more animations in the page, it may became a bit laggy, if possible, use animations through CSS.
Anyway, what I sugest you to do is (if i'm right about what you want), just do what's in here https://jsfiddle.net/bmjg5g9s/
On my main page, I have 3 links (images). Like a portal.
This is what i want to make:
When you click on one of the links, you zoom really far onto that one. So far that it filled up the full screen. But I dont want you to see whats in that page yet.
Example:
http://prezi.com/0ehjgvtr9fzu/?utm_campaign=share&utm_medium=copy (click on the cloud which says:"Main idea")
Is this possible with HTML/CSS/JavaScript?
You can't "zoom" the viewport but you can use CSS/JS to mimic the behavior by scaling elements in animations. With CSS transforms/transitions you can pretty easily scale a "page" down and then expand it on click (like your demo Flash object).
Basic demo:
#container {
transform-style : preserve-3d;
perspective : 400px;
}
#container .page {
transition : transform 500ms ease-out;
transform : scale(0.2);
}
#container .page.make-page-big {
transform : scale(1);
}
Where #container is the "viewport" and .page is a page that will be animated into full-view.
Here is a demo: http://jsfiddle.net/j1k3mcLp/
That should get you started, you can do a lot with 3D transforms as well, which will allow you to really get the effect you want.
UPDATE
Here's a demo using 3D transforms: http://jsfiddle.net/j1k3mcLp/1/
I'm looking to make a clever animation that makes three sucessive right angle quotes like » » » glow successively from left to right, then back to the left-most one again. I'm wondering if this might be a clever effect for other developers to use as well, and could be good Google-fodder.
The inspiration for this is obviously the slide-to-unlock screen on an iPhone, where the text glows in a progressive manner.
I do know about CSS3 animations with keyframes and have implemented these successfully before, but am not sure how to code in the part with the loop. I'm guessing Javascript is the answer here, with some kind of loop. I already have jQuery on the page I'm using so it wouldn't be anything extra to use jQuery functionality.
My HTML structure would be something like this:
<span class="glowquote"><span>»</span> <span>»</span> <span>»</span></span>
Any ideas on the best/most clever way to implement this? I realize not all browsers support CSS3 animations if they are to be used and honey badger don't care, I only need to support modern webkit and gecko implementations.
Edit 1: added span tags around each » to allow changing CSS properties individually for each right angle quote in JavaScript, as most people have done. For the guy who did the CSS3 method, not necessary!
Edit 2: Just for absolute clarity on what the goal is, the left-most one will glow a certain color first, using a CSS color:green etc. change and transition:color linear 0.4s etc, then the next one will do the same right after with maybe a short delay.
Edit 3: For further clarification, the exact animation I'm looking for looks like this, though in the example they've used a VERY hack-ish method that I don't think will work for a lot of people. I'm wondering if we are just coming up against the limitations of javascript/css3 or if there is more refinement possible. Link: http://css-tricks.com/examples/SlideToUnlock/
FINAL EDIT WITH SOLUTION SUMMARY: I decided to go with the pure CSS3 option but there are many valid options presented here that are all worthy of your time. I wish StackOverflow would allow for more than one correct answer. For anyone coming to this question from Google or elsewhere, make sure to browse the whole question to choose what may work the best for you! I figured that since this is mostly just for fun, I would feel okay leaving the Firefox & IE guys out in the dark. You can see what my final implemention was here: http://ezrahub.com/board/ at the top of the page, where it is used to expand the posting form. One quirk is that if you change the text size, you will also have to change the property background-size in your CSS stylesheet. I used background-size: 120px 50%; for mine, and if you play around with it you can see how the effect changes.
Hope everyone has fun with this and enjoy using it on your creations! Shout out to autistic moderators such as #JaredFarrish. (Why so mad?)
For the fun of it, here is a method using pure css. It has very limited support for browsers.
.glowquote {
background: -webkit-linear-gradient(left, black 0%, green 40%, green 60%, black 100%);
background-size: auto 50%;
padding: 0 100px;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-animation: gradient-animation infinite 1s linear;
}
http://jsfiddle.net/grc4/uF8H2/3/
Edit: The jsfiddle now partly works in firefox. The gradient animation works correctly, and the only thing that does't work is the text clipping (-moz-background-clip:text doesn't exist). This could be solved by using an image mask over the top of the background.
First, I'd put spans around the individual arrows so that you can change their CSS settings individually:
<span class="glowquote"><span>»</span> <span>»</span> <span>»</span></span>
Then, assuming you're using jQuery UI (because as far as I know core jQuery won't animate colours) you can do something like the following:
function doGlow(numTimes) {
var $arrows = $("span.glowquote span");
function nextGlow(i) {
$arrows.eq(i).animate({
color: "green"
}, 400).animate({
color: "black"
}, 400, function() {
i = (i + 1) % $arrows.length;
if (i === 0) numTimes--;
if (numTimes > 0) nextGlow(i);
});
}
nextGlow(0);
}
Demo: http://jsfiddle.net/KrL44/1
(Or here is the original version of my demo that just kept looping indefinitely: http://jsfiddle.net/KrL44/)
try this:
html:
<span class="glowquote"><span>»</span> <span>»</span> <span>»</span> </span>
js:
$(document).ready(function(){
var i = 0;
function run() {
$('.glowquote span:eq('+ i +')').animate({color: 'green'}, 500, function(){
$(this).animate({color: 'black'}, 500);
i++;
if (i > 2) { i = 0 }
run()
})
}
run()
})
http://jsfiddle.net/wQ9AT/