Javascript Canvas Image onclick not working - javascript

I am failing at getting a DOM Image onclick event to work.
var context = document.getElementById('canvas').getContext('2d');
var image = new Image();
image.src = "foo.png"
image.onclick = function(e) { console.log("clicked"); }
setInterval(function() {
context.drawImage(image, 100, 100, 50, 50);
};
Why do I not get the log message when i click on the image. In developer tools i can see the onclick function is not null for the image.

Yes, what Musa said...and a few other things.
Some changes to your code
Image.src=”foo.png” should come after the image.onclick function
Context.drawImage should be inside the image.onclick function
setInterval is not needed as far as I can see
Try this:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var context=document.getElementById("canvas").getContext("2d");
var image=new Image();
image.onload=function(){
context.drawImage(image,0,0);
}
image.src="http://i.imgur.com/nJiIJIJ.png";
document.getElementById("canvas").addEventListener("click", function(){console.log("clicked");}, false);
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

You cannot set onclick for a particular image added in canvas . You can set onclick for the whole canvas alone so you have to use any third party js or else you should do some calculations which finds that you clicked on the image of the canvas ..

Other users are right.
The image you draw on the canvas is a DOM element but it is rendered in a position which is not stored in the DOM.
This doesn't mean you can access it's position and compare it with the mouse position.
I'm using an external library here, but it does what you need: http://jsfiddle.net/Saturnix/cygUH/
this is the library used.
Since I can't post link to jsfiddles without posting the code, here's the script I've wrote for you.
function demo(g) {
g.ctx.font = "bold 16px Arial";
g.draw = function () {
g.ctx.clearRect(0, 0, g.width, g.height)
var posX = 0;
var posY = 0;
g.ctx.drawImage(image, posX, posY);
if (g.mouseX > posX && g.mouseX < image.width &&
g.mouseY > posY && g.mouseY < image.height &&
g.mousePressed)
g.ctx.fillText("You're clicking the image!", g.mouseX, g.mouseY);
}
}

You can cast a ray (with an onclick on the canvas) into the canvas and manually test your images for intersection with the ray. You should write a
objectsUnderPoint( x, y );
function that returns an array of all the images that intersect with the ray at x, y.
This is the way it is usually done in 3D engines as well. You ofcourse need to keep the image position as a property of the image for intersection testing.

Related

How to make a minimap of the whole page in Javascript

I am working on a simple JS/HTML/CSS project. I have a box that's very big (400vw, 400vh) and it acts like a map. What I want to do is a mini-map for that huge thing on the top left corner.
What I understand is that it has to be done with canvas by getting the whole page, put it in canvas and then display it in another box that's just for example 10 times smaller. If my logic is correct, then what is wrong in the code?
<html>
<head>
<script defer src="js/main.js"></script>
<link rel="stylesheet" href="css/main.css">
<style>
#minimapCanvas {
position: absolute;
top: 0;
left: 0;
}
</style>
<script>
window.onload = function() {
// Get the canvas element and its context
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
// Draw something on the main canvas
context.fillRect(0, 0, canvas.width, canvas.height);
saveCanvas();
}
function saveCanvas() {
// Get the canvas element and its context
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
// Get the image data from the canvas
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
// Create a new canvas for the minimap
var minimapCanvas = document.getElementById("minimapCanvas");
minimapCanvas.width = 100;
minimapCanvas.height = 100;
var minimapContext = minimapCanvas.getContext("2d");
// Draw the image data on the minimap canvas
minimapContext.putImageData(imageData, 0, 0, 0, 0, minimapCanvas.width, minimapCanvas.height);
}
</script>
</head>
<body>
<div class="box">
<canvas id="myCanvas" width="500" height="500"></canvas>
<canvas id="minimapCanvas" width="100" height="100"></canvas>
</div>
</body>
</html>
and after I run the application I get a big black box instead of the minimap -
I have been trying to search every possible topic about JS mini-map in stackoverflow but couldn't find my solution. I also tried Chat GPT (which usually helps me).
EDIT:
Here is a github link to my repository (NOTE: you have to run it with a live server for example in VSCode, in order for the character's image to work properly) -
https://github.com/ApooBG/minimap

drag or move image inside a canvas using touchmove in HTML5 for touch devices

I'm creating a HTML5 game which can be made to run on Android also. I went through few articles and didn't get the solution yet. I have a image which i'm generating through javascript and I want to move this image using touchmove so that I can run it in my Android device. This is the code:
gameCanvas.addEventListener("touchmove", touchXY, true);
function touchXY(e) {
if (!e)
var e = event;
e.preventDefault();
avatarX = e.targetTouches[0].pageX - gameCanvas.offsetLeft;
avatarY = e.targetTouches[0].pageY - gameCanvas.offsetTop;
}
This is not working. I got this code from https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingMouseandTouchControlstoCanvas/AddingMouseandTouchControlstoCanvas.html
And this is my canvas:
<canvas id="gameCanvas" onclick="setUpGame();" width="400" height="300"></canvas>
This is my image:
avatarImage.src = "img/avatar.png";
gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
I just want to move the image inside the canvas.
I wrote a full example, hopefully it's not too verbose.
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
</head>
<body>
<canvas id='canvas' width='512' height='512'></canvas>
<script>
var c=document.getElementById('canvas'),
ctx=c.getContext('2d'),
activeBox='none',
//populate the map with objects
box=[
{
x:256,
y:128,
width:32,
height:64
},
{
x:128,
y:64,
width:64,
height:64
},
{
x:32,
y:32,
width:32,
height:32
},
];
function draw(){
//clear the screen, draw population
ctx.clearRect(0,0,c.width,c.height);
for(var i=0;i<box.length;i++){
ctx.fillRect(box[i].x,box[i].y,box[i].width,box[i].height);
}
//repeat at 60fps if possible, pause if window looses focus
requestAnimationFrame(draw);
}
function startTouch(e){
//this makes it easier to write control flow later and keeps XY relative to canvas
var xTouch=e.touches[0].pageX-c.offsetLeft,
yTouch=e.touches[0].pageY-c.offsetTop;
//its best to go through this loop in touchstart, because it only happens once per touch
for(var i=0;i<box.length;i++){
if(xTouch>box[i].x&&xTouch<box[i].x+box[i].width){
if(yTouch>box[i].y&&yTouch<box[i].y+box[i].height){
activeBox=i;
}
}
}
}
function moveTouch(e){
//grab a box by the center
if(activeBox!='none'){
box[activeBox].x=e.changedTouches[0].pageX-box[activeBox].width/2;
box[activeBox].y=e.changedTouches[0].pageY-box[activeBox].height/2;
}
}
function endTouch(){
//clear active so that dragging empty space wont move the last active box
activeBox='none';
}
canvas.addEventListener('touchstart',startTouch);
canvas.addEventListener('touchmove',moveTouch);
canvas.addEventListener('touchend',endTouch);
window.addEventListener('load',draw);
</script>
</body>
</html>
I used fillRect for simplicity, but if you want to replace it with drawImage you'll need to create a new element for each and add a source property to the box object array. Here's a partial example.
//you need a new one of these for every image
var img=new Image();
img.src='http://www.w3schools.com/images/w3logotest2.png';
var box={
source:img,
x:Math.floor((Math.random()*256)+1),
y:Math.floor((Math.random()*256)+1)
};
//make sure the image doesnt load before the script
window.onload=function(){
ctx.drawImage(img,box.x,box.y);
}

Clear Canvas Recursively after translating it

I am trying to translate and save my canvas each time I press right key, so that it moves horizontally each time I press right key. This is the function I am using to move the canvas horizontally each time which is working for the first time and but not later.
function clearRight(){
var canvas1=document.getElementById('plot');
ctx=canvas1.getContext("2d");
ctx.restore();
ctx.translate(-1000,0);
ctx.save();
ctx.clearRect(0,0,canvas1.width,canvas1.height);
}
rightKey(){
clearRight();
draw();
}
Can anyone please point where am I going wrong in trying to move the canvas?
UPDATE
I have solved the issue I was facing.
To move the canvas horizontally
var translated=0;
function clear()
{
var canvas1=document.getElementById('plot');
var ctx=canvas1.getContext("2d");
ctx.restore();
ctx.save();
ctx.clearRect(0,0,canvas1.width,canvas1.height);
}
function rightKey()
{
clear();
ctx.clearRect(0,0,canvas1.width,canvas1.height);
translated += 10;
ctx.translate(-translated,300);
draw(ctx);
}
I'm assuming there's a reason why you're not just drawing an oversized canvas contained in a smaller div-wrapper with scrollbars enabled...so here's an alternative.
You could draw your entire plotted graph to a separate canvas.
Then to pan left/right you can draw that temporary canvas to your main canvas with an offset.
A Demo: http://jsfiddle.net/m1erickson/GfRLq/
Before panning right:
After panning right:
About dynamically changing plots:
If your plots are dynamic, then you can still use this panning technique.
Just update the tempCanvas with each new plot.
Example code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var offset=0;
// create some test data
var points=[];
for(var i=0;i<50;i++){
points.push({x:i*40,y:100+Math.random()*100-50});
}
// create a temporary canvas
var tempCanvas=document.createElement("canvas");
var tempCtx=tempCanvas.getContext("2d");
tempCanvas.width=40*points.length;
tempCanvas.height=300;
// draw your complete plot on the tempCanvas
// draw the line
tempCtx.beginPath();
tempCtx.moveTo(points[0].x,points[0].y);
for(var i=0;i<points.length;i++){
var point=points[i];
tempCtx.lineTo(point.x,point.y);
}
tempCtx.lineWidth=5;
tempCtx.strokeStyle="blue";
tempCtx.stroke();
// draw the markers
for(var i=0;i<points.length;i++){
var point=points[i];
tempCtx.beginPath();
tempCtx.arc(point.x,point.y,10,0,Math.PI*2);
tempCtx.closePath();
tempCtx.fillStyle="black";
tempCtx.fill();
tempCtx.fillStyle="white";
tempCtx.fillText(i,point.x-3,point.y+3);
}
ctx.drawImage(tempCanvas,0,0)
// function to draw the canvas with your specified offset
function drawPlotWithOffset(offset){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(tempCanvas,offset,0,canvas.width,canvas.height,0,0,canvas.width,canvas.height)
}
$("#left").click(function(){
offset-=20;
if(offset<0){offset=0;}
drawPlotWithOffset(offset);
});
$("#right").click(function(){
offset+=20;
if(offset>tempCanvas.width-canvas.width){
offset=tempCanvas.width-canvas.width;
}
drawPlotWithOffset(offset);
});
}); // end $(function(){});
</script>
</head>
<body>
<button id="left">Pan Left</button><br>
<button id="right">Pan Right</button><br>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

