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
Related
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)
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";
So, I have an <img> tag that has an onclick attribute. The onclick calls a function called analyze(this), with this being the image.
The analyze function does some things to the image that aren't entirely relevant, except for the fact that it draws it onto the <canvas> element (using the drawImage function).
But now, I want to also pick the color I just clicked on in the image. I am currently using the method answered here (the answer with 70+ votes, not the chosen one): How do I get the coordinates of a mouse click on a canvas element?
But, I think I might be doing this wrong. I have the image drawn and my functions called (and those all work), but the color picking part isn't being called. I think that this is because I didn't actually capture the event. This is generally how my code looks:
<img onclick="javascript:analyze(this);" />
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
img = new Image();
img.onload = function () {
canvaselement.drawImage(img, 0, 0, 250, 250);
...
canvaselement.onClick = function () {
var coords = canvaselement.relMouseCoords(event);
pick(img, canvaselement, coords); // pass in coordinates
}
}
img.src = img_elem.src;
}
function relMouseCoords(event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
}
while (currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX;
canvasY = event.pageY - totalOffsetY;
return {
x: canvasX,
y: canvasY
}
}
function pick(img, canvaselement, coords) {
var pickedColor = "";
canvaselement.drawImage(img, 0, 0, 250, 250);
xx = coords.x;
yy = coords.y;
var imgData = canvas.getImageData(xx, yy, 1, 1).data;
pickedColor = rgbToHex(imgData);
//alert(pickedColor);
return pickedColor;
}
So, the code never gets to the pick function. I have a feeling that it's because I didn't actually capture the onclick event. I'm also not even sure if this is the right way to get the coordinates on the canvas, I'm just sort of hoping that I even get to that part of the debugging process at this point.
Thanks for your help!
The problem is probably that you're assigning canvaselement to the results of getContext('2d') and not to the element itself, which you will need for the click event binding. Create two variables, one for the DOM element itself and one for the context, something like:
var canvaselement = document.getElementById('canvas'),
canvaselementctx = canvaselement.getContext('2d');
...
canvaselement.onClick = function() {
var coords = canvaselementctx.relMouseCoords(event);
...
}
You have a couple of errors in the code but the reason the code you got from the linked post is that you forgot to include the prototype definition it uses:
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
Now you can call relMouseCoords on the canvas element:
/// event name in lower case
canvaselement.onclick = function () {
var coords = canvaselement.relMouseCoords(event);
//...
However, you will still get problems as you don't use a canvas context for the drawing calls.
function analyze(img_elem) {
// This is getting the canvas from the page and the image in it
var canvaselement = document.getElementById('canvas').getContext('2d'),
/// get context like this
ctx = canvaselement.getContext('2d'),
img = new Image();
img.onload = function () {
/// use context to draw
ctx.drawImage(img, 0, 0, 250, 250);
//...
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.
I create image in this way:
var orc = new Image();
orc.src = "./orc.png";
I use image in objects like this:
function Character(hp, image){
this.hp = hp;
this.image = image;
};
I call it in several times, like:
unit245 = new Character(100, orc);
And I draw it in this way, for example:
ctx.drawImage(unit245.image, 15, 55, 100, 100);
How I can get mouse click or move above my unit245 on canvas?
I need something like this http://easeljs.com/examples/dragAndDrop.html but without any frameworks (except jquery)
There is no built in way. I've written a few tutorials on making movable and selectable shapes on a Canvas to help people get started with this sort of thing though.
In short you need to remember what you have drawn and where, and then check each mouse click to see if you have clicked on something.
HitTesting can be done by checking what is present at the current location over the canvas, which can be called upon mouse click or move event over the canvas (which is the basis of hit testing). This can be done by knowing what has been placed where, like the bounds of an image can be saved, and when user clicks somewhere or moved the mouse over the canvas, you can check whether it is inside the image bounds or outside it. Array or List can be used for this.
Here is how this can be done
You cannot. The canvas has no semblance of what your unit245 or Character object is. You will have to actually manually check the coordinates and see if they fall within the bounds that you have for the character.
For example (assuming your Canvas is a var named canvas):
canvas.onclick = function(e) {
if (e.x >= unit245.x && e.x <= unit245.x + unit245.width && e.y >= unit245.y && e.y <= unit245.y + unit245.height) {
alert("You clicked unit245!");
}
}
In your case:
unit245.x = 15
unit245.y = 55
unit245.width = 100
unit245.height = 100
function Item(img, x, y){
this.image = img;
this.x = x;
this.y = y;
this.canv = document.createElement("canvas");
this.canv.width = this.image.width;
this.canv.height = this.image.height;
this.ctx = this.canv.getContext('2d');
this.ctx.drawImage(this.image, 0, 0, CELL_SIZE, CELL_SIZE);
this.hit = function (mx, my) {
var clr;
clr = this.ctx.getImageData(mx - this.x, my - this.y, 1, 1).data;
if (clr[3] > 250) {
//On object
this.image = gold_glow;
} else {
//Leave object
this.image = gold;
}
};
}