Problem with JavaScript (JQuery) while working on HTML5 canvas - javascript

I am creating a drawing tool using HTML, CSS and JavaScript(JQuery) on canvas.
In desktop, it starts drawing when I click on the canvas and continues to draw even if I released the click and move the mouse ( I want it to draw only when I am holding the click and moving the mouse).
When it comes to mobile, it starts drawing after 2 touches (I don't know why) and draws as expected i.e. only when I am holding it. But here the problem is, when I touch somewhere else, instead of drawing a dot where I touched, it draws a line from where I stopped previously till the current location( for example, if I stopped at bottom-left of page previously and again touch at top-right of the page to start, it draws a line across).
I've added a lot of stuff (relevant or not I don't know) by looking at other answers but it doesn't work. Please help me out with these 2 problems. Thank you.
Here's the JS code after removing the part which I know is not causing problem:
$(document).ready(function(){
let color = "black";
let x;
let y;
let isPressed;
const canvas = $("canvas");
const colorElement = $("#color");
const ctx = canvas[0].getContext("2d");
const canvass = document.getElementById("myCanvas");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
let prevX,prevY;
canvas.on("mousedown",(e)=>{
isPressed = true;
prevX = x;
prevY = y;
x = e.pageX - ctx.canvas.offsetLeft;
y = e.pageY - ctx.canvas.offsetTop;
});
$("document").on("mouseup",(e)=>{
isPressed = false;
x = undefined;
y = undefined;
});
canvas.on("mousemove",(e)=>{
if(isPressed){
e.preventDefault(); /*added this after looking at other answers*/
e.stopPropagation(); /*added this after looking at other answers*/
x = e.pageX - ctx.canvas.offsetLeft;
y = e.pageY - ctx.canvas.offsetTop;
drawCircle(x,y);
drawLine(prevX,prevY,x,y);
prevX = x;
prevY = y;
}
});
/*for the app to work on mobile devices*/
canvass.addEventListener("touchmove", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousemove", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvass.dispatchEvent(mouseEvent);
}, false);
canvass.addEventListener("touchdown", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mousedown", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvass.dispatchEvent(mouseEvent);
}, false);
canvass.addEventListener("touchup", function (e) {
var touch = e.touches[0];
var mouseEvent = new MouseEvent("mouseup", {
clientX: touch.clientX,
clientY: touch.clientY
});
canvass.dispatchEvent(mouseEvent);
}, false);
function drawCircle(x,y){
ctx.beginPath();
ctx.arc(x,y,size,0,Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
function drawLine(x,y,x2,y2){
ctx.beginPath();
ctx.moveTo(x,y);
ctx.lineTo(x2,y2);
ctx.strokeStyle = color;
ctx.lineWidth = size*2;
ctx.stroke();
ctx.closePath();
}
colorElement.on("change",(e)=>{
color = e.target.value;
});
});

For the touch problem you have to use touchstart instead of touchdown.
More info here: https://www.w3schools.com/jsref/event_touchstart.asp
The mouse problem seems to be due to the event listener being linked to $("document") instead of canvas like the other ones. Probably the mousemove event fires before the mouseup event and prevents bubbling up with e.preventDefault(); e.stopPropagation();
Try deleting these lines and/or linking the mouseup event to canvas itself.

Related

Getting Error "addEventListener is not a function"