How to show multiple external svg graphics in a html5 canvas

I have created a html 5 canvas and added some shapes using
ctx.fillRect(X,Y,W,H); and some other javascript functions.
Now i want to add a svg graphic in multiple places of that canvas.
The reason i want to use a svg is the sharpness & quality of the image.
I tried using drawImage method in HTML5 but the image i want to draw is doesn't give a very quality output when it's added to the canvas using drawImage method.No matter how high resolution image i use it makes a very low quality image.
But i think using a svg graphic is the solution.
This is the code i used,
<canvas id="myCanvas" style="border: 2px solid #000000; width: 1280px; height: 800px;"></canvas>
<img id="location" src="location.png" alt="The Scream" width="25.5" height="41.5">
<script >
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillRect(4,4, 12,10);
ctx.fillStyle=fontBlack;
ctx.fillText("1",8,10);
ctx.fillRect(5,16, 7, 16);
ctx.fillRect(5,33, 7, 28);
ctx.fillRect(5,62, 7, 50);
ctx.fillRect(5,113, 7, 15);
ctx.fillRect(5,129, 7, 15);
//I have a list of coordinates in a javascript object list called selectedShelfList, I'm getting the necessary coordinates from that.
function drawLocations(){
for (var i=0; i<selectedShelfList.length; i++)
{
var x_coordinate = selectedShelfList[i].shelf_x;
var y_coordinate = selectedShelfList[i].shelf_y;
var img=document.getElementById("location");
ctx.drawImage(img,x_coordinate,y_coordinate,8.5,13.8);
//Instead of drawImage method i want to use a code to show a svg graphic here
}
}
</script >
As solutions i found on stackoverflow for this problem,
I tried using canvg.js as mentioned in the previous similar questions but it wasn't clear.
for example:
var ctx = document.getElementById('test').getContext('2d');
ctx.drawSvg('<svg><rect x="0" y="0" width="100" height="200" fill="red" /></svg>', 0 , 0 , 500, 500);
How shoul i give the url for external svg in this command?
Can i use the url in between the '' as the parameter for drawSvg ?
Eg: ctx.drawSvg('location.svg', 0 , 0 , 500, 500);
Is there any way i can show a svg graphic inside a canvas?
Can canvg do the trick? If so how exactly?
Please help me on this. :)
[Edited: include example of very small map pin]
Rendering a circle in a small 10 pixel boundary will always result in pixilation.
There's just too few pixels to "round" an edge.
Here's an example of a more square map pin that is far less pixelated:
You can produce this map pin in canvas code like this:
ctx.beginPath();
ctx.moveTo(100,100);
ctx.lineTo(110,100);
ctx.lineTo(110,107);
ctx.lineTo(105,115);
ctx.lineTo(100,107);
ctx.lineTo(100,100);
ctx.closePath();
ctx.fillStyle="gold";
ctx.fill();
ctx.strokeStyle="black";
ctx.lineWidth=2;
ctx.stroke();
[Original answer]
SVG scales well since it is vector drawn.
You should be able to do this and get a high quality result:
Download this test image:
https://dl.dropboxusercontent.com/u/139992952/stackoverflow/rocket.svg
Then this code will scale the svg rocket while maintaining quality:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var size=100;
var svg1=new Image();
svg1.width=150;
svg1.onload=function(){
ctx.drawImage(svg1,0,0,size,size);
}
svg1.src="rocket.svg";
$("#bigger").click(function(){
size+=100;
ctx.drawImage(svg1,0,0,size,size);
});
}); // end $(function(){});
</script>
</head>
<body>
<button id="bigger">Bigger!</button><br>
<canvas id="canvas" width=900 height=900></canvas>
</body>
</html>
Using the canvg worked!
var ctx = document.getElementById('test').getContext('2d');
ctx.drawSvg('location.svg', 0 , 0 , 100, 100);
using above code worked. Earlier I haven't add the javascript files into the html file properly.
Adding them to local folder worked
<script type="text/javascript" src="rgbcolor.js"></script>
<script type="text/javascript" src="StackBlur.js"></script>
<script type="text/javascript" src="canvg.js"></script>
Still the image quality is not that much but it's better than using an image with drawImage method.

