Why do buttons on mobile browsers appear to have a larger click area? - javascript

At work, they needed a quick and dirty example to hand-sign a document and append it to JSON as a base64 Data URL. It works all fine and dandy in itself, but my own curiosity got the better of me and I was annoyed to find out, that the area of the buttons appeared to be much larger on mobile than they looked like. But even inspecting buttons in my example project didn't help me a lot to pin down the problems.
Disclaimer: I have to also point out, for people who look for a similar solution to NOT just copy-paste this example here. It's serviceable and works probably in 99% of the cases just fine for this purpose, but the drawing is not optimal. Why? Because I keep adding points indefinitely as long as the finger touches the canvas... and with each move, it goes through the array and REDRAWS all the lines! It becomes pretty noticeable after you covered a fair portion of the small canvas. It would be more elegant to go through the tracked points and kick them out of the array once drawn and not only when we raise the finger from the touchscreen. But if you merely use it to sign something like in my example, the example should be perfectly serviceable with no frills.
I did add a snippet as well. Don't forget, it's only working on mobile browsers or if you use emulation on your chrome browser, for example.
"use strict";
const canvas = document.getElementById('sign');
const context = canvas.getContext('2d');
context.lineCap = 'round';
context.lineJoin = 'round';
context.strokeStyle = 'black';
context.lineWidth = 2;
let points = [];
let isPainting = false;
setup();
function setup() {
if (isMobile() || isIpad()) {
setupMobile();
} else {
// Some other stuff that was supposed to happen, but was canned
return;
}
}
function setupMobile() {
canvas.addEventListener('touchstart', (e) => {
addToDrawingPointsTouch(canvas, e);
if (!isPainting) {
isPainting = !isPainting;
}
const ctx = context;
// Not the most elegant solution for the initial call, but touchmove takes a bit to trigger
const initialStarting = points[0];
ctx.beginPath();
ctx.moveTo(initialStarting.x, initialStarting.y);
ctx.lineTo(initialStarting.x, initialStarting.y);
ctx.stroke();
}, { passive: true })
canvas.addEventListener('touchmove', (e) => {
if (isPainting) {
addToDrawingPointsTouch(canvas, e);
const ctx = context;
points.forEach((v, index) => {
if (points[index + 1]) {
ctx.beginPath();
ctx.moveTo(v.x, v.y);
ctx.lineTo(points[index + 1]?.x, points[index + 1]?.y);
ctx.stroke();
}
})
}
}, { passive: true })
canvas.addEventListener('touchend', () => {
if (isPainting) {
isPainting = !isPainting;
points = [];
}
}, { passive: true })
}
function addToDrawingPointsTouch(canvas, mouseEvent) {
const rect = canvas.getBoundingClientRect();
points.push({
x: (mouseEvent.touches[0].clientX - rect.left) / (rect.right - rect.left) * canvas.width,
y: (mouseEvent.touches[0].clientY - rect.top) / (rect.bottom - rect.top) * canvas.height
});
}
function isMobile() {
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
return true
} else {
return false;
}
}
function isIpad() {
if (navigator.userAgent.match(/Mac/) && navigator.maxTouchPoints && navigator.maxTouchPoints > 2) {
return true;
}
else {
false;
}
}
function clearCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
function createCanvasPngDataUrl() {
return canvas.toDataURL('image/png');
}
body {
user-select: none;
-webkit-user-select: none;
touch-action: none;
-ms-touch-action: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Page Title</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link rel='stylesheet' type='text/css' media='screen' href='main.css'>
<script defer src='main.js'></script>
</head>
<body>
<form id="form">
<canvas style="border: 1px solid black;" id="sign">
</canvas>
</form>
<div id="menu">
<button onclick="createCanvasPngDataUrl()">Create DataUrl</button>
<button onclick="clearCanvas()" id="clear">Clear</button>
</div>
</body>
</html>

Related

How to make canvas lines visible when dragging (no libraries, only vanilla JS, React.JS if necessary.)

I'd like to add lines by two clicks, when moving the mouse the line should be visible. When click left mouse button again the line should be added. Only left button should draw.
How should I change my code to do this? (for now it allows to create lines,
but they aren't visible before mouseup).
Any help is appreciated! Thanks!
const canvasEl = document.getElementById('drawContainer')
canvasEl.style.position = 'absolute'
canvasEl.style.top = '12%'
canvasEl.style.left = '32%'
var lines = [], line;
const context = canvasEl.getContext('2d')
const collapseLinesBtn = document.getElementById('collapse_lines')
let startPosition = {x: 0, y: 0}
let lineCoordinates = {x: 0, y: 0}
let isDrawStart = false
const getClientOffset = (event) => {
const {pageX, pageY} = event.clicks ? event.clicks[0] : event
const x = pageX - canvasEl.offsetLeft
const y = pageY - canvasEl.offsetTop
return { x, y }
}
const initialDraw = () => {
context.beginPath() //allows to prevent previously created lines from delete
context.moveTo(startPosition.x, startPosition.y)
context.lineTo(lineCoordinates.x, lineCoordinates.y)
context.stroke()
line = [];
line.push([lineCoordinates.x, lineCoordinates.y]);
console.log(line)
}
const mouseDownListener = (e) => {
startPosition = getClientOffset(e)
//isDrawStart = true // isDrawStart = true + clearCanvas() + initialDraw() from const mouseMoveListener = one visible line when dragging + coordinates
context.beginPath();
context.moveTo(e.offsetX, e.offsetY);
context.lineTo(e.offsetX, e.offsetY);
}
const mouseMoveListener = (event) => {
if(!isDrawStart)
return
lineCoordinates = getClientOffset(event)
//clearCanvas()
//initialDraw()
//initialDraw(!isDrawStart())
}
const mouseUpListener = (e) => {
isDrawStart = false
context.lineTo(e.offsetX, e.offsetY);
context.stroke();
}
const clearCanvas = () => {
context.clearRect(0, 0, canvasEl.width, canvasEl.height)
}
canvasEl.addEventListener('mousedown', mouseDownListener)
canvasEl.addEventListener('mousemove', mouseMoveListener)
canvasEl.addEventListener('mouseup', mouseUpListener)
collapseLinesBtn.addEventListener('click', function clear() {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
#collapse_lines {
position: absolute;
bottom: 75px;
left: 47%;
padding: 10px 25px;
font-size: 1rem;
}
</style>
<title>Test Task</title>
</head>
<body>
<div id="main_container">
<canvas id="drawContainer" width="700" height="700" style="border: 1px solid rgb(10, 10, 10)" ></canvas>
<button id="collapse_lines">collapse lines</button>
</div>
<script src="./app.js"></script>
</body>
</html>
In your mouseMoveListener just do something similar as you do in your mouseUpListener, without setting isDrawStart = false:
const mouseMoveListener = (event) => {
if(!isDrawStart)
return
lineCoordinates = getClientOffset(event)
// Clear the canvas each frame
clearCanvas()
// Similar to mouseUpListener
context.lineTo(lineCoordinates.x, lineCoordinates.y);
context.stroke();
}

Draw rectangle over uploaded image

Once I upload an image I make 2 clicks over it. That's how (x1,y1) and (x2,y2) are obtained. Given these 4 numbers I waanna draw a rectangle over the image by means for example of P5.js. Then I should say rect(x1,y1,x2,y2) but this never happens. How can I deal with this problem (maybe not by P5)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue Test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<button v-on:click="isHidden = false">Load img</button>
<img onclick="showCoords(event)" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
<script>
var test = new Vue({
el: '#app',
data: {
src: ["cat.jpg", "dog.jpg"],
z: Math.round(Math.random()),
isHidden: true,
}
})
var k=0;
var koors = [];
var flag = false;
function showCoords(event) {
if (k===0){
koors[0] = event.clientX;
koors[1] = event.clientY;
k+=1;
} else if (k===1){
flag = true;
koors[2] = event.clientX;
koors[3] = event.clientY;
}
if ((koors[3] != 0) && (flag)){
console.log(koors)
}
}
//p5
function setup(){
}
function draw(){
console.log(koors[0],koors[1],koors[2],koors[3]);
rect(koors[0],koors[1],koors[2],koors[3])
}
</script>
<script src="p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="js.js"></script>
</body>
</html>
P5 needs a canvas for rendering. If you don't initialize one, it creates it himself (and you are in trouble).
Also, P5 is powerful library, it has tools for events, image processing etc. For current example I wouldn't use any other library (vue).
I created canvas on top of the image (css) and rest is playing with P5.
var test = new Vue({
el: '#app',
data: {
src: [ "https://i.stack.imgur.com/XZ4V5.jpg", "https://i.stack.imgur.com/7bI1Y.jpg"],
z: Math.round(Math.random()),
isHidden: false,
}
})
var recStart;
var coords = {};
/*******************************************************************
* P5 setup
* run once, use for initialisation.
*
* Create a canvas, change dimensions to
* something meaningful(like image dim)
********************************************************************/
function setup(){
var canvas = createCanvas(480, 320);
canvas.parent('app');
}
/*******************************************************************
* P5 draw
* endless loop.
*
* Lets redraw rectangle until second click.
********************************************************************/
function draw(){
if(recStart)
drawRect();
}
/*******************************************************************
* drawRect
*
* Draw a rectangle. mouseX and mouseY are P5 variables
* holding mouse current position.
********************************************************************/
function drawRect(){
clear();
noFill();
stroke('red');
rect(coords.x, coords.y, mouseX-coords.x, mouseY-coords.y);
}
/*******************************************************************
* P5 mouseClicked
*
* P5 event. Again, mouseX and mouseY are used.
********************************************************************/
mouseClicked = function() {
if (mouseButton === LEFT) {
if(!recStart){ // start rectangle, give initial coords
coords.x = mouseX;
coords.y = mouseY;
recStart = true; // draw() starts to draw
} else {
recStart = false; // stop draw()
drawRect(); // draw final rectangle
coords = {}; // clear coords
}
}
};
canvas {
width: 500px;
height:500px;
z-index: 2;
border:1px solid;
position:absolute;
left: 0;
top: 0;
}
img {
z-index: 1;
position: absolute;
border: 2px solid black;
left: 0;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<img id="pilt" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
It seems that you don't call draw anywhere. You may also consider draw the image into a canvas and than draw rect in this canvas. Checking if koors[3] != 0 is really risky as user may click at 0th pixel.

Command fillRect() in Javascript does not work

I'm currently undergoing the development of a game, but I've stumbled across a problem where the fillRect() command will not work onto the HTML5 canvas, using Javascript. I do not know why this is happening, after trying to do research on it and checking my code. The HTML code is shown below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Cube Quest</title>
<style>
#game {
border: 1px dashed black;
}
</style>
</head>
<body>
<canvas id="game" width='1280' height='720'>Your browser does not support the canvas element in HTML5.</canvas>
<script>
var clicked = false; // Mouse handling event
var mouseX = event.cilentX; // Mouse X coordinate
var mouseY = event.cilentY; // Mouse Y coordinate
var canvas = document.getElementById("game"); // For canvas
var ctx = canvas.getContext("2d"); // For canvas
ctx.fillStyle = 'black'; // rectangle color selection
ctx.fillRect(10, 10, 150, 80);
</script>
</body>
</html>
I'm not the best expert on Javascript, so there is little that I know which could help me understand the reason why no rectangle shows when the code is correct.
I appreciate the help on this specific question in advance. :)
You will need to look addEventListener function in JS to made better view of situation.
Here working example :
// globals
var canvas = document.getElementById("game");
var clicked = false; // Mouse handling event
var mouseX = 0;
var mouseY = 0;
// yuor application parameters
var app = {
clearCanvas: true,
title: 'HELLO'
};
canvas.addEventListener('click' , function (event){
mouseX = event.pageX; // Mouse X coordinate
mouseY = event.pageY; // Mouse Y coordinate
console.log("Mouse x : " + mouseX + " Mouse y :" + mouseY );
drawAgain();
});
// Initial draw
var ctx = canvas.getContext("2d"); // For canvas
ctx.fillStyle = 'black'; // rectangle color selection
ctx.fillRect(mouseX, mouseY, 350, 65);
ctx.font="30px Arial";
ctx.fillStyle = 'lime';
ctx.fillText(app.title + "X:" + mouseX + " Y:" + mouseY , mouseX + 35, mouseY + 40, 250)
// Draw when you need
function drawAgain () {
if (app.clearCanvas == true){
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
ctx.fillStyle = 'black'; // rectangle color selection
ctx.fillRect(mouseX, mouseY, 350, 65);
ctx.fillStyle = 'lime';
ctx.fillText(app.title + " X:" + mouseX + " Y:" + mouseY , mouseX + 35, mouseY + 40, 400)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Cube Quest</title>
<style>
#game {
border: 1px dashed black;
}
</style>
</head>
<body>
<canvas id="game" width='1280' height='720'>Your browser does not support the canvas element in HTML5.</canvas>
<script>
</script>
</body>
</html>
Suggestion: Also learn to use removeEventListener , lot of web
developers have trouble with event conflict situation when they use
lot of lib's. For dynamic app flow methodology use removeEventListener
before setting a flags.
Having error in event.cilentX because event is not available at this moment so that is not moving to next lines of code to execute. If you want to play with event you need to attache any event listener like canvas.addEventListener('click' , function (event){//here you will get the event});. I just commented the two line now it working fine to draw rectangle.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Cube Quest</title>
<style>
#game {
border: 1px dashed black;
}
</style>
</head>
<body>
<canvas id="game" width='1280' height='720'>Your browser does not support the canvas element in HTML5.</canvas>
<script>
var clicked = false; // Mouse handling event
// var mouseX = event.cilentX; // Mouse X coordinate
// var mouseY = event.cilentY; // Mouse Y coordinate
var canvas = document.getElementById("game"); // For canvas
var ctx = canvas.getContext("2d"); // For canvas
ctx.fillStyle = 'black'; // rectangle color selection
ctx.fillRect(10, 10, 150, 80);
</script>
</body>
</html>
It's your mouse event listeners that are breaking the program.
Comment them out and it works just fine.
Here is a code snippet I use for small JavaScript games I test out.
//Mouse Events ==================
document.onmousemove = mousePos;
document.onmousedown = function() { mouse.clicked = true; };
document.onmouseup = function() { mouse.clicked = false; };
//MOUSE
var mouse = {
x: 0,
y: 0,
clicked: false
};
function mousePos (e) {
mouse.x = e.pageX - canvas.offsetLeft;
mouse.y = e.pageY - canvas.offsetTop;
}

The Process of Clearing and Redrawing a Square on a HTML Canvas

I have been working on some HTML/Javascript Code and the main thing I want to have happen is when you press any of the arrow keys, the square that I drew moves that direction.
My general train of thought on what I have to do, is when I click on the key, it first clears the canvas, then redraws it with a different x and y value based on the function.
I tried that, but what happens is that the square just vanishes and I have no clue why.
I have been attempting to debug this for a while, and if anyone has any pointers I would love to here them!
<!DOCTYPE html>
<html>
<head>
<title> HTML </title>
<style>
canvas {
border: 1px solid #000000;
}
</style>
</head>
<body>
<center>
<canvas width = "620" height = "620" id = "myCanvas" ></canvas>
</center>
<script src = "script.js" > </script>
</body>
</html>
This is the Javascript Code (Sorry for not well formatting, still getting the hang of formatting code in StackOverflow)
document.addEventListener('keydown', function(event) {
if(event.keyCode == 37)
moveLeft();
if(event.keyCode == 39 )
moveRight();
if(event.keyCode == 38)
moveUp();
if(event.keyCode == 40)
moveDown();
});
function moveLeft() {
context.clearRect(0, 0, canvas.width, canvas.height);
//Edit X and Y Values
var newX, newY;
newX = squareX + 1;
newY = squareY;
context.rect(newX, newY, squareSizeX, squareSizeY);
context.fill(newX, newY, squareSizeX, squareSizeY);
}
function moveRight() {/* Needs to be Finished */}
function moveUp() {/* Needs to be Finished */}
function moveDown() {/* Needs to be Finished */}
var canvas;
var context;
var squareX, squareY;
var squareSizeX = 75;
var squareSizeY = 75;
function renderToCanvas() {
var didRender = false;
try {
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");
/* INPUT DRAWING HERE */
//Drawing Square
squareX = canvas.width / 2;
squareY = canvas.height / 2;
context.rect(squareX, squareY, squareSizeX, squareSizeY);
context.fillRect(squareX, squareY, squareSizeX, squareSizeY);
context.stroke();
/* END DRAWING HERE */
didRender = true;
console.log("Rendered Drawing: " + didRender);
}
catch(err) {
console.log("Rendered Drawing: " + didRender);
console.log(err.message);
}
}
renderToCanvas();
In your moveLeft instead of this
context.rect(newX, newY, squareSizeX, squareSizeY);
context.fill(newX, newY, squareSizeX, squareSizeY);
Do this
context.fillRect(newX, newY, squareSizeX, squareSizeY);
Check out the docs for Canvas Context
You only use context.rect() or context.fill() when you are working on a path using context.beginPath()

Add Mouse Events for Canvas Drawing

I need to add mouse event via Javascript to below code... I have already added touch events in order to test in desktop browsers I need to add mouse events .. I tried adding mouse event to addEventListener but seems to not work I'm not pretty sure what was wrong...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=768px, maximum-scale=1.0" />
<title>rsaCanvas</title>
<script type="text/javascript" charset="utf-8">
window.addEventListener('load',function(){
// get the canvas element and its context
var canvas = document.getElementById('rsaCanvas');
var insertImage = document.getElementById('insert');
var context = canvas.getContext('2d');
//load image and annotation method
var loadData = {
imageLoad: function(){
var img = new Image();
img.src = 'the_scream.jpg';
context.drawImage(img,0,0);
}
};
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function(coors){
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function(coors){
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function(coors){
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event){
// get the touch coordinates
var coors = {
x: event.targetTouches[0].pageX,
y: event.targetTouches[0].pageY
};
// pass the coordinates to the appropriate handler
drawer[event.type](coors);
}
// attach the touchstart, touchmove, touchend event listeners.
canvas.addEventListener('touchstart',draw, false);
canvas.addEventListener('touchmove',draw, false);
canvas.addEventListener('touchend',draw, false);
insertImage.addEventListener('click',loadData.imageLoad, false);
// prevent elastic scrolling
document.body.addEventListener('touchmove',function(event){
event.preventDefault();
},false); // end body.onTouchMove
},false); // end window.onLoad
</script>
<style>
#rsaCanvas{border:5px solid #000;}
</style>
</head>
<body>
<div id="container">
<canvas id="rsaCanvas" width="400" height="500">
Sorry, your browser is not supported.
</canvas>
<button id="insert">Insert Image</button>
</div>
</body>
</html>
try this one.
function init () {
// ...
// Attach the mousemove event handler.
canvas.addEventListener('mousemove', ev_mousemove, false);
}
// The mousemove event handler.
var started = false;
function ev_mousemove (ev) {
var x, y;
// Get the mouse position relative to the canvas element.
if (ev.layerX || ev.layerX == 0) { // Firefox
x = ev.layerX;
y = ev.layerY;
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX;
y = ev.offsetY;
}
// The event handler works like a drawing pencil which tracks the mouse
// movements. We start drawing a path made up of lines.
if (!started) {
context.beginPath();
context.moveTo(x, y);
started = true;
} else {
context.lineTo(x, y);
context.stroke();
}
}
source http://dev.opera.com/articles/view/html5-canvas-painting/
if your problem is with insertImage.addEventListener('click',loadData.imageLoad, false); not showing image when u click, it is because
imageLoad: function(){
var img = new Image();
img.src = 'the_scream.jpg';
context.drawImage(img,0,0);
}
Note img.src is async, to draw the image do
img.onload = (function() {
var image = img;
return function() {context.drawImage(image, 0, 0);};
})();

Categories