Thinner html canvas stroke width - html

I have set the 2d context line width to its apparent minimum 1:
let context = this._canvas.getContext("2d");
context.lineWidth = 1;
context.stroke();
But that is still fairly wide : maybe 20 pixels. Is there any way to get it less?

The key is to ensuring that you've the same number of virtual pixels as you have actual ones. More screen pixels and the image is scaled larger. Fewer screen pixels and the image is shrunk.
Scaling up a 1 pixel wide line produces rectangles, but what happens when we scale down a 1 pixel line? The simple answer is it becomes less intense.
The following example draws 2 canvases the same size. The key difference though, is the size of the pixel buffer they each have. The first has 10,000 pixels - 100x100. The second has just 100 pixels - 10x10. Even though we've just drawn a 1 pixel wide line, you can see the very different appearances of these lines.
window.addEventListener('load', onLoaded, false);
function onLoaded(evt)
{
let can = document.querySelector('canvas');
let ctx = can.getContext('2d');
ctx.moveTo(0,0);
ctx.lineTo(can.width,can.height);
ctx.stroke();
let can2 = document.querySelectorAll('canvas')[1];
let ctx2 = can2.getContext('2d');
ctx2.moveTo(0,0);
ctx2.lineTo(can2.width,can2.height);
ctx2.stroke();
}
canvas
{
width: 100px;
height: 100px;
}
<canvas width=100 height=100></canvas><br>
<canvas width=10 height=10></canvas>

Related

Why are artifacts visible in a scaled html5 canvas?

I've seen this and this discussion about removing antialiasing in canvases, but I don't think this is the same thing.
After scaling an html5 canvas by an arbitrary value (i.e., making it responsive), I've noticed that if I draw two rectangles of the same size and in the same location, the edges of the scaled side of the first rectangle remain visible.
I've included an example snippet where I draw a grey rectangle, then draw an red rectangle on top of it. There's a one-pixel red vertical line on the left and right edges of the grey rectangle. I know it may seem trivial, but it's very noticeable in my situation.
How do I fix this? Thanks!
var example = document.getElementById("example");
var ctx = example.getContext('2d');
ctx.scale(1.13,1);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,10,50,30);
ctx.fillStyle = "Black";
ctx.font = "20px Arial";
ctx.fillText("< Looks good.",70,30);
ctx.fillStyle = "Red";
ctx.fillRect(10,50,50,30);
// This light grey rectangle should completely cover the previous red one, but it doesn't!
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,50,50,30);
ctx.fillStyle = "Black";
ctx.font = "20px Arial";
ctx.fillText("< Do you see red?",70,70);
<canvas id="example"></canvas>
You are scaling the transform matrix by a factor of 1.13 on the X axis.
So your coordinate 10, will actually end up on at coordinate 11.3 on the real pixels matrix.
You can't draw on fraction of pixels, so indeed antialiasing will kick in here.
So why does the first one looks better?
Because the mix between grey and white* is more neutral than the one between red grey and white. But even your first rect is antialiased.
Just zoom in your canvas and you'll see it, there is a one pixel band on both sides that is actually semi-transparent.
* "White" here is the one of the page's background
var example = document.createElement("canvas");
var ctx = example.getContext('2d');
ctx.scale(1.13,1);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,10,50,30);
ctx.fillStyle = "Red";
ctx.fillRect(10,50,50,30);
ctx.fillStyle = "LightGrey";
ctx.fillRect(10,50,50,30);
// draw bigger with no antialiasing
var z_ctx = zoomed.getContext('2d');
zoomed.width = example.width * 10;
zoomed.height = example.height * 10;
z_ctx.imageSmoothingEnabled = false;
z_ctx.drawImage(example, 0,0, zoomed.width, zoomed.height);
<canvas id="zoomed"></canvas>
So how to avoid this?
Well simply avoid filling at non integer pixel coordinates. This means you have to be constantly aware of your context transformation matrix too, not only of the values you pass to the drawing functions.
(Ps: also remember that stroke is an even eviler beast since it start drawing from the middle of the line, so in this case, you even have to take into considerations the lineWidth, see this Q/A on the matter).

Scaling up viewport

My game is 200 x 320 pixels and I want to scale this up to fit any screen. The problem is I need to scale this up in integer multiples of these dimensions so the upscaled pixels don't look uneven.
What should I put in my constructor, render() and resize() methods to achieve this?
In the constructor I now have:
camera = new OrthographicCamera();
camera.setToOrtho(false,200,320);
stage = new Stage(new ScalingViewport(Scaling.fill,200,320, camera));
In render():
camera.update();
game.batch.setProjectionMatrix(camera.combined);
And in resize():
int widthScale = width/200;
int heightScale = height/320;
int newScale = Math.max(widthScale, heightScale);
int multiplesWidth = newScale*200;
int multiplesHeight = newScale*320;
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
camera.setToOrtho(false, 200, 320);
My problem is that the content isn't centered. How and where can I center whatever is on the screen? I tried with:
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
but the screen is blank!
I think the problem is that you are trying to center the screen using the screen dimensions and not the world dimensions.
Try replacing
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
with
camera.position.set(stage.getViewport().getWorldWidth()/2f, stage.getViewport().getWorldHeight()/2f, 0);
EDIT:
Okay, so here is an explanation why I proposed you set the camera to (width/2, height/2). (I will leave out the third z-Coordinate from now on as it will always be 0.)
camera.position.set() sets the center point of the camera. At (0,0) the camera is positioned right at the center of the screen, depicted by the red rectangle below. What libgdx does by default, and what I proposed offsets the camera by width/2(the blue line) and height/2(the green line) which translates to the magenta rectangle.
Side note: the reason camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0); didn't work is because Gdx.graphics.getX() returns the width and height of the window in pixels, not in the measurement system the Viewport uses. This offsets the camera about a meter up and to the right, where you obviously didn't draw anything.
If you want the red rectangle as camera, you can change the last argument of
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
to false. Or use camera.position.set(0,0,0);

