Image in canvas leaves a tiled trail when panned - javascript

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.

Related

How to draw a single translucent line of varying width? [duplicate]

I'm drawing with a semi-transparent color (e.g. rgba(255,0,0,0.5)) over a canvas. When I draw over the same region again, the transparency value seems to be adding up resulting in an opaque color. Is there a way to keep the transparency value of the source (the semi-transparent color I'm using to draw) instead?
Draw to an offscreen canvas with alpha = 1.Then just render the off screen canvas to the display canvas with the ctx.globalAlpha set at whatever value you wish. That way you can draw till the sun goes down without adding anything to the alpha. It is also easy to change the alpha after you have drawn if needed.
Additional note
If you have other content included in the image, you will have to keep that on another layer as well because this method relies on the onscreen canvas being reset to a desired starting state for each update. In the snippet this is just a clearRect call. But can just as well be replaced with another existing layer, or a combination there of.
The browser can easily handle many off screen canvases, I just finished a job that had 60 full screen canvas stacked on top of each other (Note your GPU needs to have the RAM to hold the images or it's too slow) and Chrome did not even blink. Firefox and IE are just as capable.
UPDATE
I have added a snippet to demonstrate what I mean. Details in the comments of the relevant code at the bottom. Just a simple drawing interface.
// get canvas set up mouse and do the other things
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
var mouse = {
x:0,
y:0,
buttonLastRaw:0, // user modified value
buttonRaw:0,
over:false,
};
function mouseMove(event){
mouse.x = event.offsetX; mouse.y = event.offsetY;
if(mouse.x === undefined){ mouse.x = event.clientX; mouse.y = event.clientY;}
if(event.type === "mousedown"){ mouse.buttonRaw = 1;
}else if(event.type === "mouseup"){mouse.buttonRaw = 0;
}else if(event.type === "mouseout"){ mouse.buttonRaw = 0; mouse.over = false;
}else if(event.type === "mouseover"){ mouse.over = true; }
event.preventDefault();
}
canvas.addEventListener('mousemove',mouseMove);
canvas.addEventListener('mousedown',mouseMove);
canvas.addEventListener('mouseup' ,mouseMove);
canvas.addEventListener('mouseout' ,mouseMove);
canvas.addEventListener('mouseover' ,mouseMove);
canvas.addEventListener("contextmenu", function(e){ canvas.preventDefault();}, false);
// create off screen layer that we will draw to
var layer1 = document.createElement("canvas");
layer1.width = w; // same size as the onscreen canvas
layer1.height = h;
layer1.ctx = layer1.getContext("2d");
// set up drawing settings
layer1.ctx.lineCap = "round";
layer1.ctx.lineJoin = "round";
layer1.ctx.lineWidth = 16;
layer1.ctx.globalAlpha = 1; // draw to this layer with alpha set to 1;
// set up onscreen canvas
ctx.globalAlpha = 1;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "24px Arial black";
var instructions = true;
// colours to show that different layer are overwriting each other
var colours = "#F00,#FF0,#0F0,#0FF,#00F,#F0F".split(",");
var currentCol = 0;
// update on animation frame
function update(){
ctx.clearRect(0,0,w,h); // clear onscreen
var c = layer1.ctx; // short cut to the later1 context
if(mouse.buttonRaw){ // if mouse down
if(mouse.lastx === undefined){ // is this start of drawing stroke
mouse.lastx = mouse.x; // set up drawing stroke
mouse.lasty = mouse.y;
c.strokeStyle = colours[currentCol % colours.length];
currentCol += 1;
instructions = false; // tuen of the instructions as they have worked it out
ctx.globalAlpha = 0.6; // should do this near layering but lasy
}
// draw the dragged stroke to the offscreen layer
c.beginPath();
c.moveTo(mouse.lastx,mouse.lasty);
c.lineTo(mouse.x,mouse.y);
c.stroke();
mouse.lastx = mouse.x;
mouse.lasty = mouse.y;
}else{ // if the mouse button up show drawing brush and instructions if
// nothing has happened yet
mouse.lastx = undefined; // using this as a semaphore for drag start
ctx.fillStyle = colours[currentCol%colours.length];
ctx.globalAlpha = 0.6; // the brush will compound the alpha
// this can be avoided by drawing it onto
// the offscreen layer, but you will need
// another layer or some temp store to
// protect the offscreen layer. Again I am
// to lazy to implement that right now.
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,8,0,Math.PI*2);
ctx.fill();
if(instructions){ // show instructions if needed
ctx.fillStyle = "blue";
ctx.globalAlpha = 1;
ctx.fillText("Click drag mouse to draw",250,60);
}
}
// draw the offscreen layer onto the onscreen canvas at the alpha wanted
ctx.drawImage(layer1,0,0);
requestAnimationFrame(update); // do it all again.
}
mouse.lastx; // needed to draw lines.
mouse.lasty;
update()
body { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAlUlEQVRYR+2WsQ0EIQwEbXpAopbrAZESUhQ1AAkBXVEDAb6jBRP8B0s+yJpklnvvstYizRMRyjmTtVaD096buNYqzjnVB3NOaq3RGEPFhxBwAAzAAAzAAAz8gYFSijCzqmYH+ngyxqj4k3N+nkduep5Sops9wV+T5abnMUa62RM4AAZgAAZgAAZ+b8B7Lzc9PzW82RMvg0g+JLdy9xIAAAAASUVORK5CYII=');
background-size: 32px 32px;
background-repeat: repeat;
}
.canC { width:500px; height:600px;}
<canvas class="canC" id="canV" width=500 height=600></canvas>

