HTML5 Canvas drag and drop multiple texts - javascript

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).

Related

Problem with JavaScript (JQuery) while working on HTML5 canvas

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.

Creating a very large scrollable (by holding a right mouse button down) Canvas with Javascript

I have been struggling with this for a very, very long time (6 months plus). There have been some partial answers to my question but I have been unable to put the available asnswer-bits together to be able to do anything useful with it. This code will be an amazing tool for all new aspiring simple canvas game developers and will greatly benefit the community for sure.
-I need to create a very large scrollable canvas (scrolled by holding right mouse button), similar to this: https://www.desmos.com/calculator , say 50k px by 50k px and this (size)should be amendable in code.
-When we scroll, the background moves and all items, of course, will need to move with this scroll.
-There have to be some measure of scroll rate in code, which is should be amendable.
-Rendering structure need to remain as in my codepen - simple constructor objects generated on screen then rendered using request animation frame. https://codepen.io/alexhermanuk/pen/bGGXLZG
var squares = [];
var Square = function(x,y,w,h,color){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
}
Square.prototype.update = function(){
this.draw();
}
Square.prototype.draw = function(){
context.save();
context.beginPath();
context.fillStyle = this.color;
context.fillRect(this.x,this.y,this.w,this.h)
context.closePath();
context.restore();
}
for(var i=0; i<10; i++){
squares.push(new Square(width*Math.random(),height*Math.random(),100,100,"red"));
}
function animateEverything(){
context.clearRect(0,0,width,height);
context.save()
squares.forEach(Square => {Square.update(squares)})
requestAnimationFrame(animateEverything);
}
animateEverything();
}
-Canvas / scrollable area need to be full screen without overflows
-Canvas background will consist of 4 virtually identical square images (2 main images and 2 mirror images) and these images are rendered/ replicated as a background as we scroll left right up or down and will create an unlimited background image (in our case it is just a wavy ocean). So, these have to be logically rendered like a tile map of some sort... There is no easy way to provide sample images, so please, bring yours along.
I appreciate YOUR effort and thank all in advance.
Here you go. It scrolls with 'right click' drag.
I wasn't sure what you meant by scroll rate. Do you want the scroll rate displayed, or controlled? Anyways, I did both to be safe. scrollRate controls have fast it scrolls, and it defaults to 1. scrollMeter is a number that loosely represents how fast the canvas is being scrolled. It is printed in the top left corner of the canvas.
And about tiling, I made a BackgoundTile class. You can create 4 different tiles with different x, y, and x_gap, y_gap to have what you want. But if you care about performance, instead of making 4 tile objects, use just one with a combined image. Use an image editor to combine the 4 squares to make one big square if possible. Tiling is almost always a performance heavy job, and a bit of optimization goes a long way.
I also added a window/canvas resize handler, so the aspect ratio doesn't go haywire. This also prevents the canvas from going low-res when you enlarge the window/canvas.
I left animateEverything as is, but I'd recommend adding more control to rendering instead of recursively calling it indefinitely.
I hope you like this demo.
CodePen
paste below on in this link https://codepen.io/alexhermanuk/pen/bGGXLZG.
I have added vertical scroll only
you can implement horizontal in same way.
window.onload = function(){
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight,
maxWidth = 15000,
maxHeight = 15000;
var origin = { x: 0, y: 0},
scrollY = 0,
isMouseDownOn = '',
scrollBarWidth = 25,
scrollThumbHeight = 45,
navButtonSize = 25,
totalScrollHeight = height-navButtonSize*2-scrollThumbHeight;
canvas.removeEventListener('mousedown', onMouseDown);
canvas.removeEventListener('mousemove', onMouseMove);
canvas.removeEventListener('mouseup', onMouseUp);
window.removeEventListener('mouseup', onMouseUp);
canvas.addEventListener('mousedown', onMouseDown);
canvas.addEventListener('mousemove', onMouseMove);
canvas.addEventListener('mouseup', onMouseUp);
window.addEventListener('mouseup', onMouseUp);
var squares = [];
var Square = function(x,y,w,h,color){
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
}
Square.prototype.update = function(){
this.draw();
}
Square.prototype.draw = function(){
var newY = this.y - origin.y;
context.save();
context.beginPath();
context.fillStyle = this.color;
context.fillRect(this.x,newY,this.w,this.h)
context.closePath();
context.restore();
}
for(var i=0; i<1000; i++){
squares.push(new Square(width*Math.random(),maxHeight*Math.random(),100,100,"red"));
}
function animateEverything(){
context.clearRect(0,0,width,height);
context.save()
console.log(' m here')
squares.forEach(Square => {
Square.update(squares)
})
drawScrollbar();
drawOrigin ();
//requestAnimationFrame(animateEverything);
}
animateEverything();
function drawScrollbar() {
var currentScroll = scrollY+navButtonSize;
context.save();
context.beginPath();
context.fillStyle = 'blue';
context.fillRect((width-navButtonSize ),0,navButtonSize,navButtonSize);
context.closePath();
context.beginPath();
context.fillStyle = 'green';
context.fillRect((width-navButtonSize ),(navButtonSize),scrollBarWidth,(height-navButtonSize*2));
context.closePath();
context.beginPath();
context.fillStyle = 'blue';
context.fillRect((width-navButtonSize ),(height-navButtonSize),navButtonSize,navButtonSize);
context.closePath();
context.beginPath();
context.fillStyle = 'yellow';
context.fillRect((width-navButtonSize),currentScroll,scrollBarWidth,scrollThumbHeight);
context.closePath();
context.restore();
}
function onMouseDown(e) {
//console.log('onMouseDown ' , e);
detectElement(e);
}
function onMouseMove(e) {
if(isMouseDownOn === 'scrollY') {
var posY = e.pageY - canvas.offsetTop;
scrollY = Math.min( (height-navButtonSize*2-scrollThumbHeight), Math.max(posY, 0) ) ;
origin.y = (scrollY/(height-navButtonSize*2-scrollThumbHeight))*maxHeight;
animateEverything();
}
}
function onMouseUp(e) {
//console.log('onMouseUp ' , e);
isMouseDownOn = '';
}
function drawOrigin () {
context.save();
context.beginPath();
context.font = "18px Arial";
context.fillText("Origin X:"+origin.x, 10, 30);
context.fillText("Origin Y:"+origin.y, 10, 50);
context.closePath();
context.restore();
}
function detectElement(e) {
isMouseDownOn = '';
var posX = e.pageX- canvas.offsetLeft;
var posY = e.pageY- canvas.offsetTop;
var currentThumbPos = scrollY+navButtonSize;
if(posX >= width-scrollBarWidth && (posY >= currentThumbPos && posY <= currentThumbPos+scrollThumbHeight) ) {
isMouseDownOn = 'scrollY';
}
}
}

