How to draw on a HTML5 canvas with a stylus - javascript

I used onmousedown, onmousemove and onmouseup events to draw with JavaScript on a HTML5 canvas object. Everything is working.
Now I want to replace the mouse with a sylus (Wacom Intous Pro)
Therefore I replaced the mouse Events with onpointerdown, onpointerup and onpointermove.
But now, if I touch and move the pen, I do not get any onpointermove Events, instead the whole page is draged. By adding html, body {overflow: hidden} to the HTML construct I could Prevent this behaviour, but still I do not get any onpointermove Events. These I only get when the pen is above the tablet.
Has somebody an idea how to solve it?
Corrently this is the concept I use (but not working):
$(function() {
var el=document.getElementById("myDraw");
el.onpointerdown = down_handler;
el.onpointerup = up_handler;
el.onpointermove = move_handler;
ctx = el.getContext("2d");
ctx.canvas.width = window.innerWidth*0.75;
ctx.canvas.height = window.innerHeight*0.75;
});
function move_handler(ev)
{
if (onTrack>0)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.lineWidth=10*ev.pressure;
ctx.moveTo(lastX,lastY);
ctx.lineTo(xPosition,yPosition);
ctx.stroke();
lastX = xPosition;
lastY = yPosition;
}
}
function down_handler(ev)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.arc(xPosition, yPosition, 5, 0, 2 * Math.PI);
ctx.stroke();
startX = xPosition;
startY = yPosition;
lastX = xPosition;
lastY = yPosition;
onTrack=1;
var el=document.getElementById("myRemoteDraw");
el.setPointerCapture(ev.pointerId);
console.log('pointer down '+ev.pointerId);
}
function up_handler(ev)
{
var xPosition = ev.clientX-getPosition(document.getElementById("myDraw")).x;
var yPosition = ev.clientY-getPosition(document.getElementById("myDraw")).y;
ctx.beginPath();
ctx.rect(xPosition-5, yPosition-5, 10, 10);
ctx.stroke();
onTrack = 0;
var el=document.getElementById("myRemoteDraw");
el.releasePointerCapture(ev.pointerId);
console.log('pointer up '+ev.pointerId);
}

This CSS should help you:
<style>
/* Disable intrinsic user agent touch behaviors (such as panning or zooming) */
canvas {
touch-action: none;
}
</style>
or in JavaScript:
ctx.canvas.style.touchAction = "none";
More details from this link about "touch-action" and some general info in this link about inputs:
The touch-action CSS property is used to specify whether or not the browser should apply its default (native) touch behavior (such as zooming or panning) to a region. This property may be applied to all elements except: non-replaced inline elements, table rows, row groups, table columns, and column groups.
A value of auto means the browser is free to apply its default touch behavior (to the specified region) and the value of none disables the browser's default touch behavior for the region. The values pan-x and pan-y, mean that touches that begin on the specified region are only for horizontal and vertical scrolling, respectively. The value manipulation means the browser may consider touches that begin on the element are only for scrolling and zooming.

Related

Is there a way to select multiple table cells with single javascript click?

I have basically increased the cursor icon size on the webpage via javascript, and I have a table. Now I would like all overlapping elements in the table with the cursor icon to change their background color (when clicking and dragging). This means changing the area that the mousedown or mouseover is currently able to select and being able to select multiple elements. Is there a way to do this and if so how?
Here is the code for clicking:
$('td').on('mousedown', function (e1) {
loop();
flag = 1
$('td').on('mousemove', function (e2) {
console.log(document.elementsFromPoint(e2.pageX, e2.pageY))
if (flag == 1) {
var tableCell = $(this);
var setbox = $('.active').attr('id');
//document.getElementById("tableBack").style.cursor="move";
switch (setbox) {
case "lPark":
//$(this).text('P');
$(this).css('background-color', 'green');
break;
}
}
}
}
Here is the code for increasing cursor size:
var cursor = document.createElement('canvas'),
ctx = cursor.getContext('2d');
cursor.width = 100;
cursor.height = 100;
var centerX = cursor.width / 2;
var centerY = cursor.height / 2;
var radius = 40 ;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'green';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = 'black';
ctx.stroke();
//document.getElementById("tableBack").style.cursor="move";
// set image as cursor (modern browsers can take PNGs as cursor).
document.getElementById("tableBack").style.cursor = 'url(' + cursor.toDataURL() + '), auto';
Here is a picture of what I am trying to do
http://imgur.com/a/i3pEp (Sorry I don't have enough points to embed)
Notice how cursor is over 4 cells but only 1 of them gets selected, I would like to select all 4.
I somehow tried to understand your query. See, there's one thing of course you can do.
There're ample of ways to do this.However, here's a li'l algorithm I'd suggest.
1) capture ctrl key pressed event and save a flag value (suppose boolean) in some hidden field say x;
1.1) On Key down save true in x
1.2) On Key up save false in x
2) now while it is running caputre your mouse click on cell
if( x==true)
add some highlight class on that cell (which is clicked)
else
remove some highlight class on that cell
3) now for you entire table/grid you have got your highlighted class on items you've selected
Every phase can be coded as per the requirements. I hope it may give you a bit of idea.

