javascript - Why does this canvas image not rotate correctly? - javascript

Why does this canvas image not rotate correctly?
When I click the rotate button the image rotates but gets cropped weirdly. I want the image to stay intact and simply rotate.
Important: I'm not looking to rotate the div, I'm looking to rotate the actual image.
See fiddle:
http://jsfiddle.net/8V4V7/2/
Code:
function rotateBase64Image(base64data, callback) {
console.log("what we get: " + base64data);
var canvas = document.getElementById("dummyCanvas");
var ctx = canvas.getContext("2d");
var image = new Image();
image.src = base64data;
image.onload = function() {
ctx.translate(image.width, image.height);
ctx.rotate(Math.PI);
ctx.drawImage(image, 0, 0);
callback(canvas.toDataURL());
};
}
EDIT:
I need the image to rotate by 90 degrees on every click.
I tried the following but it doesn't work:
ctx.rotate(Math.PI/2);

This worked for me (I specified the height and width of the canvas inside the onload function):
function rotateBase64Image(base64data, callback) {
console.log("what we get: " + base64data);
var canvas = document.getElementById("dummyCanvas");
var ctx = canvas.getContext("2d");
var image = new Image();
image.src = base64data;
image.onload = function () {
canvas.height = image.height;
canvas.width = image.width;
ctx.translate(image.width, image.height);
ctx.rotate(Math.PI);
ctx.drawImage(image, 0, 0);
callback(canvas.toDataURL());
};
}
Edit
Updated function to rotate 90 degrees with every click (remove i in i*Math.PI to rotate 90 degrees only once)
var i = 0;
function rotateBase64Image(base64data, callback) {
console.log("what we get: " + base64data);
var canvas = document.getElementById("dummyCanvas");
var ctx = canvas.getContext("2d");
var image = new Image();
image.src = base64data;
image.onload = function() {
canvas.width = image.height;
canvas.height = image.width;
ctx.translate(canvas.width / 2, canvas.height / 2);
i++;
ctx.rotate(i*Math.PI/2);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
ctx.drawImage(image, 0, 0);
};
}
Updated Fiddle

Just some supporting info for rotations, notably the saving of the context and how to rotate around the center of the image.
var context = canvas.getContext('2d');
//save the context
//pushes a Matrix on the transformation stack
context.save();
context.translate(x, y); //where to put image
context.rotate(angle); //angle in degrees (i think...)
context.scale(scale); //optional scale
//rotate around center of image
context.drawImage(bitmap, -image.width / 2, -image.height / 2);
//or rotate around top left corner of image
context.drawImage(image, 0, 0);
//restore the canvas
//pop a matrix off the transformation stack
context.restore();
References:
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial/Transformations
http://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/

Related

Image rotation using canvas

