I have a jpeg image that has been decoded, and I am writing it to canvas with putImageData. Is it possible to then move this image around? I cannot find any documentation on it.
The idea is that I will crop a certain part of the decoded image with dirtyX and dirtyY, and now I would like to crop another part of the image. I'm using the decoded image like a spritesheet.
Use the clipping version of drawImage to draw your individual sprites to the canvas
(Don't use getImageData & putImageData for clipping since drawImage is easier and faster.)
If you want to use your decoded image as a spritesheet then you can use the canvas's clipping version of context.drawImage to clip a particular sub-section of your spritesheet and draw it to the canvas.
Here's a previous SO post about the clipping version of drawImage.
HTML / Java Script Canvas - how to draw an image between source and destination points?
Here's example code and a Demo:
Note that the way to "move" sprites on the canvas is to clear the canvas and redraw the desired sprite in it's new position. (You can't "move" an existing drawing on the canvas).
$(window).load(function(){
// canvas related variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// animation related variables
var lastFlap,lastMove;
// define a bird object
// x,y are the position of the bird on the canvas
// spriteX,spriteY is the position of the first desired
// sprite image on the spritesheet
// width,height is the size of 1 sprite image
// currentFrame is the index of which of the sprite images to display
// currentDirection. The sprite plays forward and then backward to
// accomplish 1 flap. This determines if the next frame index will
// be increased (play forward) or decreased (play backward)
var bird={
x:30,
y:30,
spriteX:0,
spriteY:52,
width:51,
height:51,
frames:4,
currentFrame:0,
currentDirection:1
}
// load the spritesheet and start the animation
var spritesheet=new Image();
spritesheet.onload=start;
spritesheet.src="https://dl.dropboxusercontent.com/u/139992952/multple/birdSpritesheet.png";
function start(){
requestAnimationFrame(animate);
}
function animate(time){
// request another animation frame
if(bird.x<canvas.width){
requestAnimationFrame(animate);
}
// if the lastFlap or lastMove times don't aren't set, then set them
if(!lastFlap){lastFlap=time;}
if(!lastMove){lastMove=time;}
// calculate the elapsed times since the last flap and the last move
var elapsedFlap=time-lastFlap;
var elapsedMove=time-lastMove;
// if 50ms have elapsed, advance to the next image in this sprite
if(elapsedFlap>50){
// advance to next sprite on the spritesheet (flap)
bird.currentFrame+=bird.currentDirection;
// clamp bird.currentFrame between 0-3 (0,1,2,3)
// (because there are 4 images that make up the whole bird sprite)
if(bird.currentFrame<0 || bird.currentFrame>bird.frames-1){
bird.currentDirection*=-1;
bird.currentFrame+=bird.currentDirection;
}
// reset the flap timer
lastFlap=time;
}
// locate the current sprite from the spritesheet
var sx=bird.spriteX+bird.currentFrame*bird.width;
var sy=bird.spriteY;
// if 100ms have elapsed, move the bird across the canvas
if(elapsedMove>100){
bird.x+=3;
lastMove=time;
}
// clear the whole canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// draw the current part of the bird sprite at the current bird.x
ctx.drawImage(spritesheet,
sx,sy,bird.width,bird.height,
bird.x,bird.y,bird.width,bird.height
);
}
}); // end $(function(){});
body{ background-color: white; }
canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>The canvas animating a clipped sprite</h4>
<canvas id="canvas" width=300 height=100></canvas>
<br>
<h4>The spritesheet</h4>
<img id=spritesheet src='https://dl.dropboxusercontent.com/u/139992952/multple/birdSpritesheet.png'>
Yes and No :-)
Since canvas draws in immediate-mode it doesn't know that you have drawn an image on the canvas. So, you cannot draw it on the canvas one single time and move the painted image around. You will have to manually redraw the complete image in each animationframe at a new postion.
You can do that using the requestAnimationFrame() as Jonas Grumann explains.
Related
I adapted an open source game to fit for my fantasy book series, Eloik.
This game
I'd like to replace the blue arc for a png image (about same size).
I know I have to draw an image but how to??
Here's the portion of the code :`
// Shield - Boomlight
context.beginPath();
context.strokeStyle = '#0066cc';
context.lineWidth = 10;
context.arc( player.position.x, player.position.y, player.radius,
player.angle + 1.6, player.angle - 1.6, true );
context.stroke();`
I tried that following code but... The png image doesn't appears at the right spot and it's not interactive with the game as the arc...`
<html>
<body>
<img id="boom" width="176" height="134" src="http://eloik.com/wp/wp-
content/uploads/2017/05/BOOMLIGHT-jeu-bd.png" alt="">
*In the Javascript :
<script>
window.onload = function() {
var image = new Image();
image.src="http://eloik.com/wp/wp-content/uploads/2017/05/BOOMLIGHT-jeu-
bd.png";
context.beginPath();
context.drawImage(image, 10, 10);
}
</script>
</body>
</html> `
So now, what's wrong ?
Thanks ! :)
First, in order to use drawImage, we need to load it. You can do it like this:
/* core.js: line 57 */
// Create a handle for the image
var shieldImage;
/* core.js: line 133 */
// Create a new image
shieldImage = new Image();
// When it's loaded, execute animate
shieldImage.onload = animate;
// Set the src
shieldImage.src = "http://eloik.com/wp/wp-content/uploads/2017/05/BOOMLIGHT-jeu-bd.png";
This way, the animate function will only be called once the image has loaded. Then, in order to position your image and rotate it, you can do this:
/* core.js: line 420 */
// Set the origin of the canvas on the player position
context.translate(player.position.x, player.position.y);
// Rotate the canvas
context.rotate(player.angle + Math.PI + .2);
// Draw the shield
context.drawImage(shieldImage, -player.radius, -player.radius, player.radius*1.5, player.radius*2.3);
// Rotate the canvas back
context.rotate(-player.angle - Math.PI - .2);
// Reset the initial origin of the canvas
context.translate(-player.position.x, -player.position.y);
Since we cannot rotate the image itself, we use this trick, which consists in rotating the canvas, drawing, and reverting the rotation of the canvas. We also translate it in order to have the rotation axis on the player position.
You'll also notice I added some numbers in there. That's because your shield image is not a perfect circle. I distorted it so that it does not look weird with the current collision system (which is based on a circle). If you want to keep the oval shape of the image, you'll need to make more serious changes to the rest of the code so that collisions apply to that shape.
And that's it, your blue arc is replaced with your PNG image (Updated JS here)
PS: You have a cool last name ! - same as mine
Is it possible to skew an image on a canvas to make it appear as a reflection of another image?
Here is an illustration:
I need to flip and skew the image to get the desired effect, but I have not been able to get it to look like that. How can this be achieved?
I have found this example:
http://jsfiddle.net/jpnrzc9y/
ctx.save();
ctx.transform(1,0,0.3,-1,0,0);
ctx.drawImage(tree1,74,-117,20,40);
ctx.restore();
They seem to set the transform based on some random values.
But I cannot make sense of it. The values seem very random to me. Im trying to create a dynamic function that allows me to determine the amount of skew and that works for all images.
You can use context.scale to flip an image vertically:
// flip the canvas vertically
context.scale(1,-1);
Here are the steps to create a skewed reflection of an image:
Move (move==translate) the 0,0 origin to the desired x,y: ctx.translate(x,y);
Draw the original image image: ctx.drawImage(img,0,0);
Move to the bottom of the original image: ctx.translate(0,img.height);
Scale to vertically flip the image: ctx.scale(1,-1);
Apply a skew: ctx.transform(1,0,skewAngle,1,0,0);
Shrink the reflected image (just for aesthetics): ctx.scale(1,0.50);
Make the reflected image 50% opacity: ctx.globalAlpha=0.50;
Draw the reflected image: ctx.drawImage(img,0,-img.height);
Clean up by setting all transforms to default: ctx.setTransform(1,0,0,1,0,0);
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 img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/character1.png";
function start(){
// 60x110
skewedReflection(img,25,25)
}
function skewedReflection(img,x,y){
// calc the 45 degree skew angle needed by ctx.transform
var skewAngle=-Math.tan(Math.PI/4);
// move the 0,0 origin to x,y
ctx.translate(x,y);
// draw the original image image
ctx.drawImage(img,0,0);
// move to the bottom of the original image
ctx.translate(0,img.height);
// use scale to flip the image
ctx.scale(1,-1);
// apply a skew
ctx.transform(1,0,skewAngle,1,0,0);
// shrink the reflected image
ctx.scale(1,0.50);
// make the reflected image 50% opacity
ctx.globalAlpha=0.50;
// draw the reflected image
ctx.drawImage(img,0,-img.height);
// clean up by setting all transforms to default
ctx.setTransform(1,0,0,1,0,0);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
It's my first time to use html5 canvas and I have no idea yet how it works.
My problem is, I have to modify the colors of the images in the canvas. It's easy if there's only one image. But, I will have more than one, in other words, overlapping images.
To further understand my question, I created an illustration. There will be 2 image files only, Image 1 and Image 2:
This is my current code (with fiddle here too):
HTML:
<canvas id="canvas1" width="600" height="600"></canvas>
JS:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
ctx.fillStyle = 'yellow'; // background color. box in the middle is transparent. try changing this to see the effect
ctx.fillRect(40,0,250,300); // not sure if there's other way to fill in the tranparent area. but I created a box behind the image
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
}
img.src = "http://s7.postimg.org/aruxhs8mz/pink.png"; //image 1
// I want to fill in the paw image too
/*ctx2.fillStyle = 'purple';
ctx2.fillRect(40,0,500,500); */
//should I declare something like this again?
var img2 = new Image();
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
}
img2.src = "http://s7.postimg.org/69smposl7/paw.png"; //image 2
//paw initially colored light blue. i would like to customize the color of this too
I should be able to fill the paw image in the middle as well and not just the main image. How to do that?
I have created a fiddle just to enlighten you with my question.
Hope someone can help me for any advice.
Thanks a bunch!
You can do your task with Compositing.
Compositing tells the canvas what to do when drawing additional new drawings (pixels) on the canvas.
In your case, 3 compositing modes are useful to learn.
Source-over Compositing
The default method of compositing is 'source-over' where new drawings are drawn over existing pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// second draw a red source rectangle
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Source-atop Compositing
'source-atop' compositing will draw new pixels only where the new pixels overlap the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='source-atop';
// second draw a red source rectangle
// (red will overwrite only where it overlapped the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Destination-over Compositing
'destination-over' compositing will draw new pixels under the existing canvas pixels.
// first draw a blue destination rectangle
ctx.fillStyle='blue';
ctx.fillRect(30,30,50,50);
// set compositing to 'source-atop'
// (the new red pixels will only be drawn where
// they overlap the existing blue pixels)
ctx.globalCompositeOperation='destination-over';
// second draw a red source rectangle
// (red will appear under the blue)
ctx.fillStyle='red';
ctx.fillRect(60,60,50,50);
then results
Here's how to use compositing to change the color of your paw.
Clear the canvas. You cannot directly change the color of anything previously drawn on the canvas, so the typical workflow of canvas is to erase it and redraw items in their new positions & colors.
Draw the paw image.
Set compositing to source-atop so new drawings will only be drawn where the existing paw pixels exist.
Fill the canvas with your new paw color using fillStyle & fillRect. This causes your paw to be recolored because the newly colored rectangle pixels will only appear where your paw pixels currently exist.
Set compositing to destination-over so new drawings will be drawn under existing pixels.
Fill the yellow box. Your paw will not be overwritten because new (yellow) pixels will be drawn "under" your paw.
Set compositing back to the default source-over so new drawings will be drawn on top of existing drawings.
Draw your frame that's transparent in the center. Your paw and the yellow background will show through the transparent center of your frame.
Here's example code and a Demo:
var can = document.getElementById('canvas1');
var ctx = can.getContext('2d');
var ctx2 = can.getContext('2d');
var images=[];
var urls=[];
urls.push('http://s7.postimg.org/aruxhs8mz/pink.png');
urls.push('http://s7.postimg.org/69smposl7/paw.png');
var imgCount=urls.length;
document.getElementById('recolor').onclick=function(){
redrawWithNewPawColor();
};
for(var i=0;i<urls.length;i++){
images[i]=new Image();
images[i].onload=myOnload;
images[i].src=urls[i];
}
function myOnload(){
imgCount--;
if(imgCount>0){return;}
start();
}
function start(){
redrawWithNewPawColor()
}
function drawWithRecoloredPaw(newPawColor){
// clear the canvas
ctx.clearRect(0,0,can.width,can.height);
// draw the paw
ctx.drawImage(images[1], 0, 0);
// set compositing to source-atop
// so only existing pixels will be overwritten
ctx.globalCompositeOperation='source-atop';
// fill with new color
ctx.fillStyle=newPawColor;
// Because of compositing, only the paw is being color filled
ctx.fillRect(0,0,can.width,can.height);
// set compositing to destination-over
// so new pixels will be draw behind existing (paw) pixels
ctx.globalCompositeOperation='destination-over';
// change the fill color to yellow
ctx.fillStyle='yellow';
// fill the yellow box
ctx.fillRect(40,0,250,300);
// set compositing to the default of source-over
ctx.globalCompositeOperation='source-over';
// draw the transparent frame
ctx.drawImage(images[0],0,0);
}
function redrawWithNewPawColor(){
drawWithRecoloredPaw(randomColor());
}
function randomColor(){
return('#'+Math.floor(Math.random()*16777215).toString(16));
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<button id='recolor'>Recolor Paw</button>
<br>
<canvas id="canvas1" width=600 height=600></canvas>
the problem is that canvas does not allow you to adjust an image, but you could create a block over the paw wich has an opacity of 0.5 and than fill in the transparent part again, you could also use this:
http://www.w3schools.com/tags/canvas_globalcompositeoperation.asp
If the image is in your own web folder you can use it with an img tag and get the data an change it
much like the W3S example Changing image in canvas
after this step it's just a matter of redrawing the canvas with the new data
This will not work if the image comes from another source, becaus of security reasons
I am having trouble rotating an image using an HTML5 canvas. I suppose that I just have the math wrong, and would appreciate any help in getting this right.
On a mobile device I am capturing a user signature on a 150px by 558px canvas. I am than attempting to create a 558px by 150px image that is just the captured signature rotated 90 degrees. Below is a snippet of the code I've currently come up with. As you can likely surmise, I do not have a good grip on the math going into this. I believe I have the procedure correct, just not the numbers.
What I'm trying to do is:
1) set the center of the canvas to the middle, offsetting by the height and width of my image
2) rotate the canvas by 90 degrees
3) draw the image
4) translate the canvas back.
EDIT: Here's a JSFiddle: http://jsfiddle.net/x9FyK/
var $signature = $("#signature");
var signatureData = $signature.jSignature("getData");
console.log(signatureData);
var img= new Image();
img.onload = function() {
var rotationCanvas = document.createElement('canvas');
rotationCanvas.width = img.height;
rotationCanvas.height = img.width;
var context = rotationCanvas.getContext("2d");
context.translate((rotationCanvas.width/2) - (img.width/2), -(rotationCanvas.height/2) - img.height/4);
context.rotate(Math.PI/2);
context.drawImage(img,0,0);
context.translate(-((rotationCanvas.width/2) - (img.width/2)), -(-(rotationCanvas.height/2) - img.height/4));
var rotatedData = rotationCanvas.toDataURL();
...Handling rotated data here
};
img.src = signatureData;
If I can provide any more information, please let me know.
Thanks in advance for your help,
There are several ways of resetting a transformed (translated+rotated) canvas back to its original state.
Low-Pointer's answer is using context.save to save the context in is original un-transformed state and using context.restore to restore the context to its original state after the drawing is done.
The other way it to undo your transforms in the reverse order that they were performed.
Also, note that context.translate will actually move the canvas origin to the center of the canvas. Since images are drawn from their top-left (not their center), you must offset drawImage by half the image width and height if you want the image centered in the canvas.
Here's and example: http://jsfiddle.net/m1erickson/EQx8V/
// translate to center-canvas
// the origin [0,0] is now center-canvas
ctx.translate(canvas.width/2,canvas.height/2);
// roate the canvas by +90% (==Math.PI/2)
ctx.rotate(Math.PI/2);
// draw the signature
// since images draw from top-left offset the draw by 1/2 width & height
ctx.drawImage(img,-img.width/2,-img.height/2);
// un-rotate the canvas by -90% (== -Math.PI/2)
ctx.rotate(-Math.PI/2);
// un-translate the canvas back to origin==top-left canvas
ctx.translate(-canvas.width/2,-canvas.height/2);
// testing...just draw a rect top-left
ctx.fillRect(0,0,25,10);
this will surely help you >>HTML5 Canvas Rotate Image :)
use some SEPARATE js function for drawing your canvas :
function drawRotated(degrees){
contex_var.clearRect(0,0,canvas.width,canvas.height);
contex_var.save();
contex_var(canvas.width/2,canvas.height/2);
contex_var.rotate(degrees*Math.PI/180);
contex_var.drawImage(image,-image.width/2,-image.width/2);
contex_var.restore();
}
Suppy some angle to any buttonclick function:
$("#clockwise").click(function(){
angleInDegrees+=30;
drawRotated(angleInDegrees);
});
I'm trying to implement pixel perfect collision detection in my canvas game, however I can't seem to get the pixel information from my sprites.
I need the x and y values for each pixel of the sprite, and from what I've read I use the getImageData() method to do that.
However, this doesn't work:
this.sprite = new Image();
this.sprite.src = 'img/player.png';
console.log(this.sprite.getImageData());
Am I maybe using the wrong type of sprite? Because I get this error in the console:
Uncaught TypeError: Object # has no method
'getImageData'
Here’s how to use a sprite’s pixel data to do pixel-perfect hit-testing
First, draw your sprite normally on your visible canvas.
Create a red-masked copy of the sprite on a hidden canvas. This copy is the exact size as the sprite but contains only transparent or red pixels.
Track the visible sprite’s bounding box. When the bounding box is clicked calculate the X/Y of the mouseclick in relation to the sprite’s bounding box (not in relation to the canvas).
Then, refer to the red-masked sprite and see if the corresponding pixel at that X/Y is red or transparent. If the pixel is red, you have a pixel-perfect hit. If the pixel is transparent, no hit.
In this illustration, assume the blue dots are the X/Y click position. Since the corresponding X/Y pixel in the red-masked canvas is “red”, this is a HIT.
Here is code to create a red-masked sprite. I don’t show the code for hit-testing here, but if you try and can’t code the hit-test, I’ll take the time to code up the hit-testing too.
Important note: to get this code to run, you must avoid the cross-domain security restriction. Be sure your image source is on your local domain, otherwise you will run into a cross domain security violation and the masking image will not be drawn…So you can't do this for your sprite source: http://otherDomain.com/picture.jpg!
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid blue;}
</style>
<script>
$(function(){
var c=document.getElementById("canvas");
var ctx=c.getContext("2d");
var img=new Image();
img.onload=function(){
ctx.drawImage(this,100,25);
// make a red-masked copy of just the sprite
// on a separate canvas
var canvasCopy=document.getElementById("canvasCopy");
var ctxCopy=canvasCopy.getContext("2d");
canvasCopy.width=this.width;
canvasCopy.height=this.height;
ctxCopy.drawImage(img,0,0);
// make a red-masked copy of the sprite on a separate canvas
var imgData=ctxCopy.getImageData(0,0,c.width,c.height);
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i+3]>0){
imgData.data[i]=255;
imgData.data[i+1]=0;
imgData.data[i+2]=0;
imgData.data[i+3]=255;
}
}
ctxCopy.putImageData(imgData,0,0);
}
img.src = "houseIcon.png";
}); // end $(function(){});
</script>
</head>
<body>
<p>Original sprite drawn on canvas at XY:100/25</p>
<canvas id="canvas" width="400" height="300"></canvas>
<p>Red-masked on canvas used for hit-testing</p>
<canvas id="canvasCopy" width="300" height="300"></canvas>
</body>
</html>
To do pixel-perfect collision testing between 2 sprites, you would:
Create a red-masked canvas for both sprite#1 and sprite#2.
First test if the bounding boxes of the 2 sprites are colliding. If the bounding boxes are not colliding, the 2 sprites are not colliding, so you can stop the hit-test here.
If the 2 sprites are possibly colliding using the bounding boxes test, create a third canvas for the collision test.
You’re going to use canvas’s compositing method to test for a collision between sprite#1 and sprite#2 by drawing both sprite#1 and sprite#2 onto the third canvas. Using compositing, only the COLLIDING pixels of the 2 sprites will be drawn on the third canvas.
Here’s how Compositing with “destination-in” works: The existing canvas content is kept where both the new shape (sprite#2) and existing shape (sprite#1) content overlap. Everything else is made transparent.
So…
Draw sprite#1 in its transformed state into the third canvas (transforms could be move, rotate, scale, skew--anything!).
Set the globalCompositeOperation to destination-in.
context.globalCompositeOperation = 'destination-over';
Draw sprite#2 in its transformed state into the third canvas.
After this draw, the third canvas contains only the colliding portions of sprite#1 and sprite#2
Test each pixel in the third canvas for non-transparent pixels. If you find any non-transparent pixels, the 2 sprites ARE COLLIDING.
Depending on what action you want to take upon collision, you might just bail out when you find the first colliding pixel.