I would like to render an object to the canvas which I rotate depending on its direction. However this object consist of multiple other objects which also should be able to rotate independently.
As far as I understand the saving and restoring of the context is the problematic part, but how do I achieve this?
class MotherObject {
childObject1: ChildObject;
childObject2: ChildObject;
constructor(private x: number, private y: number, private direction: number, private ctx: CanvasRenderingContext2D) {
this.childObject1 = new ChildObject(this.x + 50, this.y, 45, this.ctx);
this.childObject2 = new ChildObject(this.x - 50, this.y, 135, this.ctx);
}
render(): void {
this.ctx.save();
this.ctx.translate(this.x, this.y);
this.ctx.rotate(this.direction * Math.PI / 180);
this.childObject1.render();
this.childObject2.render();
this.ctx.restore();
}
}
class ChildObject {
constructor(private x: number, private y: number, private direction: number, private ctx: CanvasRenderingContext2D) { }
render(): void {
this.ctx.save();
this.ctx.translate(this.x, this.y);
this.ctx.rotate(this.direction * Math.PI / 180);
this.ctx.fillRect(0, 0, 100, 20);
this.ctx.restore();
}
}
A complete image render function.
The 2D API allows you to draw an image that is scaled, rotated, fade in/out. Rendering a image like this is sometimes called a sprite (From the old 16bit days)
Function to draw a scaled rotated faded image / sprite with the rotation around its center. x and y are the position on the canvas where the center will be. scale is 1 for no scale <1 for smaller, and >1 for larger. rot is the rotation with 0 being no rotation. Math.PI is 180 deg. Increasing rot will rotate in a clockwise direction decreasing will rotate the other way. alpha will set how transparent the image will be with 0 being invisible and 1 as fully visible. Trying to set global alpha with a value outside 0-1 range will result in no change. The code below does a check to ensure that alpha is clamped. If you trust the alpha value you can set globalAlpha directly
You can call this function without needing to restore the state as setTransform replaces the existing transformation rather than multiplying it as is done with ctx.translate, ctx.scale, ctx.rotate, ctx.transform
function drawSprite(image,x,y,scale,rot,alpha){
// if you want non uniform scaling just replace the scale
// argument with scaleX, scaleY and use
// ctx.setTransform(scaleX,0,0,scaleY,x,y);
ctx.setTransform(scale,0,0,scale,x,y);
ctx.rotate(rot);
ctx.globalAlpha = alpha < 0 ? 0 : alpha > 1 ? 1 : alpha; // if you may have
// alpha values outside
// the normal range
ctx.drawImage(image,-image.width / 2, -image.height / 2);
}
// usage
drawSprite(image,x,y,1,0,1); // draws image without rotation or scale
drawSprite(image,x,y,0.5,Math.PI/2,0.5); // draws image rotated 90 deg
// scaled to half its size
// and semi transparent
The function leaves the current transform and alpha as is. If you render elsewhere (not using this function) you need to reset the current state of the 2D context.
To default
ctx.setTransform(1,0,0,1,0,0);
ctx.globalAlpha = 1;
To keep the current state use
ctx.save();
// draw all the sprites
ctx.restore();
Related
I am trying to treat a rectangle in canvas as a piece of paper, and get the same relative coordinates returned when I hover over the same point regardless of scale, rotation, or translation of the page.
Currently I get accurate results when in portrait or inverted portrait rotations and regardless of scale/translation. However, when I switch to landscape or inverted landscape my results are off.
I've attempted to switch to rotating mouse coordinates with some trigonometric functions I found, but math is not my strong suit and it didn't work.
If someone could point me in the right direction, I would be grateful. I suspect I need to swap axis or height/width when rotating landscape but that hasn't been fruitful either.
R key rotates the "page" through 4, 90 degree changes. Coordinates of your mouse relative to the page, clamped to the page's width/height are displayed in console.
https://jsfiddle.net/2hg6u3wd/2/ (Note, JSFiddle offsets coordinates slightly for an unknown reason)
const orientation = Object.freeze({
portrait: 0,
landscape: -90,
invertedPortrait: 180,
invertedLandscape: 90
});
function CameraRotate(rotation) {
// Rotates using the center of target as origin.
ctx.translate(target.width / 2, target.height / 2);
ctx.rotate(-(currentRot * Math.PI / 180)); // Negate currentRot because ctx.rotate() is additive.
ctx.rotate(rotation * Math.PI / 180);
ctx.translate(-(target.width / 2), -(target.height / 2));
currentRot = rotation;
}
function CameraCalcRelTargetCoords(viewX, viewY) {
return {
x: clamp((viewX - ctx.getTransform().e) / ctx.getTransform().a, 0, page.width),
y: clamp((viewY - ctx.getTransform().f) / ctx.getTransform().d, 0, page.height)
};
}
function clamp(number, min, max) {
return Math.max(min, Math.min(number, max));
}
canvas.addEventListener(`mousemove`, function(e) {
console.log(CameraCalcRelTargetCoords(e.x, e.y));
});
When rotating (-)180 degrees, the scale is stored as skew since the axis are flipped. Thus you must divide by m12 and m21. This flipping also means x and y mouse coordinates need to be swapped as well.
Here is my solution:
function CameraCalcRelTargetCoords(viewX, viewY) {
// Mouse coordinates are translated to a position within the target's rectangle.
let relX, relY
if (currentRot == orientation.landscape || currentRot == orientation.invertedLandscape) {
// Landscape rotation uses skewing for scale as X/Y axis are flipped.
relX = clamp((viewY - ctx.getTransform().f) / ctx.getTransform().b, 0, target.width);
relY = clamp((viewX - ctx.getTransform().e) / ctx.getTransform().c, 0, target.height);
} else {
relX = clamp((viewX - ctx.getTransform().e) / ctx.getTransform().a, 0, target.width),
relY = clamp((viewY - ctx.getTransform().f) / ctx.getTransform().d, 0, target.height)
}
return {x: relX, y: relY};
}
I'm trying to make a pixel editor with 2 canvas. The first canvas displays a second canvas which contains the pixels. The first canvas uses drawImage to position and scale the second canvas.
When the second canvas is scaled smaller than it's original size, it starts to glitch.
Here is the canvas displayed at it's original size. When I zoom in, the second canvas get bigger and everything works perfectly.
However when I zoom out, the grid and the background (transparency) act very strangely.
To draw the second canvas on the first canvas, I use the function
ctx.drawImage(drawCanvas, offset.x, offset.y, width * pixelSize, height * pixelSize);
I have read that scaling in multiple iterations might give a better quality with images but I am not sure about a canvas.
I could fully redraw the second canvas in a lower resolution when the user zooms out, but it is a bit heavy on the cpu.
Is there any better solution that I don't know of?
Your problem comes from anti-aliasing.
Pixels aren't sub-divisible, and when you ask the computer to draw something outside of the pixel boundaries, it will try its best to render something that usually looks good to eyes, by mixing the colors so that what should have been a black 0.1 pixel line will become a light-gray pixel for instance.
This generally works good, particularly with pictures of the real word, or complex shapes like circles. However with grids... That's not so great as you experienced it.
Your case is dealing with two different cases, and you will have to deal with hem separately.
In the canvas 2D API (and a lot of 2D APIs) stroke do bleed from both sides of the coordinates you did set it. So when drawing lines of 1px wide, you need to account for a 0.5px offset to be sure it won't get rendered as two gray pixels. For more info about this, see this answer. You are probably using such a stroke for the grid.
fill on the other hand only covers the inside of the shape, so if you fill a rectangle, you need to not offset its coords from the px boundaries. This is required for the checkerboard.
Now, for boh these drawings, the best is probably to use patterns. You only need to draw a small version of it, and then the pattern will repeat it automatically, saving a lot of computation.
Scaling of a pattern can be done by calling the transform methods of the 2D context. We can even take advantage of the closest-neighbor algorithm to avoid antialising when drawing this pattern by setting the imageSmoothingEnabled property to false.
However for our grid, we may want to keep the lineWidth constant. For this we will need to generate a new pattern at every draw call.
// An helper function to create CanvasPatterns
// returns a 2DContext on which a simple `finalize` method is attached
// method which does return a CanvasPattern from the underlying canvas
function patternMaker(width, height) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.finalize = (repetition = "repeat") => ctx.createPattern(canvas, repetition);
return ctx;
}
// The checkerboard can be generated only once
const checkerboard_patt_maker = patternMaker(2, 2);
checkerboard_patt_maker.fillStyle = "#CCC";
checkerboard_patt_maker.fillRect(0,0,1,1);
checkerboard_patt_maker.fillRect(1,1,1,1);
const checkerboard_patt = checkerboard_patt_maker.finalize();
// An helper function to create grid patterns
// Since we want a constant lineWidth, no matter the zoom level
function makeGridPattern(width, height) {
width = Math.round(width);
height = Math.round(height);
const grid_patt_maker = patternMaker(width, height);
grid_patt_maker.lineWidth = 1;
// apply the 0.5 offset only if we are on integer coords
// for instance a <3,3> pattern wouldn't need any offset, 1.5 is already perfect
const x = width/2 % 1 ? width/2 : width/2 + 0.5;
const y = height/2 % 1 ? height/2 : height/2 + 0.5;
grid_patt_maker.moveTo(x, 0);
grid_patt_maker.lineTo(x, height);
grid_patt_maker.moveTo(0, y);
grid_patt_maker.lineTo(width, y);
grid_patt_maker.stroke();
return grid_patt_maker.finalize();
}
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const checkerboard_input = document.getElementById('checkerboard_input');
const grid_input = document.getElementById('grid_input');
const connector = document.getElementById('connector');
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const checkerboard_zoom = checkerboard_input.value;
const grid_zoom = grid_input.value;
// we generate a new pattern for the grid, so the lineWidth is always 1
const grid_patt = makeGridPattern(grid_zoom, grid_zoom);
// draw once the rectangle covering the whole canvas
// with normal transforms
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
// the checkerboard
ctx.fillStyle = checkerboard_patt;
// our path is already drawn, we can control only the fill
ctx.scale(checkerboard_zoom, checkerboard_zoom);
// avoid antialiasing when painting our pattern (similar to rounding the zoom level)
ctx.imageSmoothingEnabled = false;
ctx.fill();
// done, reset to normal
ctx.imageSmoothingEnabled = true;
ctx.setTransform(1, 0, 0, 1, 0, 0);
// paint the grid
ctx.fillStyle = grid_patt;
// because our grid is drawn in the middle of the pattern
ctx.translate(Math.round(grid_zoom/2), Math.round(grid_zoom/2));
ctx.fill();
// reset
ctx.setTransform(1, 0, 0, 1, 0, 0);
}
draw();
checkerboard_input.oninput = grid_input.oninput = function(e) {
if(connector.checked) {
checkerboard_input.value = grid_input.value = this.value;
}
draw();
};
connector.oninput = e => checkerboard_input.oninput();
<label>checkerboard-layer zoom<input id="checkerboard_input" type="range" min="2" max="50" step="0.1"></label><br>
<label>grid-layer zoom<input id="grid_input" type="range" min="2" max="50" step="1"></label><br>
<label>connect both zooms<input id="connector" type="checkbox"></label>
<canvas id="canvas"></canvas>
I am trying to move an object smoothly from point A to point B using HTML canvas and regular javascript.
Point A is a set of coordinates
Point B is in the case the cursor location.
I made a jsfiddle of what I have so far: https://jsfiddle.net/as9fhmw8/
while(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
ctx.save();
ctx.beginPath();
ctx.translate(projectile.x, projectile.y);
ctx.arc(0,0,5,0,2*Math.PI);
ctx.fillStyle = "blue";
ctx.fill();
ctx.stroke();
ctx.restore();
if(projectile.mouseX > projectile.x && projectile.mouseY < projectile.y)
{
var stepsize = (projectile.mouseX - projectile.x) / (projectile.y - projectile.mouseY);
projectile.x += (stepsize + 1);
}
if(projectile.mouseY < projectile.y)
{
var stepsize = (projectile.y - projectile.mouseY) / (projectile.mouseX - projectile.x);
projectile.y -= (stepsize + 1);
}
}
Essentially what I can't figure out to do is to make the while loop slower (so that it appears animated in stead of just going through every iteration and showing the result).
I also can't figure out how to prevent the Arc from duplicating so that it creates a line that is permanent, instead of appearing to move from point a to point b.
Smooth animation here is really about determining how far to move your object for each iteration of the loop.
There is a little math involved here, but it's not too bad.
Velocity
Velocity in your case is just the speed at which your particles travel in any given direction over a period of time. If you want your particle to travel 200px over the course of 4 seconds, then the velocity would be 50px / second.
With this information, you can easily determine how many pixels to move (animate) a particle given some arbitrary length of time.
pixels = pixelsPerSecond * seconds
This is great to know how many pixels to move, but doesn't translate into individual X and Y coordinates. That's where vectors come in.
Vectors
A vector in mathematics is a measurement of both direction and magnitude. For our purposes, it's like combining our velocity with an angle (47°).
One of the great properties of vectors is it can be broken down into it's individual X and Y components (for 2-Dimensional space).
So if we wanted to move our particle at 50px / second at a 47° angle, we could calculate a vector for that like so:
function Vector(magnitude, angle){
var angleRadians = (angle * Math.PI) / 180;
this.magnitudeX = magnitude * Math.cos(angleRadians);
this.magnitudeY = magnitude * Math.sin(angleRadians);
}
var moveVector = new Vector(50, 47);
The wonderful thing about this is that these values can simply be added to any set of X and Y coordinates to move them based on your velocity calculation.
Mouse Move Vector
Modeling your objects in this way has the added benefit of making things nice and mathematically consistent. The distance between your particle and the mouse is just another vector.
We can back calculate both the distance and angle using a little bit more math. Remember that guy Pythagoras? Turns out he was pretty smart.
function distanceAndAngleBetweenTwoPoints(x1, y1, x2, y2){
var x = x2 - x1,
y = y2 - y1;
return {
// x^2 + y^2 = r^2
distance: Math.sqrt(x * x + y * y),
// convert from radians to degrees
angle: Math.atan2(y, x) * 180 / Math.PI
}
}
var mouseCoords = getMouseCoords();
var data = distanceAndAngleBetweenTwoPoints(particle.x, particle.y, mouse.x, mouse.y);
//Spread movement out over three seconds
var velocity = data.distance / 3;
var toMouseVector = new Vector(velocity, data.angle);
Smoothly Animating
Animating your stuff around the screen in a way that isn't jerky means doing the following:
Run your animation loop as fast as possible
Determine how much time has passed since last time
Move each item based on elapsed time.
Re-paint the screen
For the animation loop, I would use the requestAnimationFrame API instead of setInterval as it will have better overall performance.
Clearing The Screen
Also when you re-paint the screen, just draw a big rectangle over the entire thing in whatever background color you want before re-drawing your items.
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
Putting It All Together
Here is a Fiddle demonstrating all these techniques: https://jsfiddle.net/jwcarroll/2r69j1ok/3/
Currently I have this fiddle from Blindman67 which draws Golden spiral figure 1(see image below).
function renderSpiral(pointA, pointB, turns){
var dx, dy, rad, i, ang, cx, cy, dist, a, c, angleStep, numberTurns, nTFPB, scale, styles;
// clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width,ctx.canvas.height)
// spiral stuff
a = 1; // the larger this number the larger the spiral
c = 1.358456; // constant See https://en.wikipedia.org/wiki/Golden_spiral
angleStep = Math.PI/20; // set the angular resultion for drawing
numberTurns = 6; // total half turns drawn
nTFPB = 2; // numberOfTurnsForPointB is the number of turns to point
// B should be integer and describes the number off
// turns made befor reaching point B
// get the ang from pointA to B
ang = Math.atan2(pointB.y-pointA.y,pointB.x-pointA.x);
// get the distance from A to B
dist = Math.sqrt(Math.pow(pointB.y-pointA.y,2)+Math.pow(pointB.x-pointA.x,2));
if(dist === 0){
return; // this makes no sense so exit as nothing to draw
}
// get the spiral radius at point B
rad = Math.pow(c,ang + nTFPB * 2 * Math.PI); // spiral radius at point2
// now just need to get the correct scale so the spiral fist to the
// constraints requiered.
scale = dist / rad;
// ajust the number of turns so that the spiral fills the canvas
while(Math.pow(c,Math.PI*numberTurns)*scale < ctx.canvas.width){
numberTurns += 2;
}
// set the scale, and origin to centre
ctx.setTransform(scale, 0, 0, scale, pointA.x, pointA.y)
// make it look nice create some line styles
// first just draw the line A-B
ctx.strokeStyle = "black";
ctx.lineWidth = 2 * ( 1 / scale); // because it is scaled invert the scale
// can calculate the width requiered
// ready to draw
ctx.beginPath();
ctx.moveTo(0, 0) // start at center
ctx.lineTo((pointB.x-pointA.x)*(1/scale),(pointB.y-pointA.y)*(1/scale) ); // add line
ctx.stroke(); // draw it all
// Now draw the sporal. draw it for each style
styles.forEach( function(style) {
ctx.strokeStyle = style.colour;
ctx.lineWidth = style.width * ( 1 / scale); // because it is scaled invert the scale
// can calculate the width requiered
// ready to draw
ctx.beginPath();
for( i = 0; i <= Math.PI *numberTurns; i+= angleStep){
dx = Math.cos(i); // get the vector for angle i
dy = Math.sin(i);
var rad = Math.pow(c, i); // calculate the radius
if(i === 0) {
ctx.moveTo(0, 0) // start at center
}else{
ctx.lineTo(dx * rad, dy * rad ); // add line
}
}
ctx.stroke(); // draw it all
});
ctx.setTransform(1,0,0,1,0,0); // reset tranfrom to default;
}
What I want to obtain is figure 2 (see image below).
Q1. How can I change mine spiral so line AB will fit between first and second screw while A is the start of spiral?
You can also refer to my earlier question for better understanding of my problem.
To achieve the properties you need you need to adjust your spiral like following:
choose the right angular position of the line AB
I choose 1.5*M_PI [rad] for A and 3.5*M_PI [rad] for B (on unrotated spiral)
rotate your spiral by angle of your AB line
that is easy just add the angle to the final polar -> cartesian coordinates conversion and that will rotate entire spiral so computed angular positions of A,B on spiral will match the real points AB direction
rescale your spiral to match the AB size
So compute the radiuses for angular points A,B positons on spiral and then compute the scale=|AB|-(r(b)-r(a)). Now just multiply this to compute radius of each rendered point ...
I played a bit with the golden ratio and spiral a bit and here is the result
Yellow spiral is approximation by quarter circle arcs
Aqua is the Golden spiral
As you can see they do not match so much (this is with ratio*0.75 to make them more similar but it should be just ratio) Either I have a bug somewhere, or the origin of spiral is shifted (but does not look like it) or I have wrong ratio constant ratio = 0.3063489 or the Golden rectangles are introducing higher floating round errors then I taught or I am missing something stupid.
Here the C++ source code so you can extract what you need:
//---------------------------------------------------------------------------
#include <Math.h>
//---------------------------------------------------------------------------
bool _redraw=false; // just signal to repaint window after spiral change
double Ax,Ay,Bx,By; // mouse eddited points
double gr=0.75; // golden spiral ratio scale should be 1 !!!
void GoldenSpiral_draw(TCanvas *can) // GDI draw
{
double a0,a,b,l,x,y,r=5,ratio;
// draw AB line
can->Pen->Color=clWhite;
can->MoveTo(Ax,Ay);
can->LineTo(Bx,By);
// draw A,B points
can->Pen->Color=clBlue;
can->Brush->Color=clAqua;
can->Ellipse(Ax-r,Ay-r,Ax+r,Ay+r);
can->Ellipse(Bx-r,By-r,Bx+r,By+r);
// draw golden ratio rectangles
can->Pen->Color=clDkGray;
can->Brush->Style=bsClear;
ratio=1.6180339887;
a=5.0; b=a/ratio; x=Ax; y=Ay;
y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
can->Rectangle(x,y,x+a,y+b); y-=a;
for (int i=0;i<5;i++)
{
can->Rectangle(x,y,x+a,y+a); b=a; a*=ratio; x-=a;
can->Rectangle(x,y,x+a,y+a); y+=a; b=a; a*=ratio;
can->Rectangle(x,y,x+a,y+a); x+=a; y-=b; b=a; a*=ratio;
can->Rectangle(x,y,x+a,y+a); x-=b; b=a; a*=ratio; y-=a;
}
// draw circle arc approximation of golden spiral
ratio=1.6180339887;
a=5.0; b=a/ratio; x=Ax; y=Ay; r=10000; y-=a;
y-=0.5*b; x-=0.5*b; // bias to match real golden spiral
can->Pen->Color=clYellow;
for (int i=0;i<5;i++)
{
can->Arc(x-a,y,x+a,y+a+a,+r, 0, 0,-r); b=a; a*=ratio; x-=a;
can->Arc(x,y,x+a+a,y+a+a, 0,-r,-r, 0); y+=a; b=a; a*=ratio;
can->Arc(x,y-a,x+a+a,y+a,-r, 0, 0,+r); x+=a; y-=b; b=a; a*=ratio;
can->Arc(x-a,y-a,x+a,y+a, 0,+r,+r, 0); x-=b; b=a; a*=ratio; y-=a;
}
can->Brush->Style=bsSolid;
// compute golden spiral parameters
ratio=0.3063489*gr;
x=Bx-Ax;
y=By-Ay;
l=sqrt(x*x+y*y); // l=|AB|
if (l<1.0) return; // prevent domain errors
a0=atan2(-y,x); // a=atan2(AB)
a0+=0.5*M_PI; // offset so direction of AB matches the normal
a=1.5*M_PI; r=a*exp(ratio*a); b=r;
a+=2.0*M_PI; r=a*exp(ratio*a); b=r-b;
b=l/r; // b=zoom of spiral to match AB screw distance
// draw golden spiral
can->Pen->Color=clAqua;
can->MoveTo(Ax,Ay);
for (a=0.0;a<100.0*M_PI;a+=0.001)
{
r=a*b*exp(ratio*a); if (r>512.0) break;
x=Ax+r*cos(a0+a);
y=Ay-r*sin(a0+a);
can->LineTo(x,y);
}
}
//---------------------------------------------------------------------------
You can ignore the golden ratio rectangles and circular arcs ...
change the drawings based on can-> to your gfx API. It is just GDI Canvas
Hard to say if your spiral is correct ... you can check with the golden ratio rectangles (as I did). If you got correct spiral then just apply the bullets #1,#2,#3 to it and you should be fine.
Here's a fiddle which I believe gives you the output your looking for.
https://jsfiddle.net/8a7fdg3d/4/
The main problem was starting the spiral from 0 results in the initial straight line.
Starting the spiral from 1 removes this part of the graph and then you just had to adjust the starting point of your black |AB| line.
This was done by adjusting
for( i = 0; i <= Math.PI *numberTurns; i+= angleStep)
to
for( i = 1; i <= Math.PI *numberTurns; i+= angleStep)
to change the starting point of the spiral, then changing
// ready to draw
ctx.beginPath();
ctx.moveTo(0, 0) // start at center
to
// ready to draw
ctx.beginPath();
dx = Math.cos(1); // get the vector for angle i
dy = Math.sin(1);
var rad = Math.pow(c, 1); // calculate the radius
ctx.moveTo(dx * rad, dy * rad ) // start at center
to make your |AB| line match up.
i have this code
Allocate.prototype.Rotate = function () {
var canvas = document.getElementById("underCanvas");
var context = canvas.getContext("2d");
canvas.width = canvas.width;
context.lineWidth = "1";
context.save();
context.translate(this.drawStart.x + this.img.width * 0.5,
this.drawStart.y + this.img.height * 0.5);
context.rotate(Math.Pi/2);
context.translate(-(this.drawStart.x + this.img.width * 0.5),
-(this.drawStart.y + this.img.height * 0.5));
context.drawImage(this.img, this.drawStart.x, this.drawStart.y, this.drawStart.w, this.drawStart.h, this.drawStart.x, this.drawStart.y, this.drawStart.w, this.drawStart.h);
context.rect(this.drawStart.x, this.drawStart.y, this.drawStart.w, this.drawStart.h);
context.stroke();
context.restore();
}
what i think this method of class allocate should do - is draw image (this.img) rotated by 90 degree inside a rectange. What comes out: it draws a transformed rectangle but image is still not rotated. Why?
Can anyone help me to accomplish this?
this code is a part of class allocate. i am doing sorta web paint, and want to be able to rotate allocated region. thanks.
You need to translate before rotating if you want to rotate the image by its center (adjust as needed):
/// first translate
context.translate(this.drawStart.x + this.img.width * 0.5,
this.drawStart.y + this.img.height * 0.5);
/// then rotate
context.rotate(0.5 * Math.PI); /// 90 degrees
/// then translate back
context.translate(-(this.drawStart.x + this.img.width * 0.5),
-(this.drawStart.y + this.img.height * 0.5));
/// draw image
The cause for it to not work can be several:
Is the image loaded properly (ie. is the onload handler used to make the image is available at the time it is being drawn)
What values do the properties contain and are they valid (ie. not undefined etc.)
For the rotation to work you need to choose a rotation point, or pivot. The pivot is usually the center of the image which is obtained by using its half width and height.
As rotate always rotate at the coordinate system's origin (0,0) you first need to translate this origin to where the center of the image would be.
Then rotate, and to draw the image properly you need to translate back after rotation or else the corner of the image would be in the center as images are always drawn from top left.
The rectangle must be drawn with the same transformation applied of course.