I want to pick my color from the color picker and then draw with this color in my canvas.
I get the Error:
Uncaught TypeError: colors.addEventListener is not a function
at window.onload
I use the npm color picker.
I get the Error at the call of the function:
colors.addEventListener('click', function(event)
My html:
<div class="picker"
acp-color="#EFE9E7"
acp-palette="#f44336|#e91e63|#9c27b0|#673ab7|#3f51b5|#2196f3|#03a9f4|#00bcd4|
#009688|#4caf50|#8bc34a|#cddc39|#cddc39|#ffeb3b|#ffc107|#ff9800|#ff5722|#795548|#9e9e9e|#20707|#befe7ee|#9e9e9e|#9e9e5e|#9e4e9e">
</div>
<div class="right-block">
<canvas id="paint-canvas" width="640" height="400"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/a-color-picker#1.1.8/dist/acolorpicker.js"></script>
<script src="./paint.js"></script>
My js code
var colors="#f44336";
AColorPicker.from('.picker')
.on('change', (picker, color) => {
colors= color;
});
var canvas = document.getElementById("paint-canvas");
var context = canvas.getContext("2d");
var boundings = canvas.getBoundingClientRect();
window.onload = function () {
// Definitions
// Specifications
var mouseX = 0;
var mouseY = 0;
context.strokeStyle = 'black'; // initial brush color
context.lineWidth = 1; // initial brush width
var isDrawing = false;
loadInput.addEventListener('change', (event) => this.load(event));
// Handle Colors
colors.addEventListener('click', function(event) {
context.strokeStyle = event.target.value || 'black';
});
// Mouse Down Event
canvas.addEventListener('mousedown', function(event) {
setMouseCoordinates(event);
isDrawing = true;
// Start Drawing
context.beginPath();
context.moveTo(mouseX, mouseY);
});
// Mouse Move Event
canvas.addEventListener('mousemove', function(event) {
setMouseCoordinates(event);
if(isDrawing){
context.lineTo(mouseX, mouseY);
context.stroke();
}
});
// Mouse Up Event
canvas.addEventListener('mouseup', function(event) {
setMouseCoordinates(event);
isDrawing = false;
});
// Handle Mouse Coordinates
function setMouseCoordinates(event) {
mouseX = event.clientX - boundings.left;
mouseY = event.clientY - boundings.top;
}
};
You cannot add an event listener to a string.
var colors="#f44336";
colors.addEventListener('click', function(event) {
context.strokeStyle = event.target.value || 'black';
});
String does not have this method. Given the assumption it remains a string after the picker on change event.
The thing is that, you need to change your context.strokeColor when the event fires up on the color change when picked, So you should Create a reference of the AColorPciker element and use its event emitter when your document is loaded
Then you can easily change your strokeColor, I hope this works for you.
I am adding the edited script, which should work for you.
// Initializing the color picker reference
var colorPicker = AColorPicker.from('.picker');
var canvas = document.getElementById("paint-canvas");
var context = canvas.getContext("2d");
var boundings = canvas.getBoundingClientRect();
window.onload = function () {
// Definitions
// Specifications
var mouseX = 0;
var mouseY = 0;
context.strokeStyle = 'black'; // initial brush color
context.lineWidth = 1; // initial brush width
var isDrawing = false;
// Here I am using the refernce of AColorPicker to set the context Stroke color, when the change is emitted on the color change
colorPicker.on('change', (picker, color) => {
context.strokeStyle = color;
});
// Mouse Down Event
canvas.addEventListener('mousedown', function(event) {
setMouseCoordinates(event);
isDrawing = true;
// Start Drawing
context.beginPath();
context.moveTo(mouseX, mouseY);
});
// Mouse Move Event
canvas.addEventListener('mousemove', function(event) {
setMouseCoordinates(event);
if(isDrawing){
context.lineTo(mouseX, mouseY);
context.stroke();
}
});
// Mouse Up Event
canvas.addEventListener('mouseup', function(event) {
setMouseCoordinates(event);
isDrawing = false;
});
// Handle Mouse Coordinates
function setMouseCoordinates(event) {
mouseX = event.clientX - boundings.left;
mouseY = event.clientY - boundings.top;
}
};

How do i get update to stop running when I click?

Here is a code segment where i am trying to make update stop running so that I can put a dot on the canvas. When I try to get putPoint to return clicked = true, it makes clicked equal true regardless if I click or not. I just want to return true if and only if I click.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
//canvas.width = window.innerWidth;
//canvas.height = window.innerHeight;
var radius = 2;
var dragging = false; // wether or not the mouse button is held down
ctx.lineWidth = radius * 2;
var canvasPos = getPosition(canvas);
var mouseX = 0;
var mouseY = 0;
var clicked = false;
// here is where I declare clicked = true globally
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(mouseX, mouseY, radius, 0, 2 * Math.PI, true);
ctx.fill();
requestAnimationFrame(update);
}
canvas.addEventListener("mousemove", setMousePosition, false);
function setMousePosition(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
//console.log("before update " + clicked);
if (clicked != true) {
update();
//console.log("inside update " +clicked);
}
// here is the code I want to stop when I click
//console.log("after update " + clicked);
function putPoint() {
//e.clientX and e.clientY get the mouse position
ctx.beginPath();
//e.clientX and e.clientY get the mouse position
ctx.arc(mouseX - 10, mouseY - 10, radius, 0, Math.PI * 2);
//ctx.arc(e.offsetX, e.offsetY, radius, 0, Math.PI * 2);
ctx.fill();
//console.log("inside putPoint " + clicked);
}
//putPoint puts a dot on the canvas at the mouse position. But it wont fire unless
//I stop update, which tracks my dot.
//console.log("after putPoint " + clicked);
canvas.addEventListener("mousedown", putPoint);
//console.log(putPoint());
//console.log(clicked);
function getPosition(el) {
var xPosition = 0;
var yPosition = 0;
while (el) {
xPosition += (el.offsetLeft - el.scrollLeft + el.clientLeft);
yPosition += (el.offsetTop - el.scrollTop + el.clientTop);
el = el.offsetParent;
}
return {
x: xPosition,
y: yPosition
};
}
<canvas id=myCanvas>
</canvas>
below is a smaller reproduction of the problem. basically I am trying to update my variable to true when i click on the element. But when I return true or even set clicked to to true within the test function, it still reads true wether I click or not. It doesnt dynamically change. Maybe Im using the wrong event ? im not sure.
var clicked = false;
console.log(clicked);
function test () {
return true;
}
clicked = test();
console.log(clicked);
document.getElementsByTagName("h1")[0].addEventListener("mousedown", test);
I'm inferring a bit based on clues from both the used and unused (e.g. dragging variable) parts of your first snippet, but it seems to me like you are trying to draw a point that tracks with your mouse, and then once a click event has occurred you want to start drawing points dragging after the mouse until that click is released.
First, your issue with your 'clicked' tracking
I think you are misunderstanding when different statements are executed. In your second snippet all of the statements outside of the 'test' event handler function are only executed once. The 'test' function will be called with each mouse click, but simply returns true and does not change the value of 'clicked'. So, the statement:
var clicked = false;
...and the statement:
clicked = test();
...each only execute once. Here is a quick example to show you how you could track the toggling of that value. Try a simple click and also holding the click for a second before releasing to get the idea.
var clicked = false;
var clickableArea = document.getElementById("clickable");
clickableArea.addEventListener('mousedown', function() {
clicked = true;
console.log('Clicked, value of clicked var: ' + clicked);
});
clickableArea.addEventListener('mouseup', function() {
clicked = false;
console.log('Released, value of clicked var: ' + clicked);
});
<div id="clickable">Click Me</div>
What I think you are going for with your canvas rendering:
Move the mouse around and then click and drag the mouse.
var canvas, ctx;
var radius = 2;
var mouseX = 0;
var mouseY = 0;
var clicked = false;
var dragging = false;
// manages the drawing cycle
function putPoint() {
// clear the canvas if not dragging, or just before the first draw of a dragging cycle
if(!dragging) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
dragging = clicked;
// draw
var offset = dragging ? 10 : 0;
ctx.beginPath();
ctx.arc(mouseX-offset, mouseY-offset, radius, 0, 2 * Math.PI, true);
ctx.fill();
// kick off another cycle
requestAnimationFrame(putPoint);
}
// event handlers
function trackMouse(e) {
mouseX = e.clientX;
mouseY = e.clientY;
}
function startedDragging() {
clicked = true;
}
function quitDragging() {
clicked = false;
dragging = false;
}
// only runs once when called below, sets things up, starts the drawing cycle
function start() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ctx.lineWidth = radius * 2;
// attach events to handlers
canvas.addEventListener("mousemove", trackMouse, false);
canvas.addEventListener("mousedown", startedDragging);
canvas.addEventListener("mouseup", quitDragging);
requestAnimationFrame(putPoint);
}
start(); // calling start to kick things off
<canvas id="myCanvas">
</canvas>

