Create lines in HTML5 Canvas - html

I need to make path which connected with different colours of lines, but when I assign the last colour the whole path becomes a single colour. lines assignment
My code is
var canvas=document.getElementById("mycanvas");
var ctx=canvas.getContext('2d');
ctx.strokeStyle="#f00";
ctx.lineWidth=5;
ctx.moveTo(100,100);
ctx.lineTo(150,150);
ctx.stroke();
ctx.strokeStyle="#0f0";
ctx.moveTo(150,150);
ctx.lineTo(350,200);
ctx.stroke();
ctx.strokeStyle="#00f";
ctx.moveTo(350,200);
ctx.lineTo(400,400);
ctx.stroke();

You must call ctx.beginPath() to begin each canvas path i.e.
var canvas=document.getElementById("mycanvas");
var ctx=canvas.getContext('2d');
ctx.beginPath();
ctx.strokeStyle="#f00";
ctx.lineWidth=5;
ctx.moveTo(100,100);
ctx.lineTo(150,150);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle="#0f0";
ctx.moveTo(150,150);
ctx.lineTo(350,200);
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle="#00f";
ctx.moveTo(350,200);
ctx.lineTo(400,400);
ctx.stroke();

Related

How to fill colour in Line using ctx html?

Please check this link
https://www.jovianarchive.com/content/charts/628003987200000000_.png. i am creating same chart but all the lines and geometrical shapes are dynamic. in line there is colour with combination of balck and red (Look like stripe) want the same.
My code
ctx.fillStyle = 'blue';
ctx.fill();
ctx.strokeStyle = 'blue';
ctx.stroke();
You need create a Path before call stroke method:
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.fillStyle = 'blue';
ctx.strokeStyle = 'blue';
//point 0, 0
ctx.moveTo(0, 0);
//to point 300, 150
ctx.lineTo(300, 150);
ctx.closePath();
ctx.stroke();
ctx.fill();
You can change the line width and add shadow. Look in my project:
https://codepen.io/Luis4raujo/pen/GRNEPXO
If this comments help u, vote in my answer!
You might want to consider to create a pattern. You can create it from an image or from an off screen canvas. Here's a quick demo of the second way: https://codesandbox.io/s/awesome-khorana-ystxp?file=/index.html
More info about pattern creation in html canvas: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createPattern

Using globalCompositeOperation in few phases

