Canvas jagged line issue - javascript

I've built a very simple line graph using canvas (pulling in data from google analytics). I'm having a few small issues. First off, the plot line is jagged:
http://jsfiddle.net/uJQ7K/
I'm just using lineTo for this:
ctx.lineTo((i*cellWidth) + cellWidth + padding,(tableHeight + padding) - data[i].v);
I've read that using splines can fix this but surely there's a way to draw a straight line out of the box?
Other problems I'm having are changing one attribute like strokeStyle changes all the strokes on a page. How do I just change the stroke of the plot line and not affect the appearance of the grid?

Have you tried changing your line cap?
cxt.lineCap = 'round';
EDIT -
By adding ctx.beginPath(); before your ctx.lineCap you will get a better result -
ctx.beginPath();
ctx.lineCap = 'round';
http://jsfiddle.net/uJQ7K/1/

Related

Unpredictable behaviour of HTML canvas

I observe weird behaviour of HTML canvas both in chrome and in firefox. For some reason there is one-pixel wide palid line when I fill rect over the smaller rect of different color after clipping.
Here is JS fiddle snippet: https://jsfiddle.net/srkgbxw1/7/
var my_canvas = document.getElementById('canvas'),
context = my_canvas.getContext("2d");
context.translate(0.5, 0.5)
context.fillStyle = "orange";
context.fillRect(10,10,100,100);
context.beginPath();
context.rect(20,20,90,90);
context.clip();
context.fillStyle = "white";
context.fillRect(0,0,110,110);
context.fillStyle = "orange";
context.fillRect(0,0,190,190);
This is the result:
Please help me figure out what the reason for the pallid line after third fillRect with orange?
UPD: original question's changed after I was pointed out I had made a stupid mistake in its first edition
UPD 2 I got the answer, this happens due to color interpolation at edges of clipping region because context was translated for half a pixel, which was done to get thin lines (recommended technique). To avoid interpolation, clipping region should be adjusted for half a pixel as well, then the pallid line disappears.
If you look into the logs:
TypeError: context.setStrokeStyle is not a function
Therefore, when you comment it, everything after it is executed while when you uncomment it, it fails and everything after this line is not executed.
EDIT: For the second question issue:
The issue is equivalent to this, why the blue square does not fully overlap the red square:
context.translate(0.5, 0.5);
context.fillStyle = "red";
context.fillRect(10,10,100,100);
context.fillStyle = "blue";
context.fillRect(10,10,100,100);
Because of translate, you are now drawing in float pixels, everything moved by half a pixel.
I suggest you read this for more information about how it is handled:
fillRect() not overlapping exactly when float numbers are used
Removing context.translate(0.5, 0.5); removes the "palid line".

Writing blank on canvas using fillText

I have a chart.js Donut chart in the center of which I have to show a value.There is a date field which refreshes the chart thereby affecting the central value.
var text = dataTxt,
textX = Math.round((width - ctx.measureText(text).width) / 2),
textY = height / 2;
ctx.fillText(" ", textX, textY);//attempting to write blank value.
ctx.fillText(text, textX, textY); //this writes the value.
ctx.save();
Problem is that on subsequent update of the chart the central area is not cleared & the value gets written over the old one.
For this I added the line ctx.fillText(" ", textX, textY); in above code but to no effect.
Any Ideas on how to clear up the area before the text is written there?
There is a HTML/CSS solution to write the data in the center but I cant use that since it doesnt export the central data when chart is downloaded as PNG.
In typical use cases people don't attempt to "undo" their changes on the Canvas - it's far easier to just clear the Canvas and start drawing all over again.
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Do my drawing again
myChart.update(); // With chart.js this triggers a redraw.
In your case I think you're making the mistake of thinking that the Canvas saves the text image and lets you alter it in subsequent drawings. Think of it like Microsoft Paint - once that text is on the image, you're not going to be able to get rid of it easily without undo'ing.

Canvas - fill area below or above lines