some questions about using mouse (move event) to draw lines with quintus engine

I have some questions about using mouse (move event) to draw lines with quintus engine. Can someone help me ?
Here's the thing.
I want to draw lines with mouse movement in the quintus scene,my code is like:
window.addEventListener("load",function(){
var Q = window.Q = new Quintus()
.include("Sprites, Scenes, 2D, Anim, UI")
.setup({maximize:true});
Q.Sprite.extend("Balloon",{
init:function(p){
this._super(p,{
x:100+Math.floor(Math.random()*12)*100,
y:200,
sheet:"balloon",
sprite:"balloon",
frame:Math.floor(Math.random()*12),
points:[[-50,50],[-50,-50],[50,-50],[50,50]]
});
},
});
Q.scene("start",function(stage){
stage.insert(new Q.Balloon());
});
Q.load("balloon.png",function(){
Q.sheet("balloon","balloon.png",{sx:0,sy:1,cols:12,tilew:100,tileh:100,frames:12});
Q.stageScene("start");
});
var canvas = document.getElementById("quintus");
canvas.setAttribute("width", "1920px");
canvas.setAttribute("height", "555px");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
var ctx = canvas.getContext("2d");
var listener_move = function(event){
console.log(ctx);
ctx.lineWidth = 5;
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(ctx.lastX, ctx.lastY);
ctx.lineTo(event.clientX,event.clientY);
ctx.stroke();
ctx.lastX = event.clientX;
ctx.lastY = event.clientY;
};
var listener_down = function(event){
ctx.lineWidth = 5;
ctx.strokeStyle = "#000000";
ctx.lastX = event.clientX;
ctx.lastY = event.clientY;
document.body.addEventListener("mousemove",listener_move,false);
};
var listener_up = function(event){
document.body.removeEventListener("mousemove",listener_move,false);
};
document.body.addEventListener("mousedown",listener_down,false);
document.body.addEventListener("mouseup",listener_up,false);
});
however, I get my ballon sprite ,and my log can capture the event.clientX and event.clientY , but there is no lines appear on the screen. I don't know why.
Can somebody help me ???
I have not used the quintus engine, but your mouse events and drawing code looks OK. So I think that perhaps the quintus engine is erasing and drawing on top of your ctx output, via animation-frame or some such functionality.
(Oh, and also make sure that the html canvas really has the id set to "quintus" )

