Clickable images in a canvas - javascript

I am trying to make the images inside a canvas clickable, but I am confused on how to go about that, here is what I have currently, I just want pictures[0] to be clickable and that alert to occur once it is clicked, but for some reason it is not working. How can i make it so that when picture[0] is clicked the alert goes off.
var images = document.getElementsByTagName("img");
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var left = c.offsetLeft;
var top = c.offsetTop;
var pictures = [];
//wheel
pictures[0] = new Image();
pictures[0].src = 'images/Layer8.png';
//bg
pictures[1] = new Image();
pictures[1].src = 'images/Layer0.png';
//gear
pictures[2] = new Image();
pictures[2].src = 'images/Layer7.png';
//speed
pictures[3] = new Image();
pictures[3].src = 'images/Layer14.png';
//dial1
pictures[4] = new Image();
pictures[4].src = 'images/Layer18.png';
//dial2
pictures[5] = new Image();
pictures[5].src = 'images/Layer16.png';
//dial3
pictures[6] = new Image();
pictures[6].src = 'images/Layer17.png';
c.addEventListener('click', function(event){
var x = event.pageX - left,
y = event.pageY - top;
pictures.forEach(function(fx,fy,fwidth,fheight,x,y) {
if (y > fy && y < fy + fheight
&& x > fx && x < fx + fwidth) {
alert('clicked an element');
}
});
}, false);
function load(){
ctx.drawImage(pictures[1],0,0);
ctx.drawImage(pictures[2],468,263);
ctx.drawImage(pictures[3],89,77);
ctx.drawImage(pictures[4],146,153);
ctx.drawImage(pictures[5],278,153);
ctx.drawImage(pictures[6],206,119);
ctx.drawImage(pictures[0],fx,fy,fwidth,fheight);
}
var fx = 84;
var fy = 60;
var fwidth = 283;
var fheight = 276;
window.onload = load;

Ok I understand what you are trying to do. Unfortunately, images in the canvas are not DOM elements, only the canvas itself is one. This means using jQuery you can call:
$('#myCanvas').on('click', function() {});
or natively:
c.addEventListener('click', function(){});
However, the fabricjs.com library may prove to be useful to you in this case.

Related

for loop to create Image(),but get the width and height failed

