Is it possible to keep canvas text sharp and responsive - html

When I give the canvas element width: 100% it gets pixelated even at a really small size. I tried giving the canvas itself a big size but it changes nothing. How can I keep the text sharp?
http://jsfiddle.net/kpknhuoa/

Setting Canvas Tag's size though css is not the best way do it.
Cause it enlarges the canvas's original size by a ratio of the width and height values you specify in css.
Below is how to set canvas width to the size of the window.
var can = document.getElementById('overlay'),
ctx = can.getContext("2d");
//innerWidth and innerHeight values of the window are the screen size.
can.width = window.innerWidth;
can.height = window.innerHeight;
ctx.font = "Bold 36px 'Helvetica'";
ctx.fillStyle = "black";
ctx.fillRect(0,0,1000,1000);
ctx.globalCompositeOperation = 'destination-out';
ctx.fillText("Some Text", 25, 50);

Related

Pixi.js - Unable to set sprite width to full width of canvas

Ultimately I want to have a canvas that fills the entire viewport width of the browser and have an image on the canvas fill the entire width of that canvas. I'm taking advantage of pixi.js's displacement filter so that I can create a pseudo 3d effect using a depth map underneath it. The code I'm currently using is
let app = new PIXI.Application();
var w = window.innerWidth;
var h = w / ratio;
app.view.style.width = w + 'px';
app.view.style.height = h + 'px';
document.body.appendChild(app.view);
let img = new PIXI.Sprite.from("images/horses.png");
img.width = w;
img.height = h;
app.stage.addChild(img);
depthMap = new PIXI.Sprite.from("images/horses-depth.png");
depthMap.renderable = false;
depthMap.width = img.width;
depthMap.height = img.height;
img.addChild(depthMap);
app.stage.addChild(depthMap);
displacementFilter = new PIXI.filters.DisplacementFilter(depthMap, 0);
app.stage.filters = [displacementFilter];
Here's a screenshot of it in action.
I even tried manually setting the width to the viewport pixel width on the canvas and the sprite to the actual pixel width of the viewport width and it still wasn't the right size. Manually setting the width for the viewport and sprite to the exact same number also doesn't work; the sprite is inexplicably smaller than the canvas.
I tried to look at the documentation of the sprite class and see if there is something unusual about how sprites handle widths but I couldn't find anything https://pixijs.download/dev/docs/PIXI.Sprite.html
How can I create a pixi.js sprite that fills the entire width of the canvas in order to make both the canvas and the sprite fill the entire viewport width?
pixijs.download
PixiJS API Documentation
Documentation for PixiJS library

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).

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>

How to rotate an image on the canvas and expand the parent so that the image is not cut away?

How to rotate an image on an HTML5 Canvas, without loosing any image data? I mean if rotation causes the image dimensions to increase, I want to expand the Canvas container as well, so that the image is not cut off. The following image might say better:
The brown colored box is actually the container that wraps the Canvas. I want to expand it (and the Canvas to fit the image) when the Canvas is rotated, so that the image is not cut off.
Update:
The image could be larger than the Canvas hence I'm using a bounding box method to calculate proportional sizes with the parent container to fit the image. So the Canvas's style dimensions will be the calculated ones whereas it's height and width attributes will be the image dimensions.
Any help is appreciated!
This function will resize your canvas to exactly fit the rotated image. You must supply the width and height of the image and it's current rotation angle in degrees.
[Edited: OOPS! I should have converted the angle to radians ... And ... the canvas width/height should be changed, not the css width/height]
function resizeCanvasContainer(w,h,a){
var newWidth,newHeight;
var rads=a*Math.PI/180;
var c = Math.cos(rads);
var s = Math.sin(rads);
if (s < 0) { s = -s; }
if (c < 0) { c = -c; }
newWidth = h * s + w * c;
newHeight = h * c + w * s ;
var canvas = document.getElementById('canvas');
canvas.width = newWidth + 'px';
canvas.height = newHeight + 'px';
}

html5 canvas prevent linewidth scaling

If I draw a rectangle of say linewidth=2 and then scale it to double the size of the rectangle, I get a rectangle that has its border double the size of the initial linewidth.
Is there a way to keep the linewidth to the perceived size of 2 or the original size.
In short, I want to just scale the size of the rectangle but keep the linewidth visually of size 2.
I tried setting the linewidth before and after the scale(2,2) command but the border width also increases.
One option is to divide the linewidth by the scale factor and this will work if the x and y scale factors are the same.
I don't have the option to scale the rectangle width and height and I need to use the scale command as I have other objects within the rectangle that need the scaling.
You can define path with transformation and stroke it without one. That way line width won't be transformed.
Example:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.save(); //save context without transformation
ctx.scale(2, 0.5); //make transformation
ctx.beginPath(); //define path
ctx.arc(100, 75, 50, 0, 2 * Math.PI);
ctx.restore(); //restore context without transformation
ctx.stroke(); //stroke path
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
The lineWidth should be scaled down beforehand.
ctx.lineWidth = 2 / Math.max(scaleX, scaleY);
ctx.scale(scaleX, scaleY);
ctx.fillRect(50, 50, 50, 50);
Ok, you have a couple of options:
You can do your own scaling of coordinates and dimensions, e.g.
ctx.strokeRect( scaleX * x, scaleY * y, scaleX * width, scaleY * height) ;
And you'll need to apply the scaling to all the other objects too.
Alternatively you could apply the scaling but not rely on lineWidth for drawing the border of the rectangle. A simple method would be to draw the rectangle by filling the correct region and then removing the interior, minus the border, like so:
var scaleX = 1.5, scaleY = 2;
var lineWidth = 2;
ctx.scale(scaleX, scaleY);
ctx.fillStyle = "#000";
ctx.fillRect(100, 50, 100,
ctx.clearRect(100+lineWidth/scaleX, 50+lineWidth/scaleY, 100-(2*lineWidth)/scaleX, 60-(2*lineWidth)/scaleY);