There is a shadow on the bottom and right sides of a rectangle I've drawn in the canvas on the upper-left hand corner of my window here:
http://thomasshouler.com/datavis/gugg/ratio.html
I haven't set any positive values to the relevant context attributes (shadowBlur, shadowOffsetY, etc.), so what gives?
Canvas code snippet:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.shadowBlur=0;
ctx.shadowOffsetY=0;
var width = 50;
var height = 4;
var grd=ctx.createLinearGradient(0,0,width,0);
grd.addColorStop(0,'#008000');
grd.addColorStop(0.5,'#CCCCCC');
grd.addColorStop(1,'#FF0000');
ctx.fillStyle=grd;
ctx.fillRect(0,0,width,height);
Any wisdom would be greatly appreciated.
It's related to scaling as the canvas is displayed. Your original fragment with a very small gradient and scaled up http://jsfiddle.net/CBzu4/ clearly shows the blurred outline.
Drawing larger (making sure there is no css scaling) it looks fine: http://jsfiddle.net/CBzu4/1/ and the code is the same as yours, just rearranged:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var width = 50 * 8;
var height = 4 * 8;
var grd=ctx.createLinearGradient(0,0,width,0);
grd.addColorStop(0,'#008000');
grd.addColorStop(0.5,'#FFFF00');
grd.addColorStop(1,'#FF0000');
ctx.fillStyle = grd;
ctx.fillRect(5,5,width,height);
Same code, but this time the canvas scaled up with the embedded style: http://jsfiddle.net/CBzu4/2/
<canvas id="myCanvas" style="border: 1px solid red; width: 120%; height: 120%;" width="600" height="80">
The final version is again the same as yours, no css scaling at all and no blurred outline: http://jsfiddle.net/CBzu4/3/
Related
I define a canvas as <canvas id="maincanvas" class="canvas"></canvas> with the style
.canvas {
width: 1000px;
height: 750px;
border: 1px dotted;
}
Then I try to draw a line from the upper-left to the lower-right with:
function GenerateSym() {
var c = document.getElementById("maincanvas");
var ctx = c.getContext("2d");
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(5, 5);
ctx.lineTo(995, 745);
ctx.stroke();
Unfortunately, the line seems to start just a few px too low. While it leaves the canvas entirely on the bottom-center instead of ending just before the bottom-right corner. Additionally, the line seems to be at least 3px wide with terrible anti-aliasing.
I'm running Mint 18 with Firefox 58. Thanks!
You cannot use CSS style for resizing of the canvas element, or you will run into this issue. The canvas DOM element has properties width and height which equel to attributes "width=.." and "height=.." . As said here: Canvas width and height in HTML5 .
So the correct thing to do would be:
.canvas{ style: 1px dotted;}
and
<script>
var c = document.getElementById("maincanvas");
var ctx = c.getContext("2d");
c.width= 1000;
c.height= 750;
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo(5, 5);
ctx.lineTo(995, 745);
ctx.stroke();</script>
I have the following canvas:
<canvas id="myCanvas" width="400" height="400" style="border:0px;">
Your browser does not support the HTML5 canvas tag.</canvas>
javascript:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.arc(200, 200, 100, 0, 2 * Math.PI);
ctx.stroke();
I want to write a score within the center of the canvas:
var score = 0;score++;
ctx.fillText(score,200,200);
However this score will be updated and the more digit this value will be the less centered it will be.
I attempted to use the solution offered here, but the "text" here, is merely a javascript variable named score and thus not considered a text.
Here is a fiddle where I played around to try to solve my problem:
https://jsfiddle.net/27xfvLhh/
I see two potential way to obtain the result wanted:
Is there a way to fill the text directly centered within my canvas?
Is there a way to calculate what will be the size of the text?
Thanks
Is there a way to fill the text directly centered within my canvas?
You can use the properties textAlign for horizontal alignment and textBaseline for vertical alignment. Then calculate center point by for example using half the size of the canvas:
Is there a way to calculate what will be the size of the text?
The ctx.measureText("Text").width will give you the width of the text in pixels using the current set font.
var ctx = c.getContext("2d");
ctx.font = "32px sans-serif";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Some Centered Text", ctx.canvas.width>>1, ctx.canvas.height>>1);
// measure text
var width = ctx.measureText("Some Centered Text").width;
ctx.font = "20px sans-serif";
ctx.fillText(width + "px", ctx.canvas.width>>1, 90 + ctx.canvas.height>>1);
#c {border:1px solid}
<canvas id=c width=600 height=300></canvas>
I'm relatively new to JavaScript and to get a grip on it I've been working on a 3D engine and I stumbled on something odd.
When I render a filled mesh with wire-frame disabled, a gap shows up between my triangles.
Here's an example:
// vertices
var v1 = [40,20];
var v2 = [125,35];
var v3 = [165,105];
var v4 = [35,95];
// draw on screen 1
var canvas = document.getElementById("screen");
var context = canvas.getContext("2d");
triangle(v1,v2,v3,context,true);
triangle(v3,v4,v1,context,true);
// draw on screen 2
var canvas = document.getElementById("screen2");
var context = canvas.getContext("2d");
triangle(v1,v2,v3,context,false);
triangle(v3,v4,v1,context,false);
// draw triangle method
function triangle(v1,v2,v3, context,wireframe)
{
context.beginPath();
context.moveTo(v1[0],v1[1]);
context.lineTo(v2[0],v2[1]);
context.lineTo(v3[0],v3[1]);
context.lineTo(v1[0],v1[1]);
context.closePath();
context.fillStyle = "#333";
context.fill();
if (wireframe)
{
context.strokeStyle = "#0f3";
context.stroke();
}
}
body
{
background-color: #ddd;
}
canvas
{
border:2px solid #000;
}
<canvas id="screen" width="200" height="150" ></canvas>
<canvas id="screen2" width="200" height="150"></canvas>
In essence, the two polygons don't seem to connect properly.
Making the wire-frame the same colour as triangle is not a solution. I want to apply a texture without seeing the edge.
Drawing connected polygons isn't a solution, each triangle may have its own texture transformation.
Should I overdraw triangles to close the gaps?
Is this an Anti-Aliasing issue?
Thanks in advance.
Above: Shows gap between triangles when the wire-frame has been disabled
UPDATE 9-apr-2017
I've considered various options to solve my issue, including writing a triangle routine. Picture down below. However, drawing two triangles tanks my FPS quite a lot. I need a faster way to put pixels on the canvas, but that's for another post since it's off topic.
However, I think that expanding the triangle half a pixel is a better/faster solution since the fill() method is executed by the browser internally instead of JavaScript.
One solution would be to inflate the triangles by a little bit to fill the gaps. This code inflates by 1%.
I think more ideal would be code that inflates by 0.5 pixels, however, that code would be a little more expensive as it would involve calculating vector lenghts...
// vertices
var v1 = [80,40];
var v2 = [250,70];
var v3 = [230,210];
var v4 = [70,190];
// draw on screen 1
var canvas = document.getElementById("screen");
var context = canvas.getContext("2d");
triangle(v1,v2,v3,context,true);
triangle(v3,v4,v1,context,true);
// draw on screen 2
var canvas = document.getElementById("screen2");
var context = canvas.getContext("2d");
inflateTriangle(v1,v2,v3,context,false);
inflateTriangle(v3,v4,v1,context,false);
// draw triangle method
function inflateTriangle(v1,v2,v3, context,wireframe)
{
//centre of tri
xc=(v1[0]+v2[0]+v3[0])/3;
yc=(v1[1]+v2[1]+v3[1])/3;
//inflate tri by 1%
x1= xc+(v1[0]-xc)*1.01;
x2= xc+(v2[0]-xc)*1.01;
x3= xc+(v3[0]-xc)*1.01;
y1= yc+(v1[1]-yc)*1.01;
y2= yc+(v2[1]-yc)*1.01;
y3= yc+(v3[1]-yc)*1.01;
context.beginPath();
context.moveTo(x1,y1);
context.lineTo(x2,y2);
context.lineTo(x3,y3);
context.closePath();
context.fillStyle = "#333";
context.fill();
if (wireframe)
{
context.strokeStyle = "#0f3";
context.stroke();
}
}
<body>
<canvas id="screen" width="400" height="300" style="border:2px solid #000;"></canvas>
<canvas id="screen2" width="400" height="300" style="border:2px solid #000;"></canvas>
</body>
Why is it that rendering gray text onto a canvas looks absolutely terrible? In my example, the first piece of text looks great, the second piece looks awful, and the third piece looks passable.
Here is a screenshot of what I see:
var g = document.getElementById('canvas').getContext('2d');
g.font = "12px arial";
g.fillStyle = "#bbbbbb";
g.fillText("This is some example text. (CANVAS, gray)", 0, 30);
g.fillStyle = "black";
g.fillText("This is some example text. (CANVAS, black)", 0, 60);
div {
font-family: "arial";
font-size: 12px;
color: #bbbbbb;
}
<div>This is some example text. (DOM, gray)</div>
<canvas id="canvas" width="377" height="174"></canvas>
Before marking as a duplicate, please note that I've searched on StackOverflow and Google and the answers there are insufficient for the following reasons:
- I'm not using a retina monitor -- my pixel ratio is 1, so this is not
about using a larger canvas size.
- I've tried rendering the font on the "half pixel", but that doesn't change anything. Rendering on the half pixel helps with things like lines, but not text.
Awful text rendering fixes
Oh you have found the magic colour combination. Put it down on the long list of canvas problems.
Note I only found this problem on Chrome, Firefox does not have an issues. I can not use Edge as for some reason it over heats my laptop resulting in shutdown. So don't know about Edge.
The reason is because the canvas is transparent and the standard says that fully transparent pixels must be alpha multiplied making them black (This is to stop dumb image encoders from encoding transparent colour information that can not be seen). The renderer thinks that what is under the canvas is Black, because that is the colour of transparent pixels.
How to fix.
There are three solutions, nope scratch that the second solution does not work.
There are two solutions I can think of that involve less than 100 lines of code.
Draw a the background first then draw the text on top of that
The second solution I thought would be just draw a outline in a very low alpha value. But that still picks up the bad pre-multiplied transparent black
And the third is to fill (fillRect) the canvas with the text colour and then set the comp mode to "destination-in" and the draw the text. This allows you to still have the transparent pixels under the text
Snippet showing code.
var g = document.getElementById('canvas').getContext('2d');
g.font = "12px arial";
g.fillStyle = "#bbbbbb";
g.fillText("Very bad text rendering", 0, 12);
var g = document.getElementById('canvas1').getContext('2d');
g.font = "12px arial";
g.fillStyle = "#f3f5f6"
g.fillRect(0,0,g.canvas.width,g.canvas.height);
g.fillStyle = "#bbbbbb";
g.fillText("Fixed with solid background", 0, 12);
var g = document.getElementById('canvas2').getContext('2d');
g.font = "12px arial";
g.strokeStyle = "rgba("+0xf3+","+0xf5+","+0xf6+",0.05)";
g.lineWidth = 2;
g.lineJoin = "round";
g.strokeText("Tried with pixel outline", 0, 12);
g.fillStyle = "#bbbbbb";
g.fillText("Tried with pixel outline", 0, 12);
var g = document.getElementById('canvas3').getContext('2d');
g.font = "12px arial";
g.fillStyle = "#bbbbbb";
g.fillRect(0,0,g.canvas.width,g.canvas.height);
g.globalCompositeOperation = "destination-in";
g.fillText("Fixed with comp op destination-in on text colour", 0, 12);
g.globalCompositeOperation = "source-over";
div {
font-family: "arial";
font-size: 12px;
color: #bbbbbb;
background : #f3f5f6;
}
<div>This is some example text. (DOM, gray)<br>
<canvas id="canvas" width="377" height="18"></canvas>
<canvas id="canvas1" width="377" height="18"></canvas>
<canvas id="canvas2" width="377" height="18"></canvas>
<canvas id="canvas3" width="377" height="18"></canvas>
</div>
I have some HTML 5 code that starts out with two black canvases and then updates the canvases one row at a time by drawing a red line for each row. It is supposed to look like a red curtain dropping down over the black background.
The odd part is that at first it looks like random rows (say 5%) are being skipped and left black. I thought I had a bug. But then I noticed that if I resized the browser window that the black lines all turned red. So then I thought it was some sort of browser repaint bug. Then I noticed that both Chrome and Safari have this behavior.
So do you think both these browsers have the same bug or am I doing something wrong?
Interestingly, this issue goes away if both canvases use style="width:100%; height:100%".
<html>
<table>
<tr>
<td><canvas id="myCanvas1" style="width:100%; height:100%"></canvas></td>
<td><canvas id="myCanvas2" style="width:200px; height:400px"></canvas></td>
</tr>
</table>
<script type="text/javascript">
var canvases = [document.getElementById("myCanvas1"), document.getElementById("myCanvas2")];
for(var i = 0; i < canvases.length; i++) {
var canvas = canvases[i];
var ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function updateRow(rowNum) {
var done = true;
for(var i = 0; i < canvases.length; i++) {
var canvas = canvases[i];
if(rowNum >= canvas.height) {
return;
}
done = false;
var ctx = canvas.getContext("2d");
var imageDataForRow = ctx.getImageData(0, rowNum, canvas.width, 1);
for(var offset = 0; offset < imageDataForRow.data.length; offset += 4) {
imageDataForRow.data[offset] = 255;
imageDataForRow.data[offset + 1] = 0;
imageDataForRow.data[offset + 2] = 0;
imageDataForRow.data[offset + 3] = 255;
}
ctx.putImageData(imageDataForRow, 0, rowNum);
}
if(!done) {
var paintNextRow = function() {
updateRow(rowNum + 1);
}
setTimeout(paintNextRow, 1);
}
}
updateRow(0);
</script>
This is your problem:
<canvas id="myCanvas1" style="width:100%; height:100%">
You are setting the width and height of the Canvas using CSS width and height and not the <canvas> html attributes. You are literally stretching a canvas sized 300x150 (the default) to skew across the entire screen.
You need to define the width/height either in the canvas tag:<canvas width="500" height="500">
or in code:
var canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
And not by CSS. If you did this:
<canvas style="width: 500px; height: 500px;">
Then you would have a 300x150 canvas (the default size) that was scaled/warped to be 500x500, which is almost certainly what you're getting.
It looks like you want to have the canvas width change dynamically. This is OK, but you have to do one of a few things. One would be to use the window's onresize event, and set the canvas width equal to the div's clientWidth every time this happens (if the clientWidth has changed of course). The other would be to do a simple general timer to check for the same thing every half-second or so, and set canvas.width equal to the div.style.clientWidth.
(I wrote the above freehand so there might be a typo, but you get the idea)