I can't get some image's width and height, but some on some others I can via Image.width and Image.height. Why some can and some can't?
var i = 0;
var len = imgitem.length;
for( i = 0; i < len ; i++){
imgSrc = imgitem[i].url;
imgObj = new Image();
imgObj.src = imgSrc;
imgObj.onload = function(){
console.log(imgObj.width);
imgObjW = imgObj.width;
imgObjH = imgObj.height;
};
/*限制最大height*/
if(imgObjH / imgObjW >= 1.5){
......
some HTML element
<div class="sfc-pic-play-item" style="width: 100%; overflow: hidden;"><div class="sfc-pic-play-item-wrap"><div class="c-img c-img-s" style="background:none"><img src="http://a.jpg" class="sfc-pic-play-center"></div></div></div>
As mentioned in the comment, your condition has to be inside the onload-handler, or it may/will be executed before the image is loaded.
And as Jonas w pointed out, there will also be a problem with imgObj not referencing the right object at the time.
imgitem.forEach(function(item){
var img = new Image();
img.src = item.url;
img.onload = function(){
var w = img.width,
h = img.height;
console.log(item, img, w, h);
if(h/w >= 1.5){
//...
}
}
});
If this still throws some error, we need to see how you adapted that to your code
Simple async + for loop problem:
for(i = 0; i < len ; i++){
(function(){
var imgSrc = imgitem[i].url;
var imgObj = new Image();
imgObj.src = imgSrc;
imgObj.onload = function(){
console.log(imgObj.width);
imgObjW = imgObj.width;
imgObjH = imgObj.height;
/*限制最大height*/
if(imgObjH / imgObjW >= 1.5){ ......}
};
//imgObjH is 0 as it isnt loaded yet
})();
}
As imageObj refers to the latest object, your onload handler wouldnt do what you expect. Use the upper code to fix or use this instead of imageObj in your onload. Also the if looks a bit missplaced there...

different pages in a canvas game

The Problem
I am creating a game using the HTML5 Canvas, the game has a main menu, the main menu has multiple buttons for you to choose. I am finding it difficult and confusing how I would, for example if the user presses the 'Play' button, to show the game. Here is an image of the main menu:
The Question
The question is how would I get from this page to another in my game?
I think you get the idea. I deliberately created the menu using the canvas, I know I could of made the menu using HTML for example but I cant as this is an example for students of what Canvas can do, whats good and bad etc.
The Code
<html>
<head>
<title>Sean Coyne</title>
</head>
<body onload="start_game()">
<body>
<div style id="canvas">
<canvas id="myCanvas" style="border:5px solid #410b11" height="320" width="480">
<p>Your browser does not support HTML5!</p>
</canvas>
<script type="text/javascript">
//Referencing the canvas
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var width = canvas.getAttribute('width');
var height = canvas.getAttribute('height');
//Finding the position of the mouse
var mouseX;
var mouseY;
//Images
var bgImage = new Image();
var logoImage = new Image();
var playImage = new Image();
var instructImage = new Image();
var settingsImage = new Image();
var aboutImage = new Image();
var peaceImage = new Image();
var backgroundY = 0;
var speed = 1;
//Arrays below used for mouse over function
var buttonX = [130,110,130,160];
var buttonY = [100,140,180,220];
var buttonWidth = [96,260,182,160];
var buttonHeight = [40,40,40,40];
var peaceX = [0,0];
var peaceY = [0,0];
var peaceWidth = 35;
var peaceHeight = 35;
var peaceVisible = false;
var peaceSize = peaceWidth;
var peaceRotate = 0;
var frames = 30;
var timerId = 0;
var fadeId = 0;
var time = 0.0;
peaceImage.src = "Images/peace.png";
bgImage.onload = function(){
context.drawImage(bgImage, 0, backgroundY);
};
bgImage.src = "Images/background.png";
logoImage.onload = function(){
context.drawImage(logoImage, 50, -10);
}
logoImage.src = "Images/logo.png";
playImage.onload = function(){
context.drawImage(playImage, buttonX[0], buttonY[0]);
}
playImage.src = "Images/play.png";
instructImage.onload = function(){
context.drawImage(instructImage, buttonX[1], buttonY[1]);
}
instructImage.src = "Images/instructions.png";
settingsImage.onload = function(){
context.drawImage(settingsImage, buttonX[2], buttonY[2]);
}
settingsImage.src = "Images/settings.png";
aboutImage.onload = function(){
context.drawImage(aboutImage, buttonX[3], buttonY[3]);
}
aboutImage.src = "Images/about.png";
timerId = setInterval("update()", 1000/frames);
canvas.addEventListener("mousemove", checkPos);
canvas.addEventListener("mouseup", checkClick);
function update() {
clear();
move();
draw();
}
function clear() {
context.clearRect(0, 0, width, height);
}
function move(){
backgroundY -= speed;
if(backgroundY == -1 * height){
backgroundY = 0;
}
if(peaceSize == peaceWidth){
peaceRotate = -1;
}
if(peaceSize == 0){
peaceRotate = 1;
}
peaceSize += peaceRotate;
}
function draw(){
context.drawImage(bgImage, 0, backgroundY);
context.drawImage(logoImage, 50,-10);
context.drawImage(playImage, buttonX[1], buttonY[0]);
context.drawImage(instructImage, buttonX[2], buttonY[1]);
context.drawImage(settingsImage, buttonX[2], buttonY[2]);
context.drawImage(aboutImage, buttonX[3], buttonY[3]);
if(peaceVisible == true){
context.drawImage(peaceImage, peaceX[0] - (peaceSize/2), peaceY[0], peaceSize, peaceHeight);
context.drawImage(peaceImage, peaceX[2] - (peaceSize/2), peaceY[2], peaceSize, peaceHeight);
}
}
function checkPos(mouseEvent){
if(mouseEvent.pageX || mouseEvent.pageY == 0){
mouseX = mouseEvent.pageX - this.offsetLeft;
mouseY = mouseEvent.pageY - this.offsetTop;
}else if(mouseEvent.offsetX || mouseEvent.offsetY == 0){
mouseX = mouseEvent.offsetX;
mouseY = mouseEvent.offsetY;
}
for(i = 0; i < buttonX.length; i++){
if(mouseX > buttonX[i] && mouseX < buttonX[i] + buttonWidth[i]){
if(mouseY > buttonY[i] && mouseY < buttonY[i] + buttonHeight[i]){
peaceVisible = true;
peaceX[0] = buttonX[i] - (peaceWidth/2) - 2;
peaceY[0] = buttonY[i] + 2;
peaceX[1] = buttonX[i] + buttonWidth[i] + (peaceWidth/2);
peaceY[1] = buttonY[i] + 2;
}
}else{
peaceVisible = false;
}
}
}
function checkClick(mouseEvent){
for(i = 0; i < buttonX.length; i++){
if(mouseX > buttonX[i] && mouseX < buttonX[i] + buttonWidth[i]){
if(mouseY > buttonY[i] && mouseY < buttonY[i] + buttonHeight[i]){
fadeId = setInterval("fadeOut()", 1000/frames);
clearInterval(timerId);
canvas.removeEventListener("mousemove", checkPos);
canvas.removeEventListener("mouseup", checkClick);
}
}
}
}
function fadeOut(){
context.fillStyle = "rgba(0,0,0, 0.2)";
context.fillRect (0, 0, width, height);
time += 0.1;
if(time >= 2){
clearInterval(fadeId);
time = 0;
timerId = setInterval("update()", 1000/frames);
canvas.addEventListener("mousemove", checkPos);
canvas.addEventListener("mouseup", checkClick);
}
}
</script>
</body>
</html>
What I usually do is have a switch statement inside the draw loop, and a state variable which holds the current game state (menu, playing, etc...).
Then, based on the current game state you only draw the objects required for the current scene.
Something like this:
var STATES = {
Menu: 0,
PauseMenu: 1,
Playing: 2
};
var currentState = STATES.Menu;
...
function draw() {
switch(currentState) {
case STATES.Menu:
// Draw buttons, etc..
break;
case STATES.Playing:
// Draw the game screen, the player, etc...
break;
}
}
When the user presses the Play button the only thing you have to do is:
function onPlayButtonClick() {
currentState = STATES.Playing;
// Starting the next frame the new state will be "magically" drawn
}
If you don't like the switch statement, you can create a State class that has a draw method. Then you can simply create new states, each with it's own drawing method and in the main draw loop only call the draw method of the current state.
Same goes for the update function, each state has it's own update function (in the main menu you update buttons or animate things, while playing the game you update the game world and run your physics). So, based on the current state your update function is actually different. It's up to you how you structure your code and how you call different functions based on the current state.
In each text option, you should create a smaller Canvas, only with the option text and add a 'click' event with the callbacks.
Tip: You don't need another page, just erase the main canvas and draw what you want.

Place image on canvas at same x,y position as mouse onclick

I'm trying to make a web app that lets the user insert small images of characters on an html canvas. The plan is to have the user click a button on a virtual keyboard, and then click on the canvas to have the corresponding character appear wherever the user clicked. Right now I'm just trying to get one image to appear on the canvas when clicked, but so far it isn't doing anything. Here's the code I have now, which is embedded in the html file.
<script type="text/javascript">
var mathCanvas = document.getElementById("matharea");
var ctx = mathCanvas.getContext("2d");
var el = mathCanvas;
var xPos = 0;
var yPos = 0;
while(el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)){
xPos += el.offsetLeft - el.scrollLeft;
yPos += el.offsetTop - el.scrollTop;
el = el.parentNode;
}
var img = new Image();
img.src = "five.png";
mathCanvas.onclick = function(event){
var x = event.clientX - xPos;
var y = event.clientY - yPos;
ctx.drawImage(img,x,y);
};
</script>
I've tested the onclick event, and it returns coords, but the image won't appear. What's going wrong here?
This should work since the click event will be firing on the canvas itself:
var mathCanvas = document.getElementById("matharea");
var ctx = mathCanvas.getContext("2d");
var img = new Image();
img.src = "five.png";
mathCanvas.onclick = function(event){
var x = event.offsetX;
var y = event.offsetY;
ctx.drawImage(img,x,y);
};
To center the image on the point:
var mathCanvas = document.getElementById("matharea");
var ctx = mathCanvas.getContext("2d");
var img = new Image();
img.src = "five.png";
mathCanvas.onclick = function(event){
var x = Math.round(event.offsetX - img.width/2);
var y = Math.round(event.offsetY - img.height/2);
ctx.drawImage(img,x,y);
};

JavaScript/HTML5 Canvas - Load array of images

I have a folder of images, altogether 32x32 tiles. I am trying to load these images using JavaScript, onto an HTML5 canvas.
Here's what I have:
window.onload = function(){
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var imageObj = new Image();
var tiles = [];
canvas.width = 512;
canvas.height = 352;
for (x = 0; x <= 520; x++) {
imageObj.src = "line_tile/t"+x+".png";
tiles.push(imageObj);
}
var theX;
var theY;
for (x = 0; x <= 10; x++) {
for (y = 0; y <= 15; y++) {
theX = x*32;
theY = y*32;
context.drawImage(tiles[2], theY, theX,32,32);
console.log("Tile X: "+x+" | Tile Y: "+y+" - X Pos: "+theX+" | Y Pos: "+theY);
}
}
};
The problem is that this code only loads up the last tile (in this case tile[520]). In reality I want to load all the tiles. No matter what. How do I properely put a set of images into an array and load it?
Your modifying a single instance of imageObj; so basically you end up with an array all pointing to the same instance, which ends with 520.
try
for (x = 0; x <= 520; x++) {
var imageObj = new Image(); // new instance for each image
imageObj.src = "line_tile/t"+x+".png";
tiles.push(imageObj);
}
Not strictly related to your problem but you might encounter it (with current code)
I'm not sure if you do not need first to see if the Image was actually loaded before adding it to the tiles array.

Free memory on FPS system

I start on KineticJS (and on Canvas) and i'm creating a small game for learn...
Right now, I have just 2 layers :
First with a map composed by Kinetic.Image
Second with the last time who game as draw.
I want refresh display X time per second but after 20 or 30 times the game are really slow.. And it's the same when I flood event click ( who launch the draw function too)...
Moreover, i can see in the second layer : the old text are never clean, the new are added on top... :/
var stage;
var layers = {};
var CANEVAS_WIDTH = 800;
var CANEVAS_HEIGHT = 600;
var MAP_WIDTH = 10;
var MAP_HEIGHT = 10;
var MAPPING_WIDTH = 150;
var MAPPING_HEIGHT = 88;
var LEFT_X = 0;
var LEFT_Y = MAP_WIDTH*MAPPING_HEIGHT/2;
var TOP_X = MAP_WIDTH/2*MAPPING_WIDTH;
var TOP_Y = 0;
var VIEW_X = 0;
var VIEW_Y = 0;
var CURSOR_X = 6;
var CURSOR_Y = 0;
var images = {};
function loadImages(sources, callback)
{
var loadedImages = 0;
var numImages = 0;
// get num of sources
for (var src in sources)
numImages++;
for (var src in sources)
{
images[src] = new Image();
images[src].onload = function(){
if (++loadedImages >= numImages)
callback();
};
images[src].src = sources[src];
}
}
function getMouseInfo(mousePos)
{
var info = {screen_x : mousePos.x,
screen_y : mousePos.y,
mouse_x : mousePos.x+VIEW_X,
mouse_y : mousePos.y+VIEW_Y-LEFT_Y,
onMap : 0,
map_x : -1,
map_y : -1};
map_x = -(info.mouse_y - ((LEFT_Y * info.mouse_x) / TOP_X)) / MAPPING_HEIGHT;
map_y = -(-info.mouse_y - ((LEFT_Y * info.mouse_x) / TOP_X)) / MAPPING_HEIGHT;
if(map_x >= 0 && map_x < MAP_WIDTH && map_y >= 0 && map_y < MAP_HEIGHT)
{
info.map_y = parseInt(map_y);
info.map_x = parseInt(map_x);
info.onMap = 1;
}
return info;
}
function draw()
{
drawMap();
drawFPS();
stage.add(layers.mapLayer);
stage.add(layers.fpsLayer);
}
function drawFPS()
{
layers.fpsLayer.clear();
var fps = new Kinetic.Shape(function(){
var date = new Date();
var time = date.getTime();
var context = this.getContext();
context.beginPath();
context.font = "12pt Calibri";
context.fillStyle = "red";
context.fillText("FPS : "+time, 10, 20);
});
layers.fpsLayer.add(fps);
}
function drawMap()
{
var x=0,y=0;
layers.mapLayer.clear();
var s = new Kinetic.Shape(function(){
var context = this.getContext();
context.beginPath();
context.rect(0, 0, CANEVAS_WIDTH, CANEVAS_HEIGHT);
context.fillStyle = "#000";
context.fill();
context.closePath();
});
layers.mapLayer.add(s);
for(x=0; x<MAP_WIDTH; x++)
for(y=0;y<MAP_HEIGHT; y++)
{
var img = new Kinetic.Image({
image: ((x==CURSOR_X && y==CURSOR_Y)?images.testMapCursor:images.testMap)
});
img.x = x*MAPPING_WIDTH/2 + y*MAPPING_WIDTH/2 - VIEW_X;
img.y = (MAP_WIDTH-1)*MAPPING_HEIGHT/2 - x*MAPPING_HEIGHT/2 + y*MAPPING_HEIGHT/2 - VIEW_Y;
layers.mapLayer.add(img);
}
}
function changeCursorPosition(cursor_x, cursor_y)
{
CURSOR_X = cursor_x;
CURSOR_Y = cursor_y;
draw();
}
function initStage()
{
layers.mapLayer = new Kinetic.Layer();
layers.fpsLayer = new Kinetic.Layer();
draw();
}
/*
* INIT
*/
window.onload = function(){
stage = new Kinetic.Stage("container", <?=CANEVAS_WIDTH;?>, <?=CANEVAS_HEIGHT;?>);
stage.on("mousemove", function(){
var mouseInfo = getMouseInfo(stage.getMousePosition());
if(mouseInfo.onMap)
document.body.style.cursor = "pointer";
else
document.body.style.cursor = "default";
});
stage.on("mousedown", function(){
var mouseInfo = getMouseInfo(stage.getMousePosition());
if(mouseInfo.onMap)
changeCursorPosition(mouseInfo.map_x, mouseInfo.map_y);
});
var sources = {
testMap : "testMap.png",
testMapCursor : "testMapCursor.png"
};
loadImages(sources, initStage);
};
Sorry, my english are realy bad.
Thank all.
I know someone who is trying out KineticJS. I haven't used it myself, so I apologize that I cannot provide more specific help.
Unfortunately, it is very difficult to get good performance with canvas, and it depends greatly on the browser. Last I checked, Opera 12 and IE 9 performed significantly faster than other browsers, since their 2D rendering is 3D accelerated (using OpenGL and Direct3D, respectively)
I am not sure if this applies to KineticJS, but one technique you can use to improve performance with canvas is to use multiple canvas elements, and transform their positions rather than blitting on a single surface.
I've been pretty happy with the results I've gotten using Jeash, which is wired into NME's command-line tools. The development is similar to working with Flash, but it will create an HTML5 Canvas application using your code. The same application will also be able to publish to Windows, Mac, Linux, iOS, Android, webOS and Flash, as either native C++ and OpenGL, or as SWF bytecode. This gives you a lot of options for providing the best experience for each user.
http://www.haxenme.org

Categories

Resources