making canvas register mouse move with div over it

I have a <canvas> element that spans the width and height of my webpage, like a background. It has interactive elements that rely on mouse coordinates. When the canvas was in it's own block (i.e. nothing over it) the interactive elements worked fine. But now that it's got divs over it it's not picking up any of the mouse interactions.
Below is my javascript code for the mousemove stuff. Why would items on top affect it picking up mouse xy coordinates, and how do I fix it?
var mouse = {x:-100,y:-100};
var mouseOnScreen = false;
canvas.addEventListener('mousemove', MouseMove, false);
canvas.addEventListener('mouseout', MouseOut, false);
var MouseMove = function(e) {
if (e.layerX || e.layerX == 0) {
//Reset particle positions
mouseOnScreen = true;
mouse.x = e.layerX - canvas.offsetLeft;
mouse.y = e.layerY - canvas.offsetTop;
}
}
var MouseOut = function(e) {
mouseOnScreen = false;
mouse.x = -100;
mouse.y = -100;
}
var update = function(){
var i, dx, dy, sqrDist, scale;
//...... this chunk is the only part of the function that references the mouse
dx = parts[i].x - mouse.x;
dy = parts[i].y - mouse.y;
sqrDist = Math.sqrt(dx*dx + dy*dy);
if (sqrDist < 20){
parts[i].r = true;
}
.....
}
If you don't need the top divs to be mouse-aware then set their CSS pointer-events:none; and the mouse events will filter down to your canvas underneath. Questioner needs responsive buttons placed over the canvas.
If the top divs do need to respond to mouse events, you might have to listen for mouse events on the window and convert those to canvas coordinates that your app can respond to.
You can listen for mousemove events on the window and get all moves -- even when over button elements. Also listen for mouseout events on the window. Since the canvas spans the window, you know mouseout happens when mouseevent.clientX & mouseevent.clientY report the coordinates are outside the window
Solved the issue with this code I found from another SO answer that I modified a bit.
function simulate(e) {
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("mousemove", true, true, window,
0, e.screenX, e.screenY, e.clientX, e.clientY, false, false, false, false, 0, null);
canvas.dispatchEvent(evt);
console.log("Emulated.");
}
$("body > section, body > header").each(function(){
this.addEventListener("mousemove", simulate);
});
Also, as a side note. Changed original layerX and layerY in mouse code to offsetX and offsetY to work in firefox (in addition to all other browsers)

How to move the canvas like drag and drop?

I need to move the whole canvas by touch like the movement of drag and drop
the position of the canvas is supposed to be from
flattenCanvas.height = canvas.height = SCREEN_HEIGHT - menu.container.offsetHeight;
canvas.style.position = 'absolute';
canvas.style.top = menu.container.offsetHeight+'px';
canvas.style.left = -(menu.container.offsetHeight)/2+'px';
context = canvas.getContext("2d");
and the touches for drawing or scrolling is from this
else if(event.touches.length == 1) ////one finger scrolling
{
canvas.style.top = (((event.touches[0].pageY)))+'px';
canvas.style.left = (((event.touches[0].pageX)))+'px';
}
from the last code i can move the canvas by one touch but it is not really dragging.
how to fix the canvas screen movement?
this is a demo of the webapp demo
Based on these docs, the event you want to handle is touchmove.
The code in your question appears to live inside a touchstart handler; just execute the same code in a touchmove handler to continuously update during the 'drag'.
Update
What you are trying to do is offset a target element (in this case, a canvas element) by the difference between a touch event's "current" (touchmove) and "start" (touchstart) positions. To do so, you should capture the positions of both target and touch in a touchstart handler. Then, in your touchmove handler, take the difference in touch positions, and add them to the target's start position. This might look like:
// Get your draggable element from the DOM
var draggable = document.getElementById('draggable');
// Set initial position.
draggable.style.position = 'relative'; // 'absolute' also works.
draggable.style.top = '0';
draggable.style.left = '0';
var targetStartX, targetStartY, touchStartX, touchStartY;
// Capture original coordinates of target and touch
function dragStart(e) {
targetStartX = parseInt(e.target.style.left);
targetStartY = parseInt(e.target.style.top);
touchStartX = e.touches[0].pageX;
touchStartY = e.touches[0].pageY;
}
function dragMove(e) {
// Calculate touch offsets
var touchOffsetX = e.touches[0].pageX - touchStartX,
touchOffsetY = e.touches[0].pageY - touchStartY;
// Add touch offsets to original target coordinates,
// then assign them to target element's styles.
e.target.style.left = targetStartX + touchOffsetX + 'px';
e.target.style.top = targetStartY + touchOffsetY + 'px';
}
draggable.addEventListener('touchstart', dragStart);
draggable.addEventListener('touchmove', dragMove);
Here's a live demo.

