I have an image in a canvas which users can rotate and move, using the rotate() and translate() methods.
The moving works fine, until the image is rotated.
Once rotated, translate moves the canvas relative to its rotation.. how do I avoid this?
my code on 'mousemove':
var $canvas = $('#viewport')[0];
var context = $canvas.getContext('2d');
// Clear the canvas first
context.clearRect(0, 0, $canvas.width, $canvas.width);
context.save();
// Do any rotating first
// Move to center
context.translate($canvas.width/2, $canvas.height/2);
// Rotate
context.rotate(degrees*Math.PI/180, $canvas.width, $canvas.width);
// Move back to top-left
context.translate(-$canvas.width/2, -$canvas.height/2);
// Move the canvas to dragged point
context.translate(translate_pos.x, translate_pos.y);
// Draw the image
context.drawImage(image, 0, 0, image.width, image.height);
context.restore();
Related
I am experimenting with animation in <canvas> and can't work out how to draw an image at an angle. The desired effect is a few images drawn as usual, with one image rotating slowly. (This image is not at the centre of the screen, if that makes any difference).
You need to modify the transformation matrix before drawing the image that you want rotated.
Assume image points to an HTMLImageElement object.
var x = canvas.width / 2;
var y = canvas.height / 2;
var width = image.width;
var height = image.height;
context.translate(x, y);
context.rotate(angleInRadians);
context.drawImage(image, -width / 2, -height / 2, width, height);
context.rotate(-angleInRadians);
context.translate(-x, -y);
The x, y coordinates is the center of the image on the canvas.
It is interesting that the first solution worked for so many people, it didn't give the result I needed.
In the end I had to do this:
ctx.save();
ctx.translate(positionX, positionY);
ctx.rotate(angle);
ctx.translate(-x,-y);
ctx.drawImage(image,0,0);
ctx.restore();
where (positionX, positionY) is the coordinates on the canvas that I want the image to be located at and (x, y) is the point on the image where I want the image to rotate.
I have written a function (based on Jakub's answer) that allows user to paint an image in a X,Y position based on a custom rotation in a custom rotation point:
function rotateAndPaintImage ( context, image, angleInRad , positionX, positionY, axisX, axisY ) {
context.translate( positionX, positionY );
context.rotate( angleInRad );
context.drawImage( image, -axisX, -axisY );
context.rotate( -angleInRad );
context.translate( -positionX, -positionY );
}
Then you can call it like this:
var TO_RADIANS = Math.PI/180;
ctx = document.getElementById("canvasDiv").getContext("2d");
var imgSprite = new Image();
imgSprite.src = "img/sprite.png";
// rotate 45º image "imgSprite", based on its rotation axis located at x=20,y=30 and draw it on context "ctx" of the canvas on coordinates x=200,y=100
rotateAndPaintImage ( ctx, imgSprite, 45*TO_RADIANS, 200, 100, 20, 30 );
Since I faced some rounding issues* with the default scale()-function of canvas I've implemented my own matrix-transformations for canvas. This is working fine with a sole exception, I cannot rotate an image because the drawImage() function can only be parameterized with the top left corner of the picture.
Is there any other method to draw an image on a canvas? A method that can be parameterized with at least the coordinates for the top, left and the bottom right corner?, so that I can manually rotate the coordinates?
*The issue is a one-pixel-gap between shapes after scaling by a factor < 1.
Based off of your fiddle in the comments you could use an in memory canvas as a back buffer to draw what you need to at normal size, then scale the context of your main canvas and use drawImage to draw the scaled result.
Live Demo
var canvas = document.getElementById('c');
var context = canvas.getContext('2d');
var backBuffer = document.createElement("canvas"),
bCtx = backBuffer.getContext("2d");
paint = function (x, y, scale) {
bCtx.clearRect(0,0,backBuffer.width,backBuffer.height);;
bCtx.beginPath();
bCtx.rect(x, y, 30, 30);
bCtx.fillStyle = 'black';
bCtx.fill();
bCtx.beginPath();
bCtx.rect(x + 30, y, 30, 30);
bCtx.fillStyle = 'black';
bCtx.fill();
context.save();
context.scale(scale,scale);
context.drawImage(backBuffer,0,0);
context.restore();
}
paint(10, 10, 1);
paint(10, 70, .66);
the example of the code below can be viewed here - http://dev.touch-akl.com/celebtrations/
What I have been trying to do is draw 2 images onto the canvas (glow and then flare. links for these images are below)
http://dev.touch-akl.com/celebtrations/wp-content/themes/beanstalk/img/flare.jpg
http://dev.touch-akl.com/celebtrations/wp-content/themes/beanstalk/img/blue-background.jpg
The goal is for the 'blue-background' image to sit on the canvas at the height and width of the container, and for the 'flare' image to be drawn ontop of this image with a blending mode and rotated with an animation to create a kind of twinkle effect.
My problem is that because the images I am using are rectangular when the 'flare' rotates at certain points you can see the edges of the layer underneath...
What I tried to do was find the diagonal width of the container using trigonometry and draw the 'flare' image at that width so that it always covered the whole canvas but alas you can still see the background layer at some points (but much less than before).
I need a way for the flare image to always cover the whole canvas, can anyone point me in the right direction please?
var banner = $('#banner'),
flare = document.getElementById('flare'),
glow = document.getElementById('glow'),
canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
blendMode = "multiply";
$window.load(function(){
_canvasWidth = banner.outerWidth(),
_canvasHeight = banner.outerHeight();
canvas.width = _canvasWidth;
canvas.height = _canvasHeight;
var _flareSum = (_canvasWidth * _canvasWidth) + (_canvasHeight * _canvasHeight);
_flareWidth = Math.sqrt(_flareSum);
_angle = 0;
setInterval(function() {
_angle = _angle +0.25;
// draw the bg without a blend mode
ctx.globalCompositeOperation = "source-over";
ctx.drawImage(glow, 0, 0, _canvasWidth, _canvasHeight);
ctx.save();
// clear the canvas
// ctx.clearRect(0, 0, _canvasWidth, _canvasHeight);
ctx.translate( _canvasWidth/2, _canvasHeight); // move to center point
ctx.globalCompositeOperation = blendMode;
ctx.rotate(Math.PI / 180 * (_angle)); // 1/2 a degree
ctx.drawImage(flare, -_flareWidth/2, -_flareWidth/2, _flareWidth, _flareWidth); // redraw ia=mages
ctx.restore();
//console.log(_angle)
}, 1);
If I understand correctly, you need the shortest part of the flare to still cover the canvas when the flare is rotated at any angle.
Since you're only showing half the flare at any time, the shortest part of the flare is the distance from the flare center to the top of the flare:
var flareMinHeight = flare.height/2;
The longest length the flare must cover is from the flare rotation point to the top-left of the canvas.
var dx=rotationPointX;
var dy=rotationPointY;
var requiredLength=Math.sqrt(dx*dx+dy*dy);
So you will need to scale the flare to be at least the length computed above:
var minScale = requiredLength / flareMinHeight;
hey guys i have a function that strokes a line based on the angle received from the user and also moves an image using some basic maths. the only problem is i am unable to rotate image based on that angle as if i put this inside animation frame loop it doesn't work .please help
function move()
{
var theCanvas=document.getElementById("canvas1");
var context=theCanvas.getContext("2d");
context.clearRect(0, 0, theCanvas.width, theCanvas.height );
// context.fillStyle = "#EEEEEE";
//context.fillRect(0, 0,theCanvas.width, theCanvas.height );
context.beginPath();
context.setLineDash([3,2]);
context.lineWidth=10;
context.strokeStyle="black";
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.stroke();
context.drawImage(srcImg,x1+x_fact,y1-100+y_fact);
x_fact=x_fact+0.5;
y_fact=m*x_fact+c;
requestAnimationFrame(move);
}
move();
now please suggest me a way to rotate the image on the angle input only once so it can face according to the path and move in it.
thank you in advance.
If you want to rotate the image by its corner use something like this:
... your other code here ...
var x = x1+x_fact, // for simplicity
y = y1-100+y_fact;
context.save(); // save state
context.translate(x, y); // translate to origin of rotation
context.rotate(angle); // rotate, provide angle in radians
context.drawImage(srcImg, 0, 0); // as we are translated we draw at [0,0]
context.restore(); // restore state removing transforms
If you want to rotate it by center simply translate, rotate and then translate back 50% of the image's width and height.
save/restore are relative expensive operations - you can simply reset transformation all together after each draw if you don't need transformations elsewhere:
context.setTransform(1, 0, 0, 1, 0, 0); // instead of restore(), remove save()
I am experimenting with animation in <canvas> and can't work out how to draw an image at an angle. The desired effect is a few images drawn as usual, with one image rotating slowly. (This image is not at the centre of the screen, if that makes any difference).
You need to modify the transformation matrix before drawing the image that you want rotated.
Assume image points to an HTMLImageElement object.
var x = canvas.width / 2;
var y = canvas.height / 2;
var width = image.width;
var height = image.height;
context.translate(x, y);
context.rotate(angleInRadians);
context.drawImage(image, -width / 2, -height / 2, width, height);
context.rotate(-angleInRadians);
context.translate(-x, -y);
The x, y coordinates is the center of the image on the canvas.
It is interesting that the first solution worked for so many people, it didn't give the result I needed.
In the end I had to do this:
ctx.save();
ctx.translate(positionX, positionY);
ctx.rotate(angle);
ctx.translate(-x,-y);
ctx.drawImage(image,0,0);
ctx.restore();
where (positionX, positionY) is the coordinates on the canvas that I want the image to be located at and (x, y) is the point on the image where I want the image to rotate.
I have written a function (based on Jakub's answer) that allows user to paint an image in a X,Y position based on a custom rotation in a custom rotation point:
function rotateAndPaintImage ( context, image, angleInRad , positionX, positionY, axisX, axisY ) {
context.translate( positionX, positionY );
context.rotate( angleInRad );
context.drawImage( image, -axisX, -axisY );
context.rotate( -angleInRad );
context.translate( -positionX, -positionY );
}
Then you can call it like this:
var TO_RADIANS = Math.PI/180;
ctx = document.getElementById("canvasDiv").getContext("2d");
var imgSprite = new Image();
imgSprite.src = "img/sprite.png";
// rotate 45º image "imgSprite", based on its rotation axis located at x=20,y=30 and draw it on context "ctx" of the canvas on coordinates x=200,y=100
rotateAndPaintImage ( ctx, imgSprite, 45*TO_RADIANS, 200, 100, 20, 30 );