Resize and move rectangles drawn on canvas

I am allowing the user to draw rectangles on an image. At the same time , the user should be able to resize or move any of the rectangles at any point of time.
With some help, i have been able to draw the rectangles but i am unable to come up with resizing and moving part of it.
The rectangles that are being drawn do not overlap one another and the same has to be validated while resizing and moving too.
I am using javascript and jquery.
This is a demo of what i have done so far :
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvasOffset = $("#canvas").offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var startX;
var startY;
var isDown = false;
ctx.strokeStyle = "lightgray";
ctx.lineWidth = 3;
function handleMouseDown(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousedown stuff here
startX = mouseX;
startY = mouseY;
isDown = true;
}
function handleMouseUp(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
$("#uplog").html("Up: " + mouseX + " / " + mouseY);
// Put your mouseup stuff here
isDown = false;
}
function handleMouseMove(e) {
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
if (!isDown) {
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawRectangle(mouseX, mouseY);
}
function drawRectangle(mouseX, mouseY) {
var width = mouseX - startX;
var height = mouseY - startY;
ctx.beginPath();
ctx.rect(startX, startY, width, height);
ctx.stroke();
}
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
as i am running short of time and i am not able to figure out how this can be done.
AFAK, HTML canvas element is simply an array of pixels.
Then drawing/moving/resizing rectangles is, simply again, to keep redrawing canvas.
So first, drawn objects need to be stored (maybe in array).
Second, corresponding mouse events are necessary.
Last, canvas redrawing is required.
Like:
var boxes = [];
var tmpBox = null;
document.getElementById("canvas").onmousedown = function(e) {...};
document.getElementById("canvas").onmouseup = function(e) {...};
document.getElementById("canvas").onmouseout = function(e) {...};
document.getElementById("canvas").onmousemove = function(e) {...};
Here is JSFiddle for demo: https://jsfiddle.net/wiany11/p7hxjmsj/14/
These 2 tutorials explain what you want:
http://simonsarris.com/blog/510-making-html5-canvas-useful
http://simonsarris.com/blog/225-canvas-selecting-resizing-shape
In short you should store the borders of the rectangles yourself and detect when the user clicks in the rectangle or on the border.
First you create an array to store your rectangles in
var rectangles = [];
Then you make a method to call every time you want to draw all your rectangles
function drawRectangles() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(var i = 0; i < rectangles.length; i++) {
var rect = rectangles[i];
ctx.beginPath();
ctx.rect(rect.startX, rect.startY, rect.endX, rect.endY);
ctx.stroke();
ctx.closePath();
}
}
In your mouseUp you then push the rectangles you have created to the array
function handleMouseUp() {
...
// store the rectangle as an object in your array
var rectangle = {startX: startX, endX: mouseX, startY: startY, endY: mouseY};
rectangles.push(rectangle);
drawRectangles();
}
In your other handlers you can then detect if you click in a rectangle of when your mouse will move in one
You can't just draw objects onto the canvas if you want to move them. You need to create instances of your shape objects and manage those (hit-testing and rendering as required). It is not very complex, but requires a lot more code than you have so far.
Try this tutorial: http://simonsarris.com/blog/510-making-html5-canvas-useful

