html5 canvas adding drag and drop to images drawn to the canvas - javascript
I have a webpage with an HTML5 canvas which I'm using to display a number of images as well as four 'description boxes' on the canvas.
The intention is that users will be able to drag and drop the images to their matching description boxes, however, I'm having a bit of trouble getting the dragging and dropping working.
The function I've written to add the drag and drop functionality is based on the tutorial I found at this page: http://simonsarris.com/blog/510-making-html5-canvas-useful
Obviously, I altered the code slightly, as I don't want to do everything suggested in the tutorial, and because I'm drawing images (not shapes) to the canvas. However, when viewing my page in the browser although all of the images are displayed on the canvas, the drag and drop functionality has not been added along with the new JavaScript file I've written based on that tutorial.
I'm not getting any errors in the Firebug console, and the page is displaying exactly as it was before I added the new JS function.
Can anyone spot what I'm missing?
My HTML is:
<!DOCTYPE html>
<html>
<head>
<script src = "kinetic.js" type = "text/javascript"></script>
<title>Home</title>
<script src = "drawLevelOneElements.js" type = "text/javascript"></script>
<script src = "layers&analytics.js" type = "text/javascript"></script>
<script src = "startGameDrawGameElementsDrawStartButton.js" type = "text/javascript"></script>
<script src = "interaction.js" type = "text/javascript"></script>
<script src = "dragAndDrop.js" type = "text/javascript"></script>
</head>
<body onLoad="startGame()">
<section hidden>
<img id="StartButton" src="StartButton.png" alt="Start Button" width="179" height="180" href="javascript:drawLevelOneElements();"/>
</section>
<h1>Home</h1>
<p>The purpose of this website is to teach users the basic principles of running a business by playing the game below. <br /><br /></p>
<canvas id="gameCanvas" width="1000" height="500" style="border:1px solid">
Your browser does not support the canvas element.
</canvas>
<br /><br />
<p>Use this paragraph to enter text that provides the user with instructions for how to play the game. <br />
Update the instructions so that they're appropriate to whatever level the user is currently playing.</p>
<script src = "layers&analytics.js" type = "text/javascript"></script>
<script src = "startGameDrawGameElementsDrawStartButton.js" type = "text/javascript"></script>
<script src = "variables&preloadingImages.js" type = "text/javascript"></script>
<script src = "drawLevelOneElements.js" type = "text/javascript"></script>
<script src = "interaction.js" type = "text/javascript"></script>-->
<script src = "variables&preloadingImages.js" type = "text/javascript"></script>
</body>
All of the script tags at the bottom of the page (except the last one) are actually commented out in my file, I just had to remove the comment to get it to display in a code block on here.
The javaScript I've added for the drag and drop functionality is:
function canvasState(myGameCanvas){
var bounding_box = myGameCanvas.getBoundingClientRect();
var mouseX = (mouse_event.clientX-bounding_box.left) * (myGameCanvas.width/bouding_box.width);
var mouseY = (mouse_event.clientY-bounding_box.top) * (myGameCanvas.height/bounding_box.height);
var pixels = context.getImageData(mouseX, mouseY, 1, 1);
this.valid = false; /*When set to true, the canvas will redraw everything */
this.allImagesArray; /*This is the array holding all of the images to be drawn */
this.dragging = false; /*Keep track of when the current selected object is being dragged */
this.selection = null;
this.dragOffX = 0; /*See mousedown and mousemove events for explanation */
this.dragOffY = 0;
this.interval = 30; /*This variable will be used to determine how often the draw method is called. */
/*Save a reference to the canvasState so that I'm still using this particular canvasState. */
var myState = this;
/*This stops double clicking on the canvas selecting text on the canvas */
myGameCanvas.addEventListener('selectstart', function(e) {e.preventDefault(); return false; }, false);
/*Up, down and move are for dragging */
myGameCanvas.addEventListener('mousedown', function(e){
var mouse = myState.getMouse(e);
var mX = mouse.x;
var mY = mouse.y;
var allImages = myState.allImagesArray;
var NoOfImages = allImages.length;
for (var i = 1-1; i >= 0; i--){
if(allImages[i].contains(mX, mY)){
var mySelection = allImages[i];
/*Keep track of where in the object was clicked, so that it can be
moved smoothly (see mousemove) */
myState.dragOffX = mX - mySelection.x;
myState.dragOffY = mY - mySelection.y;
myState.dragging = true;
myState.selection = mySelection;
myState.valid = false;
return;
}
}
/*If the code hasn't returned, it means that nothing has been selected.
If there was an object selected, then deselect it. */
if (myState.selection){
myState.selection = null;
myState.valid = false; /*Need to clear the old selection border */
}
}, true);
/*This event checks to see if the dragging flag has been set to true. If it has, it gets the
current mouse position and moves the selected object to that position, remembering the offset
where it was selected. If the dragging flag is false, the event does nothing. */
myGameCanvas.addEventListener('mousemove', function(e){
if(myState.dragging){
var mouse = myState.getMouse(e);
/*I don't want to drag the object by its top left corner, I want to drag from where the
object was clicked. That's why I saved the offset and use it here. */
myState.selection.x = mouse.x - myState.dragOffX;
myState.selection.y = mouse.y - myState.dragOffY;
myState.valid = false; /*Something's dragging, so I must redraw */
}
}, true);
/*All the mouseup event has to do is update the canvas state so that it is no longer dragging.
So, once the mouse button is lifted, the mousemove event should be back to doing nothing. */
myGameCanvas.addEventListener('mouseup', function(e){
myState.dragging = false;
}, true);
setInterval(function(){ myState.draw(); }, myState.interval);
canvasState.prototype.draw = function(){
/*If the state is invalid,redraw and validate. */
if (!this.valid){
var context = this.context;
var images = this.images;
this.clear();
/*Redraw the game elements here */
drawLevelOneElements();
}
}
}
Code for dragAndDrop.js:
function canvasState(myGameCanvas){
var bounding_box = myGameCanvas.getBoundingClientRect();
var mouseX = (mouse_event.clientX-bounding_box.left) * (myGameCanvas.width/bouding_box.width);
var mouseY = (mouse_event.clientY-bounding_box.top) * (myGameCanvas.height/bounding_box.height);
var pixels = context.getImageData(mouseX, mouseY, 1, 1);
this.valid = false; /*When set to true, the canvas will redraw everything */
this.allImagesArray; /*This is the array holding all of the images to be drawn */
this.dragging = false; /*Keep track of when the current selected object is being dragged */
this.selection = null;
this.dragOffX = 0; /*See mousedown and mousemove events for explanation */
this.dragOffY = 0;
this.interval = 30; /*This variable will be used to determine how often the draw method is called. */
/*Save a reference to the canvasState so that I'm still using this particular canvasState. */
var myState = this;
/*This stops double clicking on the canvas selecting text on the canvas */
myGameCanvas.addEventListener('selectstart', function(e) {e.preventDefault(); return false; }, false);
console.log("Event Listener 'selectstart' added to canvas.");
/*Up, down and move are for dragging */
myGameCanvas.addEventListener('mousedown', function(e){
console.log("Event Listener 'mousedown' added to canvas");
var mouse = myState.getMouse(e);
var mX = mouse.x;
var mY = mouse.y;
var allImages = myState.allImagesArray;
var NoOfImages = allImages.length;
for (var i = 1-1; i >= 0; i--){
if(allImages[i].contains(mX, mY)){
var mySelection = allImages[i];
/*Keep track of where in the object was clicked, so that it can be
moved smoothly (see mousemove) */
myState.dragOffX = mX - mySelection.x;
myState.dragOffY = mY - mySelection.y;
myState.dragging = true;
myState.selection = mySelection;
myState.valid = false;
return;
}
}
/*If the code hasn't returned, it means that nothing has been selected.
If there was an object selected, then deselect it. */
if (myState.selection){
myState.selection = null;
myState.valid = false; /*Need to clear the old selection border */
}
}, true);
/*This event checks to see if the dragging flag has been set to true. If it has, it gets the
current mouse position and moves the selected object to that position, remembering the offset
where it was selected. If the dragging flag is false, the event does nothing. */
myGameCanvas.addEventListener('mousemove', function(e){
console.log("Event listener 'mousemove' added to canvas.");
if(myState.dragging){
var mouse = myState.getMouse(e);
/*I don't want to drag the object by its top left corner, I want to drag from where the
object was clicked. That's why I saved the offset and use it here. */
myState.selection.x = mouse.x - myState.dragOffX;
myState.selection.y = mouse.y - myState.dragOffY;
myState.valid = false; /*Something's dragging, so I must redraw */
}
}, true);
/*All the mouseup event has to do is update the canvas state so that it is no longer dragging.
So, once the mouse button is lifted, the mousemove event should be back to doing nothing. */
myGameCanvas.addEventListener('mouseup', function(e){
console.log("Event listener 'mouseup' added to canvas.");
myState.dragging = false;
}, true);
setInterval(function(){ myState.draw(); }, myState.interval);
canvasState.prototype.draw = function(){
/*If the state is invalid,redraw and validate. */
if (!this.valid){
var context = this.context;
var images = this.images;
this.clear();
/*Redraw the game elements here */
drawLevelOneElements();
}
}
}
Code for drawLevelOneElements.js: (this now includes the call to canvasState(); )
function drawLevelOneElements(){
/*First, clear the canvas */
context.clearRect(0, 0, myGameCanvas.width, myGameCanvas.height);
/*This line clears all of the elements that were previously drawn on the canvas. */
/*Then redraw the game elements */
drawGameElements();
/*Call the function to enable drag and drop */
canvasState(document.getElementById('gameCanvas'));
/*Create the four description areas, and place them near the bottom of the canvas */
/*Create boxes with rounded corners for the description areas */
CanvasRenderingContext2D.prototype.drawDescriptionArea = function(x, y, width, height, radius, stroke){
if(typeof stroke == "undefined" ){
stroke = true;
}
if(typeof radius === "undefined"){
radius = 5;
}
this.beginPath();
this.moveTo(x + radius, y);
this.lineTo(x + width - radius, y);
this.quadraticCurveTo(x + width, y, x + width, y + radius);
this.lineTo(x + width, y + height - radius);
this.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
this.lineTo(x + radius, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - radius);
this.lineTo(x, y + radius);
this.quadraticCurveTo(x, y, x + radius, y);
this.closePath();
if(stroke){
context.stroke();
}
}
context.drawDescriptionArea(70, 400, 120, 70);
context.font = '25pt Calibri';
context.strokeText('Asset', 90, 440);
context.drawDescriptionArea(300, 400, 120, 70);
context.strokeText('Liability', 310, 440);
context.drawDescriptionArea(540, 400, 120, 70);
context.strokeText('Income', 550, 440);
context.drawDescriptionArea(750, 400, 180, 70);
context.strokeText('Expenditure', 760, 440);
/*Now draw the images to the canvas */
/*First, create variables for the x & y coordinates of the image that will be drawn.
the x & y coordinates should hold random numbers, so that the images will be
drawn in random locations on the canvas.*/
var imageX = Math.floor(Math.random()*100);
var imageY = Math.floor(Math.random()*100);
/*Create a 'table' of positions that the images will be drawn to */
var imagePositionsX = [20, 80, 140, 200, 260, 320, 380, 440, 500, 560];
var imagePositionsY = [20, 60, 100, 140, 180, 220, 260, 300, 340, 380];
/*Draw all images from assetsImageArray */
/*Use a while loop to loop through the array, get each item and draw it. */
var arrayIteration = 0;
console.log('All Images Array length: ' + allImagesArray.length); /*Display the length of the array in the console, to check it's holding the correct number of images. */
while(arrayIteration < allImagesArray.length){
var randomPositionX = Math.floor(Math.random()*10);
var randomPositionY = Math.floor(Math.random()*10);
context.drawImage(allImagesArray[arrayIteration], imageX, imageY, 50, 50);
console.log(arrayIteration); /*Display the current array position that's being drawn */
arrayIteration = arrayIteration+1;
/*Now try changing the values of imageX & imageY so that the next image is drawn to a
different location*/
imageX = imagePositionsX[randomPositionX]; /* imageX+(Math.floor(Math.random()*100)); */
imageY = imagePositionsY[randomPositionY]; /* imageY+(Math.floor(Math.random()*100)); */
}
}
In conclusion to the comments, I can answer you this: Somewhere within your code, you have to call the canvasState() function, it's not enough just to define it. Where you must call it largely depends on when you need it's functionality. By example, if you want to set up the drag and drop functionality when the page loads, you have an event handler for the body's load event:
<body onLoad="startGame()">
Somewhere in the startGame() function's code, you would call canvasState(). If you you want it when StartButton is pressed, you should call it from within the drawLevelOneElements() function. Since you were following a tutorial, the order in which things happen might matter, so there might be a particular place where you need to call your function.
One more advice. Because you seem to be learning, start with the basics and work your way up with a deep understanding of what you learned up to that point, don't just follow random tutorials.
Related
Add highlight or marker function to html canvas w/javascript
I'm working on making a nuclear physics webgame. I have created a nuclide chart on a canvas. I manually created every isotope and inputted their corresponding isotope name. A sample is below, as there are over 500 isotopes. I had to do this manually because the "grid" has to be in the form of the normal nuclide chart. The thing I need to do next is create some sort of function that will either highlight an isotope when its clicked on, or put a "marker" on the isotope when clicked on. And unhighlight or move the marker when a different isotope is clicked on. I've been at this for quite some time, but I can't figure it out. Does anyone know how I can achieve this? // hydrogen ctx.fillRect(21, 960, 25, 25); ctx.fillRect(46, 960, 25, 25); ctx.strokeRect(71, 960, 25, 25); ctx.strokeRect(96, 960, 25, 25); ctx.strokeRect(121, 960, 25, 25); ctx.strokeRect(146, 960, 25, 25); ctx.strokeRect(171, 960, 25, 25); // ctx.fillStyle = 'white'; ctx.fillText("1H", 23, 980, 15, 15); ctx.fillStyle = 'white'; ctx.fillText("2H", 48, 980, 15, 15); ctx.fillStyle = 'black'; ctx.fillText("3H", 73, 980, 15, 15); ctx.fillStyle = 'black'; ctx.fillText("4H", 98, 980, 15, 15); ctx.fillStyle = 'black'; ctx.fillText("5H", 123, 980, 15, 15); ctx.fillStyle = 'black'; ctx.fillText("6H", 148, 980, 15, 15); ctx.fillStyle = 'black'; ctx.fillText("7H", 173, 980, 15, 15);
Down here I've created a demo of how you would do such a thing like creating a hitbox. The hitbox will be represented by a square that has a random position on the canvas when the script runs. In the script we use the mousedown and mouseup event listeners to determine when the user presses and releases the mouse. On mousedown the x and y coordinates of the mouse are calculated relative to the size of the canvas. By calculating it this way you can get the exact pixel on the canvas that has been clicked upon. It then stores this value in a global variable. You'll need to know the x, y, width and height of the hitbox because you'll want to determine if the x and y of the click is within the square that is the hitbox. In the demo below the view is re-rendered whenever you click and release the click. The mouseData state changes en will show that in the new render. The render then evaluates for the hitbox if the mouseData.position coordinates are inside of the box and changes the color based on that evaluation. Then releasing the mouse triggers another re-render changing the clicked state to false and showing the original state of the hitbox. So this is basically what it is, a square of coordinates which detects if the clicked pixel is inside that square and does something if it is. const canvas = document.querySelector('#canvas'); const context = canvas.getContext('2d'); const squareSideSize = 50; canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // Stores data on the mouseclick. let mouseData = { clicked: false, position: [] // Here we will store the x and y of click as [ x, y ] } /** * Generates random number between min and max value. * Just for the purpose of drawing the hitbox. */ const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; /** * Returns an array with x and y coordinates * based on the click event. */ const getMousePositionOnCanvas = ({ clientX, clientY }) => { const canvasRect = canvas.getBoundingClientRect(); const x = clientX - canvasRect.left; const y = clientY - canvasRect.top; return [ x, y ]; }; /** * Determines if the x and y from the click is within * the x, y, width and height of the square. * Returns true or false. */ const isInHitbox = ([ x, y ], square) => ( (x >= square.x && x <= square.x + square.width) && (y >= square.y && y <= square.y + square.height) ); /** * Create random square. * This square has an x and y position as well * as a width and height. The x and y are created * to enchance the demonstration. */ const square = { x: getRandomInt(0, canvas.width - squareSideSize), y: getRandomInt(0, canvas.height - squareSideSize), width: squareSideSize, height: squareSideSize }; // Listen for mousedown and update the click position. canvas.addEventListener('mousedown', event => { mouseData.clicked = true; // Set clicked state to true. mouseData.position = getMousePositionOnCanvas(event); // Get the click position relative to the canvas. render(); // Render with clicked state. }); // Listen for mousemove and update the position. canvas.addEventListener('mousemove', event => { if (mouseData.clicked === true) { // Only act if clicked. mouseData.position = getMousePositionOnCanvas(event); // Update mouse position. render(); // Render with updated mouse position. } }) // Listen for mouseup and clear the click position. canvas.addEventListener('mouseup', event => { mouseData.clicked = false; // Set clicked state to false. mouseData.position.length = 0; // Reset the positions. render(); // Render unclicked state. }); /** * Render function which clears and draws on the canvas. * Here we determine what to draw according the data we * collection from the mouse. */ function render() { requestAnimationFrame(() => { const { x, y, width, height } = square; // Get all dimensions of the square. const { clicked, position } = mouseData; // Get data of the mouse. context.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas for a clean re-render. // Default white color. context.fillStyle = 'white'; // Check if the mouse has clicked and if the // position is inside the hitbox. Change the color // if a it is true. if (clicked === true && isInHitbox(position, square)) { context.fillStyle = 'green'; } // Draw the hitbox. context.fillRect(x, y, width, height); }); } // Initial render. render(); html, body { width: 100%; height: 100%; margin: 0; box-sizing: border-box; } body { padding: 10px; } canvas { width: 100%; height: 100%; background: #000000; } <canvas id="canvas"></canvas>
I created these functions to draw a circle onto my canvas and be able to track mouse position, test for if it's on the circle, and then listen for click down and up to be able to drag it. If there are other draw elements in the project, in this instance for me it's all 520 of my isotopes, you just need to put that code inside of your redraw function, or else everything else disappears after moving the cirlce var cw=canvas.width; var ch=canvas.height; document.body.appendChild(canvas); // used to calc canvas position relative to window function reOffset(){ var BB=canvas.getBoundingClientRect(); offsetX=BB.left; offsetY=BB.top; } var offsetX,offsetY; reOffset(); window.onscroll=function(e){ reOffset(); } window.onresize=function(e){ reOffset(); } canvas.onresize=function(e){ reOffset(); } // save relevant information about shapes drawn on the canvas var shapes=[]; // define one circle and save it in the shapes[] array shapes.push( {x:300, y:275, radius:10} ); var isDragging=false; var startX,startY; // hold the index of the shape being dragged (if any) var selectedShapeIndex; // draw the shapes on the canvas drawAll(); // listen for mouse events canvas.onmousedown=handleMouseDown; canvas.onmousemove=handleMouseMove; canvas.onmouseup=handleMouseUp; canvas.onmouseout=handleMouseOut; // given mouse X & Y (mx & my) and shape object // return true/false whether mouse is inside the shape function isMouseInShape(mx,my,shape){ if(shape.radius){ // this is a circle var dx=mx-shape.x; var dy=my-shape.y; // math test to see if mouse is inside circle if(dx*dx+dy*dy<shape.radius*shape.radius){ // yes, mouse is inside this circle return(true); } } // the mouse isn't in any of the shapes return(false); } function handleMouseDown(e){ // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // calculate the current mouse position startX=parseInt(e.clientX-offsetX); startY=parseInt(e.clientY-offsetY); // test mouse position against all shapes // post result if mouse is in a shape for(var i=0;i<shapes.length;i++){ if(isMouseInShape(startX,startY,shapes[i])){ // the mouse is inside this shape // select this shape selectedShapeIndex=i; // set the isDragging flag isDragging=true; // and return (==stop looking for // further shapes under the mouse) return; } } } function handleMouseUp(e){ // return if we're not dragging if(!isDragging){return;} // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // the drag is over -- clear the isDragging flag isDragging=false; } function handleMouseOut(e){ // return if we're not dragging if(!isDragging){return;} // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // the drag is over -- clear the isDragging flag isDragging=false; } function handleMouseMove(e){ // return if we're not dragging if(!isDragging){return;} // tell the browser we're handling this event e.preventDefault(); e.stopPropagation(); // calculate the current mouse position mouseX=parseInt(e.clientX-offsetX); mouseY=parseInt(e.clientY-offsetY); // how far has the mouse dragged from its previous mousemove position? var dx=mouseX-startX; var dy=mouseY-startY; // move the selected shape by the drag distance var selectedShape=shapes[selectedShapeIndex]; selectedShape.x+=dx; selectedShape.y+=dy; // clear the canvas and redraw all shapes drawAll(); // update the starting drag position (== the current mouse position) startX=mouseX; startY=mouseY; } // clear the canvas and // redraw all shapes in their current positions function drawAll(){ ctx.clearRect(0,0,cw,ch); for(var i=0;i<shapes.length;i++){ var shape=shapes[i]; if(shape.radius){ // it's a circle ctx.beginPath(); ctx.arc(shape.x,shape.y,shape.radius,0,Math.PI*2); ctx.fillStyle=shape.color; ctx.fill(); } }
How can I remove an image by clicking on it in Fabricjs?
I want to have two event handlers. one is when I click on the canvas based on mouseX an mouseY an image appears at a certain position (snap to grid). The second is when I click on the same image that appeared I want this image to be removed. The first event handler is implemented and work well the second doesn't work how can I make this happen? Also I guess that when I want to remove my image the first event handler triggers and add a new image it's a vicious circle. Here is my addImage() function: function addImage(spaceX, spaceY, mouseX, mouseY, sizeX, sizeY, url) { //spaceX/Y is the size of my grid //mouseX/Y are the coordinates of my mouse //sizeX/Y is the size of my canvas //url is the url of my image. fabric.Image.fromURL(url, function(oImg) { var divX = sizeX / spaceX; var resX = mouseX / sizeX * divX; var indW = Math.trunc(resX); //X index in the grid var divY = sizeY / spaceY; var resY = mouseY / sizeY * divY; var indH = Math.trunc(resY); //Y index in the grid oImg.top = indH * spaceY; oImg.left = indW * spaceX; oImg.scaleToWidth(spaceX); oImg.scaleToHeight(spaceY); oImg.selectable = false; //this is my non-working function to remove my image. oImg.on('click', function() { canvas.getActiveObject().remove(); }) canvas.add(oImg); }); }
oImg.on('mousedown', function (){ canvas.remove(this); }) use mousedown event for object. DEMO var canvas = new fabric.Canvas('canvas'); fabric.Image.fromURL('http://fabricjs.com/assets/pug.jpg', function(oImg) { oImg.on('mousedown', function (){ canvas.remove(this); }) canvas.add(oImg); }); canvas{ border:2px solid #000; } <script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script> <canvas id='canvas' width=500 height=400>
HTML5 Canvas Coordinates Chnged in chrome
I have a Sharepoint page in which i want to show a hierarchical diagram with boxes.According to my requirement those boxes should work as links to other sharepoint pages in the same site. Since sharepoint's default designer tools doesn't support designing such diagrams, I created a page with html5 canvas and the element i wanted inside that. Inside the canvas i created few boxes and lines to connect them.And i added texts inside the boxes.Then i used a mouse listener to check whether the mouse pointer hovers over a box and if so changed the pointer icon and the link to be redirected to. I added the canvas tag inside the sharepoint page by "Edit Source" and i added the javascript part using 'Embed Code' Now the code works perfectly in IE and Firefox. In chrome although the boxes,lines and text are drawn according to the coordinates i gave in the code but But when i hover the mouse over them it gives different coordinates for mouse listener in different browser sizes.So the mouse pointer doesn't change at correct locations ie: over the boxes. This doesn't happen in firefox or IE. They changes the mouse pointer when it comes over the boxes and links to the pages perfectly. Why does it change when i use chrome? And why does it only affect to the mouse listener coordinates. This is the code i used.(I have removed the repetitive parts which draws other boxes) Same in jsfiddle <canvas id="myCanvas" height="500" width="960" style="border: 1px solid;"><img src="" alt=""/> </canvas> <script> var canvas = document.getElementById("myCanvas"); var ctx; var rNBDX = 50; var rNBDY = 150; var rectWidth = 200; var rectHeight = 100; var cornerRadius = 20; var linkNBD="https://google.com"; var textNBD1 ="Google"; var linkHeight=20; var linkNum = 0; function draw(){ canvas = document.getElementById("myCanvas"); if(canvas.getContext){ ctx=canvas.getContext("2d"); //Drawing Lines ctx.lineWidth = 3; ctx.strokeStyle = '#000000'; ctx.moveTo(380, 100); ctx.lineTo(380, 125); ctx.stroke(); //Drawing Rectangles ctx.fillStyle="#0b61d0"; ctx.strokeStyle="#0b61d0"; ctx.lineJoin = "round"; ctx.lineWidth = cornerRadius; ctx.strokeRect(rNBDX+(cornerRadius/2), rNBDY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius); ctx.fillRect(rNBDX+(cornerRadius/2), rNBDY+(cornerRadius/2), rectWidth-cornerRadius, rectHeight-cornerRadius); //Drawing the Texts ctx.font='24px Segoe UI Light'; ctx.fillStyle = "#FFFFFF"; ctx.fillText(textNBD1,(rNBDX+rectWidth/2)-(ctx.measureText(textNBD1).width)/2,rNBDY+rectHeight/2); //Add mouse listeners canvas.addEventListener("mousemove", on_mousemove, false); canvas.addEventListener("click", on_click, false); } } function on_mousemove (ev) { var x, y; if (ev.layerX || ev.layerX == 0) { x = ev.layerX; y = ev.layerY; } x-=canvas.offsetLeft; y-=canvas.offsetTop; if(x>=rNBDX && x <= (rNBDX + rectWidth) && y>=rNBDY && y<= (rNBDY+rectHeight)){ document.body.style.cursor = "pointer"; linkNum=1; } else{ document.body.style.cursor = ""; } } function on_click(e) { switch (linkNum) { case 1: window.location = linkNBD; break; } } draw(); </script>
Try adjusting the mouse coordinates like this: function on_mousemove (ev) { var x, y, rect = canvas.getBoundingClientRect(); x = ev.clientX - rect.left + 1; y = ev.clientY - rect.top + 1; ... You will have to add (as in the example) the width of the left/top border though as getBoundingClientRect does not include those (you can calculate this dynamically using getComputedStyle and getPropertyValue of that for the borders).
Drawing at cursor position on canvas with JavaScript
I am trying to draw over a canvas by clicking and dragging the mouse. My problem is that, apart from the fact that the line has a very poor quality (I want a more pronounced border) it only respects the mouse position when this is at 0,0. As I move the mouse to the lower corner, the line increments its distance from it as much as when I am in the middle of the canvas, the line is already out of it. I have my code at: http://jsfiddle.net/ajTkP/12/ I will also post it here: var MDown = false; var Color = 'blue'; var Canvas = document.getElementById('canvas'); var Context = Canvas.getContext('2d'); Canvas.onselectstart = function() { return false; }; Canvas.unselectable = "on"; Canvas.style.MozUserSelect = "none"; Canvas.onmousedown = function(e) { MDown = true; Context.strokeStyle = Color; Context.lineWidth = 3; Context.lineCap = 'round'; Context.beginPath(); Context.moveTo(e.pageX - Position(Canvas).left, e.pageY - 5); } Canvas.onmouseup = function() { MDown = false; }; Canvas.onmousemove = function(e) { if (MDown) { Context.lineTo(e.pageX - Position(Canvas).left, e.pageY - 5); Context.stroke(); } } function Position(el) { var position = {left: 0, top: 0}; if (el) { if (!isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { position.left += el.offsetLeft; position.top += el.offsetTop; } } return position; } Thanks for your help!
You need to set an explicit width and height on the canvas. The default dimensions of a canvas are a width of 300 and a height of 150 (see the spec here). By setting the width and height via CSS you are just stretching the canvas. Either do: <canvas id="canvas" width="300" height="200"></canvas> or set the width/height via JavaScript: canvas.width = 300; canvas.height = 200; See the updated jsfiddle: http://jsfiddle.net/ajTkP/13/
It looks like jimr beat me to the punch about the canvas height and width. The poor quality of the line though is due to how you're drawing the line. You'll notice that you're calling stroke() on every onmousemove event. Keep in mind that it's keeping track of the path of the line from when you beginPath() to when you closePath(), so you're basically stroking the same line multiple times (every time your mouse moves). This is what's giving you the aliased (blocky-looking) lines, instead of the smooth anti-aliased lines you're expecting.
Mouse event is not defined
I have an HTML5 canvas on a web page, with a JavaScript function that draws an image on the canvas. I'm trying to add a mouse event to the image, so that when it is clicked, another JavaScript function that is called, which will update what is displayed on the canvas. When viewing the page in Firefox, nothing happens when I click the image that's displayed on the canvas. I'm using Firebug to try and see what's wrong, and it's given me the following message: mouse_event is not defined drawStartButton()index.html (line 107) startGame()index.html (line 64) (?)()index.html (line 1) event = load [Break On This Error] ...useX = (mouse_event.clientX-boundingBox.left) * (myGameCanvas.width/boundingBox.... index.html (line 107) The function I've used to draw the start button on the canvas, and mouse event I've added are below: function drawStartButton(){ image.onload = function(){ context.drawImage(image, 260.5, 60); }; image.src = "StartButton.png"; /** Now I need to add an event listener to the area of the canvas on on which the button image is displayed, in order to 'listen' for a click on the button */ var boundingBox = myGameCanvas.getBoundingClientRect(); var mouseX = (mouse_event.clientX-boundingBox.left) * (myGameCanvas.width/boundingBox.width); var mouseY = (mouse_event.clientY-boundingBox.top) * (myGameCanvas.height/boundingBox.height); var pixels = context.getImageData(mouseX, mouseY, 1, 1); } Basically, all I want to do, is that when the user clicks the button, the function below will be called, and will update the contents of the canvas: function drawLevelOneElements(){ var context = canvas.getContext("2d"); /* Draw the images for numbers 1-10.*/ var image1 = new Image(); /* Test that this code is being executed */ context.moveTo(300, 300); context.font = "11pt Calibri"; context.strokeStyle = "black"; context.strokeText("Testing",300, 300); /* End of test */ image1.onLoad = function(){ context.drawImage(image1, 50, 50); }; image1.src="1.png"; }
It seems I was able to solve this by putting the variables mouseX, mouseY and pixels in a function and setting the boundingBox.onmousemove property equal to this function: boundingBox.onmousemove = function(e){ var mouseX = e.pageX - this.offsetLeft; var mouseY = e.pageY - this.offsetTop; var pixels = context.getImageData(mouseX, mouseY, 1, 1); }