jquery animate jerky - is there a better library for scaling images? - javascript

I noticed whenever I use jquery animate with long time and small change animation becomes incredibly jerky! The only way to fix this seems to decrease time considerably and scale more. But in some projects this is not possible...
I looked into css transitions. And it didn't provide better results. Animation itself was better, but firefox was horridly jerky and in chrome animation finishes were not quite there with smoothness.
So my question is this, is there any other library with animation engine that can provide smooth scaling of images? or is there a technique that I'm missing?
This is my animation:
$('.item a').mouseover(function(){
//
var this_img = $(this).find('.img_grayscale');
var css_w = parseInt(this_img.attr("width"), 10);
var css_h = parseInt(this_img.attr("height"), 10);
// 10% of height and width calc here
var css_p_w = css_w * 10 / 100;
var css_p_h = css_h * 10 / 100;
//
var css_top = -(css_p_h / 2);
var css_left = -(css_p_w / 2);
//
this_img.stop().animate({ opacity:1 }, 100, function(){
this_img.animate({width:css_w + css_p_w, height:css_h + css_p_h, top:css_top, left:css_left}, 1200, "linear");
});
//this_img.transitionStop().transition({ opacity:1 }, 100, function(){
// this_img.transition({ scale:1.05, rotate:0.02 }, 2500, "ease");
//});
//
}).mouseout(function(){
//
var this_img = $(this).find('.img_grayscale');
var css_w = parseInt(this_img.attr("width"), 10);
var css_h = parseInt(this_img.attr("height"), 10);
//
this_img.stop().animate({ width:css_w, height:css_h, top:"0", left:"0" }, 1200, "linear", function(){
this_img.animate({ opacity:0 }, 100);
});
//this_img.transitionStop().transition({ scale:1 }, 2500, "ease", function(){
// this_img.transition({ opacity:0, rotate:0.02 }, 100)
//});
//
});
Commented out parts are jquery.transit plugin tests, this plugin provides css3 transitions.
Live example: http://mac.idev.ge:800/test/ (hover images)

Why don't you use simple CSS transitions, here's a demo.
<img src="some-image.jpg" />
CSS
img {
width: 400px;
height: 300px;
position: absolute;
top: 100px;
left: 100px;
transition: all 0.2s linear;
}
img:hover {
width: 440px;
height: 330px;
top: 85px;
left: 80px;
}

To get the smoothest look and best performance I would definately recommend using css scale transform.
There's some gotchas to be aware of though. It seems some browsers try to be clever and optimizes transforms which only results in a size change in the image. The trick is to also apply a small rotation:
-webkit-transform: scale(1.2, 1.2) rotate(0.1deg);
transform: scale(1.2, 1.2) rotate(0.1deg);
Here's a demo: http://jsfiddle.net/g2Qrv/
It scales smoothly in Chrome, FF, IE10, Opera and Safari on iOS.

Related

if background = url() - issue on IE

I am trying to achieve a situation where an animation is triggered if an element has a particular background = url(" "). I am using velocity.js as the animation plugin and am able to achieve this no problem on all browser except IE, in which it fails to recognise the background url in the if statement.
I have two examples in the code below.
The first one (the orange square) uses -
if (box.style.background == "orange")
{ run(); }
to determine whether to run the animation. IE is fine with this.
However the second (the pink triangle) uses -
if (triangle.style.background == "url(http://garyvoss.co.uk/images/triangle.svg) no-repeat")
{ runTriangle(); }
to determine whether to run the animation. IE is the only browser not to run this.
Does anyone have any idea where I'm going wrong here.
JS Fiddle example
HTML
<div id="box"></div>
<div id="triangle"></div>
CSS
#box {
width: 100px;
height: 100px;
position: absolute; }
#triangle {
width: 200px;
height: 100px;
top: 120px;
position: absolute;
z-index: 4; }
JS
window.setInterval(function runAll() {
var box = document.getElementById('box');
box.style.background = "orange";
function run() {
Velocity(box, {left: 300}, {easing: "easeInOutQuad", duration: 1000, delay: 0});
Velocity(box, {left: 10}, {easing: "easeInOutQuad", duration: 1000, delay: 0});
}
if (box.style.background == "orange")
{ run(); }
var triangle = document.getElementById('triangle');
triangle.style.background = "url(http://garyvoss.co.uk/images/triangle.svg) no-repeat";
function runTriangle() {
Velocity(triangle, {left: 300}, {easing: "easeInOutQuad", duration: 1000, delay: 200});
Velocity(triangle, {left: 10}, {easing: "easeInOutQuad", duration: 1000, delay: 0});
}
if (triangle.style.background == "url(http://garyvoss.co.uk/images/triangle.svg) no-repeat")
{ runTriangle(); }
}, 1000);
runAll();
The URLs in IE are different from webkit and/or Gecko, it is not safe to base your logic on the url paths.
if (triangle.style.background == "url(http://garyvoss.co.uk/images/triangle.svg) no-repeat" || triangle.style.background == 'url("http://garyvoss.co.uk/images/triangle.svg") no-repeat' )
{ runTriangle(); }
I've managed to find a convoluted way around the issue.
Using .match I can identify a section of the url path and apply that to the if statement so that if that section is present then the animation runs.
I've create a third example (the green circle) in this JS Fiddle that shows how it works.
var str = circle.style.background;
var res = str.match(/circle/g);
document.getElementById("check").innerHTML = res;
if (document.getElementById("check").innerHTML === "circle")
{ runCircle(); }

