HTML5 Canvas - Different Strokes - html

I have to draw a graph with 3 different lines. A line graph.
I tried doing this:
function draw()
{
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth=10;
ctx.strokeStyle="teal";
ctx.moveTo(10,CompanyA[0]);
ctx.lineTo(110,CompanyA[1]);
ctx.lineTo(210,CompanyA[2]);
ctx.stroke();
ctx.strokeStyle="green";
ctx.moveTo(10,CompanyB[0]);
ctx.lineTo(110,CompanyB[1]);
ctx.lineTo(210,CompanyB[2]);
ctx.stroke();
ctx.strokeStyle="yellow";
ctx.moveTo(10,CompanyC[0]);
ctx.lineTo(110,CompanyC[1]);
ctx.lineTo(210,CompanyC[2]);
ctx.stroke();
}
But apparently, the last stroke draws for all the lines. So I get 3 yellow lines, instead of a Teal, a Green and a Yellow one.
I tried creating three different Context (ctx1, ctx2 and ctx3), but for some reason, all were drawn with the "ctx3.stroke()" call.
What would be the correct way to do this?

Add a ctx.beginPath() call before every line, and also a ctx.closePath() after every ctx.stroke()
If you don't, every time you call the stroke() method, not only the new line will be drawn but also all the previous lines will be drawn again (with the new strokeStyle), since it's the same line path that is still open.

Although there is a functional answer here, I'd just like to add this.
var ctx1 = canvas.getContext("2d");
var ctx2 = canvas.getContext("2d");
var ctx3 = canvas.getContext("2d");
They all refer to the same object. It does not create a new context, it uses the one that's already attached to the canvas element. Delta is totally right in saying that it strokes yellow over the entire path because you did not begin a new path. ctx.beginPath() will solve your troubles.

Related

Drawing a path of points html canvas