I need to rotate an image through canvas and through the cropper.js library I can do the rotation of the image but save the image and condition it with canvas does not save me with the format of the rotation.
In my code what I have is an event that makes me rotating towards 90 or -90 degrees. Then when saving the image I need to be saved with that format.
I need to rotate on the axis of the image so that when the image is saved with rotate canvas it is saved with the correct position. I have seen some ways to do it but it has not worked for me, I do not know that I am losing.
//Events to rotate using cropper Js library
'click #rotateL': function(e, instance){
$('#target').cropper('rotate', -90);
angleInDegrees = angleInDegrees - 90 ;
drawRotated(angleInDegrees);
},
'click #rotateR': function(events, instance){
$('#target').cropper('rotate', 90);
angleInDegrees += 90;
drawRotated(angleInDegrees);
},
function drawRotated(degrees){
var canvas = $("#canvas")[0];
var context = canvas.getContext('2d');
var image = document.createElement("img");
image.src = $('#target').attr("src");
image.onload = function () {
context.drawImage(image,(canvas.width - x1) / 2,
(canvas.height - y1) / 2,x1, y1);
//ctx.drawImage(img, x, y, width, height)
}
context.clearRect(0,0,canvas.width,canvas.height);
context.save();
context.translate(canvas.width/2,canvas.height/2);
context.rotate(degrees*Math.PI/180);
context.drawImage(image, -x1/2,-y1/2);
context.restore();
var dataURL = canvas.toDataURL("image/jpeg");
Session.set("url", dataURL);
}
// event that saves the changes made when you cut the image or rotate.
'click #Save' : function(e) {
$(".loader").fadeIn("slow");
e.preventDefault();
var photoid = $('#photoid').val();
var dataURL = Session.get("url");
var photo = {
srcData : dataURL,
userid : Meteor.userId(),
photo_id : photoid
}
Meteor.call('updatePhoto',photo,function(err) {
if (!err) {
$('#photos').show();
$('#crops').hide();
canvas.height = 0;
canvas.width = 0;
//page relod is better than
//document.getElementById('target').src="";
FlowRouter.go('/search');
FlowRouter.go('/addphoto');
}
});
},
If you are using cropper.js. You can use build-in method getCroppedCanvas to get edited and cropped new canvas back. There is no bother to write on your own.
var canvas = $ ('# target').cropper ('getCroppedCanvas');
console.log (canvas)
var dataURL = canvas.toDataURL ("image / jpeg");
To rotate 90 deg clockwise on a new canvas.
// assuming that image is a loaded image.
const canvas = document.createElement("canvas");
canvas.width = image.height;
canvas.height = image.width;
const ctx = canvas.getContext("2d");
ctx.setTransform(
0,1, // x axis down the screen
-1,0, // y axis across the screen from right to left
image.height, // x origin is on the right side of the canvas
0 // y origin is at the top
);
ctx.drawImage(image,0,0);
To rotate -90Deg onto a new canvas
const canvas = document.createElement("canvas");
canvas.width = image.height;
canvas.height = image.width;
const ctx = canvas.getContext("2d");
ctx.setTransform(
0,-1, // x axis up the screen
1,0, // y axis across the screen from left to right
0, // x origin is on the left side of the canvas
image.width // y origin is at the bottom
);
ctx.drawImage(image,0,0);
The origins are where the top left corner of the image ends up being after the rotation.

Image isn't drawn after canvas is cleared (html5 canvas)

I am trying to make a variant of the boomshine game in javascript and everything works when I draw a circular shape with the arc function. However when i try to replace the arc function with the drawImage function to use a coin image instead of a circular shape I start having problems when I clear the canvas to delete the previous drawn circular shapes. If I don't clear the canvas before rendering the images, the images are drawn on the canvas except the old images are still on the canvas. But when i do clear the canvas before rendering the images again, nothing is drawn on the canvas.
I have included screenshots, the links are below.
This is how I clear the canvas:
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
This is how i draw the image:
function drawImageBall(x,y,radius,startAngle,color)
{
var img = document.createElement('img');
img.src = 'img/coin-icon.png';
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.closePath();
tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
tmpCtx.beginPath();
tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
tmpCtx.clip();
tmpCtx.closePath();
tmpCtx.restore();
};
}
Clearing canvas (screenshot)
Without clearing canvas (screenshot)
Keep in mind that downloading the img will take some time.
During that downloading time, Javascript does not stop(!). Instead JS will continue executing any following code. This is causing your unexpected problems.
So download the img just once at the start of your app. That way your drawImage will be done in the order that you expect because there will be no delay while your image is downloading.
Using your code, I maked some changes, I removed the tmpTcx.clip(), look the fidlle. Tip: For performace questions you don't need load the image every time that you want write the canvas.
Poor Example: https://jsfiddle.net/wf4z0d2h/1/
function clearCanvas(){
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
}
function drawImageBall(x,y,radius,startAngle,color)
{
if(x == undefined){x = 100;}
if(y == undefined){y = 100;}
if(radius == undefined){radius = 40;}
//var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
//img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
//img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.stroke(); // Draw it
tmpCtx.closePath();
//tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
//tmpCtx.beginPath();
//tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
////tmpCtx.clip();
//tmpCtx.stroke(); // Draw it
//tmpCtx.closePath();
//tmpCtx.restore();
//};
}
var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
//drawImageBall();
img.onload = function(){
x = 0;
y = 0;
setInterval(function(){
x = x+10;
y = y+10;
clearCanvas();
drawImageBall(x,y);
},300);
}