HTML5 Canvas drag and drop multiple texts

I have an application where the user has the ability to add captions, my only problem is that I am having problem with dragging and dropping multiple texts. With the usual mousedown, mousemove, mouseup events I am only able to drag and drop one text, I want to have the ability to drag and drop multiple texts however I don't have a clear approach to this problem. Any help would be much appreciated.
UPDATE: My code is messed up when I am trying to drag both of the texts, but I will post it anyways.
thanks
<html>
<body>
<canvas id = 'canvas'></canvas>
<textarea id = 'topCaption'></textarea>
<textarea id = 'bottomCaption'></textarea>
<script type = 'text/javascript'>
window.addEventListener('load',initCanvas,false);
function initCanvas(e)
{
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
canvas.height = 500;
canvas.width = 500;
mouse = {x:0,y:0};
dragging = false;
topCap = document.getElementById('topCaption');
bottomCap = document.getElementById('bottomCaption');
topX = 100; //top x position
topY = 100; //top y position
botX = 300; //bottom x position
botY = 300; //bottom y position
canvas.addEventListener('mousemove',MouseMove,false);
canvas.addEventListener('mouseup',MouseUp,false);
canvas.addEventListener('mousedown',MouseDown,false);
window.addEventListener('keyup',KeyUp,false);
return setInterval(keyup,10)
}
function clear()
{
context.clearRect(0,0,canvas.width,canvas.height);
}
function text(Caption,x,y)
{
context.fillStyle = '#000';
context.font = '45px Impact'; //'bold 45px impact';
context.textAlign = 'center';
context.lineCap = 'round';
context.lineJoin = 'round';
context.fill();
context.stroke();
context.fillText(Caption,x,y);
};
function MouseMove(event){
mouse.x = event.pageX - canvas.offsetLeft;
mouse.y = event.pageY - canvas.offsetTop;
if(dragging)
{
context.lineTo(mouse.x,mouse.y);
}
}
function MouseDown(event)
{
dragging = true;
setInterval(function(){
topX = mouse.x;
topY = mouse.y;
botX = mouse.x;
botY = mouse.y;
},10)
}
function MouseUp(event)
{
if(dragging)
{
dragging = false;
}
}
function KeyUp(event)
{
clear();
text(topCap.value.toUpperCase(),topX,topY);
text(bottomCap.value.toUpperCase(),botX,botY);
}
</script>
</body>
</html>
Sounds like you understand basic dragging by listening to mouse events, so here's the outline of a method to drag multiple items:
Listen for mousedown, mouseup and mousemove.
If you get a mousedown+mouseup inside a text boundingbox with <10px of mousemove in-between, "select" this text (maybe add its reference to a "selected" array)
If you get a mousedown followed by 10+ pixels of mousemove, its a "drag" (move all text in the "selected" array).

Categories