How to reset transparency when drawing overlapping content on HTML canvas?

I'm drawing with a semi-transparent color (e.g. rgba(255,0,0,0.5)) over a canvas. When I draw over the same region again, the transparency value seems to be adding up resulting in an opaque color. Is there a way to keep the transparency value of the source (the semi-transparent color I'm using to draw) instead?
Draw to an offscreen canvas with alpha = 1.Then just render the off screen canvas to the display canvas with the ctx.globalAlpha set at whatever value you wish. That way you can draw till the sun goes down without adding anything to the alpha. It is also easy to change the alpha after you have drawn if needed.
Additional note
If you have other content included in the image, you will have to keep that on another layer as well because this method relies on the onscreen canvas being reset to a desired starting state for each update. In the snippet this is just a clearRect call. But can just as well be replaced with another existing layer, or a combination there of.
The browser can easily handle many off screen canvases, I just finished a job that had 60 full screen canvas stacked on top of each other (Note your GPU needs to have the RAM to hold the images or it's too slow) and Chrome did not even blink. Firefox and IE are just as capable.
UPDATE
I have added a snippet to demonstrate what I mean. Details in the comments of the relevant code at the bottom. Just a simple drawing interface.
// get canvas set up mouse and do the other things
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;
var mouse = {
x:0,
y:0,
buttonLastRaw:0, // user modified value
buttonRaw:0,
over:false,
};
function mouseMove(event){
mouse.x = event.offsetX; mouse.y = event.offsetY;
if(mouse.x === undefined){ mouse.x = event.clientX; mouse.y = event.clientY;}
if(event.type === "mousedown"){ mouse.buttonRaw = 1;
}else if(event.type === "mouseup"){mouse.buttonRaw = 0;
}else if(event.type === "mouseout"){ mouse.buttonRaw = 0; mouse.over = false;
}else if(event.type === "mouseover"){ mouse.over = true; }
event.preventDefault();
}
canvas.addEventListener('mousemove',mouseMove);
canvas.addEventListener('mousedown',mouseMove);
canvas.addEventListener('mouseup' ,mouseMove);
canvas.addEventListener('mouseout' ,mouseMove);
canvas.addEventListener('mouseover' ,mouseMove);
canvas.addEventListener("contextmenu", function(e){ canvas.preventDefault();}, false);
// create off screen layer that we will draw to
var layer1 = document.createElement("canvas");
layer1.width = w; // same size as the onscreen canvas
layer1.height = h;
layer1.ctx = layer1.getContext("2d");
// set up drawing settings
layer1.ctx.lineCap = "round";
layer1.ctx.lineJoin = "round";
layer1.ctx.lineWidth = 16;
layer1.ctx.globalAlpha = 1; // draw to this layer with alpha set to 1;
// set up onscreen canvas
ctx.globalAlpha = 1;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = "24px Arial black";
var instructions = true;
// colours to show that different layer are overwriting each other
var colours = "#F00,#FF0,#0F0,#0FF,#00F,#F0F".split(",");
var currentCol = 0;
// update on animation frame
function update(){
ctx.clearRect(0,0,w,h); // clear onscreen
var c = layer1.ctx; // short cut to the later1 context
if(mouse.buttonRaw){ // if mouse down
if(mouse.lastx === undefined){ // is this start of drawing stroke
mouse.lastx = mouse.x; // set up drawing stroke
mouse.lasty = mouse.y;
c.strokeStyle = colours[currentCol % colours.length];
currentCol += 1;
instructions = false; // tuen of the instructions as they have worked it out
ctx.globalAlpha = 0.6; // should do this near layering but lasy
}
// draw the dragged stroke to the offscreen layer
c.beginPath();
c.moveTo(mouse.lastx,mouse.lasty);
c.lineTo(mouse.x,mouse.y);
c.stroke();
mouse.lastx = mouse.x;
mouse.lasty = mouse.y;
}else{ // if the mouse button up show drawing brush and instructions if
// nothing has happened yet
mouse.lastx = undefined; // using this as a semaphore for drag start
ctx.fillStyle = colours[currentCol%colours.length];
ctx.globalAlpha = 0.6; // the brush will compound the alpha
// this can be avoided by drawing it onto
// the offscreen layer, but you will need
// another layer or some temp store to
// protect the offscreen layer. Again I am
// to lazy to implement that right now.
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,8,0,Math.PI*2);
ctx.fill();
if(instructions){ // show instructions if needed
ctx.fillStyle = "blue";
ctx.globalAlpha = 1;
ctx.fillText("Click drag mouse to draw",250,60);
}
}
// draw the offscreen layer onto the onscreen canvas at the alpha wanted
ctx.drawImage(layer1,0,0);
requestAnimationFrame(update); // do it all again.
}
mouse.lastx; // needed to draw lines.
mouse.lasty;
update()
body { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAlUlEQVRYR+2WsQ0EIQwEbXpAopbrAZESUhQ1AAkBXVEDAb6jBRP8B0s+yJpklnvvstYizRMRyjmTtVaD096buNYqzjnVB3NOaq3RGEPFhxBwAAzAAAzAAAz8gYFSijCzqmYH+ngyxqj4k3N+nkduep5Sops9wV+T5abnMUa62RM4AAZgAAZgAAZ+b8B7Lzc9PzW82RMvg0g+JLdy9xIAAAAASUVORK5CYII=');
background-size: 32px 32px;
background-repeat: repeat;
}
.canC { width:500px; height:600px;}
<canvas class="canC" id="canV" width=500 height=600></canvas>

