Using touch events on canvas - javascript

I have a program to draw on a HTML canvas with JS. It works fine with the mouse, but when I try the same on on a touch-based device, it draws only a dot. How do I fix this?
Here's the code that handles the mouse events:
let m = document.getElementById('m-container');
const onMouseMove = (e) =>{
m.style.left = e.pageX + 'px';
m.style.top = e.pageY + 'px';
}
m.addEventListener('mousemove', draw);
m.addEventListener('mousedown', setPosition);
m.addEventListener('mouseenter', setPosition);
function setPosition(e) {
pos.x = parseInt(document.getElementById('m-container').style.left, 10);
pos.y = parseInt(document.getElementById('m-container').style.top, 10);
}
function draw(e) {
// mouse left button must be pressed
if (e.buttons !== 1 || isDragging) return;
if(mode=="pen"){
ctx.globalCompositeOperation="source-over";
} else {
ctx.globalCompositeOperation="destination-out";
}
ctx.beginPath(); // begin
ctx.lineWidth = currentDrawWidth;
ctx.lineCap = 'round';
ctx.strokeStyle = currentDrawColour;
ctx.moveTo(pos.x, pos.y); // from
setPosition(e);
ctx.lineTo(pos.x, pos.y); // to
ctx.stroke(); // draw it!
}
Here's the code to handle the touch events:
m.addEventListener('touchmove', touchdraw);
m.addEventListener('touchstart', function (e) {
setPositionTouch(e)
});
m.addEventListener('touchend', function (e) {
setPositionTouch(e)
});
function setPositionTouch(e) {
pos.x = e.clientX;
pos.y = e.clientY;
}
function touchdraw(e) {
if(mode=="pen"){
ctx.globalCompositeOperation="source-over";
} else {
ctx.globalCompositeOperation="destination-out";
}
ctx.beginPath(); // begin
ctx.lineWidth = currentDrawWidth;
ctx.lineCap = 'round';
ctx.strokeStyle = currentDrawColour;
ctx.moveTo(pos.x, pos.y); // from
setPositionTouch(e);
ctx.lineTo(pos.x, pos.y); // to
ctx.stroke(); // draw it!
}
mode is declared separately.

Related

Marker shaped canvas pen - Issues with two silmutaneous strokes

I am trying to create a marker style pencil with which one can draw on a canvas: a slightly rotated rectangle as pencil tip. My idea was to simply have to simultaneous strokes on mousedown, one of which is moved to the right and to the top a little bit so that is appears like a marker.
This is my code:
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.lineTo(pos.x, pos.y);
ctx.moveTo(pos.x + 2, pos.y + 2);
setPosition(e);
ctx.lineTo(pos.x + 2, pos.y+1);
ctx.stroke();
}
When I draw slowly, it works perfectly. When I speed up the cursor, however, one of the two strokes is not ecxecuted properly:
Does someone have an idea how I can get both strokes to work?
Thank you!
Edit: Removing some redundancy produced a new issue with the line:
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.moveTo(pos.x + 2, pos.y + 2);
ctx.stroke();
}
EDIT: example:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
resize();
var pos = { x: 0, y: 0 };
window.addEventListener('resize', resize);
document.addEventListener('mousemove', draw);
document.addEventListener('mousedown', setPosition);
document.addEventListener('mouseenter', setPosition);
function setPosition(e) {
pos.x = e.clientX + $(document).scrollLeft();
pos.y = e.clientY + $(document).scrollTop();
}
function resize() {
ctx.canvas.width = $('#canvas').width();
ctx.canvas.height = $('#canvas').height();
}
function draw(e) {
if (e.buttons !== 1) return;
ctx.beginPath();
ctx.lineWidth = 5;
ctx.lineCap = 'round';
ctx.strokeStyle = '#000000';
ctx.moveTo(pos.x, pos.y);
setPosition(e);
ctx.lineTo(pos.x + 2, pos.y+2);
ctx.stroke();
}
#canvas {
width: 200px;
height: 200px;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<canvas id="canvas"></canvas>