Make canvas line show

Ok, here is something simple, I hope. I've a div container in which I can click and what I'm trying to do is to create two perpendicular lines which cross each other where I've clicked. So far I've written those:
#fr{
float: right;
margin-top: 5%;
height: 720px;
width: 1280px;
position: relative;
background-image: url(blueboard.jpg);
border: 1px solid black;
clear:none;
}
canvas{
border:1px solid red;
float: right;
height: 720px;
width: 1280px;
clear:none;
}//css part, I actually place the canvas on top of my div
//and on html...
<canvas id="line"></canvas>
//js
function saveOO(e){
var xo, yo;
xo=e.clientX;
yo=e.clientY;
...
document.getElementById("saved").innerHTML="The (0;0) = " +"("+xo+";"+yo+")";
document.getElementById("ball").style.left=xo+'px';
document.getElementById("ball").style.top=yo+'px';
xylines(xo, yo);
...;
}
function lines(xo, yo){
var xo, yo, xl, xline, yl, yline, Dx, Dy, a, b, c, d, e;
xo=xo;
yo=yo;
a=$("#fr").position();
b=$("#fr").width();
c=$("#fr").height();
Dy=a.top+100;
Dx=a.left;
d=Dx+b;
e=Dy+c;
xline = document.getElementById("line");
xl=xline.getContext("2d");
xl.beginPath();
xl.moveTo(Dx,yo);
xl.lineTo(d,yo);
xl.lineWidth = 15;
xl.stroke();
yline = document.getElementById("line");
yl=yline.getContext("2d");
yl.beginPath();
yl.moveTo(xo,Dy);
yl.lineTo(xo,e);
yl.lineWidth = 15;
yl.stroke();}
I have, as well, checked whether all variables assign a value and everything is good with that. The crossing point should be exactly where the blue ball is, it also positions on the place where it's clicked. As you can see on the image no lines show, even if I remove the blue background. Please, help me, maybe something is missing. I'm looking forward to your answers.
:)
P.S. Dy is Y of the top left corner, Dx respectively X of the top left corner
Update 2
A slightly amended fiddle where the lines span the entire width/height of the canvas:
https://jsfiddle.net/0y37qwvw/5/
This is by using 0 as the starting point for each and canvas.width/canvas.height as the ending point.
Update
Here is a fiddle demonstrating the use of a canvas overlay and responding to a click events.
https://jsfiddle.net/0y37qwvw/4/
The important thing is to get the relative x and y co-ordinates right, which I have done using event.offsetX and event.offsetY and a fallback for when they are not implemented:
document.getElementById("canvas-overlay").addEventListener("click", function( event ) {
var x, y, clientRect;
clientRect = event.target.getBoundingClientRect();
x = (typeof event.offsetX !== 'undefined' ? event.offsetX : (event.clientX - clientRect.left));
y = (typeof event.offsetY !== 'undefined' ? event.offsetY : (event.clientY - clientRect.top));
lines(x, y, 50, 50);
}, false);
Original answer
Your code is incomplete, so it is hard to be sure what the exact problems are.
One problem though is that you have not directly set the width and height of the canvas element, which will result in the canvas being scaled.
You can do this on the canvas element:
<canvas id="line" height="720" width="1280"></canvas>
Demonstrated at:
https://jsfiddle.net/0y37qwvw/1/
You could also set the width and height programmatically:
var canvas = document.getElementById("line");
canvas.width = 1280;
canvas.height = 720;
https://jsfiddle.net/0y37qwvw/3/
Compare to what happens if the width and height are only set by CSS:
https://jsfiddle.net/0aph7yno/
If you still can't get it to work, set up a plunker/fiddle with the broken code.
Here is some code I have written, I tried to use as much as OOP style so it's easier to expand it. But still simple and clear.
http://jsfiddle.net/eeqhc2tn/2/
var canvas = document.getElementById('can');
var ctx = canvas.getContext('2d');
var drawThese = [];
canvas.width = 500;
canvas.height = 500;
render();
//Line Code
function Line(sx, sy, ex, ey) {
this.sx = sx;
this.sy = sy;
this.ex = ex;
this.ey = ey;
}
Line.prototype.draw = function () {
ctx.beginPath();
ctx.moveTo(this.sx, this.sy);
ctx.lineTo(this.ex, this.ey);
ctx.stroke();
};
Line.drawCrossLines = function (x, y) {
var horizontal = new Line(0, y, canvas.width, y);
var vertical = new Line(x, 0, x, canvas.height);
drawThese.push(horizontal);
drawThese.push(vertical);
}
canvas.addEventListener('click', function (e) {
Line.drawCrossLines(e.offsetX, e.offsetY);
});
//Circle code
function Circle(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
}
Circle.prototype.draw = function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
ctx.fill();
};
//Moving Circle Code that extends Circle
function MovingCircle() {
Circle.call(this,0,0,10);
}
MovingCircle.prototype = Object.create(Circle.prototype);
MovingCircle.prototype.constructor = MovingCircle;
MovingCircle.prototype.move = function (x, y) {
this.x = x;
this.y = y;
}
var pointer = new MovingCircle();
drawThese.push(pointer);
canvas.addEventListener('mousemove',function(e){
pointer.move(e.offsetX, e.offsetY);
});
//Rendering and animation code
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawThese.forEach(function (e) {
e.draw();
});
requestAnimationFrame(render);
}
I've found a working solution on my own, of course inspired by rpn. If you would like to make same or similar thing to what I this topic is about, a system including a container, canvas and a mouse event (click, in this case) you will have to follow this steps.
1. You cannot draw directly to a div, you've to create something like an invisible overlay for canvas and to place it perfectly over the div.
2. An important part is getting the proper coordinates, take into account that there are some tad but important differences between the various coords get methods. (`clientX/Y`, `offsetX/Y` and so on). It matters a lot.
3. Last but not at least, you should carefully consider how you get container's dimensions, as you'll need them later. Remember! We are in a container, a.k.a. working with an area which is just part of the whole window (your browser).
At first I was receiving my coordinates with clientX/Y but maybe due to some mismatches between the get method for coordinates and the one for div's dimensions I was hammered by bugs. To solve this, I've created a method from which I take mouse click coordinates with offsetX/Y. This way you have to think of you container as if it is a separate coordinate system, different from your window's(which by default is general). This means that now the top left corner of this div has x:y = (0;0);
function getcooords(){
xo = e.offsetX;
yo = e.offsetY;
DrawLines(x0, y0); //invoke func DrawLines
}
Now we go to the moment where we should draw our lines.
function DrawLines(x0,y0){
//your variables here...
w=$("#yourcontainer").width();
h=$("#yourcontainer").height();
Dy=0;
Dx=0;
}
That's the second step, Dx and Dy are the top and left properties of the div. Hence, from the what I've just said above, they will be equal to 0, both. Width and height of the container I take simply with jq but you can do it on another preferred way.
Generally speaking, that's the core of your drawing algorithm after we've taken needed dimensions and coordinates:
xl.beginPath();
xl.moveTo(b,yo);//start point of line
xl.lineTo(Dx,yo);//end point of line
xl.stroke(); //DRAW IT :)
Now, I've forgot to tell you that you have to define your lines at first, where should they be, what properties, styles should they have, etc..:
var xline = document.getElementById("line");
xl=xline.getContext("2d");
Remember! If you would like to delete, to erase the what you've drawn you'd have to define your lines FIRST and delete them after their definition. I'm saying definition, not drawing and you can draw the next 1, 2, 3... n lines and delete them over and over again if you follow this principle.
That's it guys, I hope I've explained it well, sorry for my English, probably I've made some tad mistakes but I hope you understand me. If you have any questions, please feel free to ask me, I'll try to do my best in order to help you.
:)

