User Editable Vector Shapes on a HTML5 Canvas - javascript

I want to be able to draw some basic vector shapes onto a HTML5 Canvas element.
The user should be able to use the mouse to directly draw any of these:
Square
Circle
Polygon
Onto the Canvas - these will later be used for hotspots on an image.
It is important that the user should be able to draw these vector shapes themselves by directly clicking within the canvas.
Is there an existing library that will help with this?
If there is no library, consider that I am an experienced web designer with an excellent knowledge of javascript, but I have very little experience with programming vectors. Where is the best place for me to go to fix this so I can build what I need?

fabricjs.com is your friend in this case ;)

You can use this little snippet for your first experiments...
for individual circles you must use:
mousedown (getCoordinates)
mouseup (getCoordinates)
Calculate the distance between these two points, that´s the radius.
Build an html options list for Square, Circle and Polygon.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas</title>
<style>
canvas{border:1px solid #ccc;}
</style>
</head>
<body>
<canvas id="myCanvas" width="200" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
canvas.addEventListener('click', function(evt) {
var mousePos = getMousePos(canvas, evt);
context.beginPath();
context.arc(mousePos.x,mousePos.y,5,0,2*Math.PI);
context.stroke();
}, false);
</script>
</body>
</html>

Related

How can I achieve a camera/movement style similar to Slither.io?

I'm aware that this has been asked a few times, but they are all using other libraries and I need it to work using just P5.js and JavaScript.
I have a basic game setup where players join a room using Express and Socket.io and spawn in as a ship. I want there to be a playable map area, and for what the client sees to be a section of that map with the client's ship in the middle
How can I achieve this with the HTML canvas through p5.js?
One way I've tried is to map the ship's coordinates on the whole playable area to just the width of the client's browser. This works well, but doesn't have the client's ship in the centre so they just drift off the screen if the move around enough.
Here's a diagram to explain what I'm trying to achieve:
The black box represents the whole playable area (the map).
Each green circle is a ship and each red box represents the client who is controlling that ship and the section of the map that they can see
What you're looking for is the translate function:
specifies an amount to displace objects within the display window. The x parameter specifies left/right translation, the y parameter specifies up/down translation.
You want the player to be the centre of their screen, thus you should translate such that the player is at the centre.
translate(width/2 - player.x, height/2 - player.y);
I've put together a really simple example that should point you in the right direction. It's basically just a circle that you can move around a game world, I've added a couple rectangles in so it's obvious you're moving the player with the arrow keys:
let player;
function setup() {
createCanvas(400, 400);
player = {
x: width/2,
y: height/2
}
}
function draw() {
background(220);
translate(width/2 - player.x, height/2 - player.y);
rect(-100, 100, 50, 50);
rect(100, 50, 50, 50);
if (keyIsDown(LEFT_ARROW)) {
player.x--;
} else if (keyIsDown(RIGHT_ARROW)) {
player.x++;
} else if (keyIsDown(UP_ARROW)) {
player.y--;
} else if (keyIsDown(DOWN_ARROW)) {
player.y++;
}
ellipse(player.x, player.y, 10, 10);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
Also, here's a link to the p5.js sketch

Canvas arcTo() method

I am playing with HTML5 canvas , my book says that
latest browser supports arcTo method and it has capabilities to remove
arc() function .
My question is how?
Also I am confused with this example of arcTo , why its getting formed in this way can someone explain
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<canvas id="canvas" width="500" height="500" ></canvas>
<script>
var canvas = document.getElementById("canvas");
var context = canvas.getContext('2d');
var drawScreen = function(){
context.moveTo(0,0);
context.lineTo(100,200);
context.arcTo(350,350,100,100,20);
context.stroke();
}
drawScreen();
</script>
</body>
</html>
Here is some pseudocode explaining how the JavaScript code you posted works:
1) Get the canvas, and store it to variable 'canvas'
2) Get the 2d context of 'canvas', and store it to variable 'context'
3) Initialize a function, 'drawScreen', that takes no arguments, but runs the following instructions:
a) Move the pen to (0,0) on the canvas.
b) Draw a line from the pen's current position to (100, 100)
c) Draw an arc with a tangent line that passes through (350, 350) and the current pen position, and another tangent line that passes through (350, 350) and (100, 100), around a circle with radius 20.
d) Push the updated canvas to the screen.
4) Run the function 'drawScreen'
Believe it or not, you can use arcTo or a combination of other commands to do exactly the same thing arc does, albeit with more work, and there are numerous examples of this online.