I'm drawing many things on my context. Shapes, texts, images..
I want to use achieve the same effect i'm getting using the clip method on the context with globalCompositeOperation (Using the clip is harder for me to perform and i don't know if possible for texts)
The user can draw few shapes. and then start a mask phase. to draw some more shapes, texts.. which would draw into the mask and then the next draw will be clipped in the masked phase. and then continue to regular drawing...
For ex.
The user draw this drawing
Then started masked mode and drew this 2 red lines
Then he stopped drawing into the mask, and start drawing rectangle to consider the mask
And finally applied the mask clipping and the result should look like this
I've managed to clip the rectangle with the lines if there were no earlier drawings.
// Starting the mask phase
ctx.strokeStyle = 'red';
ctx.lineWidth = 5;
ctx.beginPath();
ctx.moveTo(20, 20);
ctx.lineTo(240, 140);
ctx.moveTo(80, 20);
ctx.lineTo(300, 140);
ctx.stroke();
ctx.globalCompositeOperation = 'source-out';
ctx.fillStyle = 'cyan';
ctx.fillRect(50, 70, 250, 20);
// Setting the composition back
ctx.globalCompositeOperation = 'source-over';
but when i'm adding my drawings in the beginning of the code, the composition considering it as well.
ctx.fillStyle = 'brown';
ctx.beginPath();
ctx.arc(80, 80, 50, 50, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = 'yellow';
ctx.fillRect(80, 60, 150, 40);
ctx.fillStyle = 'black';
ctx.font = '40pt arial';
ctx.fillText('Hello', 130, 110);
// How to tell the context to start from here the compisition ???
How to tell the context to start composition from a certain point, if possible ?
I could create another canvas and draw the mask there.. and then draw the new canvas on the main canvas. But there is better solution ?
You can change the compositing mode at any point in the drawing flow by changing .globalCompositeOperation. But, as you've discovered, any new compositing mode will also affect existing canvas content.
Your intuition is correct about using a second "staging canvas" to do compositing that won't destroy your existing content.
You can use an in-memory canvas to do compositing and create your rect-with-erased-lines. Then you can drawImage this in-memory canvas to your main canvas. Since the compositing was done on your in-memory canvas, your existing circle-hello content is not undesirably affected by compositing.
Here's example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// create an in-memory canvas to do compositing
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/temp6a.png";
function start(){
ctx.drawImage(img,0,0);
addCompositedRectangle();
ctx.drawImage(c,0,0);
}
function addCompositedRectangle(){
// resize the in-memory canvas
// Note: this automatically clears any previous content
// and also resets any previous compositing modes
c.width=300; // largest width;
c.height=140; // largest height;
// Starting the mask phase
cctx.strokeStyle = 'red';
cctx.lineWidth = 5;
cctx.beginPath();
cctx.moveTo(20, 20);
cctx.lineTo(240, 140);
cctx.moveTo(80, 20);
cctx.lineTo(300, 140);
cctx.stroke();
cctx.globalCompositeOperation = 'source-out';
cctx.fillStyle = 'cyan';
cctx.fillRect(50, 70, 250, 20);
// Setting the composition back
cctx.globalCompositeOperation = 'source-over';
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Context scaling breaks some drawing

I'm trying to draw on a virtual canvas of proportions 1x1 so that I don't have to constantly multiply out the actual dimensions.
This works perfectly when I draw circles, but it would appear as though it does not work well with rectangles, and I'm not sure why.
When I have the following code:
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.scale(this.canvas.width, this.canvas.height);
ctx.beginPath();
ctx.arc(.5, .5, .1, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
It works fine, and the circle scales with the canvas.
However, when I merely change it to the following:
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.scale(this.canvas.width, this.canvas.height);
ctx.fillStyle = "blue";
ctx.fillRect(0,0, .0001, .001);
ctx.beginPath();
ctx.arc(.5, .5, .1, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
the rectangle takes up the entirety of the screen and even covers up the circle, even though the circle is drawn after the rectangle. It should, obviously, be taking up a very minute amount of space on the canvas.
It might be worth noting that this occurs in a game loop.
However, the following works as expected, with a red circle appearing above a blue backdrop
var canvas = this.canvas;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "blue";
ctx.fillRect(0,0, 50, 50);
ctx.beginPath();
ctx.arc(10, 10, 20, 0, 2*Math.PI);
ctx.fillStyle = "red";
ctx.fill();
If you're calling this repeatedly in a game loop, the scale method will increase the scale of your transform, every time through the loop. So you end up with everything growing. Since you're not showing your actual code, it's hard to tell why the circle isn't affected.
Try calling scale() only once, or use save() and restore(), or just reset the transform at the start of the loop, before calling scale():
ctx.setTransform(1, 0, 0, 1, 0, 0);

HTML5 CANVAS Shadow a bug? or a code defect?

I'm faced with the problem about drawing of canvas.
Canvas is saved to data:image once.
And it is made to redraw.
Two problems occur at this time.
A shadow will be applied to the object which has not drawn the shadow.
A shadow will become deep if save and a redraw are repeated.
<canvas id="SAMPLE" width="960" height="480"></canvas>
<script type="text/javascript" src="./jquery.js"></script>
<script>
var canvas = $("#SAMPLE"),
ctx = canvas[0].getContext("2d");
// first object (no shadow)
ctx.strokeStyle = "#0067ef";
ctx.fillStyle = "#0067ef";
ctx.lineCap = "round";
ctx.lineWidth = "15";
ctx.globalAlpha = 1;
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowColor = "#363636";
ctx.beginPath();
ctx.moveTo( 10, 10);
ctx.lineTo(200, 200);
ctx.stroke();
ctx.closePath();
// second object (has shadow)
ctx.shadowBlur = 5;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.shadowColor = "#363636";
ctx.beginPath();
ctx.moveTo(300, 10);
ctx.lineTo(400, 200);
ctx.stroke();
ctx.closePath();
// Canvas is saved to data:image once.
var save = canvas[0].toDataURL();
// clear canvas
ctx.clearRect(0, 0, 960, 480);
// redraw
var img = new Image();
img.src = save;
img.onload = function() {
ctx.drawImage(this,0,0);
};
</script>
It was normal when the picture at the time of save and before a redraw was investigated.
Two problems occur at the time of a redraw.
A shadow will be applied to the object which has not drawn the shadow.
A shadow will become deep if save and a redraw are repeated.
Does my code have a defect?
Because your fillStyle and other parameters are preserved, so upon drawing image, the effect is stacking up.
Use save() and restore() to prevent this:
ctx.save();
// first object (no shadow)
ctx.strokeStyle = "#0067ef";
ctx.fillStyle = "#0067ef";
/*
...
*/
ctx.closePath();
ctx.restore();
// Canvas is saved to data:image once.
JSFiddle demo with one extra canvas and the appended image for comparison.
Comment out the save() and restore() and re-run the fiddle, you'll notice the different.

HTML5 - Canvas Shape Stroke

I have created 2 shapes, circle and rectangle, one on top of the other to resemble a key lock. I then try to apply a stroke but its stroking both shapes. What I want it to do is just stroke the merged pattern and not any of the intersections.
context.beginPath();
context.fillStyle = "#ccc";
context.arc(115, 550, 12, 0, 2 * Math.PI, false);
context.moveTo(105, 555);
context.fillStyle = "#999";
context.rect(105, 555, 20, 30);
context.fill();
context.stroke();
context.closePath();
If I try to draw the rect first, then the arc on top there are extra line paths when you stroke, its like I have to close Path and then draw it again.
You can't use a rect and a whole circle if you want the path to do without the intersecting part.
Instead you have to draw only part of the circle and only part of the rectangle. This should do it for you:
context.beginPath();
context.fillStyle = "#ccc";
context.arc(115, 550, 12, 2.5, 2.2 * Math.PI, false);
context.moveTo(105+20, 555);
context.fillStyle = "#999";
// instead of a rect, we really want three lines
context.lineTo(105+20,555+30);
context.lineTo(105,555+30);
context.lineTo(105,555);
context.fill();
context.stroke();
context.closePath();
While working on my own irregular shape answer, I discovered a lab project by Professor Cloud that solved my problem.
This page, SVG-to-Canvas, parses an SVG graphic to Canvas code. So if you have an application like Illustrator with which you can draw and save the graphic as SVG, then you can parse the usable canvas codes and just plug them in.
You can use compositing and temporary canvas. Something like that:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var tempCanvas = document.getElementById('tempCanvas');
var tempContext = tempCanvas.getContext('2d');
tempContext.save();
// clear temp context
tempContext.clearRect(0, 0, canvas.width, canvas.height);
// draw all rects with strokes
tempContext.beginPath();
tempContext.strokeStyle='red';
tempContext.lineWidth=3;
tempContext.arc(100, 100, 60, 0, 2 * Math.PI, false);
tempContext.rect(20,150,100,200);
tempContext.stroke();
// set compositing to erase existing drawings
// where the new drawings are drawn
tempContext.globalCompositeOperation='destination-out';
// fill all rects
// This "erases" all but the outline stroke
tempContext.beginPath();
tempContext.fillStyle='blue';
tempContext.arc(100, 100, 60, 0, 2 * Math.PI, false);
tempContext.rect(20,150,100,200);
tempContext.fill();
// draw outlines from tempcanvas into canvas
ctx.drawImage(tempCanvas, 0, 0);
// draw into canvas
ctx.beginPath();
ctx.fillStyle='green';
ctx.globalAlpha = 0.2;
ctx.rect(20,150,100,200);
ctx.arc(100, 100, 60, 0, 2 * Math.PI, false);
ctx.fill();
tempContext.restore();
And a jsfiddle: https://jsfiddle.net/EvaF/8to68dtd/2/