I'm trying to draw an image to the html5 canvas, but can't quite remember exactly how it's done.
I have the following html document:
<html>
<head>
<title>Understanding Business</title>
<section hidden>
<img id = "startButton" src = "startButton.png" alt = "Start Button" height = "40" width = "50" href = "javascript:drawFirstGameElements();">
</section>
</head>
<body onLoad = "drawStartButton()">
<h1>Understanding Business</h1>
<canvas id ="gameCanvas" width = "1000" height= "500" style = "border:1px solid #000000;">Your browser does not support the HTML5 canvas.</canvas>
<script type = "text/javascript">
window.onload = function(){
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
});
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
function drawStartButton(){
/*image.onload = function(){
context.drawImage(image, 500, 250);
};
image.src = "startButton.png"; */
var startButtonImg = document.getElementById("startButton");
context.drawImage(startButtonImg, 500, 250);
}
function drawFirstGameElements(){
drawDescriptionBox1;
}
function drawDescriptionBox1(){
context.moveTo(100, 400);
context.lineTo(150, 400);
context.lineTo(150, 450);
context.lineTo(100, 450);
context.lineTo(100, 400);
context.moveTo(110, 425);
context.strokeText("Asset");
}
//Function to get mouse coordinates on canvas
function getMouseCoords(gameCanvas, event){
var rect = gameCanvas.getBoundingClientRect(), root = document.documentElement;
//return mouse position
var mouseX = event.clientX - rect.top - root.scrollTop;
var mouseY = event.clientY - rect.left - root.scrollLeft;
return{
x: mouseX,
y: mouseY
};
}
}
For some reason, when I view the page in the browser, although the canvas is displayed, the "start button image" is not displayed on the canvas. It's been a while since I last used the HTML5 canvas, and I can't quite figure out what it is I'm doing wrong... Can someone point me in the right direction?
Cheers,
Edit
Code updated to reflect changes as suggested, but I'm still having the same problem. Any other suggestions?
Typo in your code :
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
}); // HERE ADD );
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
//HERE DELETE THE }
jsfiddle : http://jsfiddle.net/RqbPn/
Related
I am creating a service to stamp a pdf with an image using PDF.js and jQuery. I managed to create a draggable object using PDF.js but the the object leaves a trail of past objects when it is dragged.
I used context.clearRect(0, 0, canvas.width, canvas.height); to clear the past objects but it also clears the underlying PDF in the canvas.
How can I drag this object without affecting the underlying PDF?
Here is what I have done so far.
I am loading the PDF to the canvas using following code.
function loadPdfPreview(base64pdf){
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';
var loadingTask = pdfjsLib.getDocument({data: base64pdf});
loadingTask.promise.then(function (pdf) {
// Fetch the first page
pdf.getPage(1).then(function (page) {
var scale = 1.0;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
canvas = document.getElementById('pdf-canvas');
context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvasOffset = $("#pdf-canvas").offset();
offsetX = canvasOffset.left;
offsetY = canvasOffset.top;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(function () {
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, function (e) {
console.log(e);
});
});
});
}
I use following function to draw the object on top of the canvas after the pdf is loaded to the canvas.
function drawObjectFromBlueprint(blueprint) {
// drawing a draggable element inside the canvas
context.strokeStyle = "lightgray";
// Clearing the previous dummy objects
context.clearRect(0, 0, canvas.width, canvas.height);
// drawing the dummy object
context.beginPath();
context.moveTo(blueprint.x, blueprint.y);
context.lineTo(blueprint.right, blueprint.y);
context.lineTo(blueprint.right, blueprint.bottom);
context.lineTo(blueprint.x, blueprint.bottom);
context.closePath();
context.fillStyle = blueprint.fill;
context.fill();
context.stroke();
}
I handle the mouse move event using the following code.
function handleMouseMove(e) {
var mouseX = parseInt(e.clientX - offsetX1);
var mouseY = parseInt(e.clientY - offsetY1);
// always update the global blueprint
blueprint.x += (mouseX - lastX);
blueprint.y += (mouseY - lastY);
blueprint.right = blueprint.x + blueprint.width;
blueprint.bottom = blueprint.y + blueprint.height;
lastX = mouseX;
lastY = mouseY;
drawObjectFromBlueprint(blueprint);
}
And I listen to the mouse move event using following code.
$("#drawable-canvas").mousemove(function (e) {
handleMouseMove(e);
});
I want to re-draw the object without affecting the underlying PDF. Tried to load the PDF and re-draw the object by clearing the whole canvas, and it does not work as intended.
After two hours of trying figures out the way to do this. I used two canvases as #Nikolaus suggested on top of each other and used the bottom canvas to load the PDF and the top canvas for the drawing.
Following is my HTML:
<div class="col-md-12" id="pdfDisplay" style="display: none;">
<div id="pageContainer" class="pdfViewer nopadding" style="background-color:transparent">
<canvas id="pdf-canvas" style="border:1px solid black"></canvas>
<canvas id="drawable-canvas" style="border:1px solid black"></canvas>
</div>
</div>
Following is my CSS to place both canvases on top of each other.
#pageContainer { position: relative; }
#drawable-canvas { position: absolute; top: 0; left: 0; }
Following is my global Javascript variables:
var isUploading = false;
var base64pdf = "";
var initX = 50;
var initY = 50;
var initWidth = 200;
var initHeight = 150;
// blueprint options are in pixels
// this blueprint holds the latest values for the draggable object
// always update this global blueprint when making a change so it holds the latest values
var blueprint = {
x: initX,
y: initY,
width: initWidth,
height: initHeight,
right: (initX+initWidth), // x + width
bottom: (initY+initHeight), // y + height
fill: "skyblue"
};
var context = null;
var canvas = null;
var drawableContext = null;
var drawableCanvas = null;
var canvasOffset = null;
var offsetX = 0;
var offsetY = 0;
var canvasOffset1 = null;
var offsetX1 = 0;
var offsetY1 = 0;
var lastX = 0;
var lastY = 0;
var mouseIsDown = false;
Javascript function to listen to mouse up, down and movement as I only need to track the mouse movements when the object is dragged using the mouse.
$("#drawable-canvas").mousedown(function (e) {
var mouseX = parseInt(e.clientX - offsetX);
var mouseY = parseInt(e.clientY - offsetY);
lastX = mouseX;
lastY = mouseY;
mouseIsDown = true;
});
$("#drawable-canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#drawable-canvas").mouseup(function (e) {
mouseIsDown = false;
});
Javascript function to load the PDF to the bottom canvas.
function loadPdfPreview(base64pdf){
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.0.943/pdf.worker.min.js';
var loadingTask = pdfjsLib.getDocument({data: base64pdf});
loadingTask.promise.then(function (pdf) {
// Fetch the first page
pdf.getPage(1).then(function (page) {
var scale = 1.0;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
canvas = document.getElementById('pdf-canvas');
context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
canvasOffset = $("#pdf-canvas").offset();
offsetX = canvasOffset.left;
offsetY = canvasOffset.top;
canvasOffset1 = $("#drawable-canvas").offset();
offsetX1 = canvasOffset1.left;
offsetY1 = canvasOffset1.top;
// creating the drawable-canvas canvas for drawing purposes without affecting the pdf-canvas
// it has identical width and height and X and Y values
drawableCanvas = document.getElementById('drawable-canvas');;
drawableCanvas.height = viewport.height;
//bringing the drawable canvas up using z-index
drawableCanvas.style.zIndex = 1;
drawableCanvas.width = viewport.width;
drawableContext = drawableCanvas.getContext('2d');
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).then(function () {
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, function (e) {
console.log(e);
});
});
});
}
Handling Mouse movement(dragging the object):
function handleMouseMove(e) {
if (!mouseIsDown) {
return;
}
var mouseX = parseInt(e.clientX - offsetX1);
var mouseY = parseInt(e.clientY - offsetY1);
// always update the global blueprint
blueprint.x += (mouseX - lastX);
blueprint.y += (mouseY - lastY);
blueprint.right = blueprint.x + blueprint.width;
blueprint.bottom = blueprint.y + blueprint.height;
lastX = mouseX;
lastY = mouseY;
drawObjectFromBlueprint(blueprint);
console.log(blueprint);
}
Javascript function to draw the object on the drawable canvas, totally independent from the pdf-canvas.
function drawObjectFromBlueprint(blueprint) {
// drawing a draggable element inside the canvas
drawableContext.strokeStyle = "lightgray";
// Clearing the previous dummy objects
drawableContext.clearRect(0, 0, drawableCanvas.width, drawableCanvas.height);
// drawing the dummy object
drawableContext.beginPath();
drawableContext.moveTo(blueprint.x, blueprint.y);
drawableContext.lineTo(blueprint.right, blueprint.y);
drawableContext.lineTo(blueprint.right, blueprint.bottom);
drawableContext.lineTo(blueprint.x, blueprint.bottom);
drawableContext.closePath();
drawableContext.fillStyle = blueprint.fill;
drawableContext.fill();
drawableContext.stroke();
}
In the callback of the page.render method, the pdf page will be drawn on the canvas. You have to save the drawn image separately so that the original image does not disappear by dragging.
// maybe globalScope...?
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// your code
page.render(renderContext).then(function () {
// Save the original page image.
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
tempCtx.drawImage(canvas, 0, 0);
// creating the dummy object on success
drawObjectFromBlueprint(blueprint);
}, ...
Next, please modify it to draw with the original page image in drawObjectFromBlueprint.
function drawObjectFromBlueprint(blueprint) {
// draw original page image
context.drawImage(tempCanvas, 0, 0);
// drawing a draggable element inside the canvas
context.strokeStyle = "lightgray";
...
}
I am using PDF.js to show PDF in browser. PDF.js uses canvas to render PDF. I have js scripts that draws the lines on the canvas when user double clicks on the canvas. It also adds X check mark to remove the already drawn line.
based on my research i cannot simply just remove the line from the canvas because underneath pixels are gone when you draw something on it. To get it working i have to store lines and then clear canvas and re-load canvas and re-draw lines
Issue
I am not able to store canvas and restore canvas. When i click on X i was able to get lines re-drawn but canvas does not get restored. Canvas remains blank
Run the demo in full page
$(function () {
var $canvas = $("#myCanvas");
var canvasEl = $canvas.get(0);
var ctx = canvasEl.getContext("2d");
var lines = [];
var backupCanvas = document.createElement("canvas");
var loadingTask = pdfjsLib.getDocument('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf');
loadingTask.promise.then(function (doc) {
console.log("This file has " + doc._pdfInfo.numPages + " pages");
doc.getPage(1).then(page => {
var scale = 1;
var viewPort = page.getViewport(scale);
canvasEl.width = viewPort.width;
canvasEl.height = viewPort.height;
canvasEl.style.width = "100%";
canvasEl.style.height = "100%";
var wrapper = document.getElementById("wrapperDiv");
wrapper.style.width = Math.floor(viewPort.width / scale) + 'px';
wrapper.style.height = Math.floor(viewPort.height / scale) + 'px';
page.render({
canvasContext: ctx,
viewport: viewPort
});
storeCanvas();
});
});
function storeCanvas() {
backupCanvas.width = canvasEl.width;
backupCanvas.height = canvasEl.height;
backupCanvas.ctx = backupCanvas.getContext("2d");
backupCanvas.ctx.drawImage(canvasEl, 0, 0);
}
function restoreCanvas() {
ctx.drawImage(backupCanvas, 0, 0);
}
$canvas.dblclick(function (e) {
var mousePos = getMousePos(canvasEl, e);
var line = { startX: 0, startY: mousePos.Y, endX: canvasEl.width, endY: mousePos.Y, pageY: e.pageY };
lines.push(line);
drawLine(line, lines.length - 1);
});
function drawLine(line, index) {
// draw line
ctx.beginPath();
ctx.strokeStyle = '#df4b26';
ctx.moveTo(line.startX, line.startY);
ctx.lineTo(line.endX, line.endY);
ctx.closePath();
ctx.stroke();
// add remove mark
var top = line.pageY;
var left = canvasEl.width + 20;
var $a = $("<a href='#' class='w-remove-line'>")
.data("line-index", index)
.attr("style", "line-height:0")
.css({ top: top, left: left, position: 'absolute' })
.html("x")
.click(function () {
var index = $(this).data("line-index");
$(".w-remove-line").remove();
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// restore canvas
restoreCanvas();
lines.splice(index, 1);
for (var i = 0; i < lines.length; i++) {
drawLine(lines[i], i);
}
});
$("body").append($a);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
X: Math.floor(evt.clientX - rect.left),
Y: Math.floor(evt.clientY - rect.top),
};
}
});
canvas {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<b> Double Click on PDF to draw line and then click on X to remove lines</b>
<div id="wrapperDiv">
<canvas id="myCanvas"></canvas>
</div>
The PDF.js render() function is async so you need to store the canvas after the render has finished. Your code is firing storeCanvas() too early and storing a blank canvas. Easy fix, render() returns a promise so ...
page.render({
canvasContext: ctx,
viewport: viewPort
}).then( () => {
storeCanvas();
});
https://jsfiddle.net/fyLant01/1/
Reference: from https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L998
/**
* Begins the process of rendering a page to the desired context.
* #param {RenderParameters} params Page render parameters.
* #return {RenderTask} An object that contains the promise, which
* is resolved when the page finishes rendering.
*/
I have a HTML canvas in Ionic app.
<canvas id="canvas" color="{{ color }}" width="800" height="600" style="position:relative;"></canvas>
In this canvas, I am loading an image. Below is the code from controller
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img = new Image();
img.src = $stateParams.imageId;
img.onload = function() {
context.drawImage(img, 0, 0, canvas.width, canvas.height);
}
After the image is loaded, users need the ability to write on the image and circle/highlight certain areas of the image.
Doesn't HTML canvas provide this feature by default? Right now I am not able to annotate anything on the image. How can I achieve this?
You need to implement this yourself.
You can do it by hooking into the mouse click / move events. using your 2d context, draw small rectangle at the mouse's current position if the most moves and the left mouse button is down.
The effect is similar to a Windows paint pencil tool. Here's a simple example.
<html>
<head>
<style>
canvas{
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var isMouseDown = false;
canvas.onmousedown = function(e){
isMouseDown = true;
}
canvas.onmouseup = function(e){
isMouseDown = false;
}
canvas.onmousemove = function(e){
if(isMouseDown === false){
return;
}
var canvasPosition = canvas.getBoundingClientRect();
var x = e.clientX - canvasPosition.left;
var y = e.clientY - canvasPosition.top;
ctx.fillRect(x, y, 2, 2);
};
</script>
</body>
</html>
I am trying to create a pannable image viewer which also allows magnification. If the zoom factor or the image size is such that the image no longer paints over the entire canvas then I wish to have the area of the canvas which does not contain the image painted with a specified background color.
My current implementation allows for zooming and panning but with the unwanted effect that the image leaves a tiled trail after it during a pan operation (much like the cards in windows Solitaire when you win a game). How do I clean up my canvas such that the image does not leave a trail and my background rectangle properly renders in my canvas?
To recreate the unwanted effect set magnification to some level at which you see the dark gray background show and then pan the image with the mouse (mouse down and drag).
Code snippet added below and Plnkr link for those who wish to muck about there.
http://plnkr.co/edit/Cl4T4d13AgPpaDFzhsq1
<!DOCTYPE html>
<html>
<head>
<style>
canvas{
border:solid 5px #333;
}
</style>
</head>
<body>
<button onclick="changeScale(0.10)">+</button>
<button onclick="changeScale(-0.10)">-</button>
<div id="container">
<canvas width="700" height="500" id ="canvas1"></canvas>
</div>
<script>
var canvas = document.getElementById('canvas1');
var context = canvas.getContext("2d");
var imageDimensions ={width:0,height:0};
var photo = new Image();
var isDown = false;
var startCoords = [];
var last = [0, 0];
var windowWidth = canvas.width;
var windowHeight = canvas.height;
var scale=1;
photo.addEventListener('load', eventPhotoLoaded , false);
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function eventPhotoLoaded(e) {
imageDimensions.width = photo.width;
imageDimensions.height = photo.height;
drawScreen();
}
function changeScale(delta){
scale += delta;
drawScreen();
}
function drawScreen(){
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
canvas.onmousedown = function(e) {
isDown = true;
startCoords = [
e.offsetX - last[0],
e.offsetY - last[1]
];
};
canvas.onmouseup = function(e) {
isDown = false;
last = [
e.offsetX - startCoords[0], // set last coordinates
e.offsetY - startCoords[1]
];
};
canvas.onmousemove = function(e)
{
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
context.setTransform(1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]);
drawScreen();
}
</script>
</body>
</html>
You need to reset the transform.
Add context.setTransform(1,0,0,1,0,0); just before you clear the canvas and that will fix your problem. It sets the current transform to the default value. Then befor the image is draw set the transform for the image.
UPDATE:
When interacting with user input such as mouse or touch events it should be handled independently of rendering. The rendering will fire only once per frame and make visual changes for any mouse changes that happened during the previous refresh interval. No rendering is done if not needed.
Dont use save and restore if you don't need to.
var canvas = document.getElementById('canvas1');
var ctx = canvas.getContext("2d");
var photo = new Image();
var mouse = {}
mouse.lastY = mouse.lastX = mouse.y = mouse.x = 0;
mouse.down = false;
var changed = true;
var scale = 1;
var imageX = 0;
var imageY = 0;
photo.src = "http://www.html5rocks.com/static/images/cors_server_flowchart.png";
function changeScale(delta){
scale += delta;
changed = true;
}
// Turns mouse button of when moving out to prevent mouse button locking if you have other mouse event handlers.
function mouseEvents(event){ // do it all in one function
if(event.type === "mouseup" || event.type === "mouseout"){
mouse.down = false;
changed = true;
}else
if(event.type === "mousedown"){
mouse.down = true;
}
mouse.x = event.offsetX;
mouse.y = event.offsetY;
if(mouse.down) {
changed = true;
}
}
canvas.addEventListener("mousemove",mouseEvents);
canvas.addEventListener("mouseup",mouseEvents);
canvas.addEventListener("mouseout",mouseEvents);
canvas.addEventListener("mousedown",mouseEvents);
function update(){
requestAnimationFrame(update);
if(photo.complete && changed){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle="#333";
ctx.fillRect(0,0, canvas.width, canvas.height);
if(mouse.down){
imageX += mouse.x - mouse.lastX;
imageY += mouse.y - mouse.lastY;
}
ctx.setTransform(scale, 0, 0, scale, imageX,imageY);
ctx.drawImage(photo,0,0);
changed = false;
}
mouse.lastX = mouse.x
mouse.lastY = mouse.y
}
requestAnimationFrame(update);
canvas{
border:solid 5px #333;
}
<button onclick="changeScale(0.10)">+</button><button onclick="changeScale(-0.10)">-</button>
<canvas width="700" height="500" id ="canvas1"></canvas>
Nice Code ;)
You are seeing the 'tiled' effect in your demonstration because you are painting the scaled image to the canvas on top of itself each time the drawScreen() function is called while dragging. You can rectify this in two simple steps.
First, you need to clear the canvas between calls to drawScreen() and second, you need to use the canvas context.save() and context.restore() methods to cleanly reset the canvas transform matrix between calls to drawScreen().
Given your code as is stands:
Create a function to clear the canvas. e.g.
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
In the canavs.onmousemove() function, call clearCanvas() and invoke context.save() before redefining the transform matrix...
canvas.onmousemove = function(e) {
if(!isDown) return;
var x = e.offsetX;
var y = e.offsetY;
/* !!! */
clearCanvas();
context.save();
context.setTransform(
1, 0, 0, 1,
x - startCoords[0], y - startCoords[1]
);
drawScreen();
}
... then conditionally invoke context.restore() at the end of drawScreen() ...
function drawScreen() {
context.fillRect(0,0, windowWidth, windowHeight);
context.fillStyle="#333333";
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
/* !!! */
if (isDown) context.restore();
}
Additionally, you may want to call clearCanvas() before rescaling the image, and the canvas background could be styled with CSS rather than .fillRect() (in drawScreen()) - which could give a performance gain on low spec devices.
Edited in light of comments from Blindman67 below
See Also
Canvas.context.save : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
Canvas.context.restore : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
requestAnimationFrame : https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame
Paul Irish, requestAnimationFrame polyfill : http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
Call context.save to save the transformation matrix before you call context.fillRect.
Then whenever you need to draw your image, call context.restore to restore the matrix.
For example:
function drawScreen(){
context.save();
context.fillStyle="#333333";
context.fillRect(0,0, windowWidth, windowHeight);
context.restore();
context.drawImage(photo,0,0,imageDimensions.width*scale,imageDimensions.height*scale);
}
Also, to further optimize, you only need to set fillStyle once until you change the size of canvas.
I want to implement dragging of an image within a canvas. I want simplest code for that. So far I have seen a lot of examples but they have used complex ways of implementation. I want an example that is easy to learn and implement.
It's pretty difficult. You'll first need to write a function that can detect when you click a particular element. However, before we can do that, we must define what we mean by "element". Is it the product of a single draw instruction (e.g. a rectangle or arc), or something complex? (Imagine I wanted to draw a cat and make the entire cat draggable as a unit.)
A canvas is nothing but a collection of pixels. If you want your program to have an idea of "shapes" or even "collections of shapes treated as a unit" you'll need to implement them yourself as data structures external to the canvas itself. Once you have that, you can write an onmousedown handler that takes the x/y point clicked and determine what shape (if any) the click falls inside of (and if it falls inside of multiple shapes, check which has the foremost z-index). Then add an onmousemove handler that erases and redraws the shape on the canvas based on the information in the shape data object.
This is a moderately difficult problem with very difficult prerequisite problems (creating data structures that can describe a wide range of shapes as well as collections of shapes). I highly recommend you use a canvas drawing library that has already solved these problems. I use cake.js but there are loads of options available.
If you don't have to use the HTML5 canvas, jQuery UI is a lot simpler:
HTML:
<img class="drag-me" src="http://www.google.com/images/srpr/logo3w.png">
JavaScript:
$(function() {
$('.drag-me').draggable();
});
See it in action:
http://jsfiddle.net/flackend/TQzSe/
The jQuery UI API has a lot of options too to make it act how you want:
http://jqueryui.com/demos/draggable/
Plus, if it doesn't do what you need, it's easy to implement yourself. Post here if you need help with that.
jsfiddle.net/Zevan/QZejF/5 This may help you.
<html>
<head>
<title>Test Page</title>
<script type="text/javascript" src="jquery.js"></script>
</head>
<body>
<canvas id="c" width = "500" height = "500" ></canvas>
<script type="text/javascript">
var canvas = $("#c");
var c = canvas[0].getContext("2d");
//var path = "http://wonderfl.net/images/icon/e/ec/ec3c/ec3c37ba9594a7b47f1126b2561efd35df2251bfm";
var path = "blue.jpg";
var path2 = "purple.jpg";
var image1 = new DragImage(path, 200, 100);
var image2 = new DragImage(path2, 300, 100);
var loop = setInterval(function() {
c.fillStyle = "gray";
c.fillRect(0, 0, 500, 500);
image1.update();
image2.update();
}, 30);
var mouseX = 0,
mouseY = 0;
var mousePressed = false;
var dragging = false;
canvas.mousemove(function(e) {
mouseX = e.offsetX;
mouseY = e.offsetY;
})
$(document).mousedown(function() {
mousePressed = true;
}).mouseup(function() {
mousePressed = false;
dragging = false;
});
function DragImage(src, x, y) {
var that = this;
var startX = 0,
startY = 0;
var drag = false;
this.x = x;
this.y = y;
var img = new Image();
img.src = src;
this.update = function() {
if (mousePressed ) {
var left = that.x;
var right = that.x + img.width;
var top = that.y;
var bottom = that.y + img.height;
if (!drag) {
startX = mouseX - that.x;
startY = mouseY - that.y;
}
if (mouseX < right && mouseX > left && mouseY < bottom && mouseY > top) {
if (!dragging){
dragging = true;
drag = true;
}
}
} else {
drag = false;
}
if (drag) {
that.x = mouseX - startX;
that.y = mouseY - startY;
}
c.drawImage(img, that.x, that.y);
}
}
</script>
</body>
</html>