I have a sequence with images of an object from different angles. I want the object to be faux rotated when the user drags its mouse, and this I have implemented.
But what I want, is when the mouse leaves the image area, that it animates the image sequence back to the default position.
For instance, I have 30 JPEGs, where 1.jpg is -180° and 30.jpg is 180°. Naturally, 15.jpg is the centered default image at 0°.
So, if the user rotates all the way to (-)180° it will rotate back to 0° after say 3 seconds. But I want the animation to be as smooth as possible. How would I go about to do this?
For the animation to be as smooth as possible, you should use a CSS sprite, an image containing all your other images, so the frames will all be loaded when the animation start.
Then you only need to call a function in a given amount of time, based on how smooth you want the animation to be, and change the background property of your image. Or, if not using sprite, assign a different src to it.
I think you should choose a frame-per-second value of no less than 25. Higher frame rate means more smooth animation, but more CPU used.
This is the basic approach
function next_frame() {
frame += 1;
// start animation again from the start if last frame reached (optional)
if ( frame == max_frames ) {
frame = 0;
}
/* change either the class of your image (if you use sprites
and specified a class for each background position) or the
background position property directly. If not using sprites,
assign a different image src based on current frame.
*/
// call the function again
setTimeout( next_frame, 1000 / 25 ); // change 25 with your desired FPS value.
}
If you want the image to animate back, you just need to apply the same approach but with the frame numbers going backwards.
Related
I created some 3D animated gifs using gnuplot. I am having trouble seeing all of the splotted data in the gifs.
Using the 'set view' command is not optimal as the splotting is automated and the data varies from plot to plot, so where one view might work for one set of data, it would not work for another set of data.
I think it would be good to let the user rotate the axis with his/her mouse in order to see different angles. I know this is possible in for instance the x11 terminal, but can it be done with a gif? Perhaps it is possible using the canvas terminal and JavaScript.
Another option might be to rotate the image around an axis automatically, so that when it loads it is already rotating, and continues to rotate as the data is animated.
Is there a way to do this in gnuplot?
Your second option is possible. You can set the viewing angle in each iteration.
set terminal gif animate delay 5 loop 0 optimize
set output "rot.gif"
unset surface
set pm3d at s
set label "sin(x)" at screen 0.7, 0.9
n = 100
do for [i=1:n] {
set view 60, i*360/n
splot sin(x) notitle
}
set output
Notes:
The key moves from iteration to iteration, so I have replaced it by a fixed label.
i*360/n creates one complete rotation of the plot within one complete animation
I want to make an animation in jQuery to move a div to another div smoothly but recalculating the coordinates of the final destination in case a user scrollsdown with his mouse for example.
The red square is absolute positioned and the blue square has a fixed position.
I had an idea of calling several times the stop() function and then animate again to the new position of the blue square but the transition isn't smooth at all.
Is there a way to set new destination from time to time but keeping it smooth?
http://codepen.io/anon/pen/gkLzh
I've created a jQuery plugin based on somebody else's Chrome experiment that inserts a canvas element into your target element, and draws an interactive starfield in the canvas.
Each time you resize the window, the canvas element is removed and then restored so that its size matches its parent element and everything animates properly; it's responsive.
However, whenever it's restored, the speed of the animation increases. Why does it do this? I thought all the variables (including speed) were reset to their defaults with the this.start() method.
You can see the code (and demo) on CodePen; you can also fork it on Github, though I think the Github version is several commits behind my own.
(Also, this is my first real jQuery plugin, so if you see any issues, by all means, let me know.)
Any clues?
Using cancelAnimationFrame alone won't necessary stop the animation loop (it turns out).
To be absolute sure you will need to use a conditional check as well - a generic example:
var isPlaying; /// our condition
function loop() {
/* funky stuff here */
If (isPlaying === true)
requestId = requestAnimationFrame(loop);
}
Then starting it:
functiom start() {
isPlaying = true;
loop();
}
Now when you want to stop the animation you need to do:
function stop() {
isPlaying = false;
/// kill any request in progress
if (requestId) cancelAnimationFrame(requestId);
}
If you don't do this you risk stacking calls to the loop - for example:
If you resize and cAF isn't stopping rAF from re-trigger the loop, the old loop will still run in the background and you will start a new loop on top of that.
That is why you see speed increases as the old and the new loop will both increment the positions before the stars are drawn to screen.
On the third re-size yet another loop is started, and eventually the whole thing will block the browser.
However
Instead of utilizing start and stop of the loop I would recommend you the following approach:
Create canvas once
Start the loop only once
In this case, a re-factoring of the whole re-size mechanism could be beneficial (for instance, separate needed initializations (width and height of element) from first time initializations that can be re-used later).
There is no need to re-init the stars for each re-size as you will use the width and height to check their boundaries (canvas will do the clipping).
When resizing you can consider using a conditional flags to prevent render while re-sizing.
Although generally, a condition to block rendering while canvas changes size is really not necessary due to the single-thread nature of JavaScript and in cases such as this you do boundary check on current element size. Canvas itself will take care of the clipping for you.
And that being said: there should be no need to re-create the canvas element each time.
This creates an unnecessary overhead. Simple set new width and height on its properties if canvas is already created:
if (typeof canvas === 'undefined')
canvas = /* only create if it doesn't exist */
canvas.width = width;
canvas.height = height;
PS: I "hampered" a version with some brute-force-ish implementations of the above. It's far from complete or of high quality but takes out some of the pain for the sake of example to give you a couple of pointers.
Please adopt to suit your needs.
Update:
To include more info from the additional comments:
when a new size is set on a canvas element its context is reset to default (fillStyle becomes transparent, strokeStyle black, transform is reset and so on).
This means that all non-default settings must be set again after each new size is set.
Setting a new size may (and typically do) clear the content of the canvas as well so that all pixels becomes transparent black.
For anybody struggling with manually updating a canvas element's dimensions:
Resizing the canvas element results in it discarding anything that's been drawn to it up to the point of the resize.
This script's animation should have continued to draw to the canvas after resize, but the only thing that would update was the fillRect of the background; the stars disappeared.
The only way to get the stars back after changing the dimensions of the canvas element: an extra call to context.strokeStyle. I have no idea why; if anybody could shed some light on the matter, I'd be much obliged.
Edit: As per comments below, everything in the canvas resets—including stroke and fill style (both default to black, apparently). So as the resize fires, I had to re-define stroke and fill styles.
I'm making a top-down shooter game that relies on the avatar always being rotated pointing to the mouse cursor. I achieve rotation like this:
//Rendering.
context.save(); //Save the context state, we're about to change it a lot.
context.translate(position[0] + picture.width/2, position[1] + picture.height/2); //Translate the context to the center of the image.
context.rotate(phi); //Rotate the context by the object's phi.
context.drawImage(picture.image, -picture.width/2, -picture.height/2); //Draw the image at the appropriate position (center of the image = [0, 0]).
context.restore(); //Get the state back.
When the phi is zero, the image is rendered in its normal quality, with sharp edges and detectable pixels. But, when I set the phi to a nonzero value (actually, when it's not 0, Pi/2, Pi, Pi+Pi/2 or 2Pi), the image looses it's sharpness and the individual pixels can't be seen anymore, because they are blurred out.
Here's a screenshot (sorry about the general bad quality of the screenshot, but I think that the difference is more than noticeable):
This is, well, a bit unacceptable. I can't have the images always blurred out! Why is this happening and can I solve it?
You could try
context.imageSmoothingEnabled = false;
See docs:
context.imageSmoothingEnabled [ = value ]
Returns whether pattern fills and the drawImage() method will attempt to smooth images if they have to rescale them (as opposed to just rendering the images with "big pixels").
Can be set, to change whether images are smoothed (true) or not (false).
If you want a true pixel-art retro style effect, you'd need to manually create rotated sprite images for several angles, look up the appropriate sprite for the current value of phi, and draw it without rotation. This obviously requires a fair amount of art work!
IF you are rotating images around their center point, make sure the image itself has an even number of pixels. Once you end up on odd coordinates the image data needs to be interpolated for the target canvas. Apple has some nice documentation on translating and rotating the canvas.
So for any image, as suggested above use rounding to snap to full pixels.
context.translate(Math.floor(img.width/2), Math.floor(img.height/2));
This way every source pixel of your image will always be drawn exactly into a pixel inside the canvas and blurring does not occur. This however is only true for multiples of 90 degrees.
It seems that all browsers do, to some extend, antialiasing in image drawing so you will probably have to provide rotated images as sprites.
According to this Chromium bug report you might be lucky there if they haven't fixed it yet. Read through and you'll learn that Ian Hickson likely opposed making antialiased image drawing optional.
(picture.width/2, picture.height/2) point won't always work.
(Math.floor(picture.width/2) + 0.5, Math.floor(picture.height/2) + 0.5) should help.
Well, actually it is something you cannot get around
If you rotate an image by a multiple of 90 degrees, your library should smart enough so that no interpolation is applied.
But as soon as you rotate an image by an angle different from a multiple of 90 degrees, you need to interpolate. As a consequence, you get that smoothing. If you are interested in the theory, you may look for a book on computer graphics or image processing.
For the concrete case of image rotation you may have a look at this paper,
http://bigwww.epfl.ch/publications/unser9502.html
I am trying to create a spot the ball game, so it will (eventually) be an image of a player kicking a ball but the ball has been removed and the player needs to click where the ball should be.
The first version went well and works.
http://enjoythespace.com/sites/game/test.html
But what I need to add is some sort of zooming so you can see more accurately where you are clicking. I been playing around and have come up with this
http://enjoythespace.com/sites/v2/demo.html
But once you click it looks great when zoomed in but when you go back to the image its way off.
I think its todo with how the image is setup, the #webpage is half the original size of the image and the #retina uses the full size of the image.
Any help?
The first problem is that you aren't setting the retina backgroundPosition correctly.
This code works (I added a zoom variable to make it clear how changing the zoom would change the calculation, but it would need other changes too):
/* Moving the retina div with the mouse
(and scrolling the background) */
zoom = 2.0;
retina.css({
left : left - sizes.retina.width/2,
top : top - sizes.retina.height/2,
backgroundPosition : ""+(-zoom*left+sizes.retina.width/2)+'px '+(-zoom*top+sizes.retina.height/2)+'px'
});
Test this by checking that all four corners are seen correctly in the retina, i.e. when you're over the corner of the main image, the corner should be in the center of the retina circle.
The second problem is if you resize the browser the position calculations are out because the offset variable isn't updated for the new size. A simple way to do this is to put this as the first line of webpage.mousemove() so the offsets are updated every time:
var offset = { left: webpage.offset().left, top: webpage.offset().top };
It looks like you are passing the top/left position click point of the zoomed image to highlight where you have clicked. What you will need to do is alter your top/left position based on whether the fisheye is over the image or not.
Does the un-zoomed image have to be part of the news page or can it be a standalone image?
If it can be standalone then the solution should be quite simple. If the zoomed in image is twice the size of the unzoomed one then you can just set the top/left values of the highlight to half the value of the zoomed, when looking at the unzoomed.
Jquery position will allow you to accurately get the position.
jQuery Position()