In Javascript, why does getBoundingClientRect() sometimes return floating point values?

I'm trying to work with an HTML5 canvas element. One thing i'm trying to do is set up a mousemove event to track the mouse on the canvas for drawing and other purposes. I have not been able to find a definitive answer on how to get the exact coordinates of the pixel in the canvas the mouse is over. I found a tutorial on the web that had this html (I added the background-color to the canvas to make it obvious on the rendered page):
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#myCanvas {
background-color: cornflowerblue;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
function writeMessage(canvas, message) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.font = '18pt Calibri';
context.fillStyle = 'black';
context.fillText(message, 10, 25);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
canvas.addEventListener('mousemove', function(evt) {
var mousePos = getMousePos(canvas, evt);
var message = 'Mouse position: ' + mousePos.x + ',' + mousePos.y;
writeMessage(canvas, message);
}, false);
</script>
</body>
</html>
When the page is loaded, you see the canvas as a blueish rectangle in the upper left corner of the page. Move the mouse over it and the text in the canvas changes to show the mouse position as two ints, one each for X and Y.
I then modified the canvas style by adding a top and left margin, and left the rest of the page unchanged.
#myCanvas {
background-color: cornflowerblue;
margin-left: 30px;
margin-top: 30px;
}
When I rendered the page now, the blue rectangle of the canvas was offset from the top and left of the page as I expected. But passing my mouse over the canvas now had the X and Y mouse coordinates that were being displayed in the canvas coming up as floats with many decimal places. Tracing through the code a bit, it seems that getBoundingClientRect() is returning a rect where the values top and left are floats.
I assume I could do something like truncate or round the values being returned by getBoundingClientRect(), but that feels like the wrong way to go about it to me.
Am i somehow using getBoundingClientRect() incorrectly, or is it expected that it should return float values?
And is there a clear cut way to get the exact X and Y coordinates of the mouse over the canvas when listening for various mouse events?
tldr; you did zoom/unzoom in your browser.
The problem
margin-left: 30px;
In this jsfiddle thats mimics your problem it does not works as you said on my computer when navigator zoom is set to 100% (normal). But if you zoom inside your browser you will notice such behavior.
margin-left: 11%;
Instead if you use % margin like in this jsfiddle you will notice it does returns floating mouse position wether zoom is on or not.
The answer
The thing is mouse position is computed as it appears on the screen : it may only have entire position coordinates since it is pixel based.
However getBoundingClientRect returns what browser computes to be "Bounding Client Rect of the element" after applying margins, zoom and others modifiers but before telling the GPU to render it. In short it returns the real position of the element which is later approximated by the GPU to be rendered within a matrix of pixels. If you use pixel margins/sizes/paddings/etc then elements positions remains integer based, but if you zoom or use em/% positioning values then it may result floating positions.
The solutions
round bounds
assume it is indeed a floating position and it's just the GPU that needs to round it in order to make it fit on the screen
Edit : The forgotten solutions

How to remove a drawn arch from canvas