HTML5 canvas zoom my drawings

Hello i create program like a paint on HTML5 canvas. I have problem i need create few tools drawing and zoom. I don't have idea how to create zoom without delay. Drawing example: http://jsfiddle.net/x5rrvcr0/
How i can zooming my drawings?
drawing code:
<style>
canvas {
background-color: #CECECE;
}
html, body {
background-color: #FFFFFF;
}
</style>
<script>
$(document).ready(function () {
var paintCanvas = document.getElementById("paintCanvas");
var paintCtx = paintCanvas.getContext("2d");
var size = 500;
paintCanvas.width = size;
paintCanvas.height = size;
var draw = false;
var prevMouseX = 0;
var prevMouseY = 0;
function getMousePos(canvas, evt) {
evt = evt.originalEvent || window.event || evt;
var rect = canvas.getBoundingClientRect();
if (evt.clientX !== undefined && evt.clientY !== undefined) {
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
}
$("#paintCanvas").on("mousedown", function(e) {
draw = true;
var coords = getMousePos(paintCanvas);
prevMouseX = coords.x;
prevMouseY = coords.y;
});
$("#paintCanvas").on("mousemove", function(e) {
if(draw) {
var coords = getMousePos(paintCanvas, e);
paintCtx.beginPath();
paintCtx.lineWidth = 10;
paintCtx.strokeStyle = "#000000";
paintCtx.moveTo(prevMouseX, prevMouseY);
paintCtx.lineTo(coords.x, coords.y);
paintCtx.stroke();
prevMouseX = coords.x;
prevMouseY = coords.y;
}
});
$("#paintCanvas").on("mouseup", function(e) {
draw = false;
});
});
</script>
<body>
<canvas id="paintCanvas"></canvas>
</body>
If you want to keep the pixelated effect in the zoom, you need to draw on a temp canvas, then only after copy that temp canvas to the main screen.
You no longer need to zoom in the temp canvas, just draw on 1:1 scale always. When copying to the view canvas, then you apply the zoom (and maybe translate) that you want.
Keep in mind that drawings are anti-aliased, so you when zooming you will see some shades of grey when drawing in black, for instance.
I kept the recording code of #FurqanZafar since it is a good idea to record things in case you want to perform undo : in that case just delete the last record entry and redraw everything.
http://jsfiddle.net/gamealchemist/x5rrvcr0/4/
function updatePaintCanvas() {
paintContext.clearRect(0, 0, paintContext.canvas.width, paintContext.canvas.height);
paintContext.save();
paintContext.translate(cvSize * 0.5, cvSize * 0.5);
paintContext.scale(scale, scale);
paintContext.drawImage(drawCanvas, -cvSize * 0.5, -cvSize * 0.5);
paintContext.restore();
}
Heres the updated fiddle: http://jsfiddle.net/x5rrvcr0/2/ with basic zooming functionality
If you draw multiple paths on mouse move then your sketch will appear broken or disconnected, instead you should only stroke a single path until "mouseup" event.
You can then store these paths in an array and later redraw them at different zoom levels:
function zoom(context, paths, styles, scale) {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.save();
applyStyles(context, styles);
scaleFromCenter(context, scale);
for (var i = 0; i < paths.length; i++) {
context.beginPath();
context.moveTo(paths[i][0].x, paths[i][0].y);
for (var j = 1; j < paths[i].length; j++)
context.lineTo(paths[i][j].x, paths[i][j].y);
context.stroke();
}
context.restore();
};

Tutorial on isPointinPath not copying to my computer

I found this helpful tutorial
http://www.rgraph.net/blog/2013/february/an-example-of-the-html5-canvas-ispointinpath-function.html
i copied it into my own text editor and nothing happens when I open it. I changed it by adding a declaration
<!DOCTYPE html>
<html lang="en">
<head>
<title>exampleMouseOver</title>
</head>
<script>
window.onload = function (e)
{
var canvas = document.getElementById('cvs');
var context = canvas.getContext('2d');
// Draw the rectangle
context.beginPath();
context.rect(50,50,100,100);
context.fill();
context.fillStyle = 'red';
// Draw the circle
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
context.fill();
context.fillStyle = 'green';
// Draw the shape
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
context.fill();
canvas.onmousemove = function (e)
{
var canvas = e.target;
var context = canvas.getContext('2d');
// This gets the mouse coordinates (relative to the canvas)
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
// Replay the rectangle path (no need to fill() it) and test it
context.beginPath();
context.rect(50,50,100,100);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the circle path (no need to fill() it) and test it
context.beginPath();
context.arc(450,175, 50, 0,2 * Math.PI, false);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Replay the irregular shape path (no need to fill() it) and test it
context.beginPath();
context.moveTo(250,100);
context.lineTo(350,175);
context.lineTo(325,215);
context.lineTo(185,195);
if (context.isPointInPath(mouseX, mouseY)) {
canvas.style.cursor = 'pointer';
return;
}
///////////////////////////////////////////////////////////////
// Return the cursor to the default style
canvas.style.cursor = 'default';
}
}
</script>
</html>
You'll need a body element and a canvas element. Also your script element needs to be inside either your head element or your body element.
The following is what the sample was using, but did not include in their sample code:
<body>
<canvas id="cvs" width="600" height="250" style="border: 1px solid gray; cursor: pointer;">[No canvas support]</canvas>
</body>
Edit: Additionally the code is calling "RGraph.getMouseXY(e)", which is in a library file that you are not referencing. You can either add a reference to that library or get the mouse position yourself.
If you want to use other parts of the RGraph library, for drawing charts, you should add the library. To add the library you should follow the instructions on the RGraph site related to downloading and starting with RGraph (http://www.rgraph.net/docs/starting-with-rgraph.html).
If this was just a sample that happened to do what you wanted to do, you should get the mouse position yourself. You can do this by changing these lines:
var mouseXY = RGraph.getMouseXY(e);
var mouseX = mouseXY[0];
var mouseY = mouseXY[1];
to these:
var mouseX = e.clientX - canvas.getBoundingClientRect().left;
var mouseY = e.clientY - canvas.getBoundingClientRect().top;
This may not be the most robust solution, but it should suffice for your purposes. Essentially you are getting the mouse position in the window, then subtracting the top-left of the canvas in the window, so that you are left with the mouse position in the canvas.

scale mouse coordinates on document relative to HTML5 canvas

I have a HTML5 canvas of a certain size on the page
<canvas id="canvas" width="200" height="100" style="border:1px solid #000000;">
Right now, the canvas is painted when the mouse is dragged over it (i.e. you click first(not necessarily inside the canvas) then start dragging the mouse over it without releasing) Script below;
$(function () {
var c = document.getElementById("canvas");
var context = c.getContext("2d");
var clickX = new Array();
var clickY = new Array();
var clickDrag = new Array();
var paint;
var $canvas=$("#canvas");
$(this).mousedown(function (e) {
paint = true;
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop);
redraw();
});
$(this).mousemove(function (e) {
if (paint) {
addClick(e.pageX - $canvas[0].offsetLeft, e.pageY - $canvas[0].offsetTop, true);
redraw();
}
});
$(this).mouseup(function (e) {
paint = false;
});
$(this).mouseleave(function (e) {
paint = false;
});
function addClick(x, y, dragging) {
clickX.push(x);
clickY.push(y);
clickDrag.push(dragging);
}
function redraw() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // Clears the canvas
context.strokeStyle = "#000000";
context.lineJoin = "round";
context.lineWidth = 2;
for (var i = 0; i < clickX.length; i++) {
context.beginPath();
if (clickDrag[i] && i) {
context.moveTo(clickX[i - 1], clickY[i - 1]);
} else {
context.moveTo(clickX[i] - 1, clickY[i]);
}
context.lineTo(clickX[i], clickY[i]);
context.closePath();
context.stroke();
}
}
});
What I need now is a way to scale all the mouse coordinates on document during the document's mouse events to the relative coordinates inside the canvas.
so that no matter wherever you drag the mouse on the document it is drawn inside the canvas (in relatively small size of course). Any Idea how to achieve this?
http://jsfiddle.net/umwc5/3/
Why I need this?
It is for a signature application, When a user scribbles the signature using a tablet on a page(without seeing the page!) the entire signature is to be registered in a small canvas.
Update
The final working fiddle
The most important thing you were missing here was to multiply by the canvas/screen ratio.
First calculate the ratio:
var docToCanv = Math.min($canvas[0].width / $('body').width(), $canvas[0].height/$('body').height());
Then use it like this:
addClick(e.pageX*docToCanv, e.pageY*docToCanv);
Depending on the additional behavior you want, you may need to adjust the location a bit, but this should get you past the current issue you are having.
Demo

Categories

Resources