I have drawn an image into canvas.
Now what i am trying is if user click on roate left button total canvas should rotate left (i.e., Image rotate to 90 degress) and if rotate right canvas should rotate right.
this is what i tried for rotation. Please suggest me the code to achieve this
var canvasId = document.getElementById("preview");
var cntxt = canvasId.getContext("2d");
cntxt.translate($("#preview").width()-1, $("#preview").height()-1);
cntxt.rotate(convertToRadians(90));
cntxt.drawImage(canvasId, 0, 0, $("#preview").width(), $("#preview").height());
function convertToRadians(degree) {
return degree*(Math.PI/180);
}
Working Solution:
// Getting the canvas
var canvasId = document.getElementById(id);
var ctx = canvasId.getContext("2d");
// Store the current transformation matrix
ctx.save();
ctx.setTransform(1,0,0,1,0,0);
// Getting the canvas width and height
var w = $("#preview").width();
var h = $("#preview").height();
ctx.clearRect(0, 0, w, h);
// Restore the transform
ctx.restore();
// TranslateX should be canvaswidth/2 and translateY should be canvasheight/2
var translateX = w/2;
var translateY = h/2;
// translating the context
ctx.translate(translateX,translateY);
// As we need to rotate the image to 90 degrees
// Where r can be 1 to 36 for 10 to 360 degree rotation
var r = 9;
ctx.rotate(r*10*Math.PI/180);
ctx.translate(-translateX,-translateY);
// Drawing canvas
ctx.drawImage(ImageObject, 0, 0, w, h);
One more doubt can't we rotate the whole canvas to keep the proper aspect ratio of the image
Here is the simple code for that,
var canvasId = document.getElementById("preview");
var ctx = canvasId.getContext("2d");
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, $("#preview").width(), $("#preview").height());
var translateX = ($("#preview").width())/2;
var translateY = ($("#preview").height())/2;
ctx.translate(translateX,translateY);
var r = 9;
ctx.rotate(r*10*Math.PI/180);
ctx.translate(-translateX,-translateY);
ctx.drawImage(imgsrc, 0, 0, $("#preview").width(), $("#preview").height());
Where r can be 1 to 36 for 10 to 360 degree rotation.
As i verified in the net it is lot simpler than what we did in the above process.
So here is the link which i have posted in my blog HTML5 canvas image rotation
Related
I'm very new to Javascript and I've started a simple game. I want the character's gun to rotate to follow the mouse. So far, movement and everything else works fine, except that when I added the rotation functionality the character seems to rotate in a huge circle around the screen. Here's the jsfiddle: https://jsfiddle.net/jvwr8bug/#
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
var mouseX = evt.clientX - rect.top;
var mouseY = evt.clientY - rect.left;
return {
x: mouseX,
y: mouseY
};
}
window.addEventListener('load', function() {
canvas.addEventListener('mousemove', function(evt) {
var m = getMousePos(canvas, evt);
mouse.x = m.x;
mouse.y = m.y;
}, false);
}, false);
The error seems to be somewhere there but obviously it could be something else
**Edit: Thanks to Blindman67 for the fix.
You were rotating the current transform by rotation each frame. ctx.rotate(a) rotates the current transform so each time it is called you increase the rotation amount by a. Think of it as a relative rotation rather than setting the absolute rotation.
To fix your code replace the canon rendering with
//cannon
//ctx.rotate(rotation); // << you had
// setTransform overwrites the current transform with a new one
// The arguments represent the vectors for the X and Y axis
// And are simply the direction and length of one pixel for each axis
// And a coordinate for the origin.
// All values are in screen/canvas pixels coordinates
// setTransform(xAxisX, xAxisY, yAxisX, yAxisY, originX, originY)
ctx.setTransform(1,0,0,1,x,y); // set center of rotation (origin) to center of gun
ctx.rotate(rotation); // rotate about that point.
ctx.fillStyle = "#989898";
ctx.fillRect(15, - 12.5, 25, 25); // draw relative to origin
ctx.lineWidth = 2;
ctx.strokeStyle = "#4f4f4f";
ctx.strokeRect( 15,- 12.5, 25, 25); // draw relative to origin
//body
ctx.fillStyle = "#5079c4";
ctx.beginPath();
ctx.arc(0, 0, size, 0, Math.PI * 2); // draw relative to origin
ctx.fill();
ctx.stroke();
// can't leave the transformed state as is because that will effect anything else
// that will be rendered. So reset to the default.
ctx.setTransform(1,0,0,1,0,0); // restore the origin to the default
And a few more problems to get it working
Just above rendering the canon get the direction to the mouse
// you had coordinates mixed up
// rotation = Math.atan2(mouse.x - y, mouse.y - x); // you had (similar)
rotation = Math.atan2(mouse.y - y, mouse.x - x);
And your mouse event listener is mixing up coordinates and not running very efficiently
Replace all your mouse code with. You don't need onload as the canvas already exists.
canvas.addEventListener('mousemove', function(evt) {
var rect = this.getBoundingClientRect();
mouse.x = evt.clientX - rect.left; // you had evt.clientX - rect.top
mouse.y = evt.clientY - rect.top; // you had evt.clientY - rect.left
}, false);
I have a image of width * height = 1442 * 1303,
I am able to read them and render to canvas successfully via webgl's texture2D.
In client side ,I am having a arraybuffer that gets the image data which is of size = width*height*4.
So, How to maintain aspect ratio of the image when my canvas width and height is window.innerWidth*0.90 and window.innerHeight*0.90.
Also, I have to directly render using arraybuffer via WEBGL 2dTexture so, I can't use any 2d canvs API such as drawImage. Please suggest something.
There's literally a million answers to this question.
First there's the size of your image, then the size you decide to draw it, and the size of the canvas, followed by the size the canvas is displayed. There's the positions of the vertices your using as well which could be anything.
See this article on WebGL which points out that WebGL uses clip space coordinates (-1 to +1) and this article points out that the size a canvas is displayed is separate from its resolution.
Let's assume you want to draw the image as large as possible and fit it to the canvas.
So first let's look up the size the canvas is being displayed
var canvasDisplayWidth = gl.canvas.clientWidth;
var canvasDisplayHeight = gl.canvas.clientHeight;
Let's assume we want to draw the image as large as possible so
first try fitting the width to the canvas
var imageDisplayWidth = canvasDisplayWidth;
var imageDisplayHeight = img.height * imageDisplayWidth / img.width;
Now let's check if it fit? If not let's use the height
if (imageDrawHeight > canvasDisplayHeight) {
imageDisplayHeight = canvasDisplayHeight;
imageDisplayWidth = img.width * imageDisplayHeight / img.height;
}
Now we need to convert imageDisplayWidth and imageDisplayHeight to the size of pixels in the canvas. Note: If the canvas is being displayed the same size
as the its resolution you can skip this step as the display size and the draw size will be the same.
// make our image take into account the pixel aspect
var canvasPixelsAcrossPerDisplayPixel = gl.canvas.width / canvasDisplayWidth;
var canvasPixelsDownPerDisplayPixel = gl.canvas.height / canvasDisplayHeight;
var imageDrawWidth = imageDisplayWidth * canvasPixelsAcrossPerDisplayPixel;
var imageDrawHeight = imageDisplayHeight * canvasPixelsDownPerDisplayPixel;
Now we need to convert that to clip space
var clipWidth = imageDrawWidth / canvas.width;
var clipHeight = imageDrawHeight / canvas.height;
Now, given a unit quad we can just scale it to fit that size.
var m = m4.identity();
// convert our square unit quad match the size we want
m4.scale(m, [clipWidth, clipHeight, 1], m);
// move our unit square from 0,0 (the center) to the bottom, top corner
m4.translate(m, [-1, 1, 0], m);
// scale our unit sqaure to cover the clip space
m4.scale(m, [2, -2, 1], m);
Now can draw with that matrix and our unit quad
var m4 = twgl.m4;
var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
var arrays = {
position: {
numComponents: 2,
data: [
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
],
},
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
// Lets make a texture using a 2d canvas
// There's a circle in the middle. If our
// code is correct it will be a circle when
// drawn (not an oval or ellipse)
var ctx = document.createElement("canvas").getContext("2d");
ctx.canvas.width = 100;
ctx.canvas.height = 75;
ctx.fillStyle = "red";
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.fillStyle = "blue";
ctx.fillRect(10, 10, ctx.canvas.width - 20, ctx.canvas.height - 20);
ctx.strokeStyle = "yellow";
ctx.lineWidth = 20;
ctx.beginPath();
ctx.arc(
ctx.canvas.width / 2, ctx.canvas.height / 2,
Math.min(ctx.canvas.width, ctx.canvas.height) / 2 - 20,
0, Math.PI * 2, false);
ctx.stroke();
var img = ctx.canvas;
var tex = twgl.createTexture(gl, {
src: img,
});
var canvasDisplayWidth = gl.canvas.clientWidth;
var canvasDisplayHeight = gl.canvas.clientHeight;
// Let's assume we want to draw the image as large as possible so
// first try fitting the width to the canvas
var imageDisplayWidth = canvasDisplayWidth;
var imageDisplayHeight = img.height * imageDisplayWidth / img.width;
// Now let's check if it fit? If not let's use the height
if (imageDisplayHeight > canvasDisplayHeight) {
imageDisplayHeight = canvasDisplayHeight;
imageDisplayWidth = img.width * imageDisplayHeight / img.height;
}
// Now we need to convert `imageDisplayWidth` and `imageDisplayHeight` to the size of pixels
// in the canvas. Note: If the canvas is being displayed the same size
// as the its resolution you can skip this step
var canvasPixelsAcrossPerDisplayPixel = gl.canvas.width / canvasDisplayWidth;
var canvasPixelsDownPerDisplayPixel = gl.canvas.height / canvasDisplayHeight;
var imageDrawWidth = imageDisplayWidth * canvasPixelsAcrossPerDisplayPixel;
var imageDrawHeight = imageDisplayHeight * canvasPixelsDownPerDisplayPixel;
// Now we need to convert that to clip space
var clipWidth = imageDrawWidth / gl.canvas.width;
var clipHeight = imageDrawHeight / gl.canvas.height;
// Now, given a unit quad we can just scale it to fit that size.
var m = m4.identity();
// convert our square unit quad to something to match the image's aspect
m4.scale(m, [clipWidth, clipHeight, 1], m);
// move our unit square from 0,0 (the center) to the bottom, left corner
m4.translate(m, [-1, 1, 0], m);
// scale our unit square to cover the clip space
m4.scale(m, [2, -2, 1], m);
var uniforms = {
texture: tex,
matrix: m,
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
<script id="vs" type="notjs">
attribute vec4 position;
uniform mat4 matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = matrix * position;
// using position since we know it's a unit quad
v_texcoord = position.xy;
}
</script>
<script id="fs" type="notjs">
precision mediump float;
uniform sampler2D texture;
varying vec2 v_texcoord;
void main() {
gl_FragColor = texture2D(texture, v_texcoord);
}
</script>
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<canvas id="c" width="50" height="100" style="width: 300px; height: 150px; border: 1px solid black;"></canvas>
How to show part of element outside of canvas from opposite side canvas. Illustration:
You need to draw twice when the shape is outside canvas' boundaries. Draw the main part first, then the same part offset by width so it gives the illusion of showing on the other side.
Manually Draw twice
This draws a shape going from right to left, when the shape is outside the left edge it will be redrawn at the right edge representing the part that is non-visible on the left side. For the opposite way (left to right) the principle is just the same, just use x with canvas' width instead of 0.
var ctx = document.querySelector("canvas").getContext("2d"),
x = 100, // start position
w = 200; // shape width
ctx.fillStyle = "#777";
(function loop() {
ctx.clearRect(0, 0, 300, 150); // clear canvas
ctx.fillRect(x, 0, w, 150); // draw main part/image/shape
if (x < 0) { // should rotate? draw secondary
ctx.fillRect(ctx.canvas.width + x, 0, w, 150); // use canvas width + x (x<0)
}
x -= 7; // animate
if (x <= -w) x = ctx.canvas.width + x; // at some point reset x
requestAnimationFrame(loop)
})();
<canvas></canvas>
Translated Pattern
To simplify this a CanvasPattern can be used. The later version of canvas allows local transforms on the pattern itself, but since this is not currently widely spread I'll show an example using normal transforms and compensated x position:
var ctx = document.querySelector("canvas").getContext("2d"),
pattern,
x = 100, // start position
w = 200; // shape width
// create pattern
ctx.fillStyle = "#777";
ctx.fillRect(x, 0, w, 150); // draw main part/image/shape
pattern = ctx.createPattern(ctx.canvas, "repeat"); // use current canvas as pattern
ctx.fillStyle = pattern; // set pattern as fillStyle
(function loop() {
ctx.setTransform(1,0,0,1,0,0); // reset transforms
ctx.clearRect(0, 0, 300, 150); // clear canvas
ctx.setTransform(1,0,0,1,x,0); // translate absolute x
ctx.fillRect(-x, 0, 300, 150); // fill using pattern, compensate transform
x -= 7; // animate
requestAnimationFrame(loop)
})();
<canvas></canvas>
I have a quadratic curve rendered on a canvas. I want to animate it by means of window.setInterval and changing it's dimensions (note not simply changing it's scale) thereafter.
How do I retain an editable reference to the path after calling context.closePath()?
I'd recommend that you maintained a reference to the path in a new Path object; that way you could modify x, y, points etc on the fly and then render it each animation step.
var testPath = new Path(100, 100, [[40, 40], [80, 80]]);
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
function Path(x, y, points)
{
this.x = x;
this.y = y;
this.points = points;
}
function update()
{
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = 'red';
ctx.moveTo(testPath.points[0][0], testPath.points[0][1]);
for (var i = 1; i < testPath.points.length; i++)
{
ctx.lineTo(testPath.points[i][0], testPath.points[i][1]);
}
ctx.stroke();
testPath.points[1][1]++; // move down
// loop
requestAnimationFrame(update);
}
update();
For some reason JSFiddle doesn't play nice with Paul Irish's requestAnimationFrame polyfill but it should work locally. I'd definitely recommend this over setInterval.
http://jsfiddle.net/d2sSg/1/
How to display part of canvas after scaling in html5
For ex:
var c =document.getElementById("mycanvas");
var canvas = c.getContext("2d");
canvas.scale(4,4);
canvas.drawImage(img,0,0);
canvas.drawImage(img,200,200);
img is some image.
Here i have scaled it some value, now it displays the top-left region of the canvas(with only the top-left image) but what if i want it to display bottom-right region(only the bottom-right image) or according to the coordinates i give to it. How can i do that?
Can someone plz help me on this? I will be very grateful.....
If you are scaling you must remember that the coordinates you use to position will also be scaled up, so if you are scaling by a factor of 4 than your coordinates will be 200 * 4 and not 200. To scale the image alone you can use the call drawImage(img,x,y,width,height) and use...
var c = document.getElementById("mycanvas");
var ctx = c.getContext("2d");
var scale = 4;
var width = img.width * scale;
var height = img.height * scale;
ctx.drawImage(img, 0, 0, width, height);
ctx.drawImage(img, 200, 200, width, height);
Or you will need to divide your coordinates by the scale factor...
var c = document.getElementById("mycanvas");
var ctx = c.getContext("2d");
var scale = 4;
ctx.scale(scale, scale);
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 200 / scale, 200 / scale);
I've put together a fiddle showing the latter approach using clipping to ensure that the image stays in its quadrant http://jsfiddle.net/ujtd2/
Edit using the state stack you can prevent having to do the conversion yourself.
var c = document.getElementById("mycanvas");
var ctx = c.getContext("2d");
var scale = 4;
// add a new item to the context state stack
ctx.save();
ctx.scale(scale, scale);
ctx.drawImage(img, 0, 0);
// discard the previous state be restoring the last state
// back to normal scale
ctx.restore();
// Set translation
ctx.translate(200, 200);
// Repeat for second image
ctx.save();
ctx.scale(scale, scale);
ctx.drawImage(img, 0, 0);
I follow now. To zoom in and show the part of the scene from a specific coordinate use translate.
ctx.scale(4, 4);
ctx.translate(-200, -200);
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 200, 200);
This zooms in by 4 and then moves the visible portion down and right by 200 pixels, by translating the drawing coordinates up and left by 200 pixels.
You can use drawImage the following way :
drawImage(
image,
sourceX,
sourceY,
sourceWidth,
sourceHeight,
destinationX,
destinationY,
destinationWidth,
destinationHeight
);
You determine the region of the source you want and then the place you want to put it on your canvas.
You can find some info here : MDN Draw image documentation