I have been trying to print arc in the html page. How can i remove the already drawn arch from the page?. i have written the below code.
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="1200" height="1000"
style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
/*ctx.beginPath();
ctx.arc(600,500,20, 0.5*Math.PI,2*Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(600,500,40, 0.5*Math.PI,2*Math.PI);
ctx.stroke();
*/
var radius=20;
for (i=0; i<10; i++)
{
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(600,500,radius, 0.5*Math.PI, 2*Math.PI);
ctx.stroke();
radius= radius+30;
}
</script>
</body>
</html>
How can i achieve this?.
Call clearRect method:
ctx.clearRect(0, 0, 1200, 1000)
The four arguments are:
axis-X of left top corner of the area to wipe
axis-Y of left top corner of the area to wipe
width of the area to wipe
height of the area to wipe
So with this method, you could wipe either the whole canvas or just a certain part of it.
If you want to remove the whole previously drawn image please take a look at the other anwers. In the comments OP made it clear that this is not what he was trying to achieve. So instead I will answer the intended question:
How do I un-stroke a path?
A bitmap is not a vector graphic. You cannot simply remove or modify things you've drawn previously. By drawing on a canvas you modify the pixel color values of its image data. If you need to undo things you have to maintain a separate data structure with the relevant data yourself.
For example you could create a copy of the image data before drawing something. Then you could return to this snapshot afterwards. HTMLCanvasElement#toDataURL returns the complete image as an url which you can use as the src of an image. Later you can draw this image on the canvas to revert all subsequent changes. HTMLCanvasElement#toBlob does about the same but it returns a blob. This might consume less memory but it's a little more inconvenient to use. The most convenient method is CanvasRenderingContext2D#getImageData. You can even use it to copy only a small part of the image. This is useful if you have a big canvas but only modify pixels in a small region.
Another way to make modifications undoable is by maintaining a detailed list of your steps. For example whenever you draw an arc you store the exact parameters as one entry in the list. steps.push({type: 'stroke', style: 'rgb(0,0,0)', shapes: [{type: 'arc', x: 600, y: 500, radius: radius, from: 0.5 * Math.PI, to: 2 * Math.PI}]}) You can remove, rearrange or modify the elements in this list any way you like and have all necessary information to draw the resulting image from scratch. Basically you've implemented just another vector graphic library.

Center align a polygon inside an html canvas

I'm dynamically drawing a polygon inside a canvas using this code I found. (the coordinates are provided by the user)
https://stackoverflow.com/a/4841057/1667856
Is it possible to move this polygon into the center of a canvas after/before it has been created?
I found out that you can use the canvas translate() method to move shapes around but I can't seem to figure out how to recalculate the coordinates so that the polygon will automatically appear in the middle of the canvas and not on its original coordinates.
As you've probably discovered, html canvas is just a pixel drawing.
The shapes you draw on the canvas are just like dried paint. They can't be moved or remolded into another shape.
The typical way of "moving" a shape is to clear the canvas and redraw the same shape with different coordinates.
You can create those coordinates by adding an offsetX and offsetY to all the polygon coordinates.
Alternatively (more simply) you can translate the coordinates.
Important note: context.translate does not just move the coordinates of your polygon. It changes every coordinate for all NEW drawings.
ctx.translate(50,50) "moves" the canvas's origin to 50,50. That means if you start drawing your polygon at 5,5 you will visually start drawing at 50+5,50+5.
Here is example code and a Demo: http://jsfiddle.net/m1erickson/2Gm73/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// calculate the middle of the canvas
var centerX=canvas.width/2;
var centerY=canvas.height/2;
// just for testing: draw crosshairs on center canvas
ctx.beginPath();
ctx.moveTo(centerX,0);
ctx.lineTo(centerX,canvas.height);
ctx.moveTo(0,centerY);
ctx.lineTo(canvas.width,centerY);
ctx.stroke();
// define some points for your polygon
var poly=[ 5,5, 100,50, 50,100, 10,90 ];
// save the canvas context in its untranslated state
ctx.save();
// translate the canvas
// the context now uses centerX,centerY as its 0,0 origin
ctx.translate(centerX,centerY);
// draw the polygon
ctx.beginPath();
ctx.moveTo(poly[0], poly[1]);
for(var i=2;i<poly.length;i+=2){
ctx.lineTo( poly[i] , poly[i+1] )
}
ctx.closePath();
ctx.fillStyle = '#f00';
ctx.fill();
// restore the context to its untranslated state
// (otherwise all further drawings will be "moved"
// just like this polygon
ctx.restore();
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
If you want your polygon to be visually centered in the canvas, you must also calculate the offset of the center of your polygon itself:
// calc the max values of x,y in the polygon and divide by 2
var centerPolyX = 100/2; // max x comes from coordinate 100,50
var centerPolyY = 100/2; // max y comes from coordinate 50,100
Then translate to center canvas minus the polygon center:
// move to center canvas
// but then move left and up by half the polygon's size
ctx.translate(centerX-centerPolyX,centerY-centerPolyY);

Categories