How do I extract a portion of an image in canvas and use it as background image for a div?

This is how my code looks:
document.addEventListener('DOMContentLoaded', function () {
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext("2d");
var imageData = ctx.getImageData(0, 0, 300, 300);
var tile = {
'id': 1,
'data': imageData,
'dataUrl': imageData.toDataUrl()
};
var div = document.createElement('div');
div.classList.add('tile');
grid.appendChild(div);
div.style.backgroundImage = ('url(' + tile.dataUrl + ')');
});
I'm trying to extract portion of the image on canvas, from (0,0) with height and width 300px, and convert that imageData object into a dataUrl to be used as a background of a div.
I get an error: imageData.toDataUrl() is not a function. How can I achieve this?
Thanks in advance!
toDataURL is an HTMLCanvasElement method you have to call it from the canvas element itself.
You could draw back your resulted imageData to the canvas after you changed its size to the wanted one, but the easiest solution is to use a second, off-screen canvas, where you will draw the first canvas thanks to the context.drawImage method:
// The crop function
var crop = function(canvas, offsetX, offsetY, width, height, callback) {
// create an in-memory canvas
var buffer = document.createElement('canvas');
var b_ctx = buffer.getContext('2d');
// set its width/height to the required ones
buffer.width = width;
buffer.height = height;
// draw the main canvas on our buffer one
// drawImage(source, source_X, source_Y, source_Width, source_Height,
// dest_X, dest_Y, dest_Width, dest_Height)
b_ctx.drawImage(canvas, offsetX, offsetY, width, height,
0, 0, buffer.width, buffer.height);
// now call the callback with the dataURL of our buffer canvas
callback(buffer.toDataURL());
};
// #main canvas Part
var canvas = document.getElementById('main');
var img = new Image();
img.crossOrigin = "Anonymous";
img.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
canvas.getContext('2d').drawImage(this, 0, 0);
// set a little timeout before calling our cropping thing
setTimeout(function() {
crop(canvas, 100, 70, 70, 70, callback)
}, 1000);
};
img.src = "https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png";
// what to do with the dataURL of our cropped image
var callback = function(dataURL) {
document.body.style.backgroundImage = 'url(' + dataURL + ')';
}
<canvas id="main" width="284" width="383"></canvas>

How to stretch image in canvas?

I need to stretch my image to the top and to the bottom in canvas.
How can I do this?
Here's the codepen
HTML
<canvas id="myCanvas" width="240" height="297" style="border:1px solid #d3d3d3;">
</canvas>
JS
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = new Image;
img.src = "http://www.w3schools.com/tags/img_the_scream.jpg"
ctx.drawImage(img, 10, 10);
}
drawImage allows you to pass the width and height as third and fourth parameter.
So your last line should be ctx.drawImage(img, 0, 0,c.width, c.height); in order to fill the entire canvas.
If you just want an image to stretch to fill in the height you can do something like this jsFiddle : https://jsfiddle.net/CanvasCode/92ekrbnz/
javascript
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = new Image;
ctx.fillStyle = "#000";
ctx.fillRect(0,0,c.width,c.height);
img.src = "http://www.w3schools.com/tags/img_the_scream.jpg"
img.onload = function () {
ctx.drawImage(img, (c.width / 2) - (img.width / 2), 0, img.width, c.height);
}
However the link you provided basically just zooms into the image.
So you can do something like this jsFiddle : https://jsfiddle.net/CanvasCode/92ekrbnz/2/
javascript
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = new Image;
var zoom = 1.0;
img.src = "http://www.w3schools.com/tags/img_the_scream.jpg"
setInterval(function () {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, c.width, c.height);
ctx.drawImage(img, (c.width / 2) - ((img.width * zoom) / 2), (c.height / 2) - ((img.height * zoom) / 2),
img.width * zoom,
img.height * zoom);
}, 1);
document.getElementById("zoomIn").onclick = function () {
zoom += 0.1;
};
document.getElementById("zoomOut").onclick = function () {
if (zoom > 0.1) {
zoom -= 0.1;
}
};
Here is a more detailed answer of how to fill or fit a canvas while also keeping the image aspect.
Fill canvas

