I'm playing with canvas in HTML5 and Javascript and I have a problem:
I'd like to apply transformations used on the current image to multiple images.
What I did:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
//transformation stuff like:
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, -img.width / 2, -img.height / 2, img.width, img.height);
ctx.beginPath();
ctx.lineTo(42, 42);
ctx.stroke();
ctx.lineTo(42, 24);
ctx.stroke();
...
ctx.rotate(Math.PI / 2);
...
};
img.src = //base64Img;
So I will apply a lot of transformations like draw some lines, crop, zoomIn etc...
How can I apply this to multiple files (more than 200) once (when these transformations are done) ?
Obviously, it will be done in multiples functions like a function to rotate, to draw a line etc.
Thank you for your help.
Put your transformations, path drawings & image drawing into a function with arguments that tell the function how each image will be treated:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/house32x32transparent.png";
function start(){
// Note: img coordinates are [centerX,centerY] rather than the usual [left,top]
drawTransformedImage(img,25,50,0,.75);
drawTransformedImage(img,75,50,Math.PI*1/6,1);
drawTransformedImage(img,150,50,Math.PI*2/6,2);
drawTransformedImage(img,225,50,Math.PI*3/6,1);
drawTransformedImage(img,275,50,Math.PI*4/6,.5);
}
function drawTransformedImage(img,cx,cy,radAngle,scale){
// save incoming styling
var lw=ctx.lineWidth;
var ss=ctx.strokeStyle;
// cache often used half-sizes
var iwHalf=img.width/2;
var ihHalf=img.height/2;
ctx.lineWidth=2;
// do the specified transformations
ctx.translate(cx,cy);
ctx.rotate(radAngle);
ctx.scale(scale,scale);
// draw the image
ctx.drawImage(img,-iwHalf,-ihHalf);
// stroke some paths
ctx.beginPath();
ctx.moveTo(-iwHalf,ihHalf);
ctx.lineTo(-iwHalf,-ihHalf);
ctx.strokeStyle='orange';
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-iwHalf,-ihHalf);
ctx.lineTo(+iwHalf,-ihHalf);
ctx.strokeStyle='blue';
ctx.stroke();
// clean up: reset transformations and stylings
ctx.setTransform(1,0,0,1,0,0);
ctx.lineWidth=lw;
ctx.strokeStyle=ss;
}
body{ background-color: white; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=150></canvas>
Transforming an Image
Your example does not show a image being transformed, making your question unclear.
The transform is independent of the image, it is used to transform pixel coordinates drawn onto the canvas. It does not affect the image. You can set the transform and then draw the 200 images and they will all have the same transformation applied when their content is rendered to the canvas.
Code example
To transform the image you must create a canvas, set the transform, then render the image onto that canvas. The canvas is now the transformed image.
An example of transforming an image.
var mirrorImage = function (image, vertical, horizontal) {
var imageResult, ctx, vF, hF, posX, posY;
// create new canvas
imageResult = document.createElement("canvas");
// set the pixels size to match the image
imageResult.width = image.width;
imageResult.height = image.height;
// create a drawable surface
ctx = imageResult.getContext("2d");
// create the mirror transformation
hF = horizontal ? -1, 0;
vF = vertical ? -1 : 0;
posX = horizontal ? image.width, 0;
posY = vertical ? image.height : 0;
// Apply the transform to the new image
ctx.setTransform(hF, 0, 0, vF, posX, posY);
// transform the original image by drawing it onto the new
ctx.drawImage(image, 0, 0);
// return the new image.
return imageResult;
}
// create image
var img = new Image();
img.src = "ship.png";
// when loaded transform the image
img.onload = function () {
img = mirrorImage(img, true, true);
// the image has been transformed.
}
To do that to 200 images you have to call mirrorImage (or what ever you are doing) for each image.
Related
There are numerous examples out there showing how to draw things onto a canvas, however, my problem is slightly different - I want to load a photo into memory, draw a shape onto exact coordinates over the photo, THEN draw/scale the photo onto a canvas. Not sure where to start with this. Are there any relevant libraries out there I can use with ionic that will allow you to do this?
Edit 1 ~ I now have this mostly working:
private properties:
#ViewChild('mainCanvas') canvasEl: ElementRef;
private _CANVAS: any;
private _CONTEXT: any;
ionViewDidEnter():
this._CANVAS = this.canvasEl.nativeElement;
this._CONTEXT = this._CANVAS.getContext('2d');
updateCanvas():
var img = new Image();
const ctx = this._CONTEXT;
const canvas = this._CANVAS;
ctx.clearRect(0, 0, this._CANVAS.width, this._CANVAS.height);
ctx.fillStyle = "#ff0000";
img.onload = (() => {
img.width = img.width;
img.height = img.height;
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = "#FF0000";
ctx.strokeRect(100, 100, 400, 400);
ctx.scale(0.5, 0.5); // this does nothing
});
img.src = (<any>window).Ionic.WebView.convertFileSrc(path);
This draws the photo then the rectangle onto the canvas, however, the resulting image is too large to fit onto the screen, so I need to scale the canvas after all drawing is complete. I tried this with ctx.scale but the canvas remains the same size regardless of which values I specify.
You cannot draw straight onto a photo, but what you can do is create an offscreen canvas that is the same size as the photo, draw the photo to it, and then draw your shapes on top.
The result can then be drawn to your main canvas e.g.
// Empty image for example purposes
const img = new Image(100, 100);
// Creating a canvas for example purposes
const mainCanvas = document.createElement('canvas');
const mainCtx = mainCanvas.getContext('2d');
// Create an offscreen buffer
const bufferCanvas = document.createElement('canvas');
const bufferCtx = bufferCanvas.getContext('2d');
// Scale the buffer canvas to match our image
bufferCanvas.width = img.width;
bufferCanvas.height = img.height;
if (bufferCtx && mainCtx) {
// Draw image to canvas
bufferCtx.drawImage(img, 0, 0);
// Draw a rectangle in the center
bufferCtx.fillRect(img.width / 2 - 5, img.height / 2 - 5, 10, 10);
// Draw the buffer to the main canvas
mainCtx.drawImage(bufferCanvas, 0, 0);
}
I am creating a Javascript game. It is about a guy who stands on top of the world. I already have an earth and now I need to rotate it but when I rotate it it also changes it place.
As you can see the earth rotates but it also changes its place. I want it to rotate just like the rotate keyframes from css. Any thoughts?
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="Style.css"/>
</head>
<body onload="draw()">
<canvas id="canvas"></canvas>
</body>
<script>
var ctx = document.getElementById("canvas").getContext('2d');
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
setInterval(draw, 10);
function draw() {
var img = new Image();
img.onload = function(){
ctx.rotate(1*Math.PI/180);
ctx.drawImage(img,canvas.width/2-200,canvas.height/2-100,300,300);
};
img.src = "earth.png";
}
</script>
</html>
The code doesn't work because it cant load the image because i have it downloaded but now you guys have the code so you can see the problem.
A quicker way that avoids having to use save and restore.
If you are drawing 100's or 1000's of images (such as for games) the use of save and restore can make the difference between playable and not. In some situations the restore call can drop the frame rate for a nice 60fps to less than 10fps.
Always be careful when using save and restore, making sure you don't have large patterns, or filters ( if supported), complex gradients, or detailed fonts when you save. You are better to remove these thing before you do the save and restore if you plan to do many of them
For single images it does not matter and the previous answer is the best solution.
General purpose sprite render with scales rotation and fade
// draws a image centered at x,y scaled by sx,sy rotate (r in radians) and faded by alpha (0-1)and
function drawImage(image,x,y,sx,sy,r,alpha){ //
ctx.setTransform(sx,0,0,sy,x,y);
ctx.rotate(r);
ctx.globalAlpha = alpha;
ctx.drawImage(image,-image.width/2,-image.height/2);
}
and without fade
function drawImage(image,x,y,sx,sy,r){ //
ctx.setTransform(sx,0,0,sy,x,y);
ctx.rotate(r);
ctx.drawImage(image,-image.width/2,-image.height/2);
}
or if you want to have the default transform after the call
function drawImage(image,x,y,sx,sy,r){ //
ctx.setTransform(sx,0,0,sy,x,y);
ctx.rotate(r);
ctx.drawImage(image,-image.width/2,-image.height/2);
ctx.setTransform(1,0,0,1,0,0);
}
As you may want to render many images in a row you can restore the canvas context with
function restoreContext(){
ctx.globalAlpha = 1;
ctx.setTransform(1,0,0,1,0,0);
}
See this fiddle
HTML
<canvas id="canvas"></canvas>
JavaScript
var ctx = document.getElementById("canvas").getContext('2d');
var canvas = document.getElementById("canvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var img;
function draw() {
img = new Image();
img.onload = function(){
setInterval(rotate, 10);
};
img.src = "https://pixabay.com/static/uploads/photo/2013/07/12/13/55/earth-147591_960_720.png";
}
draw();
var i = 0;
function rotate() {
i += 1;
drawRotatedImage(img, 100, 100, 200, 200, i);
}
var TO_RADIANS = Math.PI/180;
function drawRotatedImage(image, x, y, width, height, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle * TO_RADIANS);
ctx.drawImage(image, -width/2, -height/2, width, height);
ctx.restore();
}
My canvas is 500px x 500px.
I have a png image that is 500px x 500px:
I want to re-size the image to be say... 100px x 100px, and then use that re-sized image as part of defining a repeat pattern and then using that as a fillStyle to repeat across the whole canvas. This is what I do...
//...define canvas, ctx, width and height above
var image = new Image();
image.onload = function() {
_self = this;
drawBG();
}
image.src = 'img.png';
function drawBG() {
var space = ctx.createPattern(_self, 'repeat');
ctx.fillStyle = space;
ctx.fillRect(0, 0, width, height);
}
Now, this is all well and good if I want to waste my own time. See, the space image is the same size as the canvas. My question is... How do you first resize the original image(in javascript) to then later create a pattern with it?
P.S. How do you re-size an image on stack overflow? This image I have showing here is to big for it's purpose.
You can draw your image on a second offscreen canvas, with drawImage(img, x, y, resizedWidth, resizedHeight) and then use this canvas as pattern.
var ctx = canvas.getContext('2d');
var image = new Image();
image.onload = function() {
// create an off-screen canvas
var patt = document.createElement('canvas');
// set the resized width and height
patt.width = 50;
patt.height = 50;
patt.getContext('2d').drawImage(this, 0,0, patt.width, patt.height);
// pass the resized canvas to your createPattern
drawBG(patt);
}
image.src = 'http://lorempixel.com/500/500';
function drawBG(patternCanvas) {
var space = ctx.createPattern(patternCanvas, 'repeat');
ctx.fillStyle = space;
ctx.fillRect(0, 0, 400, 200);
}
<canvas id="canvas" width="500" height="250"></canvas>
I need to create Canvas element with image and need to append to parent for this i have done this
<html>
<head>
<script>
window.onload = function() {
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
canvas.id = "canvas_id";
canvas.setAttribute("class" ,"canvas");
canvas.height = "400";
canvas.width = "800";
var image = new Image();
image.src = "http://localhost/tile.png";
image.onload = function(){
context.drawImage(image, canvas.width, canvas.height);
}
document.body.appendChild(canvas);
}
</script>
</head>
<body>
</body>
</html>
it give blank canvas
can somebody guide me ?
You are using drawImage() the wrong way. Instead of drawing the image at (0,0) you are drawing it just outside the canvas area as width and height is where position normally goes.
The valid signatures are:
context.drawImage(image, dx, dy)
context.drawImage(image, dx, dy, dw, dh)
context.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
Where dx and dy are delta position (relative to origin, normally (0,0) when untranslated). Without width and height specified drawImage() will by default use the image's natural width and height.
The second version allows to override the default size, and the third will allow you to draw from one region to another.
Source
Corrected example:
window.onload = function() {
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
canvas.id = "canvas_id";
canvas.className = "canvas"; // should be className
canvas.height = 400; // should be numbers
canvas.width = 800;
var image = new Image();
image.onload = function() {
// or set canvas size = image, here: (this = currently loaded image)
// canvas.width = this.width;
// canvas.height = this.height;
context.drawImage(this, 0, 0); // draw at (0,0), size = image size
// or, if you want to fill the canvas independent on image size:
// context.drawImage(this, 0, 0, canvas.width, canvas.height);
}
// set src last (recommend to use relative paths where possible)
image.src = "http://lorempixel.com/output/fashion-q-c-800-400-7.jpg";
document.body.appendChild(canvas);
}
That being said, if you only need the image appended there is no need to go via canvas. Just add the image to DOM directly (I assume this is not you want, but just in case..):
var image = new Image();
image.src = "tile.png";
document.body.appendChild(image);
This is my take on it... You need to indicate the coordinates where you want to start drawing (i.e. 0, 0) and - optionally - you can specify how big (wide, height) the canvas is to be.
In my case, I make the canvas to be as big as the image (instead of an arbitrary 400x800) you may need to update that your suit your requirements.
I added some css to show how big the canvas is in relation to the image. You can update/remove that as well depending on your needs.
UPDATED
It uses an hidden image as the source.
I hope this will work for you.
window.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
canvas.id = 'canvas_id';
canvas.setAttribute("class", "canvas");
var image = new Image();
image.src = 'http://placekitten.com/g/200/300';
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0, image.width, image.height);
document.body.appendChild(canvas);
}
.canvas {
border: solid red 1px;
}
<html>
<body>
</body>
</html>
Say I have a canvas that's a rectangle. I want to take that canvas and split it diagonally and be able to manipulate those pieces to do whatever I want.
My end goal is to have a rectangle split diagonally and the 2 pieces animate off screen in opposite directions. I was thinking of doing this entirely within the canvas in an animation loop or converting each segment into an image element and using CSS3 animations to move those pieces. I'm just trying to figure out the best way to do this.
The code, codepen link, and image below are just to illustrate where I want my canvas to be split. You'll see it's not an exact split with equal sides.
http://codepen.io/FranciscoG/pen/YPjzbQ
<div id="container">
<img id="dog" src="http://i.imgur.com/1GUzYh9.jpg" width="375"
height="667">
</div>
<script>
var container = document.getElementById('container');
var dogImg = document.getElementById('dog');
function convertImageToCanvas(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
canvas.getContext("2d").drawImage(image, 0, 0);
return canvas;
}
function drawLine(canvas) {
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(0,0);
context.lineTo(345, 0);
context.lineTo(0, 567);
context.lineTo(0,0);
context.stroke();
context.closePath();
return canvas;
};
var newDog = convertImageToCanvas(dogImg);
var divided = drawLine(newDog);
container.innerHTML = "";
container.appendChild(divided)
</script>
You could always use clipping but note that this would involve save/restore calls which is a relative slow business. There has been suggested to get resetClip() included in the specs but it seem to be hard to get the message across to the group why it is needed.
In any case, I would recommend the following approach:
Create an object or function that can reproduce a single mask (path) of one of the half-side of the image (ie. the diagonal line with containing box).
For left half: Draw image, set composite mode to destination-in, draw mask, extract canvas as image
For right half: Draw image, set composite mode to destination-out, draw mask, extract canvas as image
Now put the to images (just use the canvas elements directly) inside a container so that they are stacked on top of each other.
Animate using a transition or animation class.
var img = new Image(375, 667);
img.onload = setup;
img.src = "http://i.imgur.com/1GUzYh9.jpg";
function setup() {
var path = [0,0, 345,0, 0, 567]; // last point not needed..
var left = createCanvas(this, path, "destination-in");
var right = createCanvas(this, path, "destination-out");
var cont = document.getElementById("cont");
cont.appendChild(left);
cont.appendChild(right);
// animate here by setting animation/transition class or using JS:
var x = 0;
(function loop() {
left.style.left = x + "px"; // translate is smoother, but need
right.style.left = -x + "px"; // prefix in some browser. Update as needed..
x-=5; if (x < -400) x = 0;
requestAnimationFrame(loop);
})();
function createCanvas(img, path, mode) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
// draw image
ctx.drawImage(img, 0, 0);
// create mask
ctx.moveTo(path[0], path[1]);
for(var i = 2; i < path.length; i += 2) ctx.lineTo(path[i], path[i+1]);
ctx.closePath();
// composite mode and create half
ctx.globalCompositeOperation = mode;
ctx.fill();
return canvas
}
}
#cont {
position:relative;width:375px;height:667px;overflow:hidden;
}
#cont>canvas {position:absolute;left:0;right:0;}
<div id="cont"></div>
You can use context.clip to achieive your image-splitting effect
context.clip restricts an image to being drawn withing a specified path.
You can define several of these clipping regions to divide your image into several pieces (paths).
Then in an animation loop, you can clear the canvas and redraw each of these clipping regions with an changing offset. The left clipping region will move (offset) leftward in each loop. The right clipping region will move (offset) rightward in each loop.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var nextTime=0;
var duration=1000/60*3;
var offset=0;
var paths=[];
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/Dog-With-Cute-Cat.jpg";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
paths.push({path:[{x:0,y:0},{x:150,y:0},{x:0,y:ch}],direction:-1});
paths.push({path:[{x:150,y:0},{x:0,y:ch},{x:cw,y:ch},{x:cw,y:0}],direction:1});
requestAnimationFrame(animate);
}
function draw(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<paths.length;i++){
var path=paths[i].path;
var offX=offset*paths[i].direction;
ctx.save();
ctx.beginPath();
var pt=path[0];
ctx.moveTo(pt.x+offX,pt.y);
for(var j=1;j<path.length;j++){
var pt=path[j];
ctx.lineTo(pt.x+offX,pt.y);
}
ctx.closePath();
ctx.stroke();
ctx.clip();
ctx.drawImage(img,offX,0);
ctx.restore();
}
}
function animate(time){
if(offset<cw){requestAnimationFrame(animate);}else{log('done');}
if(time<nextTime){return;}
nextTime=time+duration;
draw();
offset++;
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>