HTML5 Canvas moving alpha mask

I have a background, let's say it's green grass. On top of the background I have a black overlay. What I want now is to make a movable hole in the overlay so that you can see the background like in the image below.
I am pretty new to canvas so I'm not sure what I'm supposed to look for. Alpha mask?
So my question is how can I achieve the effect demonstrated in the image above?
If it were HTML I would have two images of the grass, one as the background and one above the overlay in a div with a border radius that can move and just calculate positions.
Thanks.
Are you looking for a moving "flashlight" kind of effect?
If so, you can do that by drawing a circular path and then using it as a clipping region with: context.clip();
Anything drawn after the .clip() will be viewed through the clipping path.
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/pRzxt/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var radius=50;
var x=100;
var dx=10;
var y=100;
var dy=10;
var delay=10;
var img=new Image();
img.onload=function(){
var canvas1=document.getElementById("image");
var ctxImg=canvas1.getContext("2d");
ctxImg.drawImage(img,0,0,img.width,img.height,0,0,canvas.width,canvas.height);
animate();
}
img.src="http://lh3.ggpht.com/_Z-i7eF_ACGI/TRxpFywLCxI/AAAAAAAAAD8/ACsxiuO_C1g/house%20vector.png";
function animate() {
if(--delay<0){
// update
x+=dx;
if(x-radius<0 || x+radius>=canvas.width){dx=-dx;}
y+=dy;
if(y-radius<0 || y+radius>=canvas.height){dy=-dy;}
delay=10;
// draw stuff
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.beginPath();
ctx.arc(x,y, radius, 0, 2 * Math.PI, false);
ctx.clip();
ctx.drawImage(img,0,0,img.width,img.height,0,0,canvas.width,canvas.height);
ctx.restore();
}
// request new frame
requestAnimFrame(function() {
animate();
});
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Image clipped by moving circle</p>
<canvas id="canvas" width=300 height=200></canvas>
<br/><p>Unclipped image</p>
<canvas id="image" width=300 height=200></canvas>
</body>
</html>
I did something similar for a little canvas test I was working on a while back. What you can do is create a div with a canvas element on top of the bottom layer and draw a radial gradient with transparency on that top layer canvas. I have a fiddle here: http://jsfiddle.net/CnEBQ/14/
In particular look at this bit of code for the radial gradient. The context is attached to the top div canvas:
var gradient = tCTX.createRadialGradient(CANVAS_SIZE.x/2, CANVAS_SIZE.y/2, 250, CANVAS_SIZE.x/2, CANVAS_SIZE.y/2, 0);
gradient.addColorStop(0, "#000");
gradient.addColorStop(1, "transparent");
tCTX.fillStyle = gradient;
tCTX.fillRect(0, 0, CANVAS_SIZE.x, CANVAS_SIZE.y);
There may be a little bad code in there, but it should give you an idea of whether this is where you want to go. You could redraw the top layer "hole" as needed on a timer or some event to get it moving. And you can play with the gradient setting to get more or less "darkness".
I'm having quite a time trying to find a good reference to the functions I'm using here, but a decent place to look for more explanation of these functions is
the Mozilla Canvas API
where the parameters to the gradient functions are explained. Especially useful since they're not immediately obvious.

Categories