Ok I have an hard issue to solve. I have a HTML5 canvas in which I draw two charts (lines). I have the points of each chart where the lines are connected and I have two y-values (X,Y in the picture) where I have to draw a line and fill above or below the chart.
I really can't seem to get it work because I try coloring everything above the certain chart and clipping it with a rectangle but I have two chart so I must have two clipping areas which gives incorrect solution.
There is a picture attached to the post to see the case
So I have a red chart and a brown chart and values for X and Y (which are the colorful lines). X is the light blue - the height to where I want to color the graph below. Y is the light gray and is the height for coloring above the brown chart.
How can I implement this wihtout knowing the crossing point of the charts and X or Y?
The code I am using is this. I call it twice for every chart. I have omitted the code for drawing the chart - it is drawing using the "points" array.
unfortunately I don't have the points of the crossing between the end of the color area and the chart (the crossing of red and light blue ; brown and light gray)
ctx.rect(clipX, clipY, clipWidth, clipHeight);
ctx.stroke();
ctx.clip();
//fill the area of the chart above or below
ctx.globalAlpha = 0.4;
ctx.strokeStyle = color;
ctx.fillStyle = color;
ctx.beginPath();
ctx.moveTo(points[0].x, points[0].y + visibleGraphicSpace);
for (var i = 1; i < points.length; i++) {
ctx.lineTo(points[i].x, points[i].y + visibleGraphicSpace);
}
ctx.closePath();
ctx.fill();
First I draw rectangule for the visible area, then I draw the chart with the given points array, close it and fill everything above or below till the end of the canvas. But this solution only takes the second filling right because it overrides the first one.
PS: I need to draw both coloring fillings not only one of them.
I hope I managed to explain it well enough. If you have any questions don't mind to ask.
Thank you for the help in advance.
You can create a clipping area (using context.clip) to make sure your blue and gray fills are contained inside the paths created by your chart. When you set a clipping area, any new drawings will not be displayed outside the clipping area.
Save some chart points in an array.
Save the top & bottom range of fill within the charted points
Define the chart path (==plot the points without stroking the points)
Create a clipping area from the path (all new drawing will be contained inside the clipping area and will not appear outside the area)
Fill the clipping area (with your blue & gray fills)
Stroke the path (with your red and maroon strokes)
Note: When you create a clipping path, it can only be "unclipped" by wrapping the clipping code inside context.save & context.restore.
Here's annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var pts0=[
{x:119,y:239},
{x:279,y:89},
{x:519,y:249},
{x:739,y:83},
{x:795,y:163},
];
var fill0={top:75,bottom:133};
var pts1=[
{x:107,y:342},
{x:309,y:523},
{x:439,y:455},
{x:727,y:537},
{x:757,y:389}
];
var fill1={top:473,bottom:547};
filledChart(pts0,fill0,'red','skyblue');
filledChart(pts1,fill1,'maroon','lightgray');
function filledChart(pts,fill,strokecolor,fillcolor){
// define the path
// This doesn't stroke the path, it just "initializes" it for use
ctx.beginPath();
ctx.moveTo(pts[0].x,pts[0].y);
for(var i=0;i<pts.length;i++){
var pt=pts[i];
ctx.lineTo(pt.x,pt.y);
}
// save the un-clipped context state
ctx.save();
// Create a clipping area from the path
// All new drawing will be contained inside
// the clipping area
ctx.clip();
// fill some of the clipping area
ctx.fillStyle=fillcolor;
ctx.fillRect(0,fill.top,cw,fill.bottom-fill.top);
// restore the un-clipped context state
// (the clip is un-done)
ctx.restore();
// stroke the path
ctx.strokeStyle=strokecolor;
ctx.lineWidth=2;
ctx.stroke();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=800 height=550></canvas>
You could try to make the 'fill' before the chart.
You create the fill color.
You create the 'mask' (white color)
You create the chart line
An example, with only one chart, but can easily be changed to use two charts: https://jsfiddle.net/eLwc96fj/
var c2 = document.getElementById('test').getContext('2d');
// Create a colored rectangle
c2.fillStyle = '#0f0';
c2.rect(80,0, 200,70);
c2.fill();
// Create the 'mask' - it has the same path than the chart, but then follow the above rectangle.
c2.beginPath();
c2.fillStyle = '#fff';
c2.moveTo(80, 80);
c2.lineTo(120,50);
c2.lineTo(180, 90);
c2.lineTo(250, 40);
c2.lineTo(280, 120);
c2.lineTo(280, 0);
c2.lineTo(80, 0);
c2.closePath();
c2.fill();
// Draw the chart itself
c2.strokeStyle = '#f00';
c2.beginPath();
c2.moveTo(80, 80);
c2.lineTo(120,50);
c2.lineTo(180, 90);
c2.lineTo(250, 40);
c2.lineTo(280, 120);
c2.stroke();

JavaScript canvas, getting flat lines

I'm working on project which have to draw graphs. Everything is quite good, but noticed one problem, the lines are showing strange.. it seems like someone draw my graph with brush holding horizontally.. when line goes down everything is OK, but when line is going horizontal the it becomes much smaller ... I can't find what's the problem could be..
Please help, because I started to get wrong graphs when it needs to draw horizontal line...
Here is the link to my project:
http://www.unolita.lt/images/signalai/Documents/Koreliacine%20funkcija.html
You can clearly see my problem on 1st picture..
Here is it's code:
function drawSignal()
{
var canvas = document.getElementById("canvSignal");
if (canvas.getContext)
{
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
function Signalas()
{
<...>
ctx.beginPath();
ctx.moveTo(x, y);
ctx.strokeStyle = "black";
<...>
y=250- Sn[n] ;
ctx.lineTo(x, y);
ctx.stroke(x, y);
<...>
To put all code here was too much problematic..
This is due to the fact lines are drawn over all pixels they're over (on canvas positionning is in float). When you want to draw precise vertical or horizontal lines in javascript on a canvas, you'd better have them in half ints.
Possible Solution : If you have to draw a line with an odd numbered width, then you will have to offset the center of your line by 0.5 up or down. That way rendering will happen at boundary of pixel and not in middle, and you will always have a sharp line with no residue at the end edges.
So add 0.5 for odd numbered line width so that your points should be half numbered
ctx.lineTo(x+0.5, y+0.5);
ctx.stroke(x+0.5, y+0.5);
I have modified your code like this in line number 134 and 135 and got a output like this . Hope, this helps
Refer Here :
incorrect display lineWidth=1 at html5 canvas
HTML5 Canvas and Line Width
Line Width in Canvas

Circle(ARC) gradient fill with canvas JS

I need to draw a dynamic donut chart - something similar to -
http://194.90.28.56/~dev1/t.jpg
The green part indicates the percentage (in this case 27%) - it must be dynamic.
I think I need to do something like - Android - How to draw an arc based gradient
But with JS..
Thanks.
Great question. Gradients along paths in canvas are hard. The easiest way is to fudge it.
Instead of thinking of your image as a gradient that follows a circular path, think of it as two linear gradients.
One on the left side, going from green to gray, top to bottom.
The other on the right side, going from white to gray, top to bottom.
Imagine a square made of those two gradients:
Now imagine a circle cutting through:
That's all you gotta do.
To "cut" through like that its easiest to use clipping regions, so I've made an example doing that.
Here's the live example: http://jsfiddle.net/simonsarris/Msdkv/
Code below! Hope that helps.
var greenPart = ctx.createLinearGradient(0,0,0,100);
greenPart.addColorStop(0, 'palegreen');
greenPart.addColorStop(1, 'lightgray');
var whitePart = ctx.createLinearGradient(0,0,0,100);
whitePart.addColorStop(0, 'white');
whitePart.addColorStop(1, 'lightgray');
var width = 20;
ctx.lineWidth = width;
// First we make a clipping region for the left half
ctx.save();
ctx.beginPath();
ctx.rect(-width, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the left half
ctx.strokeStyle = greenPart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default
// Then we make a clipping region for the right half
ctx.save();
ctx.beginPath();
ctx.rect(50, -width, 50+width, 100 + width*2);
ctx.clip();
// Then we draw the right half
ctx.strokeStyle = whitePart;
ctx.beginPath();
ctx.arc(50,50,50,0,Math.PI*2, false);
ctx.stroke();
ctx.restore(); // restore clipping region to default

Categories

Resources