I need to do an action onclick of a particular (point) or a rectangle in a canvas.
Example:
$(document).ready(function(){
var canvas = $('#myCanvas').get(0);
if (!canvas.getContext) { return; }
var ctx = canvas.getContext('2d');
ctx.fillRect(150,140,8,8);
ctx.fillRect(200,120,8,8);
ctx.fillRect(200,160,8,8);
});
I need to connect two points with a line and another two points with a curve using javascript .How can i do this?
You need to maintain the regions yourselves. There are no objects on a canvas, only pixels and the browser does not know anything about it.
Demo here
You can do something like this (simplified):
// define the regions - common for draw/redraw and check
var rect1 = [150,140,8,8];
var rect2 = [200,120,8,8];
var rect3 = [200,160,8,8];
var regions = [rect1, rect2, rect3];
Now on your init you can use the same array to render all the rectangles:
$(document).ready(function(){
var canvas = $('#myCanvas').get(0);
if (!canvas.getContext) { return; }
var ctx = canvas.getContext('2d');
//use the array also to render the boxes
for (var i = 0, r; r = regions[i]; i++) {
ctx.fillRect(r[0],r[1],r[2],r[3]);
}
});
On the click event you check the array to see if the mouse coordinate (corrected for canvas) is inside any of the rectangles:
$('#myCanvas').on('click', function(e){
var pos = getMousePos(this, e);
// check if we got a hit
for (var i = 0, r; r = regions[i]; i++) {
if (pos.x >= r[0] && pos.x <= (r[0] + r[2]) &&
pos.y >= r[1] && pos.y <= (r[1] + r[3])) {
alert('Region ' + i + ' was hit');
}
}
});
//get mouse position relative to canvas
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
Also remember to redraw the canvas if the window is re-sized or for other reason clears the canvas (browser dialogs etc.).
To connect the boxes you need to store the first hit position and when you get a second hit draw a line between them.
Demo with lines here
Add to the global vars and also make canvas and context available from global (see fiddle for related modifications in onready):
var x1 = -1, y1;
var canvas = myCanvas;
var ctx = canvas.getContext('2d');
And in the click event:
$('#myCanvas').on('click', function(e){
var pos = getMousePos(this, e);
for (var i = 0, r; r = regions[i]; i++) {
if (pos.x >= r[0] && pos.x <= (r[0] + r[2]) &&
pos.y >= r[1] && pos.y <= (r[1] + r[3])) {
//first hit? then store the coords
if (x1 === -1) {
x1 = pos.x;
y1 = pos.y;
} else {
//draw line from first point to this
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
x1 = -1; //reset (or keep if you want continuous lines).
};
}
}
});
Related
I have a array of abstract shapes coordinates. I want to split that shape into a number of squares and get their coordinates like shown in the image shown below:
For that i have tried is:
Find the extreme left and top coordinates and further with the loop find the relative position of the square coordinates. But this includes the square on outer side of it.
My Code:
var intersections = [];
var y = -9999;
var x = -9999;
var ym = 9999;
var xm = 9999;
var boundry = [];
//loop to find the extreme cordinates
for(var k=0; k <points.length; k++){
var pt = [];
pt.push(points[k].x);
pt.push(points[k].y);
boundry.push(pt);
if(points[k].x > x){
x = points[k].x;
}
if(points[k].y > y){
y = points[k].y;
}
if(points[k].x < xm){
xm = points[k].x;
}
if(points[k].y < ym){
ym = points[k].y;
}
}
for(var o = ym; o < y; o = o + split_diff){
console.log('o',o);
for(var i = xm; i < x; i = i + split_diff){
//divided points!
var isInside = checkIn(o,x);
if(isInside){
intersections.push(o,x);
}
}
}
What i want
Is there any other way to achieve same without getting the outer squares. like any external library or so. Thank you for the help in advance.
function split_polygon(points){
// above code here
}
//array of all the intersection cordinates
To cope with all kinds of polygons (even non-convex), you could scan across a horizontal line where that line crosses the polygon and so determine which are the line segment(s) of that line that are interior to the polygon.
Once you have that it is rather easy to pick the points on those line segments.
Repeat for several horizontal lines (at the required vertical distance).
Here is a snippet that allows you to click on a canvas to define a polygon. To create the final closing edge of the polygon, click the button. At that time the algorithm to create that dot-grid will also kick in.
function intersectionY(edge, y) {
const [[x1, y1], [x2, y2]] = edge;
const dir = Math.sign(y2 - y1);
if (dir && (y1 - y)*(y2 - y) <= 0) return { x: x1 + (y-y1)/(y2-y1) * (x2-x1), dir };
}
function tilePolygon(points, tileSize){
const minY = Math.min(...points.map(p => p[1]));
const maxY = Math.max(...points.map(p => p[1]));
const minX = Math.min(...points.map(p => p[0]));
const gridPoints = [];
for (let y = minY; y <= maxY; y += tileSize) {
// Collect x-coordinates where polygon crosses this horizontal line (y)
const cuts = [];
let prev = null;
for (let i = 0; i < points.length; i++) {
const cut = intersectionY([points[i], points[(i+1)%points.length]], y);
if (!cut) continue;
if (!prev || prev.dir !== cut.dir) cuts.push(cut);
prev = cut;
}
if (prev && prev.dir === cuts[0].dir) cuts.pop();
// Now go through those cuts from left to right toggling whether we are in/out the polygon
let dirSum = 0;
let startX = null;
for (let cut of cuts.sort((a, b) => a.x - b.x)) {
dirSum += cut.dir;
if (dirSum % 2) { // Entering polygon
if (startX === null) startX = cut.x;
} else if (startX !== null) { // Exiting polygon
// Genereate grid points on this horizontal line segement
for (let x = minX + Math.ceil((startX - minX) / tileSize)*tileSize; x <= cut.x; x += tileSize) {
gridPoints.push([x, y]);
}
startX = null;
}
}
}
return gridPoints;
}
function controller() { // I/O for this interactive snippet
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const points = [];
function drawLines(points, close, color = "black") {
ctx.beginPath();
ctx.moveTo(...points[0]);
for (let point of points) ctx.lineTo(...point);
if (close) ctx.closePath();
ctx.strokeStyle = color;
ctx.stroke();
}
function drawPoint(x, y, color = "black") {
ctx.fillStyle = color;
ctx.fillRect(x, y, 1, 1);
}
document.querySelector("#polclose").addEventListener("click", function () {
const gridPoints = tilePolygon(points, 10);
// Output:
drawLines(points, true);
gridPoints.forEach(p => drawPoint(...p, "red"));
});
canvas.addEventListener("click", function(e) {
const x = e.clientX - this.offsetLeft;
const y = e.clientY - this.offsetTop;
points.push([x, y]);
drawLines(points, false);
});
canvas.addEventListener("mousemove", function(e) {
const x = e.clientX - this.offsetLeft;
const y = e.clientY - this.offsetTop;
document.querySelector("#output").textContent = x + ", " + y;
});
}
controller();
canvas { border: 1px solid }
Click at different coordinates to create a polygon:<br>
<canvas></canvas><br><button id="polclose">Complete Polygon</button>
<div id="output"></div>
I'm trying to build an html canvas pad that will allow a user to drag and drop a dot on the pad, which will then return two values (one for Y axis, and one for Y axis), which I can use to trigger effects using the web audio API.
I've already sorted out the web Audio API portion of the problem.
The User:
Clicks and drags the dot to anywhere on the X/Y grid
On Drop we will have an X & Y value (perhaps in hidden range inputs), that trigger eventListeners.
The X value eventListener affects the wet/dry of the delay effect
The Y value eventListener affects the delay_time of the delay effect
so far I've been able to create and render the canvas and circle, and add event listeners on the svg element and the window. With the idea being that I can detect when an event occurs inside the canvas and when that click event leaves the canvas.
// Draw SVG pad
function drawDelayPad() {
var canvas = document.getElementById('delayPad');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var rectangle = new Path2D();
rectangle.rect(1, 1, 200, 200);
var circle = new Path2D();
circle.moveTo(150, 150);
circle.arc(100, 35, 10, 0 , 2 * Math.PI);
ctx.stroke(rectangle);
ctx.fill(circle);
}
}
// Listener on canvas
var canvas = document.getElementById('delayPad');
canvas.addEventListener("mousedown", function(){
console.log("click inside our canvas")
})
// Listener on document to check if we're outside the canvas
window.addEventListener("mouseup", function(){
console.log("outside our canvas")
});
So I think what I need to determine now is that when a click event does occur inside of the canvas, how far it is from the cirle, and if it does fall within the bounds of the circle, I should redraw it as long as the mousedown event is active.
Any help would be greatly appreciated.
I've found a nice little solution that kind of confirms my suspicions surrounding a hit counter! All credit really goes to rectangleWorld since I was for the most part just able to modify the example they had available.
Here's a codepen
// Draw SVG pad
function canvasApp(canvasID) {
var theCanvas = document.getElementById(canvasID);
var context = theCanvas.getContext("2d");
init();
var numShapes;
var shapes;
var dragIndex;
var dragging;
var mouseX;
var mouseY;
var dragHoldX;
var dragHoldY;
function init() {
numShapes = 1;
shapes = [];
makeShapes();
drawScreen();
theCanvas.addEventListener("mousedown", mouseDownListener, false);
}
function makeShapes() {
var i;
var tempX;
var tempY;
var tempRad;
var tempR;
var tempG;
var tempB;
var tempColor;
var tempShape;
for (i = 0; i < numShapes; i++) {
// My canvas element is 240x240
tempRad = 10;
tempX = 0 + tempRad;
tempY = 240 - tempRad;
tempR = Math.floor(Math.random() * 255);
tempG = Math.floor(Math.random() * 255);
tempB = Math.floor(Math.random() * 255);
tempColor = "rgb(" + tempR + "," + tempG + "," + tempB + ")";
tempShape = {
x: tempX,
y: tempY,
rad: tempRad,
color: tempColor
};
shapes.push(tempShape);
}
}
function mouseDownListener(evt) {
var i;
//We are going to pay attention to the layering order of the objects so that if a mouse down occurs over more than object,
//only the topmost one will be dragged.
var highestIndex = -1;
//getting mouse position correctly, being mindful of resizing that may have occured in the browser:
var bRect = theCanvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left) * (theCanvas.width / bRect.width);
mouseY = (evt.clientY - bRect.top) * (theCanvas.height / bRect.height);
//find which shape was clicked
for (i = 0; i < numShapes; i++) {
if (hitTest(shapes[i], mouseX, mouseY)) {
dragging = true;
if (i > highestIndex) {
//We will pay attention to the point on the object where the mouse is "holding" the object:
dragHoldX = mouseX - shapes[i].x;
dragHoldY = mouseY - shapes[i].y;
highestIndex = i;
dragIndex = i;
}
}
}
if (dragging) {
window.addEventListener("mousemove", mouseMoveListener, false);
}
theCanvas.removeEventListener("mousedown", mouseDownListener, false);
window.addEventListener("mouseup", mouseUpListener, false);
//code below prevents the mouse down from having an effect on the main browser window:
if (evt.preventDefault) {
evt.preventDefault();
} //standard
else if (evt.returnValue) {
evt.returnValue = false;
} //older IE
return false;
}
function mouseUpListener(evt) {
theCanvas.addEventListener("mousedown", mouseDownListener, false);
window.removeEventListener("mouseup", mouseUpListener, false);
if (dragging) {
dragging = false;
window.removeEventListener("mousemove", mouseMoveListener, false);
}
}
function mouseMoveListener(evt) {
var posX;
var posY;
var shapeRad = shapes[dragIndex].rad;
var minX = shapeRad;
var maxX = theCanvas.width - shapeRad;
var minY = shapeRad;
var maxY = theCanvas.height - shapeRad;
//getting mouse position correctly
var bRect = theCanvas.getBoundingClientRect();
mouseX = (evt.clientX - bRect.left) * (theCanvas.width / bRect.width);
mouseY = (evt.clientY - bRect.top) * (theCanvas.height / bRect.height);
// Divide by width of canvas and multiply to get percentage out of 100
var DelayTime = ((mouseX / 240) * 100);
// Invert returned value to get percentage out of 100
var DelayFeedback = (100 - (mouseY / 240) * 100);
// Set delay time as a portion of 2seconds
delayEffect.delayTime.value = DelayTime / 100 * 2.0;
// set delay feedback gain as value of random number
delayFeedback.gain.value = (DelayFeedback / 100 * 1.0);
//clamp x and y positions to prevent object from dragging outside of canvas
posX = mouseX - dragHoldX;
posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
posY = mouseY - dragHoldY;
posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);
shapes[dragIndex].x = posX;
shapes[dragIndex].y = posY;
drawScreen();
}
function hitTest(shape, mx, my) {
var dx;
var dy;
dx = mx - shape.x;
dy = my - shape.y;
//a "hit" will be registered if the distance away from the center is less than the radius of the circular object
return (dx * dx + dy * dy < shape.rad * shape.rad);
}
function drawShapes() {
var i;
for (i = 0; i < numShapes; i++) {
context.fillStyle = shapes[i].color;
context.beginPath();
context.arc(shapes[i].x, shapes[i].y, shapes[i].rad, 0, 2 * Math.PI, false);
context.closePath();
context.fill();
}
}
function drawScreen() {
context.fillStyle = "#000000";
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
drawShapes();
}
}
window.addEventListener("load", windowLoadHandler, false);
function windowLoadHandler() {
canvasApp('delayPad');
}
There are still a few shortcomings, for instance the mouseMoveListener, although constricting the movement of the circle, will continue to increase your x & y values. Meaning you'll either have to use your existing listeners to check when the drag event has exited the circle, or much more simply, you could set an upper limit to your X and Y values.
You'll have to create an object which will store your x and y values.
In below example I called it pad.
This object will serve both your canvas visualization, and your audio processing.
These are both outputs (respectively visual and audio), while the input will be user gesture (e.g mousemove).
The inputs update the pad object, while outputs read it.
[Note]: This example will only work in newest Chrome and Firefox since it uses MediaElement.captureStream() which is not yet widely implemented.
const viz_out = canvas.getContext('2d');
let aud_out, mainVolume;
// our pad object holding the coordinates
const pad = {
x: 0,
y: 0,
down: false,
rad: 10
};
let canvRect = canvas.getBoundingClientRect();
function mousemove(event) {
if (!aud_out || !pad.down) {
return;
}
pad.x = event.clientX - canvRect.left;
pad.y = canvRect.height - (event.clientY - canvRect.top); // inverts y axis
// all actions are splitted
updateViz();
updateAud();
updateLog();
}
viz_out.setTransform(1, 0, 0, -1, 0, 300) // invert y axis on the canvas too
// simply draws a circle where at our pad's coords
function updateViz() {
viz_out.clearRect(0, 0, canvas.width, canvas.height);
viz_out.beginPath();
viz_out.arc(pad.x, pad.y, pad.rad, 0, Math.PI * 2);
viz_out.fill();
}
// You'll do it as you wish, here it just modifies a biquadFilter
function updateAud() {
const default_freq = 350;
const max_freq = 6000;
const y_ratio = pad.y / 300;
aud_out.frequency.value = (default_freq + (max_freq * y_ratio)) - default_freq;
aud_out.Q.value = (pad.x / 300) * 10;
mainVolume.value = 1 + ((pad.y + pad.x) / 75);
}
function updateLog() {
log.textContent = `x:${~~pad.x} y:${~~pad.y}`;
}
canvas.addEventListener('mousedown', e => pad.down = true);
canvas.addEventListener('mouseup', e => pad.down = false);
canvas.addEventListener('mousemove', mousemove);
btn.onclick = e => {
btn.textContent = 'stop';
startLoadingAudio();
btn.onclick = e => {
mainVolume.value = 0;
}
}
window.onscroll = window.onresize = e => canvRect = canvas.getBoundingClientRect();
function startLoadingAudio() {
const audio = new Audio();
audio.loop = true;
audio.muted = true;
audio.onloadedmetadata = e => {
audio.play();
const stream = audio.captureStream ? audio.captureStream() : audio.mozCaptureStream();
initAudioProcessor(stream);
updateLog();
window.onscroll();
updateViz();
}
// FF will "taint" the stream, even if the media is served with correct CORS...
fetch("https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3").then(resp => resp.blob()).then(b => audio.src = URL.createObjectURL(b));
function initAudioProcessor(stream) {
var a_ctx = new AudioContext();
var gainNode = a_ctx.createGain();
var biquadFilter = a_ctx.createBiquadFilter();
var source = a_ctx.createMediaStreamSource(stream);
source.connect(biquadFilter);
biquadFilter.connect(gainNode);
gainNode.connect(a_ctx.destination);
aud_out = biquadFilter;
mainVolume = gainNode.gain;
biquadFilter.type = "bandpass";
}
}
canvas {
border: 1px solid;
}
<button id="btn">
start
</button>
<pre id="log"></pre>
<canvas id="canvas" width="300" height="300"></canvas>
So the following code will make an array of circles. I want to be able to click on a circle and have a popup tell me how many rows and columns along that circle is starting from (1,1).
Does anyone know how to do this?
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var gCanvasElement = ctx;
ctx.strokeStyle="#FF0000";
ctx.strokeRect(20,20,800,600);
// Positions are hardcoded to make sure that circle starts from the right place
var startX = 55;
var startY = 55;
console.clear();
for(var i=1;i<=8;i++){
console.group(i);
for(var j=1;j<=i;j++){
ctx.beginPath();
ctx.strokeStyle='green';
//radius is hardcoded to 30 for testing purpose
ctx.arc(startX*j + (j-1)*10,startY*i + (i-1)*10,30,0,2*Math.PI);
ctx.stroke();
//console log
console.group(j);
console.log(startX*j + (j-1)*10);
console.log(startY*i + (i-1)*10);
console.groupEnd(j);
}
console.groupEnd(i);
}
As I've written in my comment canvas is just a raster image. It does not store shapes unlike vector graphics (SVG). So you need to use math to find if the user clicked the circle or not.
http://jsfiddle.net/tarabyte/Jct6j/
function distance(x1, y1, x2, y2) { //helper to get distance between 2 points
return Math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
function center(i) { //helper to get coordinates of i-th circle
return (i*2-1)*radius + (i-1)*step/2;
}
c.addEventListener('click', function(ev){ //click lister
var x = ev.pageX - c.offsetLeft - (startX - radius), //normalized coords
y = ev.pageY - c.offsetTop - (startY - radius), //normalized coords
grid = 2*radius + step/2,
i, j, d;
if(x < 0 || y < 0) { return;} //out of circles
//cell inside your grid
i = Math.ceil(x/grid);
j = Math.ceil(y/grid);
if(i > j) { return;} //out of circles
d = distance(x, y, center(i), center(j));
if(d > radius) {return;} //to far from center
console.log(i, j);
}, false);
I want to add 4 text boxes which will give me the coordinates of a rectangle and if I manually edit the coordinates it should change /alter the rectangle as well.
Please tell me how to proceed with this solution.
In my example if you click ROI it will draw a rectangle I want the upper and lower X and Y coordinates of the same.
the working fiddle is http://jsfiddle.net/qf6Ub/2/
// references to canvas and context
var oImageBuffer = document.createElement('img');
var oCanvas = document.getElementById("SetupImageCanvas");
var o2DContext = oCanvas.getContext("2d");
// set default context states
o2DContext.lineWidth = 1;
o2DContext.translate(0.50, 0.50); // anti-aliasing trick for sharper lines
// vars to save user drawings
var layers = [];
var currentColor = "black";
// vars for dragging
var bDragging = false;
var startX, startY;
// vars for user-selected status
var $roiCheckbox = document.getElementById("btnROI");
var $layersCheckbox = document.getElementById("btnLAYER");
var $patches = document.getElementById('txtPatchCount');
var $mouse = document.getElementById("MouseCoords");
var roiIsChecked = false;
var layersIsChecked = false;
var patchCount = 0;
// listen for mouse events
oCanvas.addEventListener('mousedown', MouseDownEvent, false);
oCanvas.addEventListener('mouseup', MouseUpEvent, false);
oCanvas.addEventListener('mousemove', MouseMoveEvent, false);
oCanvas.addEventListener('mouseout', MouseOutEvent, false);
$("#txtPatchCount").keyup(function () {
getStatus();
// clear the canvas
o2DContext.clearRect(0, 0, oCanvas.width, oCanvas.height);
// redraw all previously saved line-pairs and roi
for (var i = 0; i < layers.length; i++) {
var layer = layers[i];
if (layer.patchCount > 0) {
layer.patchCount = patchCount;
}
draw(layer);
}
});
// mouse event handlers
function MouseDownEvent(e) {
e.preventDefault();
startX = e.clientX - this.offsetLeft;
startY = e.clientY - this.offsetTop;
currentColor = randomColor();
getStatus();
bDragging = true;
}
function MouseUpEvent(e) {
if (!bDragging) {
return;
}
e.preventDefault();
bDragging = false;
mouseX = e.clientX - this.offsetLeft;
mouseY = e.clientY - this.offsetTop;
layers.push({
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY,
color: currentColor,
drawLayer: layersIsChecked,
patchCount: patchCount,
});
}
function MouseOutEvent(e) {
MouseUpEvent(e);
}
function MouseMoveEvent(e) {
if (!bDragging) {
return;
}
var mouseX = e.clientX - this.offsetLeft;
var mouseY = e.clientY - this.offsetTop;
// clear the canvas
o2DContext.clearRect(0, 0, oCanvas.width, oCanvas.height);
// redraw all previously saved line-pairs and roi
for (var i = 0; i < layers.length; i++) {
draw(layers[i]);
}
// create a temporary layer+roi object
var tempLayer = {
x1: startX,
y1: startY,
x2: mouseX,
y2: mouseY,
color: currentColor,
drawLayer: layersIsChecked,
patchCount: patchCount,
};
// draw the temporary layer+roi object
draw(tempLayer);
// Display the current mouse coordinates.
$mouse.innerHTML = "(" + mouseX + "," + mouseY + ")" + patchCount;
}
function draw(layer) {
if (layer.drawLayer) {
// set context state
o2DContext.lineWidth = 0.50;
o2DContext.strokeStyle = layer.color;
// draw parallel lines
hline(layer.y1);
hline(layer.y2);
}
if (layer.patchCount > 0) {
// set context state
o2DContext.lineWidth = 1.5;
o2DContext.strokeStyle = '#0F0';
// draw regions
o2DContext.strokeRect(layer.x1, layer.y1, (layer.x2 - layer.x1), (layer.y2 - layer.y1));
var w = layer.x2 - layer.x1;
o2DContext.beginPath();
for (var i = 1; i < layer.patchCount; i++) {
var x = layer.x1 + i * w / layer.patchCount;
o2DContext.moveTo(x, layer.y1);
o2DContext.lineTo(x, layer.y2);
}
o2DContext.stroke();
}
}
function getStatus() {
roiIsChecked = $roiCheckbox.checked;
layersIsChecked = $layersCheckbox.checked;
patchCount = $patches.value;
if (!roiIsChecked || !patchCount) {
patchCount = 0;
}
}
function randomColor() {
return ('#' + Math.floor(Math.random() * 16777215).toString(16));
}
function hline(y) {
o2DContext.beginPath();
o2DContext.moveTo(0, y);
o2DContext.lineTo(oCanvas.width, y);
o2DContext.stroke();
}
document.getElementById("MouseCoords").innerHTML = "(" + x + "," + y + "); "
+"("+ oPixel.x + "," + oPixel.y + "); "
+"("+ oCanvasRect.left + "," + oCanvasRect.top + ")";
}
Ok, I went back to the drawing board and came up with this FIDDLE.
It provides the dimensions of the div and its location from the top and left of the container.
You can calculate the exact coordinates from those numbers.
JS
var divwidth = $('.coord').width();
var divheight = $('.coord').height();
var pos = $('.coord').offset();
$('#divdimensions').html(divwidth + ',' + divheight);
$('#divposition').html( pos.left + ',' + pos.top );
I wrote a JavaScript that allows a user to draw with their mouse on an HTML5 canvas (similar to MS Paint).
Right now, I have 2 problems:
The drawing feature only works if the HTML5 canvas element is positioned at the top left corner (0, 0) of the web page, otherwise it doesn't work at all OR the drawing is off center.
I'm unable to erase the drawing. When I erase the drawing it erases BUT as soon as I start drawing again, it comes back.
My code is below:
HTML Canvas
<canvas id="can1" width="500" height="500"></canvas>1
JavaScript for Canvas Drawing
// Variables
var x1;
var y1;
var isPressed = false;
var myCanvas;
var myContext;
function startCanvas() {
// Canvas stuff
myCanvas = document.getElementById("can1");
myContext = myCanvas.getContext("2d");
// Specify a black background, and white lines that are 3 pixels thick.
myContext.fillStyle = '#fff';
myContext.strokeStyle = '#fff';
myContext.fillRect(0, 0, 500, 500);
myContext.lineWidth = 3;
myContext.fill();
}
function functionMouseDown(e) {
// Get coordinates
x1 = e.clientX
y1 = e.clientY;
isPressed = true;
}
function functionMouseMove(e) {
// If mouse is down and moved start drawing line
if (isPressed == true) {
drawLine(e);
}
}
function functionMouseUp() {
// Stop drawing line
isPressed = false;
//myContext.closePath();
//myContext.stroke();
}
function drawLine(e) {
// Draw line
var x = e.clientX;
var y = e.clientY;
myContext.strokeStyle = '#cc0000';
myContext.lineWidth = 1;
myContext.moveTo(x1, y1);
myContext.lineTo(x, y);
myContext.stroke();
// Set start coordinates to current coordinates
x1 = x;
y1 = y;
}
JavaScript that I use to erase canvas:
myContext.clearRect(0, 0, 500, 500);
I use the following function to accomplish this
function relMouseCoords(event){/*needs fixing for general case*/
var totalOffsetX = 0
var totalOffsetY = 0
var canvasX = 0
var canvasY = 0
var currentElement = this
do{
totalOffsetX += currentElement.offsetLeft
totalOffsetY += currentElement.offsetTop
}
while(currentElement = currentElement.offsetParent)
canvasX = event.pageX - totalOffsetX
canvasY = event.pageY - totalOffsetY
return {x:canvasX, y:canvasY}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
then
var cord = e.target.relMouseCoords(e);
x1 = cord.x;
y1 = cord.y;
...
var cord = e.target.relMouseCoords(e);
var x = cord.x;
var y =cord.y;
http://jsfiddle.net/mowglisanu/u3rvT/1/
The simplest solution is to set the off set of the canvas using myCanvas.offsetLeft and myCanvas.offsetTop.