CSS or JS transition for button that changes size from text changes?

I have an Angular app with a button that has a label of "+"
On mouse-over I call element.append(' Add a New Number'); This adds that text new to the + in the label.
Use clicks the button, new number is added, label of button is returned to "+"
I would like to animate the button size change and/or the txt label change. So far, just adding a css transition to width does nothing.
Thoughts?
UPDATE:
To help clarify, this is a bootstrap input group button. I don't want to set widths or css transforms, to avoid breaking the group either here or at other screen sizes.
here are the 2 states:
I was simply letting the existing button stretch due to the injection of more words.
I am probably guessing you don't have a predefined width. anyways you could use transform-origin and scale to achieve such an effect
FIDDLE HERE
HTML:
<button id="btn">Click</button>
CSS:
#btn {
outline: none;
border:none;
background: orange;
padding: 1em 1.5em;
-webkit-transition: .3s;
-o-transition: .3s;
transition: .3s;
}
#btn:hover {
-webkit-transform: scaleX(1.2);
-ms-transform: scaleX(1.2);
-o-transform: scaleX(1.2);
transform: scaleX(1.2);
-webkit-transform-origin:0 0;
-moz-transform-origin:0 0;
-ms-transform-origin:0 0;
-o-transform-origin:0 0;
transform-origin:0 0;
}
you should use CSS transforms for animations rather than a property like width. The animation is slightly jerky , so you might want to work on it a bit more.
You had jQuery tagged, so this is how I would do it.
All the transitions. fade + animate
function changeButtonText(button, text){
// jQuery it
$button = $(button);
// get orinal css'es
oooon = $button.css('text-align');
doooo = $button.css('overflow');
treee = $button.css('white-space');
$button.css('text-align', 'left').css('overflow', 'hidden').css('white-space', 'nowrap');;
// get new width first
$tmpBtn = $button.clone().append(text).css('opacity', '0.0').appendTo('body');
newWidth = $tmpBtn.outerWidth();
$tmpBtn.remove();
// now stretch the button out
$button.animate({width: newWidth+"px"});
// fade texts into the butt
$button.append('<span style="display:none">'+text+'</span>');
$btnText = $button.find('span').fadeIn('slow');
return {
'text-align':oooon,
'overflow':doooo,
'white-space':treee
};
}
Fiddle
I think that with bootstrap CSS and Angular - it will be more complex, but this is how I would go about it programatically. You'll have to deal with the model and the data differently - and you should probably build a directive to repeat the action and integrate with Angular smoothly:
HTML
<div class="thing">+ <span id="message">
<span id='target'></span>
</span></div>
JavaScript
$('.thing').hover( function() {
var originalWidth = $(this).outerWidth();
$messageHolder = $(this).find('#message');
$target = $(this).find('#target');
$target.text('Some helpful text');
var targetWidth = $target.outerWidth();
$messageHolder.animate({
width: targetWidth
}, {
duration: 200,
complete: function() {
$messageHolder.animate({
opacity: 1
}, 500);
}
});
});
$('.thing').on('click', function() {
$target = $(this).find('#target');
$target.empty();
$messageHolder = $(this).find('#message');
$messageHolder.animate({
opacity: 0
}, {
duration: 200,
complete: function() {
$messageHolder.animate({
width: 0
}, 200);
}
});
});
I'm sure that Angular's ng-animate library watches the dom and also has an excellent way of animating things as they change in the model/controller or whatever they are calling it. This is probably something what it looks like behind the scenes.
Good luck!
jsFiddle

jQuery animating issues?

