How to move image on canvas using arrow keys in javascript - javascript
I've tried a few different ways that I have seen on here, but I can't quite get my image to move. Whenever I try adapting code for arrow key presses, it just seems to make my canvas shrink and my player model (spaceperson) disappear.
here is the "drawing board" I keep returning to, and what I have so far.
// Get the canvas and context
var canvas = document.getElementById("space");
var ctx = canvas.getContext("2d");
canvas.width = 1920;
canvas.height = 700;
// Create the image object
var spaceperson = new Image();
// Add onload event handler
spaceperson.onload = function () {
// Done loading, now we can use the image
ctx.drawImage(spaceperson, 280, 300);
};
// artwork by Harrison Marley (using make8bitart.com)
spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";`
I am quite new to javascript, and I am just trying to work out how I can move the specperson image using arrow keys. I was trying to make a class for space person to access their x,y values, but I can't seem to draw the image without using .onload
here a more complete example:
//just a utility
function image(url, callback){
var img = new Image();
if(typeof callback === "function"){
img.onload = function(){
//just to ensure that the callback is executed async
setTimeout(function(){ callback(img, url) }, 0)
}
}
img.src = url;
return img;
}
//a utility to keep a value constrained between a min and a max
function clamp(v, min, max){
return v > min? v < max? v: max: min;
}
//returns a function that can be called with a keyCode or one of the known aliases
//and returns true||false wether the button is down
var isKeyDown = (function(aliases){
for(var i=256, keyDown=Array(i); i--; )keyDown[i]=false;
var handler = function(e){
keyDown[e.keyCode] = e.type === "keydown";
e.preventDefault(); //scrolling; if you have to suppress it
};
addEventListener("keydown", handler, false);
addEventListener("keyup", handler, false);
return function(key){
return(true === keyDown[ key in aliases? aliases[ key ]: key ])
}
})({
//some aliases, to be extended
up: 38,
down: 40,
left: 37,
right: 39
});
// Get the canvas and context
var canvas = document.getElementById("space");
canvas.width = 1920;
canvas.height = 700;
var ctx = canvas.getContext("2d");
//the acutal image is just a little-part of what defines your figue
var spaceperson = {
image: image("//i.imgur.com/Eh9Dpq2.png", function(img){
spaceperson.width = img.naturalWidth;
spaceperson.height = img.naturalHeight;
//start the rendering by calling update
update();
}),
//position
x: 60, y: 310,
width: 0, height: 0,
speed: 200 // 200px/s
};
var lastCall = 0; //to calculate the (real) time between two update-calls
//the render-fucntion
function update(){
//taking account for (sometimes changing) framerates
var now = Date.now(), time = lastCall|0 && (now-lastCall)/1000;
lastCall = now;
requestAnimationFrame(update);
var sp = spaceperson,
speed = sp.speed;
//checking the pressed buttons and calculates the direction
//two opposite buttons cancel out each other, like left and right
var dx = (isKeyDown('right') - isKeyDown('left')) * time,
dy = (isKeyDown('down') - isKeyDown('up')) * time;
//fix the speed for diagonals
if(dx && dy) speed *= 0.7071067811865475; // * 1 / Math.sqrt(2)
if(dx) { //there is some movement on the x-axes
sp.x = clamp(
//calculate the new x-Position
//currentPos + direction * speed
sp.x + dx * sp.speed,
//restraining the result to the bounds of the map
0, canvas.width - sp.width
);
}
//same for y
if(dy) sp.y = clamp(sp.y + dy * sp.speed, 0, canvas.height - sp.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(sp.image, sp.x, sp.y);
}
Edit:
A quick question (I hope); if I was to later add other objects, would I check for collisions in update()?
This is still just a very basic example. The main purpose of the update()-function should be to work as the main event-loop.
To trigger all Events that have to happen each frame in the order they have to happen.
var lastCall = 0;
function update(){
//I always want a next frame
requestAnimationFrame(update);
//handle timing
var now = Date.now(),
//time since the last call in seconds
//cause usually it's easier for us to think in
//tems like 50px/s than 0.05px/ms or 0.8333px/frame
time = lastCall|0 && (now-lastCall) / 1000;
lastCall = now;
movePlayer(time);
moveEnemies(time);
moveBullets(time);
collisionDetection();
render();
}
function render(){
ctx.clear(0, 0, canvas.width, canvas.height);
drawBackground(ctx);
for(var i=0; i<enemies.length; ++i)
enemies[i].render(ctx);
player.render(ctx);
}
Not saying that you have to implement all these functions now, but to give you an idea of a possible structure.
Don't be scared to break big tasks (functions) up into subtasks.
And it might make sense to give each enemy a move()-function so you can implement different movement-patterns per enemy,
or you say that the pattern is (and will be) all the same for each enemy, parameterized at the best, then you can handle that in a loop.
Same thing for rendering, as I'm showing in the last part of code.
Here's some slightly modified code from a game I was noodling around with a while back. If you want to see more code, check out the complete JS on GitHub. The game is incomplete but you should gather some helpful clues as to how to move an image around the canvas.
var spaceperson = {
speed: 256,
other_stuff: ''
},
keysDown = [],
update,
main;
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
update = function (modifier) {
if (38 in keysDown && spaceperson.y > 0) { // UP
spaceperson.y -= spaceperson.speed * modifier;
}
if (40 in keysDown && spaceperson.y < CANVAS_HEIGHT - SPACEPERSON_HEIGHT) { // DOWN
spaceperson.y += spaceperson.speed * modifier;
}
if (37 in keysDown && spaceperson.x > 0) { // LEFT
spaceperson.x -= spaceperson.speed * modifier;
}
if (39 in keysDown && spaceperson.x < CANVAS_WIDTH - SPACEPERSON_WIDTH) { // RIGHT
spaceperson.x += spaceperson.speed * modifier;
}
}
I'm not sure but i think this can help.
// Get the canvas and context
var canvas = document.getElementById("space");
var ctx = canvas.getContext("2d");
canvas.width = 1920;
canvas.height = 700;
var x = 280;
var y = 300;
// Create the image object
var spaceperson = new Image();
spaceperson.addEventListener("keypress", press);
// Add onload event handler
spaceperson.onload = function () {
// Done loading, now we can use the image
ctx.drawImage(spaceperson, x, y);
};
function press(event) {
if(event.keyCode == 37) {//LEFT
x = x - 1;
} else if(event.keyCode == 38) {//UP
y = y - 1;
} else if(event.keyCode ==39) {//RIGHT
x = x + 1;
} else if(event.keyCode == 40) {//DOWN
y = y + 1;
}
draw();
}
function draw(){
ctx.drawImage(spaceperson,x,y);
}
// artwork by Harrison Marley (using make8bitart.com)
spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";
I found a solution!
// Get the canvas and context
var canvas = document.getElementById("space");
var ctx = canvas.getContext("2d");
canvas.width = 1920;
canvas.height = 700;
var xPos = 60;
var yPos = 310;
// Create the image object
var spaceperson = new Image();
// Add onload event handler
spaceperson.onload = function () {
// Done loading, now we can use the image
ctx.drawImage(spaceperson, xPos, yPos);
};
function move(e){
if(e.keyCode==39){
xPos+=10;
}
if(e.keyCode==37){
xPos-=10;
}
if(e.keyCode==38){
yPos-=10;
}
if(e.keyCode==40){
yPos+=10;
}
canvas.width=canvas.width;
ctx.drawImage(spaceperson, xPos, yPos);
}
document.onkeydown = move;
// artwork by Harrison Marley
spaceperson.src = "http://i.imgur.com/Eh9Dpq2.png";
Related
Uncaught TypeError: Cannot set property 'onclick' of null. Tried window.onload
So, I'm relatively new to Javascrip. Though what I want to do is give a moving image on my canvas an id, so that I can ultimately use onclick to use the image as a clickable image, so I can redirect the user to another page, which is what would happen when the image is clicked. Here is my code so far. I need help. If you need any more clarification I will try to explain further. var ctx; var imgBg; var imgDrops; var x = 0; var y = 0; var noOfDrops = 50; var fallingDrops = []; function drawBackground(){ ctx.drawImage(imgBg, 0, 0); //Background } function draw() { drawBackground(); for (var i=0; i< noOfDrops; i++) { ctx.drawImage (fallingDrops[i].image, fallingDrops[i].x, fallingDrops[i].y); //The rain drop fallingDrops[i].y += fallingDrops[i].speed; //Set the falling speed if (fallingDrops[i].y > 1000) { //Repeat the raindrop when it falls out of view fallingDrops[i].y = -25 //Account for the image size fallingDrops[i].x = Math.random() * 10000; //Make it appear randomly along the width } } } function setup() { var canvas = document.getElementById('canvasRegn'); if (canvas.getContext) { ctx = canvas.getContext('2d'); imgBg = new Image(); imgBg.src = "http://images.susu.org/unionfilms/films/backgrounds/hd/space-jam.jpg"; setInterval(draw, 36); for (var i = 0; i < noOfDrops; i++) { // Charles Barkley var fallingDr = new Object(); fallingDr["image"] = new Image(); fallingDr.image.src = 'http://xenboards.ignimgs.com/external_data/attachments/8/8795-f09b907a01726a25ca2fbd2f588e3f0e.jpg'; fallingDr["x"] = Math.random() * 10000; fallingDr["y"] = Math.random() * 5; fallingDr["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr); // Bugs bunny var fallingDr2 = new Object(); fallingDr2["image"] = new Image(); fallingDr2.image.src = 'http://i.imgur.com/zN2CSAf.png' fallingDr2["x"] = Math.random() * 10000; fallingDr2["y"] = Math.random() * 5; fallingDr2["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr2); // Michael Jordan var fallingDr3 = new Object(); fallingDr3["image"] = new Image(); fallingDr3.image.src = 'http://i.imgur.com/XxvJiGg.png' fallingDr3["x"] = Math.random() * 10000; fallingDr3["y"] = Math.random() * 5; fallingDr3["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr3); // Daffy duck var fallingDr4 = new Object(); fallingDr4["image"] = new Image(); fallingDr4.image.src = 'http://i.imgur.com/QZogw2L.png' fallingDr4["x"] = Math.random() * 10000; fallingDr4["y"] = Math.random() * 5; fallingDr4["speed"] = 3 + Math.random() * 5; fallingDrops.push(fallingDr4); fallingDr4.image.id = "Daffy"; } } } setup(); window.onload = function(){ document.getElementById("Daffy").onclick=function(){ alert("Hello World"); } }
Try: fallingDr4.image.onclick=function(){ alert(this.id); } should alert "Duffy".
Your problem is that you're trying to catch the click event on an element that is not in the document and hence, not clickable by the user. When you call var img = new Image() a new <img> element is created, with all its properties that you can already modify in your javascript. But this element is only available to your scripts, and is not displayed into the page until you call document.anyElement.appendChild(img). So it's better to consider this as an imageObject more than to an element (even if it actually also is). What is in your document, and accessible to your user, is the <canvas> element. So if you want to know if the user has clicked, you will have to attach the eventListener to this canvasElement. But the canvasElement doesn't know what it does represent. When you call context.drawImage(), you're just applying the pixels from the imageSource to the ones of the canvas, and all reference to the original imageObject are lost. To workaround this, you'll then have to store the position of your drawn image into the canvas, and then check if the click event you caught was inside these positions. Click events' clientX and clientY properties of the Event passed as arguments of you handler will give you the position of the cursor when the event occurred. But the positions are relative to the top-left corner of the window. So you'll also need to make these relative to the top-left corner of your canvas, which can be done by calling the getBoundingClientRect() method of the canvasElement. Here is a simplified example : // this function will be called at image's load var init = function() { // a reference to our "in-screen" canvasElement var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); // "kitty" will be the js object that will help us know if we clicked on our image // "this" refers to the imageObject var kitty = { width: this.width, height: this.height, // random positions left: Math.random() * (canvas.width - this.width), top: Math.random() * (canvas.height - this.height), }; // draw our image at the random positions we created ctx.drawImage(this, kitty.left, kitty.top, kitty.width, kitty.height); // here we're listening to mousemove event, // but click event shares the same clientX & clientY properties var moveHandler = function(evt) { // in the "evt" object passed, we can get the x and y positions relative to the window // so we make these relatives to the canvas ones var canvasRect = canvas.getBoundingClientRect(); var x = evt.clientX - canvasRect.left; var y = evt.clientY - canvasRect.top; // now we've got our relative positions, we can check if we were inside the image if (x >= kitty.left && x <= (kitty.left + kitty.width) && y >= kitty.top && y <= (kitty.top + kitty.height) ) { // we are over the image, do something canvas.style.cursor = 'pointer'; document.body.style.backgroundColor = 'lightblue'; } else { canvas.style.cursor = 'default'; document.body.style.backgroundColor = 'transparent'; } }; // attach this event handler to the canvasElement canvas.addEventListener('mousemove', moveHandler); }; // this will create an imageObject, which will stay "off-screen" (never appendded to the document) var img = new Image(); // wait that the image has loaded before trying to make any magic img.onload = init; img.src = "http://lorempixel.com/200/70/cats"; body { width: 100vw; text-align: center; } canvas { margin: 0 auto; position: relative; } <canvas id="canvas" width="500" height="500"></canvas> (you may need to scroll to actually see the image, or go fullscreen)
How to make javascript canvas draw faster?
I have the following code to display an ECG. I use the canvas to draw the graph background (each grid of 2.5 mm dimension). Later I'm taking the y coordinates from an array array_1 (x coordinates are calculated within the program). The problem with this approach is it will take around 40 seconds to plot the entire graph since there are 1250 values within array array_1. What I could do is I could do the plotting part within a loop in which case, the entire graph is plotted as soon as the page is loaded. But, I need the plotting to happen over the course of 5 seconds. Not more. Not less. How would I alter the code to do this? Please help. <!DOCTYPE html> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <canvas id="canvas" width="1350" height="1300" style="background-color: white;"></canvas> <script type='text/javascript'> var canvas = document.getElementById("canvas"); var ctxt = canvas.getContext("2d"); var n1 = 1; var n1_x=49; //Graph x coordinate starting pixel. var n1_y=72;//Graph y coordinate starting pixel. var array_1 = []// array from which y coordinates are taken. Has 1250 elements var ctx = canvas.getContext("2d"); var x=0; var y=0; var Line_position=-1; while(x<=1350)//graph width { ctxt.lineWidth = "0.5"; Line_position=Line_position+1; if(Line_position%5==0) { ctxt.lineWidth = "1.5"; } ctxt.strokeStyle = "black"; ctxt.beginPath(); ctxt.moveTo(x, 0); ctxt.lineTo(x, 1300); ctxt.stroke(); x=x+9.43; } Line_position=-1; while(y<=1300)//graph height { ctxt.lineWidth = "0.5"; Line_position=Line_position+1; if(Line_position%5==0) { ctxt.lineWidth = "1.5"; } ctxt.strokeStyle = "black"; ctxt.beginPath(); ctxt.moveTo(0, y); ctxt.lineTo(1350,y); ctxt.stroke(); y=y+9.43; } drawWave(); function drawWave() { requestAnimationFrame(drawWave); ctx.lineWidth = "1"; ctx.strokeStyle = 'blue'; ctx.beginPath(); ctx.moveTo(n1_x- 1, n1_y+array_1[n1-1]);//move to the pixel position ctx.lineTo(n1_x, n1_y+array_1[n1]);//Draw to the pixel position ctx.stroke(); n1_x=n1_x+0.374;//Incrementing pixel so as to traverse x axis. n1++; } </script> </body> </html> Here is the array: array_1 = [69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,72,72,72,72,72,72,72,73,73,74,74,74,74,74,74,74,73,73,73,73,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,73,73,73,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,71,72,72,72,73,73,73,72,72,72,73,73,73,74,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,73,73,73,72,72,72,71,101,71,70,70,70,69,68,68,67,67,66,66,67,67,69,70,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,74,76,77,76,70,57,40,22,11,11,22,40,57,69,73,73,71,71,71,72,72,73,73,74,74,74,73,72,72,72,72,72,72,72,72,72,72,72,72,71,71,70,70,71,71,71,71,70,70,69,69,69,69,69,69,69,68,68,68,67,67,66,66,65,65,64,63,63,62,62,62,62,62,62,62,62,63,63,64,65,66,67,68,68,69,70,71,72,72,72,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,73,73,73,73,72,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,73,73,74,74,74,74,74,74,73,73,72,73,73,73,74,73,73,72,72,72,73,73,73,72,72,73,73,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,70,70,70,70,70,69,69,68,67,67,67,67,68,69,71,72,72,73,73,73,73,74,74,74,74,74,73,73,73,73,75,77,78,76,67,53,35,18,8,10,23,41,58,69,73,72,71,70,71,72,73,73,73,73,73,73,73,73,72,72,73,73,73,73,72,71,71,70,70,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,68,67,67,67,67,67,66,65,65,65,64,63,62,61,61,61,60,60,60,59,60,60,60,61,62,63,65,66,66,67,68,69,70,71,72,72,72,72,73,73,73,72,72,72,72,72,72,72,73,73,73,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,71,72,72,73,73,73,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,72,73,73,73,73,73,73,72,73,73,73,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,71,71,70,70,69,69,69,68,67,67,66,65,66,66,68,69,70,71,72,72,73,73,73,73,73,73,74,74,74,74,74,74,76,78,78,74,64,48,29,13,5,10,26,45,62,71,73,72,71,71,72,73,73,73,73,73,74,74,74,73,72,72,72,73,73,73,73,73,73,73,72,72,72,72,71,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,67,66,66,66,66,65,65,64,63,62,62,61,61,60,60,60,60,61,62,62,63,64,65,66,67,68,70,71,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,75,75,74,74,74,73,73,73,74,73,73,73,73,73,74,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,74,74,74,73,73,73,73,73,73,73,73,73,73,72,72,72,72,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,72,72,71,70,70,70,69,69,68,68,67,67,66,67,67,68,69,70,71,72,73,73,74,74,73,73,73,74,75,75,74,73,73,74,76,78,75,67,52,32,15,5,8,22,41,59,69,73,72,71,70,71,72,72,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,72,72,72,71,71,71,70,70,70,70,70,70,70,69,69,69,69,68,68,68,68,67,67,66,65,65,64,64,64,63,62,61,60,60,60,60,60,61,61,62,62,63,64,65,65,66,67,68,69,70,71,71,71,71,71,71,72,72,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,71,71,71,72,72,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,71,71,71,70,70,70,70,69,69,68,67,67,68,69,71,72,73,73,73,73,73,73,73,73,74,75,75,75,74,74,74,75,77,77,75,67,52,34,18,10,12,26,45,62,71,74,73,72,72,72,73,74,74,74,75,75,74,74,74,74,74,74,74,74,74,73,73,73,73,74,74,73,73,73,73,73,73,73,72,72,71,71,71,71,71,70,70,70,69,69,69,68,68,68,68,67,66,65,64,63,63,62,62,62,63,63,63,63,64,65,66,67,69,69,70,71,72,72,73,73,74,74,74,74,75,75,76,76,74,72,70,70,69,69 ];
I'd probably go about the task something like this. As mentioned in a comment, we need to draw a number of the data-points per-frame. How many we draw depends on the speed that the browser is able to supply an animation frame. I've hard-coded the value to 4, since that seems to work on my machine, but with not much more work you can probably make the code time itself and adjust this value on the fly so that your animation runs for as close as possible to the target time. I had a quick go, but the results were awful, I'll leave that as an exercise in research or thought for the reader. By keeping track of how many frames we've already drawn for the current 'refresh-cycle', we know how far to index into the array for the first point to be drawn for each frame. I've tried to parameterize the code as much as possible, but it's late and I'm tired, I may have overlooked something somewhere. <!doctype html> <html> <head> <script> function byId(id,parent){return (parent == undefined ? document : parent).getElementById(id);} window.addEventListener('load', onDocLoaded, false); function onDocLoaded(evt) { drawBkg(byId('canvas'), 9.43, 5, "0.5", "1.5", "black"); drawCurFrame(); } var dataSamples = [69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,69,72,72,72,72,72,72,72,73,73,74,74,74,74,74,74,74,73,73,73,73,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,74,74,74,73,73,73,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,71,72,72,72,73,73,73,72,72,72,73,73,73,74,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,73,73,73,72,72,72,71,101,71,70,70,70,69,68,68,67,67,66,66,67,67,69,70,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,74,76,77,76,70,57,40,22,11,11,22,40,57,69,73,73,71,71,71,72,72,73,73,74,74,74,73,72,72,72,72,72,72,72,72,72,72,72,72,71,71,70,70,71,71,71,71,70,70,69,69,69,69,69,69,69,68,68,68,67,67,66,66,65,65,64,63,63,62,62,62,62,62,62,62,62,63,63,64,65,66,67,68,68,69,70,71,72,72,72,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,73,73,73,73,72,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,73,73,74,74,74,74,74,74,73,73,72,73,73,73,74,73,73,72,72,72,73,73,73,72,72,73,73,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,70,70,70,70,70,69,69,68,67,67,67,67,68,69,71,72,72,73,73,73,73,74,74,74,74,74,73,73,73,73,75,77,78,76,67,53,35,18,8,10,23,41,58,69,73,72,71,70,71,72,73,73,73,73,73,73,73,73,72,72,73,73,73,73,72,71,71,70,70,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,68,67,67,67,67,67,66,65,65,65,64,63,62,61,61,61,60,60,60,59,60,60,60,61,62,63,65,66,66,67,68,69,70,71,72,72,72,72,73,73,73,72,72,72,72,72,72,72,73,73,73,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,71,71,72,72,73,73,73,72,72,72,72,72,72,73,73,73,73,73,73,73,73,73,72,73,73,73,73,73,73,72,73,73,73,73,73,73,73,72,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,71,71,70,70,69,69,69,68,67,67,66,65,66,66,68,69,70,71,72,72,73,73,73,73,73,73,74,74,74,74,74,74,76,78,78,74,64,48,29,13,5,10,26,45,62,71,73,72,71,71,72,73,73,73,73,73,74,74,74,73,72,72,72,73,73,73,73,73,73,73,72,72,72,72,71,71,71,71,71,71,71,71,71,70,70,69,69,69,69,68,67,66,66,66,66,65,65,64,63,62,62,61,61,60,60,60,60,61,62,62,63,64,65,66,67,68,70,71,72,72,72,72,72,72,73,73,73,73,73,73,73,74,74,75,75,74,74,74,73,73,73,74,73,73,73,73,73,74,74,74,74,74,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,74,74,74,73,73,73,73,73,73,73,73,73,73,72,72,72,72,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,73,73,72,72,71,70,70,70,69,69,68,68,67,67,66,67,67,68,69,70,71,72,73,73,74,74,73,73,73,74,75,75,74,73,73,74,76,78,75,67,52,32,15,5,8,22,41,59,69,73,72,71,70,71,72,72,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,72,72,72,71,71,71,70,70,70,70,70,70,70,69,69,69,69,68,68,68,68,67,67,66,65,65,64,64,64,63,62,61,60,60,60,60,60,61,61,62,62,63,64,65,65,66,67,68,69,70,71,71,71,71,71,71,72,72,73,73,73,72,72,73,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,72,72,72,71,71,71,71,71,71,71,72,72,72,72,72,72,72,72,72,71,71,71,72,72,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,72,72,73,73,73,73,73,72,72,72,73,73,74,73,73,73,73,73,73,73,73,73,73,73,73,72,72,72,72,71,71,71,70,70,70,70,69,69,68,67,67,68,69,71,72,73,73,73,73,73,73,73,73,74,75,75,75,74,74,74,75,77,77,75,67,52,34,18,10,12,26,45,62,71,74,73,72,72,72,73,74,74,74,75,75,74,74,74,74,74,74,74,74,74,73,73,73,73,74,74,73,73,73,73,73,73,73,72,72,71,71,71,71,71,70,70,70,69,69,69,68,68,68,68,67,66,65,64,63,63,62,62,62,63,63,63,63,64,65,66,67,69,69,70,71,72,72,73,73,74,74,74,74,75,75,76,76,74,72,70,70,69,69 ]; function drawBkg(canvasElem, squareSize, numSquaresPerBlock, minorLineWidthStr, majorLineWidthStr, lineColStr) { var nLinesDone = 0; var i, curX, curY; var ctx = canvasElem.getContext('2d'); ctx.clearRect(0,0,canvasElem.width,canvasElem.height); // draw the vertical lines curX=0; ctx.strokeStyle = lineColStr; while (curX < canvasElem.width) { if (nLinesDone % numSquaresPerBlock == 0) ctx.lineWidth = majorLineWidthStr; else ctx.lineWidth = minorLineWidthStr; ctx.beginPath(); ctx.moveTo(curX, 0); ctx.lineTo(curX, canvasElem.height); ctx.stroke(); curX += squareSize; nLinesDone++; } // draw the horizontal lines curY=0; nLinesDone = 0; while (curY < canvasElem.height) { if (nLinesDone % numSquaresPerBlock == 0) ctx.lineWidth = majorLineWidthStr; else ctx.lineWidth = minorLineWidthStr; ctx.beginPath(); ctx.moveTo(0, curY); ctx.lineTo(canvasElem.width, curY); ctx.stroke(); curY += squareSize; nLinesDone++; } } // position that will be treated as 0,0 when drawing our points. var originX=49; var originY=72; function drawSamples(nSamplesToDraw, firstSample, lineWidthStr, lineColourStr) { var can = byId('canvas'); var ctx = can.getContext('2d'); ctx.strokeStyle = lineColourStr; ctx.lineWidth = lineWidthStr; console.log(firstSample); ctx.beginPath(); ctx.moveTo( originX+firstSample-1, dataSamples[firstSample-1]+originY ); for (var i=0; i<nSamplesToDraw; i++) { var curSample = dataSamples[i + firstSample]; ctx.lineTo( originX+firstSample+i, curSample+originY ); } ctx.stroke(); } var curFrame=0; var nPointsPerFrame = 4; function drawCurFrame() { if ((dataSamples.length - (nPointsPerFrame * curFrame)) < nPointsPerFrame) // will we over-run the end of the array of datapoints? { curFrame = 0; // if so, reset drawBkg(byId('canvas'), 9.43, 5, "0.5", "1.5", "black"); } drawSamples(nPointsPerFrame, nPointsPerFrame*curFrame, "1", "blue"); curFrame++; requestAnimationFrame( drawCurFrame ); } </script> <style> #canvas { border: solid 1px black; background-color: #FFFFFF; } </style> </head> <body> <div id='txt'></div> <canvas id="canvas" width="1350" height="1300"></canvas> </body> </html>
Update Now that I see you have provided some more info I get what you want. The problem is you need to draw a fixed number of line segments within time t. As you do not know how long each frame could take you can not rely on a fixed frame rate. The alternative it to just use the current time and save the end time. Get the start time and then each frame draw all the should be drawn until the current time. As the line segments being drawn will not be displayed until the next screen refresh the time you get will be approx 16ms behind so will need to adjust for that. What I have done is keep track of the average frame time and used half that time to estimate when the new canvas update will be displayed. Its a bit pedantic but might as well show how to get a required time as close as possible. If you dont care its a few ms out then just remove the average frame time stuff. You will be at most 30ms off on a slow machine. var canvas; // canvas var ctx; function getCanvas () { // to do // get canvas and context } function drawGrid () { // to do // draw the grid } function drawTimedLine(){ if(canvas === undefined){ // if the canvas not available get it getCanvas(); } // clear the canvas is repeating animation ctx.clearRect(0, 0, canvas.width, canvas.height); drawGrid(); var array_1 = ; // your data // All stuff needed for timed animation. // The frame you render will not be displayed untill the next // vertical refresh which is unknown, Assume it is one frame. var startDelay = 1000; // if Needed time in milliseconds to delay start var endTime; // hold the time that the animation has to end var lastDataPoint; // holds the last point drawn to var timeToDraw = 5 * 1000; // how long the animation should last var repeatAfter = 1 *1000; // if you want to repeat the animatoin var frameCount = 0; // count the frames to get average frame time var startTime; //the start time; var numberPoints = array_1.length; // number of points; var startX = 49; // line starts at var yOffset = 72; // line Y offset var endX = 512; // line ends at. var width = endX - startX; // width var xStep = width / numberPoints; // X step per point var pointsPerMS = numberPoints / timeToDraw; // get how many points per ms should be drawn // function to draw function drawWave() { // variable needed var averageframeTime, timeLeft, i, currentTime; currentTime = new Date().valueOf(); // gets the time in millisecond; if (startTime === undefined) { // Is this the first frame startTime = currentTime; // save the start time; endTime = currentTime + timeToDraw; // workout when the end time is; lastDataPoint = 0; // set the data position to the start; averageframeTime = 0; // no frames counted so frame time is zero } else { frameCount += 1; // count the frames // get the average frame time averageframeTime = (currentTime - startTime) / frameCount; } // get the time this frame // will most likely be displayed // then calculate how long // till the end timeLeft = endTime - Math.min(endTime, currentTime + averageframeTime / 2); // now get where we should // be when the frame is presented pointPos = Math.floor(pointsPerMS * (timeToDraw - timeLeft)); // now draw the points from where we last left of // till the new pos; ctx.lineWidth = 4; ctx.strokeStyle = 'blue'; ctx.beginPath(); ctx.moveTo( // move to first point lastDataPoint * xStep + startX, array_1[lastDataPoint] + yOffset ); // draw each line from the last drawn to the new position for (i = lastDataPoint + 1; i <= pointPos && i < numberPoints; i++) { // Add the line segment ctx.lineTo( i * xStep + startX, array_1[i] + yOffset ); } ctx.stroke(); // execute the render commands lastDataPoint = pointPos; // update the last point if (pointPos < numberPoints) { // are we there yet??? requestAnimationFrame(drawWave); // no so request another frame }else{ // if you want to repeat the animation setTimeout(drawTimedLine , repeatAfter ); } } // start the line animation with delay if needed setTimeout(drawWave,startDelay); } // use this if you want it to start as soon as page is ready. document.addEventListener("DOMContentLoaded",drawTimedLine); // or use if you want it to start when page has images loaded and is ready // document.addEventListener("load",drawTimedLine); I have also added the ability to repeat the animation. If not needed just remove that code My original answer Dont know what the problem is with speed as it runs quite well on my machine. To set up a better start use function startFunction(){ // your code } document.addEventListener("DOMContentLoaded",startFunction); This will wait until the page has loaded and parsed the page. Images and other media may not have loaded but the page is ready to be manipulated. Not sure what you mean with 5 seconds. Assuming you may want the thing to sart in 5 seconds. The following will do that. document.addEventListener("DOMContentLoaded",function() {setTimeout(startFunction,5000);}); I would ask why plot the graph one entry at a time with requestAnimationFrame 1250 is not that many lines to draw. If you add ctx.beginPath() ctx.moveTo(/*first point*/) then loop all points with ctx.moveTo(/*points*/) then ctx.stroke() will run realtime on but the slowest of devices. BTW ctx.lineWidth is a Number not a string. Also you have two context? Use the one context for the canvas. Remove ctxt and just use ctx and finally you don't need to add type='text/javascript' to the script tag as Javascript is the default.
1) It cannot take that long to draw 1000 lines, even 100000 lines won't take more than 10 ms on any decent Browser. Look else where the time is lost. 2) The core issue of your code is that it lacks modularity. Split your code into a few clear functions, group the parameters into a few objects only, name and indent things properly. Below an (incomplete but working) example of how this might look. var cv, ctx; var data = null; var debug = true; // --------------------------------------- // define here all graphic related parameters var gfxParams = { canvasWidth: 600, canvasHeight: 600, gridColor: '#A66', gridSpacing: 10, gridLineWidth: 0.5, gridStrongLinesEvery: 5, lineColor: '#AEB', lastLineColor: '#8A9' // , ... }; // define here all animation related parameters var animationParams = { duration: 5, startTime: -1 } // --------------------------------------- // main // --------------------------------------- window.onload = function() { data = getData(); setupCanvas(data); launchAnimation(); } // --------------------------------------- // function setupCanvas(data) { cv = document.getElementById('cv'); cv.width = gfxParams.canvasWidth; cv.height = gfxParams.canvasHeight; ctx = cv.getContext('2d'); // here you should translate and scale the context // so that it shows your data. } function drawGrid(ctx) { var i = 0, pos = 0, lw = gfxParams.gridLineWidth; ctx.fillStyle = gfxParams.gridColor; var vLineCount = gfxParams.canvasWidth / gfxParams.gridSpacing; for (i = 0; i < vLineCount; i++) { pos = i * gfxParams.gridSpacing; ctx.fillRect(pos, 0, lw, gfxParams.canvasHeight); } var hLineCount = gfxParams.canvasHeight / gfxParams.gridSpacing; for (i = 0; i < hLineCount; i++) { pos = i * gfxParams.gridSpacing; ctx.fillRect(0, pos, gfxParams.canvasWidth, lw); } } function animate() { requestAnimationFrame(animate); var now = Date.now(); // erase screen ctx.clearRect(0, 0, gfxParams.canvasWidth, gfxParams.canvasHeight); // draw grid drawGrid(ctx); // draw lines var lastIndex = getLastDrawnIndex(data, now - animationParams.startTime); drawLines(ctx, data, lastIndex); if (debug) { ctx.save(); ctx.fillStyle = '#000'; ctx.fillText(lastIndex + ' lines drawn. Time elapsed : ' + (now - animationParams.startTime), 10, 10); ctx.restore(); } } // comment function launchAnimation() { requestAnimationFrame(animate); animationParams.startTime = Date.now(); } // comment function getData() { var newData = []; for (var i = 0; i < 500; i++) { newData.push([Math.random() * 600, Math.random() * 600]); } return newData; } // comment function getLastDrawnIndex(data, timeElapsed_ms) { var timeElapsed = timeElapsed_ms / 1000; if (timeElapsed >= animationParams.duration) return data.length - 1; return Math.floor(data.length * timeElapsed / animationParams.duration); } function drawLines(ctx, data, lastIndex) { ctx.strokeStyle = gfxParams.lineColor; // other ctx setup here. for (var i = 0; i < lastIndex - 1; i++) { drawLine(ctx, data[i], data[i + 1]); } ctx.strokeStyle = gfxParams.lastLineColor; drawLine(ctx, data[lastIndex - 1], data[lastIndex]); } function drawLine(ctx, p1, p2) { ctx.beginPath(); ctx.moveTo(p1[0], p1[1]); ctx.lineTo(p2[0], p2[1]); ctx.stroke(); } <canvas id='cv'></canvas>
drawImage with Canvas is sending image to the back of the canvas [closed]
Closed. This question needs debugging details. It is not currently accepting answers. Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question. Closed 8 years ago. Improve this question I have been doing a little experimenting with canvas, creating drawings using lines shapes text etc, and inserting .png files. The inserting .png files is the bit that I cannot get to work. Edit: Undesired behaviour of this code: I load shapes to the graphics context, then load an image file to the graphics context, however when the graphics context is drawn, the image is at behind the shapes, despite being drawn last. I wanted the image file to be at the top, in front of the shapes. Desired behaviour: To bring image file to the front of the canvas, so it is not hidden by shapes drawn in the graphics context. function loadImage(name) { images[name] = new Image(); images[name].src = "DogWalking/" + name + ".png"; images[name].onload = function() { graphics.drawImage(this, 0, 300); canvas.bringToFront(this); }; } the function for drawing is called here: function draw() { graphics.save(); // to make sure changes don't carry over from one call to the next graphics.fillStyle = "transparent"; // background color graphics.fillRect(0,0,wWidth, wHeight); graphics.fillStyle = "black"; applyLimits(graphics,xleft,xright,ytop,ybottom,true); graphics.lineWidth = pixelSize; world.draw(graphics); graphics.drawImage(images["dog-walking11"],200,200); graphics.restore(); } code for the whole page is <!DOCTYPE html> <html> <meta charset="UTF-8"> <head> <title>Hierarchical Modeling 2D</title> <style> #messagediv { position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 0; background-color: indigo; } #canvasdiv { position: absolute; left: 0; top: 0; z-index: 10; background-color: transparent; } </style> <script type="text/javascript" src="rgbcolor.js"></script> <script type="text/javascript"> "use strict"; var totalResources = 17; var numResourcesLoaded = 0; var images = {}; function loadImage(name) { images[name] = new Image(); images[name].src = "DogWalking/" + name + ".png"; images[name].onload = function() { // graphics.drawImage(this, 0, 300); canvas.bringToFront(this); } } var canvas; // DOM object corresponding to the canvas var graphics; // 2D graphics context for drawing on the canvas var ctx; // 2D graphics context for drawing on the canvas var myNumber = 0, myNumber2 = 0, myInterval, myInterval2, myelement, thisdiv, printx; var mycoords = new Array(); var pcoords = new Array(); //coordinates of the portal. //var pcoords = [[0,0], [50,300], [250,150]]; //coordinates of the portal. var nocoords = 2; var frameNumber = 0; // Current frame number. var frameNumber2 = 0; var sun; var sun2; var ground; var world; var pixelSize; var wWidth; var wHeight; var portals = new Array("calendar1","alternativsearch","art1", "directory1"); var portalsval = new Array(); var portalsobj; var leftj = new Array(3,1,4,2); var forwards = "http://www.alternativworld.com"; // ---------------- Set Page Layout ---------------- // function to set size of canvas and location of portals function pageLayout() { var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0]; wWidth = w.innerWidth || e.clientWidth || g.clientWidth; wHeight = w.innerHeight|| e.clientHeight|| g.clientHeight; // Adjust wWidth and wHeight if ratio does not match scenary 7 by 5. if (wWidth/wHeight != 7/5) if (wWidth/wHeight > 7/5) { var widthPortion = 5 * wWidth/wHeight; wWidth = wWidth * 7 / widthPortion; } else { var heightPortion = 7 * wHeight/wWidth; wHeight = wHeight * 5 / heightPortion; } var widthheight, localerror = false; widthheight = Math.min(wWidth, wHeight); if(widthheight < 400){ var localerror = true; } if (localerror == true) alert("Warning, the page size of your browser or your screen resolution may be too small to correctly view this web page."); var theCanvas = d.getElementById("theCanvas"); theCanvas.height = wHeight; theCanvas.width = wWidth; } //Function to listen to the mouse events and see if a link is selected. function doMouseDown(evt) { var r = canvas.getBoundingClientRect(); var x = Math.round(evt.clientX - r.left); var y = Math.round(evt.clientY - r.top); alert(evt.clientX+ " " + evt.clientY); for (var i = portals.length+1; i >= 0; i--) { var p = pcoords[i]; if (Math.abs(p[0] - x) <= 50 && Math.abs(p[1] - y) <= 50) { document.location.href = forwards; return; } else if (Math.abs(0 - x) <= 50 && Math.abs(0 - y) <= 50){ document.location.href = "http://www.alternativeuk.co.uk"; return; } } } // ---------------- The object-oriented scene graph API ------------------ /** * The base class for all nodes in the scene graph data structure. */ function SceneGraphNode() { this.fillColor = null; // If non-null, the default fillStyle for this node. this.strokeColor = null; // If non-null, the default strokeStyle for this node. } SceneGraphNode.prototype.doDraw = function(g) { // This method is meant to be abstract and must be // OVERRIDDEN in any actual object in the scene graph. // It is not meant to be called; it is called by draw(). throw "doDraw not implemented in SceneGraphNode" } SceneGraphNode.prototype.draw = function(g) { // This method should be CALLED to draw the object // represented by this SceneGraphNode. It should NOT // ordinarily be overridden in subclasses. graphics.save(); if (this.fillColor) { g.fillStyle = this.fillColor; } if (this.strokeColor) { g.strokeStyle = this.strokeColor; } this.doDraw(g); graphics.restore(); } SceneGraphNode.prototype.setFillColor = function(color) { // Sets fillColor for this node to color. // Color should be a legal CSS color string, or null. this.fillColor = color; return this; } SceneGraphNode.prototype.setStrokeColor = function(color) { // Sets strokeColor for this node to color. // Color should be a legal CSS color string, or null. this.strokeColor = color; return this; } SceneGraphNode.prototype.setColor = function(color) { // Sets both the fillColor and strokeColor to color. // Color should be a legal CSS color string, or null. this.fillColor = color; this.strokeColor = color; return this; } /** * Defines a subclass, CompoundObject, of SceneGraphNode to represent * an object that is made up of sub-objects. Initially, there are no * sub-objects. */ function CompoundObject() { SceneGraphNode.call(this); // do superclass initialization this.subobjects = []; // the list of sub-objects of this object } CompoundObject.prototype = new SceneGraphNode(); // (makes it a subclass!) CompoundObject.prototype.add = function(node) { // Add node a subobject of this object. Note that the // return value is a reference to this node, to allow chaining // of method calls. this.subobjects.push(node); return this; } CompoundObject.prototype.doDraw = function(g) { // Just call the sub-objects' draw() methods. for (var i = 0; i < this.subobjects.length; i++) this.subobjects[i].draw(g); } /** * Define a subclass, TransformedObject, of SceneGraphNode that * represents an object along with a modeling transformation to * be applied to that object. The object must be specified in * the constructor. The transformation is specified by calling * the setScale(), setRotate() and setTranslate() methods. Note that * each of these methods returns a reference to the TransformedObject * as its return value, to allow for chaining of method calls. * The modeling transformations are always applied to the object * in the order scale, then rotate, then translate. */ function TransformedObject(object) { SceneGraphNode.call(this); // do superclass initialization this.object = object; this.rotationInDegrees = 0; this.scaleX = 1; this.scaleY = 1; this.translateX = 0; this.translateY = 0; } TransformedObject.prototype = new SceneGraphNode(); // (makes it a subclass!) TransformedObject.prototype.setRotation = function(angle) { // Set the angle of rotation, measured in DEGREES. The rotation // is always about the origin. this.rotationInDegrees = angle; return this; } TransformedObject.prototype.setScale = function(sx, sy) { // Sets scaling factors. this.scaleX = sx; this.scaleY = sy; return this; } TransformedObject.prototype.setTranslation = function(dx,dy) { // Set translation mounts. this.translateX = dx; this.translateY = dy; return this; } TransformedObject.prototype.doDraw = function(g) { // Draws the object, with its modeling transformation. g.save(); if (this.translateX != 0 || this.translateY != 0) { g.translate(this.translateX, this.translateY); } if (this.rotationInDegrees != 0) { g.rotate(this.rotationInDegrees/180*Math.PI); } if (this.scaleX != 1 || this.scaleY != 1) { g.scale(this.scaleX, this.scaleY); } this.object.draw(g); g.restore(); } /** * A subclass of SceneGraphNode representing filled triangles. * The constructor specifies the vertices of the triangle: * (x1,y1), (x2,y2), and (x3,y3). */ function Triangle(x1,y1,x2,y2,x3,y3) { SceneGraphNode.call(this); this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.x3 = x3; this.y3 = y3; } Triangle.prototype = new SceneGraphNode(); Triangle.prototype.doDraw = function(g) { g.beginPath(); g.moveTo(this.x1,this.y1); g.lineTo(this.x2,this.y2); g.lineTo(this.x3,this.y3); g.closePath(); g.fill(); } /** * Directly create a line object as a SceneGraphNode with a * custom doDraw() method. line is of length 1 and * extends along the x-axis from (0,0) to (1,0). */ var line = new SceneGraphNode(); line.doDraw = function(g) { g.beginPath(); g.moveTo(0,0); g.lineTo(1,0); g.stroke(); } /** * Directly create a filled rectangle object as a SceneGraphNode with a * custom doDraw() method. filledRect is a square with side 1, centered * at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5). */ var filledRect = new SceneGraphNode(); filledRect.doDraw = function(g) { g.fillRect(-0.5,-0.5,1,1); } /** * Directly create a rectangle object as a SceneGraphNode with a * custom doDraw() method. rect is a square with side 1, centered * at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5). Only the * outline of the square is drawn. */ var rect = new SceneGraphNode(); rect.doDraw = function(g) { g.strokeRect(-0.5,-0.5,1,1); } /** * Directly create a filled circle object as a SceneGraphNode with a * custom doDraw() method. filledCircle is a circle with radius 0.5 * (diameter 1), centered at (0,0). */ var filledCircle = new SceneGraphNode(); filledCircle.doDraw = function(g) { g.beginPath(); g.arc(0,0,0.5,0,2*Math.PI); g.fill(); } var clickHere = new SceneGraphNode(); clickHere.doDraw = function(g) { g.fillText("click here :)",0,0) } /** * Directly create a circle object as a SceneGraphNode with a * custom doDraw() method. filledCircle is a circle with radius 0.5 * (diameter 1), centered at (0,0). Only the outline of the circle * is drawn. */ var circle = new SceneGraphNode(); circle.doDraw = function(g) { g.beginPath(); g.arc(0,0,0.5,0,2*Math.PI); g.stroke(); } var dog = new SceneGraphNode(); dog.doDraw = function(g) { g.drawImage(images["dog-walking11"],-2, 2); alert(images["dog-walking11"].name); } // -------------------- Specific to this application ---------------------------- /* * Define two extra basic objects as SceneGraphNodes with custom doDraw() methods. * One represents the ground, the other a vane for a windmill. */ var ground = new SceneGraphNode(); ground.doDraw = function(g) { g.beginPath(); g.moveTo(0,-1); g.lineTo(0,0.8); g.lineTo(1.5,1.65); g.lineTo(1.8,1.3); g.lineTo(3,2.1); g.lineTo(4.7,0.7); g.lineTo(6.1,1.2); g.lineTo(7,0.8); g.lineTo(7,-1); g.closePath(); g.fill(); } var windmillVane = new SceneGraphNode(); windmillVane.doDraw = function(g) { g.beginPath(); g.moveTo(0,0); g.lineTo(0.5,0.1); g.lineTo(1.5,0); g.lineTo(0.5,-0.1); g.closePath(); g.fill(); } var world; // A SceneGraphNode representing the entire picture. This should // be created in the createWorld() method. var pixelSize; // The size of one pixel, in the transformed coordinates. // This is used as the default width of a stroke. var background = "#C8C8FF"; // A CSS color string giving the background color. // the draw() function fills the canvas with this color. var xleft = 0; // The requested xy-limits on the canvas, after the var xright = 7; // coordinate transformation has been applied. var ybottom = -1; // The transformation is applied in the draw() function. var ytop = 4; var frameNumber = 0; // Current frame number. var cart; // TransformedObjects that are animated. var wheel; var sun; var clickText1; var clickText2; var rotor; /** * Create the scene graph data structure. The global variable world must * refer to the root node of the scene graph. This function is called in * the init() function. */ function createWorld() { pageLayout(); var i; var sunTemp = new CompoundObject(); sunTemp.setColor("yellow"); // color for filled circle and light rays for (i = 0; i < 12; i++) { // add the 12 light rays, with different rotations sunTemp.add( new TransformedObject(line).setScale(0.75,0.75).setRotation(i*30) ); } sunTemp.add( filledCircle ); // the face of the sun sunTemp.add( new TransformedObject(circle).setColor("#B40000") ); // outlines the face sun = new TransformedObject(sunTemp); clickText1 = new TransformedObject(clickHere).setColor("#B40000").setScale(0.01,-0.01); var wheelTemp = new CompoundObject(); wheelTemp.setColor("black"); // color for all but one of the subobjects wheelTemp.add( new TransformedObject(filledCircle).setScale(2,2) ); wheelTemp.add( new TransformedObject(filledCircle).setScale(1.6,1.6).setColor("#CCCCCC") ); wheelTemp.add( new TransformedObject(filledCircle).setScale(0.4,0.4) ); for (i = 0; i < 12; i++) { // add the 12 spokes wheelTemp.add( new TransformedObject(line).setRotation(i*30) ); } wheel = new TransformedObject(wheelTemp); var cartTemp = new CompoundObject(); cartTemp.setColor("red"); // color for the rectangular body of the cart cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(1.5,-0.1) ); cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(-1.5,-0.1) ); cartTemp.add( new TransformedObject(filledRect).setScale(5,2).setTranslation(0,1) ); // the body of the cart cart = new TransformedObject(cartTemp).setScale(0.3,0.3); clickText2 = new TransformedObject(clickHere).setColor("yellow").setScale(0.01,-0.01); var rotorTemp = new CompoundObject(); // a "rotor" consisting of three vanes rotorTemp.setColor( "#C86464" ); // color for all of the vanes rotorTemp.add( windmillVane ); rotorTemp.add( new TransformedObject(windmillVane).setRotation(120) ); rotorTemp.add( new TransformedObject(windmillVane).setRotation(240) ); rotor = new TransformedObject(rotorTemp); var windmill = new CompoundObject(); windmill.setColor("#E0C8C8"); // color for the pole windmill.add( new TransformedObject(filledRect).setScale(0.1,3).setTranslation(0,1.5) ); // the pole windmill.add( new TransformedObject(rotor).setTranslation(0,3) ); // the rotating vanes world = new CompoundObject(); world.setColor("#00961E"); // color used for the ground only world.add(ground); //world.add( new TransformedObject(filledRect).setScale(7,0.8).setTranslation(3.5,0).setColor("#646496") ); // road //world.add( new TransformedObject(filledRect).setScale(7,0.06).setTranslation(3.5,0).setColor("white") ); // line in road world.add( new TransformedObject(windmill).setScale(0.6,0.6).setTranslation(0.75,1) ); world.add( new TransformedObject(windmill).setScale(0.4,0.4).setTranslation(2.2,1.3) ); world.add( new TransformedObject(windmill).setScale(0.7,0.7).setTranslation(3.7,0.8) ); world.add( new TransformedObject(sun).setTranslation(5.5,3.3) ); world.add( new TransformedObject(clickText1).setTranslation(5.25,3.3) ); world.add( cart ); world.add( clickText2 ); //alert(2); } /** * This will be called before each frame is drawn. */ function updateFrame() { frameNumber++; if (frameNumber>= 312){ frameNumber = 0; frameNumber2 = 1; } cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0); clickText2.setTranslation(-3.3 + 13*(frameNumber % 300) / 300.0, 0.25); if (typeof(pcoords[5]) != 'undefined') { pcoords[5][0] = (-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*canvas.width / (xright-xleft); pcoords[5][1] = (0.25-ytop)*canvas.height / (ybottom-ytop); } wheel.setRotation(-frameNumber*3.1); sun.setRotation(-frameNumber); rotor.setRotation(frameNumber * 2.7); } // ------------------------------- graphics support functions -------------------------- /** * Draw one frame of the animation. Probably doesn't need to be changed, * except maybe to change the setting of preserveAspect in applyLimits(). */ function draw() { graphics.save(); // to make sure changes don't carry over from one call to the next graphics.fillStyle = "transparent"; // background color graphics.fillRect(0,0,wWidth, wHeight); graphics.fillStyle = "black"; applyLimits(graphics,xleft,xright,ytop,ybottom,true); graphics.lineWidth = pixelSize; world.draw(graphics); graphics.drawImage(images["dog-walking11"],200,200); graphics.restore(); } /** * Applies a coordinate transformation to the graphics context, to map * xleft,xright,ytop,ybottom to the edges of the canvas. This is called * by draw(). This does not need to be changed. */ //pcoords[0][0] = //pcoords[0][1]= function applyLimits(g, xleft, xright, ytop, ybottom, preserveAspect) { var width = canvas.width; // The width of this drawing area, in pixels. var height = canvas.height; // The height of this drawing area, in pixels. var k = portals.length; var j; var i = 0, widthheight, myradius; var localerror = false; if (pcoords.length < k) { while (portals[i]){ j = i + 1; if (width > 100){ var rWidth = width/(k + 1); rWidth= Math.floor(rWidth); } else { var lWidth = 0; var rWidth = 0; } if (height > 100){ var bHeight = height/(k + 1); bHeight= Math.floor(bHeight); } else { var tHeight = 0; var bHeight = 0; } var myleft = leftj[i] * rWidth - 50; var mytop = j * bHeight - 50; pcoords[i]= new Array; pcoords[i][0] = myleft; pcoords[i][1] = mytop; i = i + 1; } } if (preserveAspect) { // Adjust the limits to match the aspect ratio of the drawing area. var displayAspect = Math.abs(height / width); var requestedAspect = Math.abs(( ybottom-ytop ) / ( xright-xleft )); var excess; if (displayAspect > requestedAspect) { excess = (ybottom-ytop) * (displayAspect/requestedAspect - 1); ybottom += excess/2; ytop -= excess/2; } else if (displayAspect < requestedAspect) { excess = (xright-xleft) * (requestedAspect/displayAspect - 1); xright += excess/2; xleft -= excess/2; } } var pixelWidth = Math.abs(( xright - xleft ) / width); var pixelHeight = Math.abs(( ybottom - ytop ) / height); pixelSize = Math.min(pixelWidth,pixelHeight); if (frameNumber == 4 || frameNumber == 5){ pcoords.push([(5.25-xleft)*width / (xright-xleft),(3.3-ytop)*height / (ybottom-ytop)]); pcoords.push([(-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*width / (xright-xleft), (0.25-ytop)*height / (ybottom-ytop)]); } g.scale( width / (xright-xleft), height / (ybottom-ytop) ); g.translate( -xleft, -ytop ); // if (frameNumber < 3) } //------------------ Animation framework ------------------------------ var animationTimeout = null; // A null value means the animation is off. // Otherwise, this is the timeout ID. function frame() { // Draw one frame of the animation, and schedule the next frame. updateFrame(); draw(); canvas.addEventListener("mousedown", doMouseDown, false); animationTimeout = setTimeout(frame, 33); } function setAnimationRunning(run) { if ( run ) { if (animationTimeout == null) { // If the animation is not already running, start // it by scheduling a call to frame(). animationTimeout = setTimeout(frame, 33); } } else { if (animationTimeout != null) { // If the animation is running, stop it by // canceling the next scheduled call to frame(). clearTimeout(animationTimeout); } animationTimeout = null; // Indicates that animation is off. } } //----------------------- initialization ------------------------------- function init() { try { canvas = document.getElementById("theCanvas"); if(typeof G_vmlCanvasManager != 'undefined') { canvas = G_vmlCanvasManager.initElement(canvas); } graphics = canvas.getContext("2d"); } catch (e) { document.getElementById("message").innerHTML = "Sorry, this page requires canvas graphics, but<br>" + "it looks like your browser does not support it<br>" + "Reported error: " + e; return; } // add any other necessary initialization document.getElementById("animateCheck").checked = true; // Make sure box is checked! loadImage("dog-walking11"); createWorld(); setAnimationRunning(true); // start the animation } </script> </head> <body onload="init()" style="background-color: rgb(220,220,220)"> <div id="messagediv"> <h2>Hierarchical Modeling Example</h2> <!-- For error reporting: the contents of the noscript tag are shown only if JavaScript is not available. The paragraph with id="message" is for reporting errors using JavaScript.--> <noscript><b>This page requires JavaScript, which is<br> not enabled in your browser.</b></noscript> <p id="message" style="color:red"></p> <p><input type="checkbox" id="animateCheck" onchange="setAnimationRunning(this.checked)"> <label for="animateCheck">Run Animation</label> </p> </div> <div id="canvasdiv"> <canvas id="theCanvas" width= "400" height= "300" style="background-color: transparent"></canvas> </div> </body> </html>
It seems that I had to use graphics.drawImage() after using graphics.restore(). Though I was trying to draw the image in the correct order (after), compared to drawing the rectangles, circles etc, the shapes did not come out of the buffer onto the page until after the restore function(). When I was calling drawImage, I assumed that it was loading it onto the buffered canvas with the rest of the things, ready to be drawn in the correct order, when in fact it was putting it straight onto the page. It seems strange since it was being called with the graphics context, so I thought it would be added to the rest of the canvas graphics, but i appear to be wrong.
How to assign onclick on image drawn in canvas?
I'm trying to get an event to work on an image when the user clicks on it. var canvas = document.createElement("canvas"); canvas.width = 800; canvas.height = 600; canvas.style = "border:2px solid black"; canvas.addEventListener('click', clickReporter, false); var ctx = canvas.getContext("2d"); document.body.appendChild(canvas); var clickhere = new Image(); clickhere.onload = function () { draw(); }; clickhere.src = "clickhere.png"; function draw() { ctx.drawImage(clickhere, 200, 200); } function clickReporter(e) { alert("Thanks for clicking!"); } Obviously all this code will just let the alert box go off as long as the user clicks in the canvas. The image is 100 by 100 pixels.
First off: You apparently have an error in you code in regards to the image (at least in the example you provide): var button = new Image(); clickhere.onload = function () { draw(); }; clickhere.src = "clickhere.png"; function draw() { ctx.drawImage(clickhere, 200, 200); } Should be like this for the example to work: var button = new Image(); /// use button for these as well - button.onload = function () { /// here draw(); }; button.src = "clickhere.png"; /// here function draw() { ctx.drawImage(button, 200, 200); /// and here (or use 'this' instead) } The next problem Canvas doesn't know what we draw into it so we need to make sure we provide all the underlying logic ourselves to handle these sort of things. For example: Here is one way to check if the region the image was drawn into is clicked: function clickReporter(e) { /// assign event to some variable /// adjust mouse click position to be relative to canvas: var rect = this.getBoundingClientRect(), x = e.clientX - rect.left, y = e.clientY - rect.top; /// check x/y coordinate against the image position and dimension if (x >= 200 && x <= (200 + button.width) && y >= 200 && y <= (200 + button.height)) { alert("Thanks for clicking!"); } } You might want to convert those semi-absolute bounds to something more dynamic by for example using an image with a custom object where you store its x and y position and so forth. But you should get the idea. Update: A modified fiddle here
Resizing Canvas Elements with Mouse Events with EaselJS
I'm trying to draw elements to a canvas and then allow the user to resize them by clicking and dragging them. I've implemented this with Kinetic JS library, but my development team is moving over to Easel JS. Here is the code with the Kinetic library. This works well enough. I get about this far in Easel JS, but this code doesn't do anything. The canvas is totally blank. I realize that I'm not sure how to: call mouse events -- here I'm trying to attach them to the DOM element, as in this tutorial where to set the resize function -- separate function? called within the tick() function? grab the mouse coordinates var canvas; var stage; var rect; var mousePos = new Point(); var update = true; var rectX = 10; var rectY = 10; var rectW = 100; var rectH = 50; var rectXOffset = 50; var rectYOffset = 50; var newWidth; var newHeight; function init() { // create stage and point it to the canvas: canvas = document.getElementById("testCanvas"); stage = new Stage(canvas); // overlay canvas used to draw target and line canvasWrapper = $("#testCanvas"); // listen for a mouse down event canvasWrapper.mousedown(onMouseDown); // listen for mouse up event canvasWrapper.mouseup(onMouseUp); // enable touch interactions if supported on the current device: if (Touch.isSupported()) { Touch.enable(stage); } // enabled mouse over / out events stage.enableMouseOver(10); // start drawing instructions var rect = new Shape(); rect.graphics .setStrokeStyle(1) .beginStroke(Graphics.getRGB(25,25,112,.7)) .drawRect(rectX,rectY,rectW,rectH); // add rectangle to stage stage.addChild(rect); // render stage stage.update(); // set the tick interval to 24 fps Tick.setInterval(1000/24); // listen for tick event Tick.addListener(window, true); // pause Tick.setPaused(false); } //called when user clicks on canvas function onMouseDown(e) { mousePos.x = e.stageX; mousePos.y = e.stageY // check to see if mouse is within offset of rectangle if(mousePos.x <= (rectW + rectXOffset) || mousePos.y <= (rectH + rectYOffset)) { // set update to true update = true; // unpause tick Tick.setPaused(false); } } function onMouseUp(e) { update = false; Tick.setPaused(true); } function tick() { // this set makes it so the stage only re-renders when an event handler indicates a change has happened. if (update) { if(mousePos.x - rectX < 50) { newWidth = 50; } else { newWidth = mousePos.x - rectX; } if(mousePos.y - rectY < 50) { newHeight = 50; } else { newHeight = mousePos.y -rectY; } rectW = newWidth; rectH = newHeight; rect.clear(); rect.graphics .setStrokeStyle(1) .beginStroke(Graphics.getRGB(65,65,65,.7)) .drawRect(0,0,rectW,rectH); // add rectangle to stage stage.addChild(rect); update = false; // only update once stage.update(); } } I know it shouldn't be this difficult and would appreciate any help.