I have the following code that allows the user to upload an image which gets put into a canvas, but once it has been drawn I want users to be able to rotate the image with the click of a button, but I don't know how to re-access the image object to be able to rotate the canvas. The code below is what works:
onFilePicked (e) {
const files = e.target.files;
for (let file of files) {
if(file !== undefined) {
let image = {
thumbnail: '/img/spinner.gif'
};
this.images.push(image);
this.loadImage(file, image);
}
}
},
loadImage(file, image) {
const fr = new FileReader();
fr.readAsDataURL(file);
fr.addEventListener('load', () => {
var img = new Image();
img.src = fr.result;
img.onload = () => {
image.thumbnail = this.resizeImage(img, 400, 300);
image.large = this.resizeImage(img, 1280, 960);
}
})
},
resizeImage(origImg, maxWidth, maxHeight) {
let scale = 1;
if (origImg.width > maxWidth) {
scale = maxWidth / origImg.width;
}
if (origImg.height > maxHeight) {
let scale2 = maxHeight / origImg.height;
if (scale2 < scale) scale = scale2;
}
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = origImg.width * scale;
canvas.height= origImg.height * scale;
ctx.drawImage(origImg, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL("image/jpeg");
},
And seen below is the function I built out to rotate the image- it works in that if I replace the code inside of the resizeImage function with the code below that the image is drawn in a way that is rotated correctly, but I don't know how to access the origImg object to be able to redraw the canvas in a separate function.
rotateImage(origImg, maxWidth, maxHeight){
let scale = 1;
if (origImg.width > maxWidth) {
scale = maxWidth / origImg.width;
}
if (origImg.height > maxHeight) {
let scale2 = maxHeight / origImg.height;
if (scale2 < scale) scale = scale2;
}
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = origImg.height * scale;
canvas.height= origImg.width * scale;
ctx.translate(canvas.width, 0);
ctx.rotate(90 * Math.PI / 180);
ctx.drawImage(origImg, 0, 0, canvas.height, canvas.width);
return canvas.toDataURL("image/jpeg");
},
Running this function as-is triggers the following console error:
Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(CSSImageValue or HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap or OffscreenCanvas)'
How do I get/reuse the origImg object from the resizeImage function so I can use it in the rotateImage function?
you can try with this code:
var myCanvas = document.getElementById('my_canvas_id');
var ctx = myCanvas.getContext('2d');
var img = new Image;
img.onload = function(){
ctx.drawImage(origImg,0,0); // Or at whatever offset you like
};
And apply your code insede onload function of img and finally transform img source to date URL
Try this code, based on one file picker, two buttons. The first one resize image and the second one rotete the image
function resizeImg()
{
var oPicker = document.getElementById('avatar');
var oImage = document.getElementById('imgOut');
var file = oPicker.files[0];
const fr = new FileReader();
fr.readAsDataURL(file);
fr.addEventListener('load', () => {
var img = new Image();
img.src = fr.result;
img.onload = () => {
oImage.thumbnail = this.resizeImage(img, 400, 300);
oImage.src = this.resizeImage(img, 1280, 960);
}
})
}
function rotateImg()
{
var imgOut = document.getElementById('imgOut');
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let scale = 1;
canvas.width = imgOut.height * scale;
canvas.height= imgOut.width * scale;
ctx.translate(canvas.width, 0);
ctx.rotate(90 * Math.PI / 180);
ctx.drawImage(imgOut, 0, 0, canvas.height, canvas.width);
imgOut.src = canvas.toDataURL("image/jpeg");
}
function resizeImage(origImg, maxWidth, maxHeight) {
let scale = 1;
if (origImg.width > maxWidth) {
scale = maxWidth / origImg.width;
}
if (origImg.height > maxHeight) {
let scale2 = maxHeight / origImg.height;
if (scale2 < scale) scale = scale2;
}
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = origImg.width * scale;
canvas.height= origImg.height * scale;
ctx.drawImage(origImg, 0, 0, canvas.width, canvas.height);
return canvas.toDataURL("image/jpeg");
}
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Image test</h1>
<img src="" id="imgOut" />
<label for="avatar">Choose a profile picture:</label>
<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg">
<input type="button" id="resImg" onclick="resizeImg()" value="Resize" />
<input type="button" id="rotImg" onclick="rotateImg()" value="Rotate" />
</body>
</html>
As you have a part in onFilePicked() where you store something about the images:
let image = {
thumbnail: '/img/spinner.gif'
};
this.images.push(image);
and later update the same objects in loadImage() (well, an event handler in it) as
image.thumbnail = this.resizeImage(img, 400, 300);
image.large = this.resizeImage(img, 1280, 960);
It could be simply extended to
image.original = img;
image.thumbnail = this.resizeImage(img, 400, 300);
image.large = this.resizeImage(img, 1280, 960);
Starting from this point, the objects in your images array would have an original field, storing the original, non-resized variant of the image.
Related
I have a very specific project I'm working on and after 2 weeks the best option seems to be using a bitmap within an empty movie clip. It's perfect apart from one issue - I can't figure out how to pixelate the image.
Here is my code so far:
init_image = () => {
props.image = null
_.holder_grid.bitmap = null
props.image = new Image()
props.image.src = "images/myImage.jpg"
props.image.onload = function() {
stage.holder_grid.bitmap = new createjs.Bitmap(props.image)
stage.holder_grid.bitmap.scale = props.image_scale
stage.holder_grid.addChild(_.holder_grid.bitmap);
const coords = redraw.get_centre()
stage.holder_grid.bitmap.x = coords.x
stage.holder_grid.bitmap.y = coords.y
settings.update()
}
}
init_image()
Its all working as I want but I can't figure out how to pixelate the image.
I found one solution where I draw the image using canvas.context.drawImage() but due to the parameters of the project it's not adequate. That worked like so:
const canvas = document.getElementById("canvas2")
const base = document.getElementById("canvas")
const ctx = canvas.getContext("2d")
const image = new Image()
props.image = image
image.src = "images/myImage.jpg"
image.onload = function() {
const width = base.clientWidth
const height = base.clientHeight
canvas.width= width
canvas.height= height
const scale= props.image_scale
const x = (ctx.canvas.width - image.width * scale) / 2
const y = (ctx.canvas.height - image.height * scale) / 2
//draws tiny
const size = props.pixelate/1
const w = canvas.width * size
const h = canvas.height * size
ctx.drawImage(image, 0, 0, w, h);
// turn off image aliasing
ctx.msImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
// enlarge the minimized image to full size
ctx.drawImage(canvas, 0, 0, w, h, x, y, image.width * scale, image.height * scale);
}
So basically you draw it small then use that small instance as the image source to draw it bigger and viola, it's pixelated.... but due to other issues I've encountered doing this I cannot use this method.
Can anyone help me with this issue please?
The pixelation in your example is accomplished by drawing the original image at a lower resolution to an html <canvas> element. With createJS however you don't have built-in support for manipulating the sources of it's own Bitmap object.
There's hope though. Besides URL's to images, the constructor to a Bitmap also accepts a <canvas> element. So the trick here is loading the image, preparing - thus pixelating it - using a temporary <canvas> and once it's done, pass this element to the Bitmap constructor.
For example:
let stage = new createjs.Stage("canvas");
function pixelate(src) {
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let image = new Image();
image.crossOrigin = "anonymous";
image.onload = (e) => {
canvas.width = e.target.width;
canvas.height = e.target.height;
const width = e.target.width;
const height = e.target.height;
canvas.width = width;
canvas.height = height;
const scale = 1;
const x = (ctx.canvas.width - image.width * scale) / 2;
const y = (ctx.canvas.height - image.height * scale) / 2;
const size = 0.125 / 1;
const w = canvas.width * size;
const h = canvas.height * size;
ctx.drawImage(e.target, 0, 0, w, h);
ctx.msImageSmoothingEnabled = false;
ctx.mozImageSmoothingEnabled = false;
ctx.webkitImageSmoothingEnabled = false;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(canvas, 0, 0, w, h, x, y, e.target.width * scale, e.target.height * scale);
let bitmap = new createjs.Bitmap(canvas);
stage.addChild(bitmap);
stage.update();
}
image.src = src;
}
pixelate("https://api.codetabs.com/v1/proxy?quest=https://picsum.photos/id/237/400/300");
<script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
<canvas id="canvas" width="400" height="300"></canvas>
I got an image, it could be of every size but I need to take the center area of 86x86. Also I've to do that in javascript when a new image is loaded, replacing the old one.
At the end of the code I need to have my 's src updated with the new image.
function loadedImage(elem,gCount){
var crop = { //just messing with numbers
top : 10,
left : 10,
right : 20,
bottom : 20,
};
var file = elem.files[0]; //I take the loaded image
var img = document.getElementsByName('imag')[gCount]; //I take the interessed <img>
var canvas = document.createElement("canvas"); //create canvas
canvas.width = crop.right - crop.left; //set dimensions
canvas.height = crop.bottom - crop.top;
var ctx = canvas.getContext("2d"); // so we can draw
var image = new Image();
image.setAttribute('crossOrigin', 'anonymous');
image.width = img.width;
image.height = img.height;
image.src = window.URL.createObjectURL(file);
ctx.drawImage(image, -crop.left, -crop.top);
image.src = canvas.toDataURL("image/png");
img.src = image.src;
}
No image is shown
Use a load event listener to wait for your image to load. Once it's ready, you can then start drawing.
To draw the center part of your image, the source x-coordinate should be half the width of your image, minus half the width of the crop. (The source y-coordinate can be calculated in a similar way.)
var input = document.getElementsByName('input')[0];
input.addEventListener('change', function (e) {
var file = e.target.files[0];
drawCroppedImage(file, 86, 86);
});
function drawCroppedImage(file, w, h) {
var canvas = document.getElementById('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
var image = new Image();
image.addEventListener('load', function (e) {
var sx = (image.width / 2) - (w / 2), // Source X
sy = (image.height / 2) - (h / 2), // Source Y
dx = 0, // Destination X
dy = 0; // Destination Y
ctx.drawImage(image, sx, sy, w, h, dx, dy, w, h);
});
image.src = URL.createObjectURL(file);
}
<input type="file" name="input">
<br><br>
<canvas id="canvas" width="86" height="86"></canvas>
So I am trying to build a Meme-Creator. You already can upload the pics as the background-img from the canvas. Now I am trying to do the Meme font but it isnt really working. However it is creating a big gap in the picture. Thanks for your help and attention! :D
var canvas = document.getElementById("meme-canvas");
var ctx = canvas.getContext("2d");
var width = canvas.width;
var height = canvas.height;
function readImage() {
if (this.files && this.files[0]) {
var FR = new FileReader();
FR.onload = function(e) {
var img = new Image();
img.addEventListener("load", function() {
ctx.clearRect(0, 0, width, height);
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
});
img.src = e.target.result;
};
FR.readAsDataURL(this.files[0]);
}
}
function Text() {
ctx.clearRect(0, 0, width, height);
var textT = document.getElementById("top-text").value;
var textB = document.getElementById("bottom-text").value;
ctx.font = "60px Impact";
ctx.lineWidth = 3;
ctx.strokeText(textT, 10, 65);
ctx.strokeText(textB, 10, 400);
}
document.getElementById("image-input").addEventListener("change", readImage, false);
<input type="file" id="image-input" accept="image/*">
<input type="text" id="top-text" oninput="Text()">
<input type="text" id="bottom-text" oninput="Text()">
<canvas style="position: absolute; width: 400px; top: 100px; left: 10px;" id="meme-canvas"></canvas>
I've made a few changes to your script. because you need to draw the image several times I've written a function that draws the image. The canvas is cleared every time one of the 2 text input changes it's value, and every time you have to redraw everything.
Also textT and textB can be declared only once. You don't need to declare them in the Text function.
I need to use the font size (60) more than once, so I've made a variable fontSize = 60
Since you don't know the size of your canvas ( depends on the size of the uploaded image ) you need to calculate the position for the bottom text textB = height - fontSize/2 where height is the height of the canvas: height = canvas.height = img.height;
I hope it's useful.
var canvas = document.getElementById("meme-canvas");
var ctx = canvas.getContext("2d");
var width = (canvas.width = 400);
var height = (canvas.height = 400);
var fontSize = 60;
var img = new Image();
var textT = document.getElementById("top-text");
var textB = document.getElementById("bottom-text");
function readImage() {
if (this.files && this.files[0]) {
var FR = new FileReader();
FR.onload = function(e) {
img.addEventListener("load", function() {
width = canvas.width = img.width;
height = canvas.height = img.height;
drawImage();
});
img.src = e.target.result;
};
FR.readAsDataURL(this.files[0]);
}
}
function drawImage() {
ctx.drawImage(img, 0, 0);
}
function Text() {
ctx.font = fontSize + "px Impact";
ctx.textAlign = "center";
ctx.lineWidth = 3;
ctx.clearRect(0, 0, width, height);
drawImage();
ctx.strokeText(textT.value, width / 2, 65);
ctx.strokeText(textB.value, width / 2, height - fontSize/2);
}
document
.getElementById("image-input")
.addEventListener("change", readImage, false);
textT.addEventListener("input", Text);
textB.addEventListener("input", Text);
canvas{position: absolute; left: 10px; top:60px;border:1px solid #d9d9d9;}
<input type="file" id="image-input" accept="image/*">
<input type="text" id="top-text" />
<input type="text" id="bottom-text" />
<canvas style="" id="meme-canvas"></canvas>
A few of the issues I encountered:
If you change the size of the canvas you should not use the variables, because those wont be correct, or you will need to keep track of them
If you do clearRect you have to be aware what are you clearing and re-draw it.
The font did not look too clear so I added a white shadow for better contrast
The bottom text needed to be relative to the canvas height
Here is the code:
var canvas = document.getElementById("meme-canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
function readImage() {
if (this.files && this.files[0]) {
var FR = new FileReader();
FR.onload = function(e) {
img.addEventListener("load", function() {
canvas.width = img.width;
canvas.height = img.height;
draw();
});
img.src = e.target.result;
};
FR.readAsDataURL(this.files[0]);
}
}
function draw() {
var textT = document.getElementById("top-text").value;
var textB = document.getElementById("bottom-text").value;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.font = "60px Impact";
ctx.shadowColor = "white"
ctx.shadowOffsetX = ctx.shadowOffsetY = 2
ctx.lineWidth = 3;
if (img) ctx.drawImage(img, 0, 0);
ctx.strokeText(textT, 10, 45);
ctx.strokeText(textB, 10, canvas.height -10);
}
document.getElementById("image-input")
.addEventListener("change", readImage, false);
<input type="file" id="image-input" accept="image/*"><br>
<input type="text" id="top-text" placeholder="Top text" oninput="draw()"><br>
<input type="text" id="bottom-text" placeholder="Bottom text" oninput="draw()"><br>
<canvas id="meme-canvas"></canvas>
I'm developing a game using HTML5 canvas element and native javascript. I have different sprites for game objects. Is it possible to rotate sprites using native javascript?
For example, I have a sprite image like this:
I use Image for this sprite:
var image = new Image(...);
image.src = "...";
After loading I want to rotate this image and save different projections in local variables:
var sprite_left = rotate(image, 0),
sprite_top = rotate(image, 90),
sprite_right = rotate(image, 180),
sprite_right = rotate(image, 270);
The rotate function should look like this:
function rotate(sourceImage, angle){
...
}
Could anybody help me to write the rotate function?
EDIT:
I have decided to share my code, which I used to test my sprites:
var wait = function (image, completed, count) {
if (count == null) count = 0;
if (!image.complete && count < 1000) {
count++;
window.setTimeout(function () {
wait(image, completed, count);
console.log('waiting...');
}, 10);
}
else {
completed();
}
},
rotateW = function (image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var p = document.createElement("p");
p.innerText = "W: ";
p.appendChild(canvas);
document.body.appendChild(p);
var context = canvas.getContext("2d");
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(Math.PI);
context.translate(-canvas.width / 2, -canvas.height / 2);
context.drawImage(image, 0, 0);
var newImage = new Image();
newImage.src = canvas.toDataURL("image/png");
return newImage;
},
rotateE = function (image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var p = document.createElement("p");
p.innerText = "E: ";
p.appendChild(canvas);
document.body.appendChild(p);
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var newImage = new Image();
newImage.src = canvas.toDataURL("image/png");
return newImage;
},
rotateS = function (image, frameCount) {
var canvas = document.createElement("canvas");
canvas.width = image.height * frameCount;
canvas.height = image.width / frameCount;
var p = document.createElement("p");
p.innerText = "S: ";
p.appendChild(canvas);
document.body.appendChild(p);
var context = canvas.getContext("2d");
context.translate(image.height / 2, image.width / (2 * frameCount));
context.rotate(Math.PI / 2);
var i = frameCount;
while (i--> 0) {
context.drawImage(image, - image.width / 2 , - ( 0.5 + i ) * image.height);
}
var newImage = new Image();
newImage.src = canvas.toDataURL("image/png");
return newImage;
},
rotateN = function (image, frameCount) {
var canvas = document.createElement("canvas");
canvas.width = image.height * frameCount;
canvas.height = image.width / frameCount;
var p = document.createElement("p");
p.innerText = "N: ";
p.appendChild(canvas);
document.body.appendChild(p);
var context = canvas.getContext("2d");
context.translate(image.height / 2, image.width / (2 * frameCount));
context.rotate( 3 * Math.PI / 2);
var i = frameCount;
while (i-- > 0) {
context.drawImage(image, -image.width / 2, (frameCount - i - 1.5) * image.height);
}
var newImage = new Image();
newImage.src = canvas.toDataURL("image/png");
return newImage;
};
/*
N
|
W----O----E
|
S
*/
getSprites = function (image, frameCount) {
var sprite = {
N: rotateN(image, frameCount),
S: rotateS(image, frameCount),
W: rotateW(image, frameCount),
E: rotateE(image, frameCount)
};
return [
sprite.W, // left
sprite.N, // up
sprite.E, // right
sprite.S] // down
};
$.sprite = {
register: function (options) {
var image = new Image();
image.src = options.src;
wait(image, function () {
var sprites = getSprites(image, options.frameCount);
});
}
};
The final result is:
the following function will create a new Canvas out
of img (which might be an image or a canvas).
Give it an angle in radian, or 'N', 'S', 'W' for
the corresponding rotation.
function createRotatedImage(img, angle) {
angle = (angle == 'N') ? -Math.PI/2 :
(angle == 'S') ? Math.PI/2 :
(angle == 'W') ? Math.PI :
angle ;
var newCanvas = document.createElement('canvas');
newCanvas.width = img.width ;
newCanvas.height = img.height ;
var newCtx = newCanvas.getContext('2d') ;
newCtx.save () ;
newCtx.translate ( img.width / 2, img.height / 2) ;
newCtx.rotate (angle);
newCtx.drawImage ( img, - img.width / 2, - img.height / 2) ;
newCtx.restore () ;
}
Use a <canvas> to pre-render the different rotations
Store these in memory using via toBlob, optionally converting these blobs to URLs with window.URL.createObjectURL
Swap URLs as desired.
See this MDN page for canvas options
Absolutely! It's not as simple as rotating the image, though. You need to rotate the context from the canvas, and draw that image on the rotated context, and then restore it.
context.save();
context.rotate(angle);
//DRAW IT!
context.restore();
How about having a function like :
Image.prototype.rotate = function(angle) {
var c = document.createElement("canvas");
c.width = this.width;
c.height = this.height;
var ctx = c.getContext("2d");
ctx.rotate(angle);
var imgData = ctx.createImageData(this.width, this.height);
ctx.putImageData(imgData);
return new Image(imgData);
}
var img1 = new Image();
var img2 = img1.rotate(90);
Ofcourse it's just a quick sample to give you an idea.
I got this script:
function capture(video, scaleFactor) {
if(scaleFactor == null){
scaleFactor = 1;
}
var w = video.videoWidth * scaleFactor;
var h = video.videoHeight * scaleFactor;
var canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx.drawImage(video, 0, 0, w, h);
return canvas;
}
function shoot(){
var video = document.getElementById(videoId);
var output = document.getElementById('output');
var canvas = capture(video, scaleFactor);
canvas.onclick = function(){
window.open(this.toDataURL());
};
snapshots.unshift(canvas);
output.innerHTML = '';
for(var i=0; i<1; i++){
output.appendChild(snapshots[i]);
}
}
What I want to do is export the snapshot to a bitmap image. I read that I could use this line:
canvas.toDataURL('image/jpeg');
But I don't know where to add it.
Any ideas?
ctx.drawImage(video, 0, 0, w, h);
canvas.toDataURL(...)
toDataURL will return you a string which is usually base64 encoded image (file) content. You can display it in image tag by < img src="the string"/>. Or you can use javascript to do whatever you want...
Pass it to window.open
canvas.onclick = function () {
window.open(canvas.toDataURL('image/png'));
};
Full Example : http://www.nihilogic.dk/labs/canvas2image/