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.
Related
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>
Up until now, I have only used Javascript in an environment that doesn't require any HTML, like the one on Khan Academy here. However, now I'm using sublime text, and I realize that I can't just write ellipse() and have it show up on the screen. Now I need the element. However, according to w3schools, now, for a circle to show up, I need to do this:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(95,50,40,0,2*Math.PI);
ctx.stroke();
Ugh. I really don't want to have to write "ctx" before every single line of code I make, or learn a bunch of new commands like "beginPath" and "lineTo". Is there a way I can create the canvas, and still make a program without all this messy stuff? Or, if that's not possible, could someone point me to a library that can? Thanks so much!
While this approach still doesn't free you from the responsibility of knowing your way around the canvas element, you can make use of the with keyword to avoid continually typing ctx - the approach is not one I use and as per the MDN page Statements and declarations>with you can run into problems with ambiguity.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
with (ctx)
{
beginPath();
arc(95,50,40,0,2*Math.PI);
stroke();
}
<canvas id='myCanvas'></canvas>
Not really sure what the question is. But from what I understood you need to learn how to add a canvas to a HTML file and link your JS to the same. If so, here's how to do it.
HTML as below
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<title>Your Canvas Application</title>
<!-- link your js file as below -->
<script src = "path/to/yourjsfile.js">
</head>
<body>
<canvas id="myCanvas" width="500" height="500"></canvas>
</body>
JS file (yourjsfile.js) as below
window.onload = function () {
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext("2d");
c.beginPath();
c.arc(95,50,40,0,2*Math.PI);
c.stroke();
};
You really can not entirely get rid of 'ctx', but instead you can shorten it for something like 'c'. Rather than looking for libraries, I recommend you learn the basics correct first. Then you can go ahead and explore the other options.
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.
Using getImageData, I'm trying to create a radial fade effect on an image where point(0,0) has an alpha of 0 and point(img.width, img.height) has an alpha of 0.5 sort of like this:
(The last point has an alpha of 1.0, but imagine it's 0.5.)
I've been able to create a linear alpha increase in my for loop using
imgData.data[i+3]=i/imgData.length;
but the left edge (obviously) becomes more defined as the loop iterates.
I've tried using two loops, one to create row "breaks" in the array, and the other to handle the alpha manipulation like so:
for(var j=0;j<imgData.height;j++){
for(var i=0;i<imgData.data.length;i+=4){
imgData.data[i+3] = 0 + 125 * i/imgData.data.length;
}
}
but the performance of this is really slow and it doesn't achieve the fade that I'm looking for. I'm also not sure I'm doing that properly. I also tried Faster Canvas Pixel Manipulation with Typed Arrays as outlined on the Mozilla Hacks site, but the image wasn't even drawn to the canvas using that method. (I'm using Chrome - not sure if that's the issue or if it's my code.)
Any ideas?
EDIT: Thanks to markE for the great solution - it's beautifully quick. I mentioned this in my comment to him, but here's the full code for creating a greyscale image (part of my requirements) that has the fade pictured in mark's demo.
img_width = window.innerWidth/2.5;
img_height = img_width * (9/16);
//hidden_ctx is hidden behind a background layer
var g1=hidden_ctx.createRadialGradient(500,500,550,500,500,650);
g1.addColorStop(0.00,"rgba(0,0,0,0.7)");
g1.addColorStop(0.30,"rgba(0,0,0,0.35)");
g1.addColorStop(1.00,"rgba(0,0,0,0.00)");
hidden_ctx.fillStyle=g1;
hidden_ctx.fillRect(0, 0, img_width, img_height);
hidden_ctx.globalCompositeOperation="source-in";
//custom drawImage method where selected[num] contains
//contains images & metadata
drawIt(hidden_ctx, selected[num].image[0], 0, 0, img_width, img_height);
imgData=hidden_ctx.getImageData(0, 0, img_width, img_height);
// invert colors
for (var i=0;i<imgData.data.length;i+=4)
{
grayscaled = 0.34 * imgData.data[i] + 0.5 * imgData.data[i + 1] + 0.16 * imgData.data[i + 2];
imgData.data[i]=grayscaled;
imgData.data[i+1]=grayscaled;
imgData.data[i+2]=grayscaled;
}
//places the converted image in the lower right corner where
//WIDTH/HEIGHT is window.innerWidth/window.innerHeight
ctx.putImageData(imgData, WIDTH-img_width, HEIGHT-img_height);
You can use compositing to achieve a fade effect more efficiently.
draw a radial gradient that fades to transparent in the top-left corner.
Adjust the radial gradient to meet your design needs.
set compositing to source-in which causes any subsequent draws to only appear over existing non-transparent pixels.
draw your image (the image will fade the same way your radial gradient did)
Using compositing is especially efficient if the device has a GPU because compositing will use it.
Example code and a Demo: http://jsfiddle.net/m1erickson/thqamsLk/
<!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>
canvas{ background-color: lightgray; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/reef.jpg";
function start(){
canvas.width=img.width;
canvas.height=img.height;
var g1=ctx.createRadialGradient(500,500,550,500,500,650);
g1.addColorStop(0.00,"rgba(0,0,0,1.00)");
g1.addColorStop(0.30,"rgba(0,0,0,0.75)");
g1.addColorStop(1.00,"rgba(0,0,0,0.00)");
ctx.fillStyle=g1;
ctx.fillRect(0,0,cw,ch);
ctx.globalCompositeOperation="source-in";
ctx.drawImage(img,0,0);
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Fading an image using compositing</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I'm working on some code which is drawing to a canvas. One part of the code draws some lines onto the canvas. The position and colour of those lines don't change, but they often need to be redrawn because other code may have affected it (eg: drawn over the top of it).
There can be several hundred lines to draw, and in these cases, profiling shows me that it's taking ~200ms to draw, so I'm looking to optimise this somewhat.
One thing I noticed was that when drawing to the canvas, you basically are adding points to a path and then once ready, you can fill or stroke that path. Though the pixels on the canvas are out of date, if I were able to keep a reference to the path, then updating would be as simple as re-stroking the previously constructed path.
My question is: how on earth do you get a Path object?
The fill and stroke methods appear to accept a path object, and the spec defines the methods for Path, but I can't seem to find the actual Path class anywhere...
So, just to recap:
I have something like this:
function update() {
context.beginPath();
// lots of lines added to the default path...
context.moveTo(x1, y1); context.lineTo(somewhere, else);
context.moveTo(x2, y2); context.lineTo(somewhere, else);
context.stroke();
}
What I'd like is something like this:
function update() {
if (!this.path) {
this.path = new Path(); // <-- here's the magic
this.path.moveTo(x1, y2); this.path.lineTo(somewhere, else); // etc
}
this.path.stroke();
}
The canvas spec calls for a Path object that is not implemented in browsers yet.
BTW, when implemented, the Path object will be useful in hit-testing when combined with context.isPointInPath(myPath); Someday...
Here's how you could create your own Path object until the browsers catch up:
Create a JS object that contains a canvas where your path strokes are drawn.
When you want to do myPath.stroke(), use myVisibleContext.drawImage(myPath.context,0,0) to "blit" the path's canvas onto your drawing canvas.
Demo: http://jsfiddle.net/m1erickson/QLJv8/
Code:
<!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");
function Path(maxWidth,maxHeight,color,linewidth,drawingContext){
this.width=maxWidth;
this.height=maxHeight;
this.drawingCtx=drawingContext;
this.points=[]
this.canvas=document.createElement("canvas");
this.canvas.width=maxWidth;
this.canvas.height=maxHeight;
this.ctx=this.canvas.getContext("2d");
this.ctx.strokeStyle=color;
this.ctx.lineWidth=linewidth;
this.lastX;
this.lastY;
}
Path.prototype.moveTo=function(x,y){
this.lastX=x;
this.lastY=y;
}
Path.prototype.lineTo=function(x,y){
this.ctx.moveTo(this.lastX,this.lastY);
this.ctx.lineTo(x,y);
this.ctx.stroke();
this.lastX=x;
this.lastY=y;
}
Path.prototype.stroke=function(){
this.drawingCtx.drawImage(this.canvas,0,0);
}
// create a new path object
var p=new Path(300,300,"blue",2,ctx);
// set the Path's drawing commands
p.moveTo(69,91);
p.lineTo(250,150);
p.moveTo(69,208);
p.lineTo(180,54);
p.lineTo(180,245);
p.lineTo(69,91);
p.moveTo(69,208);
p.lineTo(250,150);
// draw the Path.canvas to the drawing canvas
p.stroke();
// tests...
$("#stroke").click(function(){
p.stroke();
});
$("#erase").click(function(){
ctx.clearRect(0,0,canvas.width,canvas.height);
});
}); // end $(function(){});
</script>
</head>
<body>
<button id="stroke">Path.stroke</button><br>
<button id="erase">Erase main canvas</button><br>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Turns out, it's just that no browser supports it yet, according to this blog (dated 24th January 2013) http://www.rgraph.net/blog/2013/january/html5-canvas-path-objects.html
Their is no path support in canvas, but why don't use svg line and set its zIndex to be on top of others.
None of the canvas drawing API let you hold references to objects.
Canvas lets you draw pixels in a bitmap, not create and manipulate objects like SVG does.
If you're looking to optimize performance and you want to reuse the same path over and over again, you might want to draw it once in a separate canvas oject, and then draw that canvas into your other canvas using drawImage (which can take a canvas as argument).