HTML5: whiteboard dimension?

I'm creating a simple whiteboard using html5 canvas.
I need to give the canvas a width and height. This causes the whiteboard to not work properly.
If I remove the width and height from the canvas, it works fine!
This is my code:
var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
// Fill Window Width and Height
myCanvas.width = window.innerWidth;
myCanvas.height = window.innerHeight;
// Set Background Color
ctx.fillStyle="#fff";
ctx.fillRect(0,0,myCanvas.width,myCanvas.height);
// Mouse Event Handlers
if(myCanvas){
var isDown = false;
var canvasX, canvasY;
ctx.lineWidth = 5;
$(myCanvas)
.mousedown(function(e){
isDown = true;
ctx.beginPath();
canvasX = e.pageX - myCanvas.offsetLeft;
canvasY = e.pageY - myCanvas.offsetTop;
ctx.moveTo(canvasX, canvasY);
})
.mousemove(function(e){
if(isDown !== false) {
canvasX = e.pageX - myCanvas.offsetLeft;
canvasY = e.pageY - myCanvas.offsetTop;
ctx.lineTo(canvasX, canvasY);
ctx.strokeStyle = "#000";
ctx.stroke();
}
})
.mouseup(function(e){
isDown = false;
ctx.closePath();
});
}
// Touch Events Handlers
draw = {
started: false,
start: function(evt) {
ctx.beginPath();
ctx.moveTo(
evt.touches[0].pageX,
evt.touches[0].pageY
);
this.started = true;
},
move: function(evt) {
if (this.started) {
ctx.lineTo(
evt.touches[0].pageX,
evt.touches[0].pageY
);
ctx.strokeStyle = "#000";
ctx.lineWidth = 5;
ctx.stroke();
}
},
end: function(evt) {
this.started = false;
}
};
// Touch Events
myCanvas.addEventListener('touchstart', draw.start, false);
myCanvas.addEventListener('touchend', draw.end, false);
myCanvas.addEventListener('touchmove', draw.move, false);
// Disable Page Move
document.body.addEventListener('touchmove',function(evt){
evt.preventDefault();
},false);
#myCanvas{
width:90%;
height:200px;
border:solid 1px #c8def0;
border-radius:10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="myCanvas">
Sorry, your browser does not support HTML5 canvas technology.
</canvas>
basically, when I run the above code, the mouse doesn't go all the way down the canvas but if I remove the canvas width and height, it works fine.
Could someone please advice on this issue?
Thanks in advance.

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

How to give a preview for the line tool?

I'm trying to make a drawing app and currently working on the line tool and want to have a preview like exists in Microsoft Paint and other drawing applications.
I'm using HTML5 Canvas and Javascript and am also using typical canvas drawing API like so:
context.beginPath();
context.moveTo(originX, originY);
context.lineTo(mousePos.x, mousePos.y);
context.stroke();
However, it starts to look like this when the user is trying to determine which line he wants because it doesn't erase the previous line:
The point in the middle is the origin from which the user began drawing the line from. I can't clear the canvas every time because there are other things possibly drawn. Only solution I can think of is implementing some sort of undo functionality but that seems like it would be slow and sloppy.
Does anyone have any idea on how to implement this sort of preview functionality?
What I would recommend is using more than one canvas.
Live Demo
So basically they do the preliminary drawing on the top one that continues to clear, and once they release the mouse button it then draws it to the lower "permanent" canvas.
Code from the fiddle, figured Id add it for reference since jsFiddle has been slow lately.
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
drawCanvas = document.getElementById("drawCanvas"),
drawCtx = drawCanvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0,
curX = 0,
curY = 0,
startX = 0,
startY = 0,
lineThickness = 1;
canvas.width = canvas.height = 600;
drawCanvas.width = drawCanvas.height = 600;
drawCanvas.onmousedown = function(e) {
startX = e.pageX - this.offsetLeft;
startY = e.pageY - this.offsetTop;
painting = true;
};
drawCanvas.onmouseup = function(e){
painting = false;
ctx.strokeStyle = "#000";
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(lastX, lastY);
ctx.stroke();
drawCtx.clearRect(0, 0, 600, 600);
}
drawCanvas.onmouseclick = function(e) {
startX = e.pageX - this.offsetLeft;
startY = e.pageY - this.offsetTop;
painting = true;
};
drawCanvas.onmousemove = function(e) {
if(painting){
lastX = e.pageX - this.offsetLeft;
lastY = e.pageY - this.offsetTop;
drawCtx.clearRect(0,0,600,600);
drawCtx.beginPath();
drawCtx.moveTo(startX ,startY );
drawCtx.lineTo(lastX, lastY);
drawCtx.stroke();
}
}
​

Categories