So far, I have a pretty decent code to animate a center box. For some reason, when I preview this on different browsers and computer (Mac and PC) I get different results. One may show a faster animation speed, while the other is perfect. I have also noticed that when the box is being animated from left to right, there is a stutter, and the animation jerks. I can't really explain it more than that. My code is below:
$(document).ready(function(){
isAnimating = false;
$('.wrapper').on('click', '.arrow-left', function() {
if(isAnimating) return;
isAnimating = true;
var $current = $(this).parents('.signupBox');
var $next = $(this).parents('.signupBox').next();
$current.stop(true,true).animate({
left: "200%"
}, 500, 'linear', function() {
$current.css({
left: "-200%"
}).appendTo('.wrapper'); // move to end of stack
$next.css({
left: "-200%"
}).stop(true,true).animate({
left: "0%"
}, 500, 'linear', function() {
isAnimating = false;
});
});
}).on('click', '.arrow-right', function() {
if(isAnimating) return;
isAnimating = true;
var $current = $(this).parents('.signupBox');
var $next = $(this).parents('.signupBox').siblings().last();
$current.stop(true,true).animate({
left: "-200%"
}, 500, 'linear', function() {
$current.css({
left: "200%"
});
$next.prependTo('.wrapper') // move to front of stack
.css({
left: "200%"
}).stop(true,true).animate({
left: "0%"
}, 500, 'linear', function() {
isAnimating = false;
});
});
});
});
Some CSS:
.signupBox:first-child {
display: block;
}
.signupBox {
display: none;
}
.wrapper
{
overflow: hidden;
width: 100%;
height: 100%;
margin: 0px;
padding: 0px;
position: absolute;
}
Here's a JSFiddle showing what happens, hopefully you can see what's the issue from there.
Then animation from right to left (click on the < symbol and you will see a slign change in speed.
Different browsers could have different results when using JS and css transitions, it actually depends on your machine speed, browser speed as well. It could depend on how many opened tabs you have in each browser, browsers plugins could freeze animations as well. Other JS events as well.
I have tested your code in Chrome, FF (wasn't able to check it in IE11, it seems there are JS errors on jsfiddler using jQuery). Didn't mentioned something strange.
I could recommend to use Greenshock JS animating library. http://www.greensock.com/get-started-js/
They say it's x20 times faster jQuery animate. But i think actually may be in 2,3 )
That libruary is based on Flash library that was used by Action Script coders to create beauty animations in Flash.

-webkit-transform performance issues in chrome

We are using a jQuery plugin for some UI-effects. The plugin works great, but in chrome it melts the cpu's. The plugin tries to css-transform an image. Here is an image example:
<img width="1600" height="568" alt="" src="foo.png" style="width: 1598px; height: 567px; left: -209px; top: -2px; opacity: 1; transform-origin: center top 0px; transition-duration: 0s; transform: scale(1);">
and here the code that is causing the problem in chrome ($img beeing an jQuery object):
$img.css({
"-webkit-transition-duration":"20s",
"-webkit-transition-timing-function":"ease",
"-webkit-transform":"scale(0.73) rotate(0.1deg)",
"-webkit-perspective":"0"
});
The problematic part is "-webkit-transform". In Firefox there is no performance problem with the equivalent CSS transformation.
Is this issue known, is there an alternative way doing it?
EDIT:
Using the 3d variant does not solve the problem here:
$img.css({
"-webkit-transition-duration":"20s",
"-webkit-transition-timing-function":"ease",
"-webkit-transform":"scale3d(0.73,0.73,0.73) rotate3d(0,0,0,0.1deg)",
"-webkit-perspective":"0"
});
EDIT2:
After looking more into the chrome devtools timeline, I can see lots of "Composite Layers" events (every 15ms). I also noticed (after enabling the FPS counter) that the framerate always is about 60 FPS when using the css transformation.
If I use a simple $.animate() to scale the image the FPS is around 20 max and there are less "Composite Layers" events (about every 40ms).
Looks like the heavy (re-)painting causes the problem.
You should use 3d transformations with the depth transform as the identity transformation to force the GPU to handle the operation instead of the CPU. Use scale3d and rotate3d instead of scale and rotate.
To rorate images I use jQueryRotate plugin, to scale you can change width and height of img tag with jQuery animate method.
See this link.
$(document.body).ready(function(){
$("img").mouseover(function(){
var width = $(this).width();
var height = $(this).height();
var toResize = Math.random() * 20 - 10;
var newWidth = parseInt(width + toResize * width/height);
var newHeight = parseInt(height + toResize * height/width);
$(this).animate({
width: newWidth + 'px',
height: newHeight + 'px'
}, 100, function(){
//complete
});
var angle = Math.random() * 360;
$(this).rotate({animateTo: angle});
});
});

jQuery: animate to height of div

I have this function:
function fixedFeeSize(i){
var num1 = $('#num' + i);
if (num1.hasClass("extended")) {
num1.stop(true, true).animate({height: '59px'},500);
num1.removeClass("extended");
}else{
var height = 0;
num1.animate({height: '360px'},500);
num1.addClass("extended");
}
return null;
}
Which expands / contracts a div, however I am struggling to get it to expand to the height of the div as each div (there will be 10+) is going to be different heights.
I tried num1.animate({height: 'auto'},500); which has not worked.
Is this the effect you're after?
jQuery(".menu a").mouseenter(function(){
jQuery(this).stop().animate({
height: 200, opacity: 0.5
}, 1000, "linear");
});
jQuery(".menu a").mouseleave(function(){
jQuery(this).stop().animate({
height: 18, opacity: 1
}, 1000, "linear");
});
Here's a jsfiddle:
http://jsfiddle.net/kKAZx/1/
For further reading, take a look at the full documentation of the .animate function: http://api.jquery.com/animate/
You can combine effects to make all kinds of fantastic, standard-compliant effects.

Categories

Resources