Infinite rotation with snap.svg - javascript

Is there a way for a svg element to be rotating infinitely?
Snap offers a way to rotate by a certain amount, but how can I make to rotate infinitely?

There's a few different ways to do this, As Alvin and Monkeyinsight say, you can do it with CSS, and also as mentioned do it with SVG markup. I'm guessing you don't want SVG markup as thats probably why you are using Snap in the first place (although you could always element.parse( as an inbetween stage maybe)
Just as an alternative, as sometimes you need to combine more complex bits, I'll show how to do an infinite animation in Snap with a callback...
var s = Snap("#svg");
var block = s.rect(100, 100, 100, 100);
function infRotate( el ) {
el.transform('r0,150,150'); // some kind of transform reset, or removing the previous completed transform maybe needed.
el.animate({ transform: 'r360,150,150' }, 2000, mina.linear, infRotate.bind( null, el));
};
infRotate( block );
This has a simple callback on the completion of an animation, so this could be used for more complex functions.
jsfiddle
I suspect the CSS/SVG markup solutions would be more efficient, but possibly less flexible with other Snap stuff.
Edit: You may want to keep an eye on memory if this would be in a situation where it really would be running for a long time.

Related

Animating SVG elements with GSAP

I am tasked with creating an interactive SVG infographic with few slides for a browser game, it has 5 steps total. When you click prev / next buttons or a specific step, the whole svg should animate itself to the correct step.
In terms of code, this is what I have:
function Slide() {
// Cache all top-level svg elements for later use
this.el = $('svg#betfair-slider');
this.hands = this.el.find('#hands_8_');
this.cardsDesk = this.el.find('g#center-cards');
this.textContainer = $('.step-text');
// Use a shared timeline across all slides, so there are no overlapping tweens
this.tween = new TimelineLite();
}
function Slide2 () {
// Inherit from the main slide
Slide.call(this);
// each slide has its own supporting explanatory text
this.html = [
'<h3>Preflop</h3>',
'<p>Amet sit lorem ipsum dolar</p>'
].join('');
// the main openslide method
this.openSlide = function() {
// find the cards that need to be animated for this specific slide
var cards = this.hands.find('.card');
// fade out each card
this.tween.staggerTo(cards, 0.2, { opacity: 0 }, 0.2);
// Render supporting text
this.textContainer.html(this.html);
};
}
Here are the cards I am trying to get:
I am able to fetch them using the this.hands.find('.card'); jquery method (I can see them in the console), but I just can't animate them. I have tried animating different attributes (opacity, x, y, left, etc), but nothing happens - nothing moves on the screen.
I am able to do this.hands.find('.card').hide() and change the css using jQuery, but why TimelineLite doesn't work here? I also tried this approach:
var cards = this.hands.find('.card');
for (var i = cards.length - 1; i >= 0; i -= 1) {
var card = cards[i];
this.tween.to(card, 0.2, { opacity: 0 });
}
But still no success... The interesting thing is that when I attach an onComplete callback on the tweens, THEY ARE fired, but absolutely nothing moves on the screen. You can check out everything here.
Thanks for the help in advance, any suggestions are more then welcome.
I think you might be misunderstanding how timelines work (or perhaps I'm misunderstanding your intent). Like all tweens, timelines start playing immediately by default. So if you're using one timeline and shoving all your tweens in there based on user interaction (meaning time elapses between when you create it and when you populate it...and populate it more...), its playhead will already have advanced. I bet that's causing your tweens to jump to their end state almost immediately. That's not a bug - it's how things are supposed to work, although there's some logic in GSAP to adjust behavior in certain circumstances to make it more intuitive.
I see several options:
Just use TweenMax instead of a TimelineLite. Like TweenMax.staggerTo(...) and TweenMax.to(...).
Or just create a TimelineLite for each user interaction (instead of one for ALL of your tweens globally). That way you'll populate it right away and it'll play as you expected.
You could use one global timeline if you really want to, but you might just need to manage its playhead if you're going to be inserting tweens on each user interaction rather than all at once.
If you're still having trouble, don't hesitate to post in the GSAP-dedicated forums at http://greensock.com/forums/ and it's be super helpful if you included a codepen or jsfiddle reduced test case so that we can tinker and very quickly see what's going on and how the changes affect the result.
Happy tweening!

CSS/JS scrolling glitch effect (performance)

I am trying to achieve a "crt-like" scrolling glitch effect using Javascript and CSS. I have come up with the following code which clones the content and applies clip to make it look like it scrolls while adding random horizontal offset.
function scanglitch() {
var e = $('#wrapper').clone().appendTo('#glitchcontainer');
var i = 0;
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css("z-index",200);
var interval = setInterval(function () {
e.css({"clip": "rect(" + i + "px,3830px," + (i + 15) + "px,0px)"});
e.css({"left": Math.round(Math.random() * 10) + "px"});
i+=4;
if (i > window.innerHeight) {
e.remove();
window.clearInterval(interval);
}
}, 40);
}
Fiddle (Click on the text to see the effect)
I am actually quite pleased with the effect, but the implementation is obviously a hack. As a result the performance is unacceptably low (chrome cpu usage spikes from 5% to 50% when the effect is triggered).
Could someone help me figure out a way to achieve this effect in a more elegant and less performance-heavy way?
UPDATE:
I have implemented your suggestions: Using translate instead of left, scrolling with translate instead of a js loop, calculations outside of the css tag and using requestAnimationFrame(). The code is nicer and more predictable now, but the animations are still very taxing.
New fiddle
You can try using requestAnimationFrame (it is available in almost all browsers). Seems to make a big difference in Chrome.
JSFiddle
EDIT
Here's a transition-only version, and while it doesn't even implement the jitter, it's useful for comparison. Surprisingly(?) it shows about the same, if not more, CPU usage. (You can check the Profile and Timeline tabs in Chrome)
CSS3 Transition-Only JSFiddle
Here's some information about why that should be expected. Essentially, CSS transitions and requestAnimationFrame work very similarly under the hood.
I would delegate as much as possible to css transitions. So instead of moving the clip with js in the interval callback, transition it from top to bottom (example of transitioning).
You could try something similar with the left property, there's no random easing function but maybe you could achieve something similar with one of the bounce functions. Maybe change the easing function with an interval that's less frequent than your current one.
Also, just by slowing the interval of your current solution you'd get visually ok results with less CPU usage.
Side-note: for a completely different route you can replicate your html in a canvas and apply some effects to that. Google has plenty of results for "canvas glitch".
Update: here's my version of your latest fiddle
I get about 10 % less cpu usage with it when comparing to yours. Key differences are:
uses a timeout instead of requestAnimationFrame. requestAnimationFrame is meant to keep framerate high and the animation smooth but we don't need that for the random offsetting. Timeout is also better than an interval since the loop function is quaranteed to finish before next iteration starts.
removed the transparent background, since transparency has a rendering cost

Is it okay to use .animate with 0 duration all the time? Is there a better alternative?

I've grown used to code my jquery animation by doing the following.
Set the initial state of the element (things like width, height, top, left, opacity, etc...) using either css or javascript. This initial state is the state at the end of the animation.
Use .animate with 0 duration to move the element to the state in which elements are at the beginning of the animation.
Use a regular .animate with the appropriate duration to do the animation in which at the end, the elements are back to state in step 1.
Here is an example.
/* css file */
.animatedObject
{
width:60%;
}
// javascript file
$('.animatedObject')
.animate({width: '-=20%'}, 0)
.animate({width: '+=20%'}, 1000);
There are several advantage using this code at least for me. First, it looks clear to me what I'm trying to animate. Second, if javascript is disabled, the object would be at the end state of the animation which is often where I want it to be for graceful degradation. Third, objects can change position slightly for adjustments using css and the animation would still look largely the same.
The reason I'm not using the .css is that it if I try to replace the animate with 0 duration with the .css method, I would get differing animation starting point. I don't think it support += or -=, or if it does, it behaves differently.
So, is this a good way to do this? What is the industry standard way?
Well you could still use .css():
var anObj = $('.animatedObject');
anObj.css("width", anObj.css("width") - (20 / anObj.css("width"))).animate({
width: '+=20%'
}, 1000);
I believe this would work faster.
Edit:
I did a little benchmark for you, using jsperf.com. Here are my results (using Google Chrome):
.animate({}, 0)
Code:
$('.animatedObject')
.animate({width: '-=20%'}, 0)
.animate({width: '+=20%'}, 1000);
End Results:
10,013 operations per second
±7.48%
fastest
.css();
Code:
var anObj = $('.animatedObject');
anObj.css("width", anObj.css("width") - (20 / anObj.css("width"))).animate({
width: '+=20%'
}, 1000);
End Results:
2,477 operations per second
±6.39%
75% slower
Conclusion
Keep the animation function. It turns out using .css() is actually slower. I guess it's the fact that you have 2 extra functions. It's not worth it. This was actually a surprise to me as I thought .css() would function faster than it did. Test this yourself in your browser!

.mousemove and memory, do I need to optimize this or not?

I've created a simple demo of a light-test-thing here: http://jsfiddle.net/CGr9d/
When I record the memory usage using the Chrome dev tools I get this: http://cl.ly/LSDl, it basically go up until a certain point and then go down again and start over until it reaches the previous high point again.
is this normal/OK?
Is there any way to optimize my code to make it less memory intensive?
This is my mousemove function:
$('body').mousemove(function(e) {
//2000 is half the image width/height, of course used for centering
$('.light-circle').css({ backgroundPosition: (e.pageX-2000)+'px '+(e.pageY-2000)+'px' });
});
Thanks.
If the elements matching the selector .light-circle don't change, you can optimize a bit like this:
var circles = $('.light-circle');
$('body').mousemove(function(e) {
//2000 is half the image width/height, of course used for centering
circles.css({ backgroundPosition: (e.pageX-2000)+'px '+(e.pageY-2000)+'px' });
});
That way you're not re-querying the DOM, allocating a new jQuery object, etc., etc., on every mouse move.
But it's perfectly normal in a garbage collected environment to see memory increase, then decrease, then increase again.

Subtle font size animation over short distances with long durations in jQuery

I want to animate some text in a slow and subtle way. jQuery animations work with integer values, so if you want to animate a size over say 10px in two seconds, you see a series of small steps at five FPS which looks jerky.
Here's a jsFiddle that shows what I'm talking about.
I found a similar question about animating positions, but the top/left/etc properties are integral, and so the accepted answer says it's not possible. However font-size can be animated as a real number, if jQuery will spit real numbers out.
I will also want to chain together a series of such animations.
Any ideas?
I had another look a the minimum point value which is visually recognisable. The smallest unit for pt I noticed change in was 0.2pt.
However, I noticed that when applying the change in steps of 0.2 points over a period of 1 millisecond per increment in a while loop that it still looked a little "laggy". May not if not running in jsfiddle though.
The point is that if you want to change the font-size smoothly by 10 points you must apply the change in steps of 0.2pt or 0.25pt or 0.5pt (what ever you find smoothest) at a time and you then must use an interval of 1 to stay smooth but you should not apply a different interval as otherwise the incremental steps are to small to notice and the whole animation ends up choppy again.
I think trying to force this 10pt change over 2 seconds will always look choppy no matter what framework you use, due to the lack of visual change on the font-size in the lower decimals.
This worked for me quite well:
(I'm not taking decreasing font-size animation into account in this example but that can be added off course)
function smoothAnimation($selector, startPoints, points){
var increments = 0.2;
var currentPoints = startPoints;
var endPoints = currentPoints + points;
while(currentPoints < endPoints){
currentPoints += increments;
$selector.animate({"font-size": currentPoints.toString() + "pt"}, 1, "swing");
}
}
$('#msg').click(function() {
$msg = $('#msg');
$msg.animate({"font-size": "80pt"}, 400, "swing");
smoothAnimation($msg, 80, 10);
$msg.animate({"font-size": "40pt"}, 400, "swing");
});
DEMO - smooth(ish) font-size animation
To make it look smoother increase the increments value to 0.25 or even 0.5. Ones you have nice smooth step you can set the points to any other value and the animation stays smooth as long as you don't force a 2 second animation interval.
Another option would be direct manipulations css transformations - here's an example (I only included webkit css to make it readable, but similar functions exist in all modern browsers). The "ease-out" property includes the fast-then-slow functionality you were aiming for. It's certainly smoother than what you've been able to get so far, but it's quite blurry - not sure if it's a trade-off you'd want to make. And as it's an experimental property, you'd still probably want your existing jQuery animation as a fallback for older browsers.
jQuery animations are terrible. Look into another tweening solution that utilizes requestAnimationFrame technique or a better timing mechanism. Maybe try a tween lib like tween.js look at the Rome demo, nice slow moving clouds...
Right now, only firefox has support for sub-pixel rendering of fonts, so animations in other browsers will always snap to pixel.

Categories