HTML5: Canvas width and height

Consider:
<canvas id="myCanvas" width="200" height="300" />
Are those units in pixels? If not, is there a workaround to change it?
Yes, those units are always in pixels and applies to the bitmap the canvas element uses. However, if there is no size defined on the element using CSS (ie. style attribute or using a style sheet) the element will automatically adopt to the size of its bitmap.
There is no other way of setting the size of the bitmap than by number of pixels. Using CSS will only change the size of the element itself, not the bitmap, stretching whatever is drawn to the bitmap to fit the element.
To use other units you will have to manually calculate these using JavaScript, for example:
// using % of viewport for canvas bitmap (pixel ratio not considered)
var canvas = document.getElementById("myCanvas"),
vwWidth = window.innerWidth,
vwHeight = window.innerHeight,
percent = 60;
canvas.width = Math.round(vwWidth * percent / 100); // integer pixels
canvas.height = Math.round(vwHeight * percent / 100);
// not to be confused with the style property which affects CSS, ie:
// canvas.style.width = "60%"; // CSS only, does not affect bitmap
If you want to support retina then you need to use the window.devicePixelRatio and multiply it with the sizes. In this case CSS would be necessary as well (combine with code above):
var pxRatio = window.devicePixelRatio || 1,
width = Math.round(vwWidth * percent / 100),
height = Math.round(vwHeight * percent / 100);
canvas.width = width * pxRatio;
canvas.height = height * pxRatio;
canvas.style.width = width + "px";
canvas.style.height = height + "px";
Almost all size parameters in HTML5 are going to be in pixels. If you're experiencing issues drawing the canvas with your current code, try taking out the self-closing block at the end:
<canvas id="myCanvas" width="200" height="300"></canvas>
You may also consider viewing the properties of the canvas element as defined by W3 Schools: http://www.w3schools.com/html/html5_canvas.asp
use css to setup your canvas styles
<canvas id="el"></canvas>
<style>
#el{
display: block;
width:100%;
height: 100%;
}
</style>

Generating a random image with specific content at each page reload/refresh

So I have a banner on a site, but I want to make it so that each time the page loads, a different image appears. More precisely, I want (say 50) squares (say having a black border, white fill) of random size (say from 5 pixels to 20 pixels in size) in random positions of a 750x63 px frame, with a white background.
What would be the best way to do this? I know a little JavaScript and HTML (and am very willing to learn more), but I really have no idea where to start. This is for my personal webpage, which I wish to spruce up a bit. Right now the fanciest code I have is some JavaScript for a simple Lightbox interface.
Wow, that was easier and more fun than I expected. Here's the code, that goes in the <body> section of my HTML code, optimized for a 750x80px frame. The random integer generator I got from this other question.
<canvas id="canvas" width="750" height="80"></canvas>
<script type="text/javascript">
//Random integer generator
function getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
//Loop to make 80 squares
for (var i=0;i<80;i++) {
//The x-position of the square, with 5px padding in frame
var sx = getRandomInt(5,705);
//The y-position of the square, with 5px padding in frame
var sy = getRandomInt(5,35);
//The height of the square, smallest 8x8px, largest 40x40px
var sh = getRandomInt(8,40);
//First, create a black square
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect (sx, sy, sh, sh);
//Second, create a white square that's 4px shorter and thinner,
//leaving a boundary of 2px
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect (sx+2, sy+2, sh-4, sh-4);
}
}
draw();
</script>
The approach I used is from a Mozilla Developers page. The result is something like this:
Hooray!

Scaling and cropping an image with fixed dimensions

I want to display an image, and it should be transformed like this:
If for example, my original image is 200x300 pixels in size, I want it to have a width of 150, and then scale the height accordingly. Then I want to crop the image, so that the result has a dimension of 150x150 pixels.
I've tried several ways, but haven't figured out how to do it.
You can calculate the scale factor by dividing new width by old width:
var scale : Number = 150 / myImage.width;
myImage.scaleX = myImage.scaleY = scale;
To crop, either use a mask on the scaled clip, or draw to a new Bitmap:
var myBitmapData : BitmapData = new BitmapData ( 150, 150 );
// use concatenated matrix of the image to scale the new bitmap data
var matrix : Matrix = myImage.transform.concatenatedMatrix;
myBitmapData.draw ( myImage, matrix );
var myBitmap : Bitmap = new Bitmap ( myBitmapData );
addChild ( myBitmap );
Depending on how many images there are, and what you are going to do with them later, you should always test both possibilities for performance.