Draw a straight line between two points selected in a mouse down event

I have code which draws a line as I move my mouse while clicking the mouse (mousedown event and mousemove event).
I also want a straight line to be drawn from the beginning of the point (where I first clicked the point, mousedown event) to the end (where I lift the mouse event, mouseup event).
(function() {
var canvas = document.querySelector('#paint');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {
x: 0,
y: 0
};
var last_mouse = {
x: 0,
y: 0
};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
/* Drawing on Paint App */
ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
canvas.addEventListener('mousedown', function(e) {
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = function() {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
};
}());
#sketch{
width: 100%;
height: 200px;
background-color: #CCCCCC;
}
<div id="sketch">
<canvas id="paint">
</canvas>
</div>
You want to store the coordinates of the mouseDown event and then use them to draw a single line to the coordinates of the mouseUp event. I modified your code to show a way that can be done:
(function() {
var canvas = document.querySelector('#paint');
var ctx = canvas.getContext('2d');
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var mouse = {
x: 0,
y: 0
};
var last_mouse = {
x: 0,
y: 0
};
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) {
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
}, false);
/* Drawing on Paint App */
ctx.lineWidth = 5;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
canvas.addEventListener('mousedown', function(e) {
initialPoint = {x:mouse.x, y:mouse.y}
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', function() {
drawStraightLine()
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = function() {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.strokeStyle = "#000000";
ctx.closePath();
ctx.stroke();
};
let initialPoint
const drawStraightLine = function() {
ctx.beginPath();
ctx.moveTo(initialPoint.x, initialPoint.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.strokeStyle = "#FF000077";
ctx.stroke();
}
}());
#sketch{
width: 100%;
height: 180px;
background-color: #DDDDDD;
}
<div id="sketch">
<canvas id="paint" />
</div>
initialPoint is the mouse position when the button is pressed and drawStarightLine() is the method executed when said button is released. Also added different colors to the lines to make it obvious.
Just add another position object eg
const mouseDownAt = {x: 0, y: 0};
Then when the mouse down event happens record that position
canvas.addEventListener('mousedown', function(e) {
mouseDownAt.x = e.pageX - this.offsetLeft;
mouseDownAt.y = e.pageY - this.offsetTop;
canvas.addEventListener('mousemove', onPaint, false);
}, false);
On the mouse up event draw the closing line
canvas.addEventListener('mouseup', function() {
lastMouse.x = mouse.x;
lastMouse.y = mouse.y;
mouse.x = e.pageX - this.offsetLeft;
mouse.y = e.pageY - this.offsetTop;
// if mouse has moved?? draw the last little bit
if (mouse.x !== last_mouse.x || mouse.y !== last_mouse.y) {
onPaint();
}
// set the end position
lastMouse.x = mouse.x;
lastMouse.y = mouse.y;
// use the mouse down at pos as the new position
mouse.x = mouseDownAt.x;
mouse.y = mouseDownAt.y;
// draw the line
onPaint();
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
BTW I am not sure if you know that using closePath as you do will render the path segment twice and will reduce the quality of the line (too strong alphas on the anti aliasing) . closePath is like lineTo (draws a line segment) and not related to ctx.beginPath
function onPaint () {
ctx.beginPath();
ctx.lineTo(lastMouse.x, lastMouse.y); // after beginPath lineTo is the same
// moveTo
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
};

Javascript - Need to get x and y coordinates only from the stroke of a SVG path?

I have a canvas in which, is drawn an element svg (example a circle), the user is responsible for drawing with the mouse through this figure, I save the dots x and y drawn by the user in an array, but I dont know how to get the dots only from svg stroke.
My problem is:
Using isPointInStroke() I can see if the point is in the stroke but If I don't have the total points array of the stroke, it's impossible to know if the user has drawn 100% of the SVG figure. In the previous way if the user draws half of the drawing but correctly, it would give me 100% success.
function init() {
canvas = document.getElementById('can');
ctx = canvas.getContext("2d");
w = canvas.width;
h = canvas.height;
var svgPathCirculo=" M125,200a75,75 0 1,0 150,0a75,75 0 1,0 -150,0";
var circulo = new Path2D(svgPathCirculo);
ctx.lineWidth = 5;
ctx.setLineDash([5, 15]);
ctx.stroke(circulo);
// Just example to check if it works
if(ctx.isPointInStroke(circulo, 125, 200)){
ctx.arc(200,200,3,0,2*Math.PI);
ctx.fill();
};
canvas.addEventListener("mousemove", function (e) {
findxy('move', e)
}, false);
canvas.addEventListener("mousedown", function (e) {
findxy('down', e)
}, false);
canvas.addEventListener("mouseup", function (e) {
findxy('up', e)
}, false);
canvas.addEventListener("mouseout", function (e) {
findxy('out', e)
}, false);
}
I use the canvas to draw on it and svg to display predefined shapes for the user to follow as a template while drawing (such as drawing booklets for young children).
function draw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x;
ctx.lineWidth = y;
ctx.stroke();
ctx.closePath();
}
function findxy(res, e) {
if (res == 'down') {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
if(!arrayCoordenadas.includes({x:currX,y:currY})){
arrayCoordenadas.push({x:currX,y:currY});
}
flag = true;
dot_flag = true;
if (dot_flag) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(currX, currY, 2, 2);
ctx.closePath();
dot_flag = false;
}
}
if (res == 'up' || res == "out") {
flag = false;
}
if (res == 'move') {
if (flag) {
prevX = currX;
prevY = currY;
currX = e.clientX - canvas.offsetLeft;
currY = e.clientY - canvas.offsetTop;
if(!arrayCoordenadas.includes({x:currX,y:currY})){
arrayCoordenadas.push({x:currX,y:currY});
}
draw();
}
}
}
I need to know each of the x and y coordinates of the svg stroke path.
Example: Example of what I mean
I've added a function to detect the mouse position in the canvas and now currX became curr.x ... etc
If you are using Path2Dthis is how you detect if a point {x,y} is in the stroke:
ctx.isPointInStroke(the_path, x, y)
Next comes my code. The user can draw only inside the stroke.
Now the code is working but I don't think you may know if the user has drawn 100% of the SVG figure. You may push the points inside the array of points and calculate the length of the path, and compare it with the length of the circle, but I don't think this would do.
let prev = {},
curr = {};
let flag = false;
let circulo;
function init() {
canvas = document.getElementById("can");
ctx = canvas.getContext("2d");
w = canvas.width = 400;
h = canvas.height = 400;
var svgPathCirculo = "M125,200a75,75 0 1,0 150,0a75,75 0 1,0 -150,0";
circulo = new Path2D(svgPathCirculo);
ctx.lineWidth =10;
ctx.setLineDash([5, 15]);
ctx.stroke(circulo);
canvas.addEventListener("mousemove", move, false);
canvas.addEventListener("mousedown", down, false);
canvas.addEventListener("mouseup", up, false);
canvas.addEventListener("mouseout", up, false);
}
function draw(prev, curr, trazado) {
ctx.setLineDash([]); //unset linedash
ctx.lineCap = "round";
ctx.strokeStyle = "gold"
ctx.lineWidth =5;
if (
ctx.isPointInStroke(trazado, curr.x, curr.y) &&
ctx.isPointInStroke(trazado, prev.x, prev.y)
) {
ctx.beginPath();
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(curr.x, curr.y);
ctx.stroke();
}
}
function down(e) {
prev = oMousePos(canvas, e);
curr = oMousePos(canvas, e);
flag = true;
}
function up(e) {
flag = false;
}
function move(e) {
if (flag) {
curr = oMousePos(canvas, e);
draw(prev, curr, circulo);
prev = { x: curr.x, y: curr.y };
}
}
function oMousePos(canvas, evt) {
var ClientRect = canvas.getBoundingClientRect();
return {
//objeto
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
};
}
init();
canvas{border:1px solid}
<canvas id="can"></canvas>

How to store boxes from canvas?

I have a canvas, which will display videos taken from file upload on the first layer, and the second layer allow rectangles to be drawn. As I am trying to annotate videos and not still images, I will need the annotations to be saved then cleared, and then redrawn again and again until the video is over. However, I have no clue how I should do so as I am relatively new to JavaScript.
This is the code I have for now for the drawing of the annotations:
// Drawing boxes
function handleMouseDown(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
console.log(mouseX, mouseY);
$("#downlog").html("Down: " + mouseX + " / " + mouseY);
// Put your mousedown stuff here
if (mouseIsDown) {
console.log('1');
canvas2.style.cursor = "crosshair";
mouseIsDown = false;
mouseIsUp = false;
console.log(mouseIsDown);
} else {
handleMouseUp();
}
mouseIsDown = false;
mouseIsUp = true;
}
function handleMouseUp(e) { // array? user input?
mouseIsDown = false;
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
/*if (mouseIsUp) {
console.log('2');*/
draw();
}
function draw() {
/* context2.clearRect(0, 0, canvas2.width, canvas2.height);*/
context2.beginPath();
context2.rect(startX, startY, mouseX - startX, mouseY - startY);
context2.strokeStyle = "limegreen";
context2.lineWidth = 2;
context2.stroke();
canvas2.style.cursor = "default";
}
$("#canvas2").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas2").mouseup(function(e) {
handleMouseUp(e);
});
function clearcanvas()
{
var canvas2 = document.getElementById('canvas2'),
context2 = canvas2.getContext("2d");
context2.clearRect(0, 0, canvas2.width, canvas2.height);
}
I will really appreciate any help, thank you!
Please read the comments. I hope my code is clear enough.
let c = document.getElementById("canvas");
let ctx = c.getContext("2d");
// the array of all rectangles
let rectsRy = [];
// the actual rectangle, the one that is beeing drawn
let o={};
// a variable to store the mouse position
let m = {},
// a variable to store the point where you begin to draw the rectangle
start = {};
// a boolean
let isDrawing = false;
function handleMouseDown(e) {
start = oMousePos(c, e);
isDrawing = true;
//console.log(start.x, start.y);
c.style.cursor = "crosshair";
}
function handleMouseMove(e) {
if(isDrawing){
m = oMousePos(c, e);
draw();
}
}
function handleMouseUp(e) { // array? user input?
c.style.cursor = "default";
isDrawing = false;
// push a new rectangle into the rects array
rectsRy.push({x:o.x,y:o.y,w:o.w,h:o.h});
}
function draw() {
o.x = start.x;
o.y = start.y;
o.w = m.x - start.x;
o.h = m.y - start.y;
clearcanvas();
// draw all the rectangles saved in the rectsRy
rectsRy.map(r => {drawRect(r)})
// draw the actual rectangle
drawRect(o);
}
c.addEventListener("mousedown", handleMouseDown);
c.addEventListener("mousemove", handleMouseMove);
c.addEventListener("mouseup", handleMouseUp);
function clearcanvas(){
ctx.clearRect(0, 0, c.width, c.height);
}
function drawRect(o){
ctx.strokeStyle = "limegreen";
ctx.lineWidth = 2;
ctx.beginPath(o);
ctx.rect(o.x,o.y,o.w,o.h);
ctx.stroke();
}
// a function to detect the mouse position
// the function returns an object
function oMousePos(canvas, evt) {
let ClientRect = canvas.getBoundingClientRect();
return {
x: Math.round(evt.clientX - ClientRect.left),
y: Math.round(evt.clientY - ClientRect.top)
}
}
canvas{border:1px solid #d9d9d9;}
<canvas id="canvas" width="600" height="300">

HTML5 Canvas why fillText() method clears everything after clearRect()

When i call writeTextToCanvas method before clearCanvas method, it works perfectly,
if i call clearCanvas method first than writeTextToCanvas, it doesn't works, drawing functions etc. all works after clearCanvas but fillText doesn't work and also clears all canvas when i call fillText
when i set context.globalAlpha = 0.5 before fillText, i can barely see text on the canvas but anything in canvas erased somehow
var canvas;
var context;
var selectedRole = DRAW_TOOLS.PENCIL;
function initDrawingCanvas(index) {
var question = document.getElementsByClassName('question').item(index);
canvas = question.getElementsByClassName('questionDrawingCanvas').item(0);
context = canvas.getContext("2d");
canvasWidth = canvas.width;
canvasHeight = canvas.height;
compositeOperation = context.globalCompositeOperation;
canvas.addEventListener("touchmove", onCanvasTouchMove, false);
canvas.addEventListener("touchstart", onCanvasTouchStart, false);
canvas.addEventListener("touchstop", onCanvasTouchStop, false);
}
var startX = 0;
var startY = 0;
var endX = 0;
var endY = 0;
var compositeOperation;
function onCanvasTouchMove(event){
endX = event.changedTouches[0].clientX - $(canvas).offset().left;
endY = event.changedTouches[0].clientY - $(canvas).offset().top;
if(selectedRole == DRAW_TOOLS.PENCIL){
context.beginPath();
context.moveTo(startX, startY);
context.lineTo(endX, endY);
context.strokeStyle = $('.colorPicker').css('background-color');
context.lineWidth = parseInt($('#pencilSize').attr("data-size"));
context.stroke();
}else if(selectedRole == DRAW_TOOLS.ERASER){
context.save();
context.globalCompositeOperation = "destination-out";
context.arc(endX,endY,25,0,Math.PI*2,false);
context.fill();
context.restore();
}
startX = endX;
startY = endY;
}
function onCanvasTouchStart(event){
$("#colorPicker").css("display", "none");
$("#pencilSize").css("display", "none");
$("#textColorPicker").css("display", "none");
$("#canvas_textSize").css("display", "none");
startX = event.changedTouches[0].pageX - $(canvas).offset().left;
startY = event.changedTouches[0].pageY - $(canvas).offset().top;
if(selectedRole == DRAW_TOOLS.TEXT){
showConfirmDialog('<textarea id="canvasTextArea" class="canvasTextArea"></textarea>', RESOURCES.CANVAS_TEXT_TITLE, {positiveButton: RESOURCES.OKAY, negativeButton: RESOURCES.CANCEL}, function(){
var text = $("#canvasTextArea").val();
if(isNullOrUndefined(text)) return;
writeTextToCanvas(text);
hideDialog();
}, function(){
hideDialog();
});
$("#canvasTextArea").focus();
$("#canvasTextArea").css("font-size", $('#textSizePicker').attr("data-size") + "px");
$("#canvasTextArea").css("color", $('.textColorPicker').css("background-color"));
}
}
function onCanvasTouchStop(event){
console.log(event);
}
function clearCanvas(){
context.clearRect(0, 0, canvas.width, canvas.height);
}
function getCanvasContent(){
return canvas.toDataURL();
}
function writeTextToCanvas(text) {
context.globalCompositeOperation = "source-over";
context.textBaseline = "top";
context.font = $('#textSizePicker').attr("data-size") + 'px sans-serif';
context.fillStyle = $('.textColorPicker').css("background-color");
context.fillText(text, startX, startY);
}
What should i use to clear canvas or clear some parts like eraser then i can fillText normally?
Your call to clearRect() is stopping the entire script because it throws an error (check the console). You're not passing in a rectangle to the call.
It needs to be called like:
context.clearRect(0, 0, canvas.width, canvas.height);
Here's a working example: http://jsfiddle.net/pe19L15m

Categories

Resources