Say we have a canvas:
<canvas id="one" width="100" height="200"></canvas>
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
// Sample graphic
context.beginPath();
context.rect(10, 10, 20, 50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
// create button
var button = document.getElementById("rotate");
button.onclick = function () {
// rotate the canvas 90 degrees each time the button is pressed
rotate();
}
var myImageData, rotating = false;
var rotate = function () {
if (!rotating) {
rotating = true;
// store current data to an image
myImageData = new Image();
myImageData.src = canvas.toDataURL();
myImageData.onload = function () {
// reset the canvas with new dimensions
canvas.width = ch;
canvas.height = cw;
cw = canvas.width;
ch = canvas.height;
context.save();
// translate and rotate
context.translate(cw, ch / cw);
context.rotate(Math.PI / 2);
// draw the previows image, now rotated
context.drawImage(myImageData, 0, 0);
context.restore();
// clear the temporary image
myImageData = null;
rotating = false;
}
}
}
And on a button click the canvas gets rotated -90 degrees anticlockwise (around the centre) and the dimensions of the canvas get also updated, so in a sense, it looks like this afterwards:
I want to rotate a canvas element to the anticlockwise rotation. I have used this code but it's not working as I want.
JavaScript has a built-in rotate() function for canvas context:
context.rotate( angle * Math.PI / 180);
The problem is that the rotation will only affect drawings made AFTER the rotation is done, which means you will need to:
Clear the canvas first: context.clearRect(0, 0, canvas.width, canvas.height);
Rotate the context context.rotate( 270 * Math.PI / 180);
Redraw the graphics
Thus, I recommend wrapping the graphics we want to draw in a function to make it easier to call after every rotation:
function drawGraphics() {
context.beginPath();
context.rect(10, 10, 20, 50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
}
Related
I am working on animation optimisation and i wanted to try out canvas to see how it performs but i am not experienced well in canvas and i dont know how to prepare concept of this kind of animation.
this is the gif that shows how animation should rotate like:
this is my current code of js:
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var loop = function() {
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
// draw image
ctx.save();
ctx.translate(cx, cy);
ctx.rotate(Math.PI / 180 * a);
ctx.translate(-cx, -cy);
ctx.drawImage(img, cx - (img.width / 2), cy - (img.height / 2));
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
img.onload = function() {
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
and the working demo on fiddle.
Could someone show how to add to the code so the image would rotate horizontally like on the gif?
EDIT ----
I added the spin, as it was also something to do, but still struggling on how to rotate it.
To get around the problem of rotating the object along two axes (faking one by mapping width to a sine wave), you can use an offscreen canvas to render the coin rotating around one axis, then render that canvas applying the second rotation ;
//make an offscreen canvas for rendering the coin rotating around one axis
var offscreenCanvas = document.createElement('canvas');
var cvs = document.getElementById('coin-spin'),
ctx = cvs.getContext('2d'),
w = cvs.width = 400,
h = cvs.height = 400,
cx = w / 2,
cy = h / 2,
a = 0;
var img = new Image();
var frameCount = 0;
var loop = function() {
frameCount++;
// BG
ctx.fillStyle = '#ccc';
ctx.fillRect(0, 0, w, h);
offscreenContext.fillStyle = '#ccc';
offscreenContext.fillRect(0, 0, w, h);
//determine how wide to render the offscreen canvas so we can fake
//rotation around the second axis
var imgRenderWidth = offscreenCanvas.width * Math.sin(frameCount/10.0)
//render the coin rotating around one axis to the offscreen canvas
offscreenContext.save();
offscreenContext.translate(img.width/2, img.height/2);
offscreenContext.rotate(Math.PI / 180 * a);
offscreenContext.translate((0-img.width)/2, (0-img.height)/2);
offscreenContext.drawImage(img, 0,0);
offscreenContext.restore();
// draw offscreen canvas to the screen with our precalculated width
ctx.save();
ctx.drawImage(offscreenCanvas, cx - (imgRenderWidth / 2), cy - (offscreenCanvas.height / 2), imgRenderWidth, offscreenCanvas.height);
ctx.restore();
// axis
ctx.strokeStyle = '#000';
ctx.beginPath();
ctx.moveTo(cx, 0);
ctx.lineTo(cx, h);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, cy);
ctx.lineTo(w, cy);
ctx.stroke();
//mod angle
a++;
window.requestAnimationFrame(loop);
};
//once the image has loaded, we know what size our offscreen canvas needs to be
img.onload = function() {
offscreenCanvas.width = img.width;
offscreenCanvas.height = img.height;
loop();
};
img.src = 'https://image.ibb.co/gqkeXx/coin.png';
//prepare the offscreen context so we can render to it later
var offscreenContext = offscreenCanvas.getContext('2d');
https://jsfiddle.net/ay3h5vuo/
I'm developing web app using canvas and I made three. canvas, canvas_panorama and canvas_image.
First one is something like main canvas, conteiner for the others. canvas_panorama is a background for canvas_image.
After canvas is right clicked, I'm computing angle to rotate canvas_image:
function getAngle( e, pw /*canvas*/ ){
var offset = pw.offset();
var center_x = (offset.left) + ($(pw).width() / 2);
var center_y = (offset.top) + ($(pw).height() / 2);
var mouse_x = e.pageX;
var mouse_y = e.pageY;
var radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
angle = radians;
}
After I have an angle I'm trying to rotate canvas_image like this:
function redraw(){
var p1 = ctx.transformedPoint(0,0);
var p2 = ctx.transformedPoint(canvas.width,canvas.height);
ctx.clearRect( p1.x, p1.y, p2.x-p1.x, p2.y-p1.y );
canvas_image_ctx.drawImage(image_img, 0, 0, 150, 150);
canvas_panorama_ctx.drawImage(panorama_img, 0, 0, 600, 300);
canvas_panorama_ctx.drawImage(canvas_image, 20, 20);
// rotate panorama_img around its center
// x = x + 0.5 * width
// y = y + 0.5 * height
canvas_panorama_ctx.translate(95, 95);
canvas_panorama_ctx.rotate(angle);
// translate to back
canvas_panorama_ctx.translate(-95, -95);
ctx.drawImage(canvas_panorama, 0, 0);
}
But this rotates both canvas_image and canvas_panorama. It should only rotate canvas_image
JSFiddle to show you my problem
I think you are confusing yourself with this idea of multiple canvases.
Once in the drawImage() method, every of your canvases are just images, and could be just one or even just plain shapes.
Transformation methods do apply to the canvas' context's matrix, and will have effect only if you do some drawing operations when they are set.
Note : To reset your context matrix, you can either use save(); and restore() methods which will also save all other properties of your context, so if you only need to reset the transform, then it's preferred to simply reset the transformation matrix to its default : ctx.setTransform(1,0,0,1,0,0).
Here is a simplified example to make things clearer :
var ctx = canvas.getContext('2d');
// a single shape, with the border of the context matrix
var drawRect = function(){
ctx.beginPath();
ctx.rect(10, 10, 50, 20);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.rect(0, 0, canvas.width, canvas.height);
ctx.stroke();
};
// set the color of our shapes
var gradient = ctx.createLinearGradient(0,0,70,0);
gradient.addColorStop(0,"green");
gradient.addColorStop(1,"yellow");
ctx.fillStyle = gradient;
// here comes the actual drawings
//we don't have modified the transform matrix yet
ctx.strokeStyle = "green";
drawRect();
// here we translate of 100px then we do rotate the context of 45deg
ctx.translate(100, 0)
ctx.rotate(Math.PI/4)
ctx.strokeStyle = "red";
drawRect();
// reset the matrix
ctx.setTransform(1,0,0,1,0,0);
// here we move of 150px to the right and 25px to the bottom
ctx.translate(150, 25)
ctx.strokeStyle = "blue";
drawRect();
// reset the matrix
ctx.setTransform(1,0,0,1,0,0);
<canvas id="canvas" width="500" height="200"></canvas>
In your code, you are setting the transformations on the canvas that does represent your image, and you do draw every of your canvases at each call.
What you want instead, is to set the transformation on the main canvas only, and draw the non-transformed image :
var main_ctx = canvas.getContext('2d');
var img_canvas = canvas.cloneNode();
var bg_canvas = canvas.cloneNode();
var angle = 0;
// draw on the main canvas, and only on the main canvas
var drawToMain = function(){
// first clear the canvas
main_ctx.clearRect(0,0,canvas.width, canvas.height);
// draw the background image
main_ctx.drawImage(bg_canvas, 0,0);
// do the transforms
main_ctx.translate(img_canvas.width/2, img_canvas.height/2);
main_ctx.rotate(angle);
main_ctx.translate(-img_canvas.width/2, -img_canvas.height/2);
// draw the img with the transforms applied
main_ctx.drawImage(img_canvas, 0,0);
// reset the transforms
main_ctx.setTransform(1,0,0,1,0,0);
};
// I changed the event to a simple onclick
canvas.onclick = function(e){
e.preventDefault();
angle+=Math.PI/8;
drawToMain();
}
// a dirty image loader
var init = function(){
var img = (this.src.indexOf('lena')>0);
var this_canvas = img ? img_canvas : bg_canvas;
this_canvas.width = this.width;
this_canvas.height = this.height;
this_canvas.getContext('2d').drawImage(this, 0,0);
if(!--toLoad){
drawToMain();
}
};
var toLoad = 2;
var img = new Image();
img.onload = init;
img.src = "http://pgmagick.readthedocs.org/en/latest/_images/lena_scale.jpg";
var bg = new Image();
bg.onload = init;
bg.src = 'http://www.fnordware.com/superpng/pnggradHDrgba.png';
<canvas id="canvas" width="500" height="300"></canvas>
I drawn a circle in canvas and put an image near the border. Now I have absolutely no idea..I want to drag the image around the circle but the top of the arrow image should always be on the border.
For Example: I drag the arrow from the top to the left at nine o'clock. Now the arrow image needs to be rotated 90 degrees.
http://jsfiddle.net/L5twk3ak/1/
canvas = document.getElementById('test');
var context = canvas.getContext("2d");
var points = [];
var radius = 55;
imageBG = new Image();
imageBG.onload = function() {context.drawImage(imageBG, 148, 100, 15, 15);};
imageBG.src = 'https://www.nanamee.com/upload/images/5945/5945_p.jpg';
for(var degree = 0; degree < 360; degree++)
{
var radians = degree * Math.PI / 179;
var x = 150 + radius * Math.cos(radians);
var y = 150 + radius * Math.sin(radians);
points.push({x : x, y : y});
}
context.beginPath();
context.moveTo(points[0].x + 4, points[0].y + 4)
for(var i = 1; i < points.length; i++)
{
var pt = points[i];
context.lineTo(pt.x + 4, pt.y + 4);
}
context.strokeStyle = "black";
context.lineWidth = 1;
context.stroke();
context.closePath();
<canvas id="test" width="400" height="400">Your browser does not support the HTML5 canvas tag.</canvas>
You need to :
Draw your Arc as we're supposed to (unless you have better plans with lineTo() )
calculate the mouse position inside the canvas - on mousemove.
calculate the resultant degree depending on Mouse Position vs. the Arc center.
cache your image for reuse
create draw functions (one for the Arc, the other for drawing the Image after translating the canvas context). That way on (click+drag) mousemove you can simply reuse them to draw your objects into Canvas.
I'll not show you how to implement the click+drag cause it's pretty trivial: you simply need to apply your draw functions if both CLICK+MOUSEMOVE are registered.
Here's the interesting calculations part:
var canvas = document.getElementById('test'); // Store in variable!
var context = canvas.getContext("2d");
var circle = {rad: 55, x:100, y:100}; // Object for ease of use
var img = {src:'//placehold.it/13x13/000', x:0 ,y:0, w:13, h:13};
var arrowImg; // Store for later Image reference
function drawArc(){
context.beginPath();
context.arc(circle.x, circle.y, circle.rad, 0, Math.PI*2, true);
context.strokeStyle = "#000";
context.lineWidth = 1;
context.stroke();
context.closePath();
}
function drawImg( deg ){
context.save(); // save before we mess with ctx translations
context.translate(circle.y, circle.x); // temporarily translate the ctx
// to the Arc center coordinates.
context.rotate(deg*Math.PI/180); // we need Radians so deg*Math.PI/180
context.drawImage(arrowImg, circle.rad-img.w, -img.h/2);
context.restore(); // restore to default
}
function calcDeg(e){ // Retrieve degree from mouse position vs. arc center
var mPos = {
x : e.pageX-canvas.offsetLeft-circle.x,
y : e.pageY-canvas.offsetTop-circle.y
};
var getAtan = Math.atan2(mPos.y, mPos.x);
return getAtan*180/Math.PI;
}
drawArc(); // Draw the ARc
arrowImg = new Image(); // Create Image Obj
arrowImg.onload = function(){ drawImg(-90) }; // onload draw the Image
arrowImg.src = img.src;
canvas.addEventListener("mousemove", function(evt){
canvas.width = canvas.width; // clear the canvas
drawArc(); // Draw Arc
drawImg( calcDeg(evt) ); // Draw Image at the calculated degree
}, false);
canvas{background:#eee;}
<canvas id="test" width="400" height="400">Your browser does not support the HTML5 canvas tag.</canvas>
Not clear? Goog, than ask
I have written up an example, being new to canvas I need help transforming a basic circle into a letter.
Fiddle
Here is the code you will see in the fiddle. Note the circle drawing is what is in the center of the canvas, I am trying to use a letter like B with a cool font in place of the circle, I am just not sure how to implement this.
So just imagine where the circle is a letter being in that spot, still transparent with a rectangle overlaying the image with the color rgba(255,255,255,0.7).
var canvas = document.getElementById('c');
// resize the canvas to fill browser window dynamically
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
/**
* Your drawings need to be inside this function otherwise they will be reset when
* you resize the browser window and the canvas goes will be cleared.
*/
drawStuff();
}
resizeCanvas();
function drawStuff() {
if (canvas.getContext) {
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 70;
// Full rectangle
context.fillStyle = 'rgba(255,255,255,0.7)';
context.fillRect(0, 0, window.innerWidth, window.innerHeight);
// Inner circle
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// Add a letter instead of a circle?
// Hmm
context.fillStyle = 'rgba(0,0,0,1)';
context.globalCompositeOperation = 'destination-out';
context.fill();
}
}
why not
context.fillStyle = 'rgba(0,0,0,1)';
context.strokeStyle = "#F00";
context.font = "bold 60pt Arial";
context.globalCompositeOperation = 'destination-out';
context.fillText("B", 20, 50);
http://jsfiddle.net/pqD87/
Here again with centering
context.fillStyle = 'rgba(0,0,0,1)';
context.globalCompositeOperation = 'destination-out';
context.textAlign = 'center';
context.font="150px Times";
context.fillText("A",centerX,centerY+40);
http://jsfiddle.net/x4BuF/1/
I've been able to draw images using a background image source and change the opacity of the background image to 25% like so...
var context = document.getElementById('myCanvas').getContext('2d');
context.globalAlpha=.25;
var img = new Image();
img.onload = function(){
context.drawImage(img, 0, 0);
}
img.src = 'pie_crust.png';
And I've been able to draw single arcs...
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 100;
var startingAngle = 30 * Math.PI/180;
var endingAngle = 60 * Math.PI/180;
var counterclockwise = true;
context.arc(centerX, centerY, radius, startingAngle, endingAngle, counterclockwise);
context.lineWidth = 20;
context.strokeStyle = "black"; // line color
context.stroke();
However I haven't been able to change the opacity of a context's arc. For example I have a pie crust (pie_crust.png).
I would like for the user to specify two sets of start and end angles. Let's say the first set is (30, 60) and the second set is (135, 180) and counterclockwise is set to true. I would like those two arcs to have an opacity of 25% and the left over pie crust to have an opacity of 0% so that the resulting canvas would look like this:
How can I use canvas to achieve this effect?
You need to just draw image using pie-formed clipping paths, like this:
context.beginPath();
context.arc(centerX, centerY, radius, Math.PI/6, Math.PI/3, true);
context.moveTo(centerX, centerY);
context.closePath();
context.clip();