I just wonder how do the Canvas transformations work. Lets say i have a canvas with a circle drawn somewhere inside of it, and i want to scale the circle, so its center point will not move.
So i thought about doing the following:
translate(-circle.x, -circle.y);
scale(factor,factor);
translate(circle.x,circle.y);
// Now, Draw the circle by calling arc() and fill()
Is it the right way to do it? I just don't understand whether the canvas was designed to remember the order that i call the transformations.
Thanks.
Yes, you are correct.
The canvas accumulates all transforms and applies them to any future drawing.
So if you scale 2X, your circle will be drawn at 2X…and(!) every draw after that will be 2X.
That’s where saving the context is useful.
If you want to scale your circle by 2X but then have every subsequent drawing be at normal 1X you can use this pattern.
// save the current 1X context
Context.save();
// move (translate) to where you want your circle’s center to be
Context.translate(50,50)
// scale the context
Context.scale(2,2);
// draw your circle
// note: since we’re already translated to your circles center, we draw at [0,0].
Context.arc(0,0,25,0,Math.PI*2,false);
// restore the context to it’s beginning state: 1X and not-translated
Context.restore();
After Context.restore, your translate and scale will not apply to further drawings.
Related
Alright, so I have a good deal of experience with HTML and CSS, and some experience with Javascript (I can write basic functions and have coded in similar languages).
I'm looking to start some visual projects and am specifically interested in getting into particle systems. I have an idea for something similar to Codecademy's name generator here (https://www.codecademy.com/courses/animate-your-name/0/1) where particles are mapped to a word and move if hovered over. It seems as though alphabet.js is what's really behind Codecademy's demo however I can't understand exactly how they mapped the particles to a word, etc.
I've done some basic tutorials just creating rudimentary particles in a canvas but I'm not sure a canvas is the best way to go - demos that utilize one of the many libraries available (such as http://soulwire.github.io/sketch.js/examples/particles.html) don't use a canvas.
So my question is - what is the best way for a beginner/intermediate in Javascript to start with particle systems? Specifically to accomplish the Codecademy name effect or similar? Should I try to use canvas or which library would be best to start with and how would you recommend starting?
The code for this project is achievable for your intermediate JS programmer status.
How the CodeAcademy project works ...
Start by building each letter out of circles and saving each circle's centerpoint in an array. The alphabet.js script holds that array of circle centerpoints.
On mousemove events, test which circles are within a specified radius of the mouse position. Then animate each of those discovered circles radially outward from the mouse position using simple trigonometry.
When the mouse moves again, test which circles are no longer within the specified radius of the current mouse position. Then animate each of those "outside" circles back towards their original positions.
You can also use native html5 canvas without any libraries...
Another approach allowing any text to be "dissolved" and reassembled
Start by drawing the text on the canvas. BTW, this approach will "dissolve" any drawing, not just text.
Use context.getImageData to fetch the opacity value of every pixel on the canvas. Determine which pixels on the canvas contain parts of the text. You can tell if a pixel is part of the text because it will be opaque rather than transparent.
Now do the same procedure that CodeAcademy did with their circles -- but use your pixels:
On mousemove events, test which pixels are within a specified radius of the mouse position. Then animate each of those discovered pixels radially outward from the mouse position using simple trigonometry.
When the mouse moves again, test which pixels are no longer within the specified radius of the current mouse position. Then animate each of those "outside" pixels back towards their original positions.
[Addition: mousemove event to test if circles are within mouse distance]
Note: You probably want to keep an animation frame running that moves circles closer or further from their original positions based on a flag (isInside) for each circle.
function handleMouseMove(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calc the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// test each circle to see if it's inside or outside
// radius of 40px to current mouse position
// circles[] is an array of circle objects shaped like this
// {x:,y:,r:,originalX:,originalY:,isInside:}
var radius=40;
for(var i=0;i<circles.length;i++){
var c=circles[i];
var dx=c.x-mouseX;
var dy=c.y-mouseY;
if(dx*dx+dy*dy<radius*radius){
c.isInside=true;
// move c.x & c.y away from its originalX & originalY
}else{
c.isInside=false;
// if the circle is not already back at it's originalX, originalY
// then move c.x & c.y back towards its originalX, originalY
}
}
}
I want to draw a semi-complex element on a canvas rotated without rotating the canvas so that I don't need to calculate all of the various x/y points of the element.
I think that the basic process I need to use is:
translate the 0,0 point to the spot the drawn element will be rotated around,
rotate the canvas,
draw the element,
rotate the canvas back,
restore the origin point.
I will need to do this more than once. I've read that the rotate / rotate back part can introduce some error, with the final image being off just a bit. Is there a way to avoid this?
Before you perform the rotation and translation, call context.save(). This will create a snapshot of the current transformation of the canvas (as well as some other things, like drawing style, clip region, etc., but not the pixel data) and store it on a stack.
After you drew the shape, call context.restore(). This will pop the last saved state from the state stack and restore the current drawing state of the canvas to it.
You can do this as often as you want without accumulating any rounding differences.
Example function:
function drawImageRotated(x, y, rotation, image) {
context.save();
context.translate(x, y);
context.rotate(rotation);
context.drawImage(image, -image.width / 2, -image.height / 2);
context.restore();
// context translation and rotation are now on the same state they were
// before the function call
}
For more information about the canvas state, refer to the canvas specification.
I have problem with canvas createPattern. I have two boxes, both will move after pressing a keyarrow:
Example:
http://jsfiddle.net/wA73R/1/
The problem is that the box background filled by createPattern also is moving. How to avoid that? Is there any solution? The big box is only an example (drawImage is not the good solution for me, I need something that will repeat background image).
Thank you for help
The problem is that the box background filled by createPattern also is moving.
Actually your problem is that the background is not moving - it is static, while you are drawing your rectangle to different positions.
How to avoid that?
The pattern will always be drawn at the coordinate origin, whose actual position is defined by the current transformation. In future you will be able to transform the pattern itself with the setTransform method, but since that currently is not implemented anywhere you instead will have to change the global transformation matrix.
In your case it means, that instead of drawing your rectangle at x/y, you translate the whole context to x/y and draw your rectangle at 0/0 then:
ctx.fillStyle=pattern;
ctx.save();
ctx.translate(boxes[i].x - left , boxes[i].y);
ctx.fillRect(0, 0, boxes[i].width, boxes[i].height);
ctx.restore();
(updated demo)
I wanted to know if Html5 supports shape translation in canvas..For instance I have a rectangle,is it possible to apply a transformation to it?
canvas = document.getElementById('Canvas');
context =canvas.getContext('2d');
context.rect(myRectangle.x,myRectangle.y,myRectangle.width,myRectangle.height);
There's a few different methods for animating and changing the position that you want to draw your thingy. Either way, if you're after an animation, you're going to need to clear your canvas and keep drawing - like a flip book if you will.
Choices for setting the newly drawn item include:
moveTo - to move to the new position of your thing
translate - to translate the centre point of the canvas and keep the drawing positions the same, but move the underlying coordinate system
.rect(newX, newY, height, width) - drawing the specific position
I mocked together a (contrived) example of using translate on a canvas - which will move the the animating box around the position of your cursor. It's done in a loop - and I'd suggest checking out Paul Irish's article on requestAnimFrame for better animation loops. Here's the example: http://jsbin.com/afofur/2/edit#preview
As the comments say in the previous answer - SVG maintains a object model, so you can reference objects on the page, canvas is a bitmap API (basically), and once the pixels are committed to the canvas, there's no reference to the method or shape behind the drawing, it's just pixels to the canvas API.
No, once it is drawn to the canvas you can't change it anymore, there is no in-memory representation of the shapes you draw on the canvas. However, you can transform the canvas before you draw the shape and reset transform (canvas.setTransform(1, 0, 0, 1, 0, 0)) after you've drawn the shape.
Edit
Remember that the canvas API doesn't keep track of which objects you draw. It just fills the pixels with a color where you ask it to draw a rectangle. If you want to make animations, you will have to keep track of which rectangle you drawn yourself (make an object with properties x, y, width, height). Then you will have to do the following in each animation step:
clear the canvas
update the objects for the new time frame
redraw the canvas
You can find a tutorial here.
I'm drawing a symbol with simple lines, but want the user to be able to specify the rotation (in 90degrees only).
Which also means that the dimensions of my canvas change.
No I calculate the dimensions, and set the canvas size. Then I set the center of the rotation to the center of my canvas (via ctx.translate) and rotate to the arbitrary degrees.
Now my problem is: How do I set the translation back to the upper left corner, so I can draw my symbol normally from that position? Do I really have to calculate the values with the rotation?
Thanks.
Great question! translate rotate and scale are all functions that operate upon the current matrix. This gives you lots of options. Probably the simplest thing is to do a save restore on the context matrix
ctx.save();
ctx.translate ( to the center );
ctx.rotate ( do rotation );
//Draw rotated stuff
ctx.restore();
//Draw non-rotated stuff
Now after you call restore, the matrix reverts to how it was before the last save - In opengl, this is actually a stack, and you can push many contexts, but I'm not sure if webgl supports that.
This link may also be helpful: https://developer.mozilla.org/en/drawing_graphics_with_canvas
Hope this helps.
Update:
Yes. Okay, so you are misunderstanding something a little bit. The translations and rotations are applied before the drawing. This is because of a lot of complex math, and is really beyond the scope of this question. So if you want to draw part of your canvas rotated one way, and the other part of it rotated a different way, you first save, then apply the transformations, do the first part of the drawing, then restore to get back to the pre-transformed state. At which point, you can repeat.
So, for example, you can do this:
ctx.save();
ctx.translate ( x_center, y_center );
ctx.rotate ( 90 );
//Draw your rotated stuff starting at the center
ctx.translate ( -x_center, -y_center );
//Draw your main frame stuff that is all rotated around the center
ctx.restore();
ctx.save();
ctx.rotate ( 90 );
//Draw your text which is rotated around the top-left corner
ctx.restore();
In this way, you have 1 drawing function, and you simply setup a context before you draw the different components.