Rotate canvas 90 degrees clockwise and update width height

Say we have a canvas:
<canvas id="one" width="100" height="200"></canvas>
And on a button click the canvas gets rotated 90 degrees clockwise (around the center) and the dimensions of the canvas get also updated, so in a sense it looks like this afterwards:
<canvas id="one" width="200" height="100"></canvas>
Note that the id of the canvas is the same.
Imagine simply rotating an image clockwise without it being cropped or being padded.
Any suggestions before I do it the long way of creating a new canvas and rotating and copying pixel by pixel?
UPDATE sample code with suggestion from comments still not working:
function imageRotatecw90(){
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var myImageData = context.getImageData(0,0, cw,ch);
context.save();
context.translate(cw / 2, ch / 2);
context.rotate(Math.PI/2);
context.putImageData(myImageData, 0, 0);
context.restore();
canvas.width=ch;
canvas.height=cw;
}
FiddleJS
Look at this DEMO.
To achieve the results seen in demo, I made use of canvas.toDataURL to cache the canvas into an image, then reset the canvas to their new dimensions, translate and rotate the context properly and finally draw the cached image back to modified canvas.
That way you easily rotate the canvas without need to redraw everything again. But because anti-aliasing methods used by browser, each time this operation is done you'll notice some blurriness in result. If you don't like this behavior the only solution I could figure out is to draw everything again, what is much more difficult to track.
Here follows the code:
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;
}
}
}
Rotation
Note it is not possible to rotate a single element.
ctx.save();
ctx.rotate(0.17);
// Clear the current drawings.
ctx.fillRect()
// draw your object
ctx.restore();
Width/height adjustment
The only way I ever found to properly deal with display ratios, screen sizes etc:
canvas.width = 20;// DO NOT USE PIXELS
canvas.height = 40; // AGAIN NO PIXELS
Notice I am intentionally not using canvas.style.width or canvas.style.height. Also for an adjustable canvas don't rely on CSS or media queries to do the transformations, they are a headache because of the pixel ratio differences. JavaScript automatically accounts for those.
Update
You also have to update the width and the height before you draw. Not sure what you are trying to achieve, but I guess this isn't a problem:
Demo here
var canvas = document.getElementById("one");
var context = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
canvas.width = 200;
canvas.height = 400;
// Sample graphic
context.beginPath();
context.rect(10,10,20,50);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
var myImageData = context.getImageData(0, 0, cw, ch);
context.save();
context.translate(cw / 2, ch / 2);
context.putImageData(myImageData, 0, 0);
context.rotate(0.20);
If you want to rotate an image by 90 degrees this might be helpful:
export const rotateBase64Image = async (base64data: string) => {
const image = new Image();
image.src = base64data;
return new Promise<string>((resolve, reject) => {
image.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("cannnot get context '2d'");
canvas.width = image.height;
canvas.height = image.width;
ctx.setTransform(0, 1, -1, 0, canvas.width, 0); // overwrite existing transform
ctx!.drawImage(image, 0, 0);
canvas.toBlob((blob) => {
if (!blob) {
return reject("Canvas is empty");
}
const fileUrl = window.URL.createObjectURL(blob);
resolve(fileUrl);
}, "image/jpeg");
};
});
};
If you don't have image in base64 format you can do it like this:
const handleRotate = async () => {
const res = await fetch(link);
const blob = await res.blob();
const b64: string = await blobToB64(blob);
const rotatedImage = await rotateBase64Image(b64)
setLink(rotatedImage);
}
Here is my blobTob64 function:
export const blobToB64 = async (blob) => {
return new Promise((resolve, _) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.readAsDataURL(blob);
});
};

Categories

Resources