Multiple canvas polygon detection map using isPointInPath() and responsive design

I have an array of canvas objects that draw correctly. i have three problems:
Offset. I have tested the code below in JS fiddle and it works, but when i export it my web page, the variables get skewed. The detection happens, but not in the right place. the page width is set in CSS, and the actual canvas area is centered using a margin:0 auto call, however it is smaller than the page width.
<canvas id="canvas" width="780" height="690" style="position:absolute;"></canvas>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var $results = $("#results");
// define the polygon items
var polyArray = new Array (6);
polyArray [0] =[{x:50,y:236}, {x:200,y:115}, {x:350,y:50}, {x:350,y:300}, {x:232,y:325}, {x:75,y:300}];
polyArray [1] =[{x:350,y:55}, {x:350,y:300}, {x:510,y:300}, {x:510,y:205}, {x:578,y:172}, {x:690,y:96}, {x:650,y:17}];
polyArray [2] =[{x:510,y:300}, {x:510,y:200}, {x:715,y:113}, {x:780,y:200}, {x:780,y:485}, {x:625,y:468}, {x:605,y:456}, {x:605,y:428}];
polyArray [3] =[{x:0,y:446}, {x:284,y:320}, {x:255,y:540}, {x:240,y:566}, {x:73,y:600}, {x:0,y:565}];
polyArray [4] =[{x:355,y:305}, {x:510,y:305}, {x:604,y:423}, {x:604,y:460}, {x:628,y:484}, {x:610,y:513}, {x:587,y:468}, {x:537,y:426}, {x:500,y:400}, {x:447,y:424}, {x:312,y:365}, {x:307,y:314 }];
polyArray [5] =[{x:350,y:425}, {x:415,y:421}, {x:455,y:434}, {x:495,y:411}, {x:550,y:444}, {x:618,y:590}, {x:570,y:616}, {x:359,y:597}, {x:333,y:522}];
// call the function to draw all the objects in the array
define(polyArray);
// call through the array to draw the objects
function define(polygon) {
ctx.beginPath();
for (var i = 0; i < polygon.length; i++) {
ctx.moveTo(polygon[i][0].x, polygon[i][0].y);
for (var j = 1; j < polygon[i].length; j++) {
ctx.lineTo(polygon[i][j].x, polygon[i][j].y);
}
ctx.fill();
}
ctx.closePath();
}
function hitTest(polygon) {
// redefine the polygon
define(polygon);
// ask isPointInPath to hit test the mouse position
// against the current path
return (ctx.isPointInPath(mouseX, mouseY));
}
function handleMouseMove(e) {
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// check if the mouse is inside the polygon
var isInside = hitTest(polyArray);
if (isInside) {
canvas.style.cursor = 'pointer';
$results.text("Mouse is inside the area);
} else {
canvas.style.cursor = 'default';
$results.text("Outside");
}
}
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
Detecting which object has been hovered over. What needs to happen is on hover of one the array shapes should effect some CSS/JS. How can i assign an ID variable and detect it?
when i bring responsive design into the equation i'm a bit stuck for how to incorporate this offset and the poly co-ords to scale appropriately.
Any point in the right direction would be greatly appreciated.
Question#1: Getting accurate mouse position after the canvas has moved
Whenever you move your canvas (fex: margin: 0 auto), you must recalculate your offsetX and offsetY values:
If you manually change the canvas element's CSS (fex: canvas.style.margin='50px' inside javascript), then you must also manually call reOffset().
// cache the canvas's offset positions since the
// offset positions are used often
var offsetX,offsetY;
// call this once at the beginning of your app
// and whenever you change the canvas's position on the page
// (eg call when you change margins, scroll, etc)
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
// have the browser auto-reset offsetX & offsetX when
// the viewport scrolls or resizes
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
Question#2 Detecting hovers & blurs over your polygons
Your hitTest function will test if the mouse is currently inside a specified polygon. So inside handleMousemove you could call hitText for each of the polygons inside your polyArray.
Keep a flag variable indicating the index# of the last polygon the mouse was inside (or -1 to indicate the mouse was outside all polygons. When your flag variable value changes, you know there has been either a hover-event or a blur-event. Compare the last and current flag variables to determine which polygon is now hovered or blurred.
Question#3 Incorporating a responsive design
Mouse coordinates reported by the browser into e.clientX and e.clientY are always in unscaled values relative to the browser viewport.
So if you:
Click the mouse and use e.clientX/e.clientY to determine the mouse is at [100,100],
Scale your canvas: context.scale(2,2),
And reclick without moving the mouse from its original [100,100] position,
Then:
Using e.clientX/e.clientY to detect the mouse coordinates will still report the position as [100,100] even if the canvas has been scaled and the mouse is at [200,200] relative to the scaled canvas.
The fix:
You must scale the browser's reported mouse position to match the scaling factor of the canvas:
// Determine how much you want to scale the canvas
var scaleFactor=2.00;
// scale the canvas
context.scale(scaleFactor,scaleFactor);
// also scale the mouse position reported by the browser
mouseX=parseInt(e.clientX-offsetX)*scaleFactor;
mouseY=parseInt(e.clientY-offsetY)*scaleFactor;

Categories