I made a double pendulum with canvas.
Here is the result: https://jsfiddle.net/zndo9vh4/
As you guys can see a trace is drawn everytime the second part of the pendulum moves, and my way of doing that is by appending each coordinate to a "trace" array.
var trace = []
trace.push([x2,y2]);
And then I draw the trace by joining each coordinate with the last one:
for (let i = 1; i < trace.length; i++) {
c.moveTo(trace[i][0], trace[i][1])
c.lineTo(trace[i-1][0], trace[i-1][1])
}
I want to improve it. What i've tried so far is only adding coordinates that aren't already in the array, but it's not a big improvent because the lines are drawn every loop
var trace = []
if(trace.includes([x2, y2]) != true){
trace.push([x2,y2]);
}
The way I think could be a good improvement is by having 2 canvas (I don't know if its possible) and then draw each point but only in that canvas so I doesnt have to be redrawn. But I dont know how to implement that.
Thanks in advice
Your improvement idea is great. You can indeed have two canvases!
There are two ways to go about it.
Offscreen canvas
Using what's called an offscreen canvas (a canvas that is created in JavaScript but not added to the DOM), you can draw all the points onto it and then using drawImage (which can accept a canvas element) pass the canvas to the main context.
var offscreenCanvas = document.createElement('canvas');
var offscreenC = offscreenCanvas.getContext('2d');
offscreenCanvas.width = canvas.width;
offscreenCanvas.height = canvas.height;
// in animate function, draw points onto the offscreen canvas instead
// of the regular canvas as they are added
if(trace.includes([x2, y2]) != true){
trace.push([x2,y2]);
var i = trace.length-1;
if (i > 1) {
offscreenC.strokeStyle = 'white'
offscreenC.beginPath();
offscreenC.moveTo(trace[i][0], trace[i][1])
offscreenC.lineTo(trace[i-1][0], trace[i-1][1])
offscreenC.stroke();
}
}
c.drawImage(offscreenCanvas, 0, 0);
Layered Canvases
One of the downsides to the offscreen canvas approach is that you have to draw it to the main canvas every frame. You can further improve the approach by layering two canvases on top of one another, where the top one is just the pendulum and the bottom one the trace.
This way, you never have to redraw the offscreen canvas onto the main canvas, and save yourself some rendering time.
Updated jsfiddle

Eraser tool in html5 canvas

Hi i am building a windows store app with html5 and javascript in my app i am trying to implement an eraser tool but this is problematic because if the user moves an image or another layer to where they've previously erased, they see the white drawing where they erased.
i have been trying to do the eraser tool from different ways for example i have changed the default globalCompositeOperation to "destination-out" like this code
//Here is the error.
if (clickTool[j] == "eraser") {
ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = 'rgba(255,0,0,0.5);';
ctx.strokeStyle = 'rgba(255,0,0,0.5);';
}
else {
ctx.globalCompositeOperation = "source-over";
ctx.strokeStyle = clickColor[j];
}
but unfortunately it doesn´t work for me. i have uploaded all my code to this link:
My code
Please i would like to somebody could help me.
Thanks and i'm sorry for my speech , i'm mexican.
Use multiple layers. Have one canvas for the background image and another for the drawing; that why you never erase any of the background image.
If you need to, you can have multiple layers as they don't generally impact performance.
And of course if you can combine layers, say the last drawn squiggle to the background layer, if you deem a drawing to be "permanent".
Maintain a array of mid points. Use the globalCompositeOperation as 'destination-out' first and 'source-over' later to make a transparent eraser trail .
Following is the code that you need to use with a mouse move function
var handleMouseMove = function (event) {
midPt = new createjs.Point(oldPt.x + stage.mouseX>>1, oldPt.y+stage.mouseY>>1);
if(curTool.type=="eraser"){
var tempcanvas = document.getElementById('drawcanvas');
var tempctx=tempcanvas.getContext("2d");
tempctx.beginPath();
tempctx.globalCompositeOperation = "destination-out";
tempctx.arc(midPt.x, midPt.y, 20, 0, Math.PI * 2, false);
tempctx.fill();
tempctx.closePath();
tempctx.globalCompositeOperation = "source-over";
drawingCanvas.graphics.clear();
// keep updating the array for points
arrMidPtx.push(midPt.x);
arrMidPty.push(midPt.y);
stage.addChild(drawingCanvas);
stage.update();
}
};
I use this code to make a eraser that behaves like pen and fills up transparent color instead of white

Scale command applying to multiple shapes

Wondering why the scale parameter of the variable ´ell´ is applying to the next two circles I have created as well:
var ell=canvas.getContext("2d")
ell.beginPath()
ell.lineWidth=2
ell.fillStyle="#FFFFFF"
ell.strokeStyle="#000000"
ell.scale(1.2,0.5)
ell.arc(125,190,30,0,2*Math.PI,false)
ell.fill()
ell.stroke()
var circ=canvas.getContext("2d")
circ.beginPath()
circ.lineWidth=1
circ.fillStyle="#FFFFFF"
circ.strokeStyle="#000000"
circ.arc(150,95,15,0,2*Math.PI,false)
circ.fill()
circ.stroke()
var circ2=canvas.getContext("2d")
circ2.beginPath()
circ2.fillStyle="#1d1d1d"
circ2.arc(155,90,4,0,2*Math.PI,false)
circ2.fill()
It's supposed to be an eyeball, the first shape is an oval and the next two are supposed to be circles, however they are getting squished by the scale command, and their positions are thrown off as well.
Any help appreciated, thanks!!
You could either use setTransform and reset the scale manualy or
call save() before applying the scale and call restore() when your done with it.
var ctx=canvas.getContext("2d")
ctx.save(); // save the context state
ctx.beginPath();
ctx.lineWidth=2;
ctx.fillStyle="#FFFFFF";
ctx.strokeStyle="#000000";
ctx.scale(1.2,0.5);
ctx.arc(125,190,30,0,2*Math.PI,false);
ctx.fill();
ctx.stroke();
ctx.restore(); // restore the state to the last save()
// you can reuse the same context
ctx.save();
ctx.beginPath();
draw the second circle...
take a look at
https://developer.mozilla.org/en/Canvas_tutorial/Transformations

convert masked movieclip into bitmap and save it on server

i have a
dsgnArea----> a movieclip
dsgnArea is masked by dsgnAreaMask, which inturn is a movieclip
dsgnArea.mask=dsgnAreaMask;
the width,height and position of dsgnAreaMask and dsgnArea are same.
i dynamically added multiple movieclips and labels to dsgnArea;
like..
dsgnArea.addChild(movieClip1);
dsgnArea.addChild(movieClip2);
dsgnArea.addChild(label1);
dsgnArea.addChild(label2); and so on...
these movieclips (movieClip1,movieClip2,......) and labels(label1,label2,....) positions can be altered in runtime..
but as i masked the dsgnArea with dsgnAreaMask, only a portion of the added movieClips and labels are visible...
So, my problem is to grab that visible portion in dsgnArea into a bitmap,like a screenshot of that particular dsgnArea, and save it in my server.
please help me out in this problem.
Say s is the DisplayObject object you want to capture and m is the mask applied on it.
var maskRect:Rectangle = m.getRect(s);
var matrix:Matrix = new Matrix(1, 0, 0, 1, -maskRect.x, -maskRect.y);
var w:Number = Math.min(s.width, maskRect.right) - maskRect.x;
var h:Number = Math.min(s.height, maskRect.bottom) - maskRect.y;
var bd:BitmapData = new BitmapData(w, h);
bd.draw(s, matrix);
Does that work?
The BitmapData draw method is what you are looking for. You can use it's clipRect param to define what you would like to draw (the masked parts).
Quasimondo did a handy little method to do this (take a snapshot of the whole displayObject), it's available here: http://www.quasimondo.com/archives/000670.php
I don't know if it works with masked content though.
if it doesn't, the trick would be to translate the whole content by the size of the mask
var bounds:Rectangle = dsgnAreaMask.getBounds( dsgnAreaMask );
instead of using the content of the clip
var bounds:Rectangle = clip.getBounds( clip );
as far as saving file to server is concerned, the question was asked (answered?) here AS3 Save Media File to server

Flip <canvas> (rotate 180deg) after being published on page

I'm trying to rotate a canvas element AFTER it's been appended to the DOM.
Canvas is 600x50 and this is the code at hand:
var canvas = document.getElementsByTagName('canvas')[2];
var ctx = canvas.getContext('2d');
ctx.translate(300, 25); // rotate # center
ctx.rotate(angle * Math.PI/180);
which isn't accomplishing the task. Am I missing something?
Thanks
Dug around and found this working solution;
context.scale(1,-1); //flip vertically
context.translate(0,-height); //move beneath original position
works wonders!