I asked a similar question and it got labelled as a dupe despite the supposed dupe not answering my question.
Edit: The dupe was [stackoverflow.com/questions/17211920/make-canvas-height-auto][1]
I am using a canvas to print a thermal receipt and as you might imagine the receipt can be varying heights depending on how many items are on it.
I need the canvas therefore to adjust to the content.
I know of canvas.height = window.innerHeight and similar but i don't see how this can help me.
html
<body onload='onDrawReceipt(shipment, returns, company, customer, img)'>
<form onsubmit='return false;'>
<table id= "thermal-receipt-table" style='width:100%;'>
<tbody>
<tr >
<td colspan='2' class='left'>
<div id='canvasFrame'>
<canvas id='canvasPaper' width='576' height= '640' >
</canvas>
</div>
</td>
</tr>
</tbody>
</table>
</form>
js
function onDrawReceipt(order, returns, company, customer, img) {
var canvas = document.getElementById('canvasPaper');
if (canvas.getContext) {
var context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
context.textBaseline = 'top';
lineSpace = 20;
leftPosition = 0;
//centerPosition = canvas.width / 2;
centerPosition = (canvas.width + 26) / 2;
//rightPosition = canvas.width;
rightPosition = (canvas.width - 0);
// cursor = 0;
cursor = 80;
//rest of code taking content to more than 640px in height.
}
You can rely on contentContainerNode.offsetHeight
var canvas = document.getElementById('canvasPaper')
var contentContainerNode = document.getElementById('myContentContainer')
canvas.height = contentContainerNode.offsetHeight
Related
I found some code in CodePen.io that produces a "matrix" effect and I want to try to integrate it on my website. This is the code: https://codepen.io/gnsp/pen/vYBQZJm .
What I am trying to do is to use this script as the background for my one-page website: I want this to be shown behind text and image blocks instead of a picture or a color background.
The page builder I am using is GoodLayers and it allows a "code" block which places a block that you can write javascript in which then gets interpreted.
This is the code:
<style> body {
margin: 0;
height: 100vh;
width: 100vw;
} </style>
<div>
<canvas id= "canv" height:window.innerHeight width:window.innerWidth style="z-index=0"> </canvas>
</div>
<script>
const canvas = document.getElementById('canv');
const ctx = canvas.getContext('2d');
const w = canvas.width = document.body.offsetWidth;
const h = canvas.height = document.body.offsetHeight;
const cols = Math.floor(w / 20) + 1;
const ypos = Array(cols).fill(0);
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, w, h);
function matrix () {
ctx.fillStyle = '#0001';
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = '#0f0';
ctx.font = '15pt monospace';
ypos.forEach((y, ind) => {
const text = String.fromCharCode(70,105,68,105) ;
const x = ind * 20;
ctx.fillText(text, x, y);
if (y > 100 + Math.random() * 10000) ypos[ind] = 0;
else ypos[ind] = y + 20;
});
}
setInterval(matrix, 50);
</script>
I have placed it inside a section wrapper.
This is how it looks in the backend.
And this is how it looks in the frontend: not centered and appears as a block that does not overlap with anything.
That's how it looks when I scroll down.
I want for it to either move with the viewport or better- to be fixed and just cover the whole page from top to bottom.
I am extremely new to this and hardly ever hardcoded anything in CSS or JS without the use of UI of various page builders so please understand that.
Introduction
I'm trying to deal with blurry visuals on my canvas animation. The blurriness is especially prevalent on mobile-devices, retina and high-dpi (dots-per-inch) screens.
I'm looking for a way to ensure the pixels that are drawn using the canvas look their best on low-dpi screens and high-dpi screens. As a solution to this problem I red multiple articles about canvas-down-scaling and followed this tutorial:
https://www.kirupa.com/canvas/canvas_high_dpi_retina.htm
Integrating down-scaling in the project
The project in which I want to implement down-scaling can be found below and consists of a few important features:
There is a (big) main canvas. (Performance optimization)
There are multiple (pre-rendered) smaller canvasses that are used to draw and load a image into. (Performance optimization)
The canvas is animated. (In the code snippet, there is no visible animation but the animation function is intergrated.)
Question
What im trying to achieve: The problem I'm facing seems quite simple. When the website (with the canvas) is opened on a mobile device (eg. an Iphone, with more pixels per inch then a regular desktop). The images appear more blurry. What I'm actually trying to achieve is to remove this blurriness from the images. I red this and it stated that blurriness can be removed by downsampling. I tried to incorporate this technique in the code provided, but it did not work completely. The images just became larger and I was unable to scale the images back to the original size. snippet it is not implemented correctly, the output is still blurry. What did I do wrong and how am I able to fix this issue?
Explanation of the code snippet
The variable devicePixelRatio is set to 2 to simulate a high-dpi phone screen, low-dpi screens have a devicePixelRatio of 1.
Multiple pre-rendered canvasses generated is the function spawn is the snippet there are 5 different canvasses, but on the production environment there are 10's.
If there are any pieces of information missing or questions about this post, please let me know. Thanks a lot!
Code Snippet
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d' );
var circles = [];
//Simulate Retina screen = 2, Normal screen = 1
let devicePixelRatio = 2
function mainCanvasPixelRatio() {
// get current size of the canvas
let rect = canvas.getBoundingClientRect();
// increase the actual size of our canvas
canvas.width = rect.width * devicePixelRatio;
canvas.height = rect.height * devicePixelRatio;
// ensure all drawing operations are scaled
c.scale(devicePixelRatio, devicePixelRatio);
// scale everything down using CSS
canvas.style.width = rect.width + 'px';
canvas.style.height = rect.height + 'px';
}
// Initial Spawn
function spawn() {
for (let i = 0; i < 2; i++) {
//Set Radius
let radius = parseInt(i*30);
//Give position
let x = Math.round((canvas.width/devicePixelRatio) / 2);
let y = Math.round((canvas.height /devicePixelRatio) / 2);
//Begin Prerender canvas
let PreRenderCanvas = document.createElement('canvas');
const tmp = PreRenderCanvas.getContext("2d");
//Set PreRenderCanvas width and height
let PreRenderCanvasWidth = ((radius*2)*1.5)+1;
let PreRenderCanvasHeight = ((radius*2)*1.5)+1;
//Increase the actual size of PreRenderCanvas
PreRenderCanvas.width = PreRenderCanvasWidth * devicePixelRatio;
PreRenderCanvas.height = PreRenderCanvasHeight * devicePixelRatio;
//Scale PreRenderCanvas down using CSS
PreRenderCanvas.style.width = PreRenderCanvasWidth + 'px';
PreRenderCanvas.style.height = PreRenderCanvasHeight + 'px';
//Ensure PreRenderCanvas drawing operations are scaled
tmp.scale(devicePixelRatio, devicePixelRatio);
//Init image
const image= new Image();
//Get center of PreRenderCanvas
let m_canvasCenterX = (PreRenderCanvas.width/devicePixelRatio) * .5;
let m_canvasCenterY = (PreRenderCanvas.height/devicePixelRatio) * .5;
//Draw red circle on PreRenderCanvas
tmp.strokeStyle = "red";
tmp.beginPath();
tmp.arc((m_canvasCenterX), (m_canvasCenterY), ((PreRenderCanvas.width/devicePixelRatio)/3) , 0, 2 * Math.PI);
tmp.lineWidth = 2;
tmp.stroke();
tmp.restore();
tmp.closePath()
//Set Image
image .src= "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3"
//Get padding
let paddingX = (PreRenderCanvas.width/devicePixelRatio)/5;
let paddingY = (PreRenderCanvas.height/devicePixelRatio)/5;
//Load image
image.onload = function () {
tmp.beginPath()
tmp.drawImage(image, paddingX,paddingY, (PreRenderCanvas.width/devicePixelRatio)-(paddingX*2),(PreRenderCanvas.height/devicePixelRatio)-(paddingY*2));
tmp.closePath()
}
let circle = new Circle(x, y, c ,PreRenderCanvas);
circles.push(circle)
}
}
// Circle parameters
function Circle(x, y, c ,m_canvas) {
this.x = x;
this.y = y;
this.c = c;
this.m_canvas = m_canvas;
}
//Draw circle on canvas
Circle.prototype = {
//Draw circle on canvas
draw: function () {
this.c.drawImage( this.m_canvas, (this.x - (this.m_canvas.width)/2), (this.y - this.m_canvas.height/2));
}
};
// Animate
function animate() {
//Clear canvas each time
c.clearRect(0, 0, (canvas.width /devicePixelRatio), (canvas.height /devicePixelRatio));
//Draw in reverse for info overlap
circles.slice().reverse().forEach(function( circle ) {
circle.draw();
});
requestAnimationFrame(animate);
}
mainCanvasPixelRatio()
spawn()
animate()
#mainCanvas {
background:blue;
}
<canvas id="mainCanvas"></canvas>
<br>
<!DOCTYPE html>
<html>
<body>
<p>Image to use:</p>
<img id="scream" width="220" height="277"
src="pic_the_scream.jpg" alt="The Scream">
<p>Canvas:</p>
<canvas id="myCanvas" width="240" height="297"
style="border:1px solid #d3d3d3;">
</canvas>
<script>
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 10, 10);
};
</script>
</body>
In my very simple code example below, I am trying to draw a box, 10 pixels in from the margin all the way around the outside of a canvas. Despite getting what seem like legit values from the screen extends of the browser window, I am only getting an L-shaped line, which seems really odd. My code seems very straight forward. So 2 questions:
Why is it so small (eg not going across the whole browser window?
Why isn't it a box like the code suggests it should be?
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100% height=100%></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Here's the result which you can clearly see is both way too small and not a box at all:
Your problem stems from the common misunderstanding of the canvas' height and width properties. You're giving the values as percents, but that wont work. From MDN:
HTMLCanvasElement.height Is a positive integer reflecting the height
HTML attribute of the element interpreted in CSS pixels. When
the attribute is not specified, or if it is set to an invalid value,
like a negative, the default value of 150 is used.
So you should use plain integers instead. If you need a dynamic size, you can use javascript to update the values.
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width="628" height="200"></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Because you are using window / document width & height,
e.g. the entire webpage, so the lines goes outside of the canvas.
you should instead use the canvas element's width & height.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100 height=100></canvas>
To achieve "fullscreen", something you can do is to use a parent element or the body tag and then "copy" it's width & height to the canvas afterwards.
Note that i use vh & vw instead of % to avoid any stretching issues, etc.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
updateCanvasSize();
function updateCanvasSize(){
canvas.width = bodySim.clientWidth;
canvas.height = bodySim.clientHeight;
draw();
}
function draw(){
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
} //end of draw
#bodySim {
width: 100vw;
height: 100vh;
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<div id="bodySim">
<canvas id="DemoCanvas" ></canvas>
</div>
I'm using createJS to drawn inside the canvas. I have my canvas set to occupy the browser window maintaining aspect ratio using the resize() function.
This is the code:
mytext = new createjs.Text("Lorem ipsum dolor sit amet 2","19px Calibri","#073949");
mytext.x = 450
mytext.y = 300;
stage.addChild(mytext);
resize = function() {
var canvas = document.getElementById('canvas');
var canvasRatio = canvas.height / canvas.width;
var windowRatio = window.innerHeight / window.innerWidth;
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight - 35;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
}()
What happens is that the text gets blurry (decrease of quality) when the canvas resizes.
http://i.imgur.com/RQOSajs.png
vs
http://i.imgur.com/Xwhf5c5.png
How can I solve this issue?
Since you are using CreateJS, you can simply resize the canvas, and scale the entire stage to redraw everything at the new size:
// just showing width to simplify the example:
var newWidth = 800;
var scale = newWidth/myCanvas.width;
myCanvas.width = newWidth;
myStage.scaleX = myStage.scaleY = scale;
myStage.update(); // draw at the new size.
#Adam's answer is correct as far as scaling the canvas goes. You do NOT want to scale with CSS, as it will stretch your canvas instead of changing its pixel dimensions. Set the width and height of the canvas using JavaScript instead.
stage.canvas.width = window.innerWidth;
stage.canvas.height = window.innerHeight;
As you stated in your comment, this will only change the canvas size, and not reposition or scale your content. You will have to do this manually. This is fairly simple. Generally, I recommend putting your "resize" listener in the JavaScript in your HTML file, rather than on a frame script.
First, determine the scale, based on the size of the window and the size of your content. You can use the exportRoot.nominalBounds.width and exportRoot.nominalBounds.height which is the bounds of the first frame. If you want to scale something else, use its nominalBounds instead.
Note that nominalBounds is appended to all MovieClips exported from Flash/Animate. If you enable multi-frame bounds, and want to use those, you will have to modify your approach.
The main idea is to use the original, unscaled size of your contents.
var bounds = exportRoot.nominalBounds;
// Uses the larger of the width or height. This will "fill" the viewport.
// Change to Math.min to "fit" instead.
var scale = Math.max(window.innerWidth / bounds.width, window.innerHeight / bounds.height);
exportRoot.scaleX = exportRoot.scaleY = scale;
You can then center it if you want.
exportRoot.x = *window.innerWidth - bounds.width*scale)/2;
exportRoot.y = *window.innerHeight - bounds.height*scale)/2;
Here is a quick sample of a responsive canvas using a simple shape as the scaling contents:
http://jsfiddle.net/lannymcnie/4yy08pax/
Doing this with Flash/Animate CC export has come up a few times, so it is on my list of future EaselJS demos to include on createjs.com, and in the EaselJS GitHub.
I hope this helps.
Take a look at my jsfiddle : https://jsfiddle.net/CanvasCode/ecr7o551/1/
Basically you just store the original canvas size and then use that to work out new positions and sizes
html
<canvas id="canvas" width="400" height="400">
Canvas was unable to start up.
</canvas>
<button onclick="resize()">Click me</button>
javascript
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var originalWidth = canvas.width;
var originalHeight = canvas.height;
render = function()
{
context.fillStyle = "#DDD";
context.fillRect(0,0, originalWidth * (canvas.width / originalWidth), originalHeight * (canvas.height / originalHeight));
context.fillStyle = "#000";
var fontSize = 48 * (canvas.width / originalWidth);
context.font = fontSize+"px serif";
context.fillText("Hello world", 100 * (canvas.width / originalWidth), 200 * (canvas.height / originalHeight));
}
resize = function()
{
canvas.width = 800;
canvas.height = 600;
render();
}
render();
The HTML5 canvas element works with two different sizes
Visual size on screen, controlled via CSS, like you're setting with canvas.style.width/height
Size of pixel buffer for the drawing, controlled via numeric width and height pixel attributes on the canvas element.
The browser will stretch the buffer to fit the size on screen, so if the two values are not 1:1 ratio text will look blurry.
Try adding the following lines to your code
canvas.width = width;
canvas.height = height;
I created a function to resize all the elements on the screen after resizing the canvas. It saves the initial coordinates and scales for the elements with the original width of 900 px and then it changes them according to the current width ratio relative to the original width ratio. The text isn't blurry/bad quality anymore.
resize = function() {
var canvas = document.getElementById('canvas');
var canvasRatio = canvas.height / canvas.width;
var windowRatio = window.innerHeight / window.innerWidth;
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.width = width;
canvas.height = height;
reziseElements();
};
reziseElements = function()
{
var canvrat = canvas.width / 900;
//Simplified
stage.scaleX = stage.scaleY = canvrat;
//Old Version
/*for (i=0; i<stage.numChildren ;i++)
{
currentChild = stage.getChildAt(i);
if (typeof currentChild.oscaleX == 'undefined')
{
currentChild.oscaleX = currentChild.scaleX;
currentChild.ox = currentChild.x;
currentChild.oy = currentChild.y;
}
}
for (i=0; i<stage.numChildren ;i++)
{
currentChild = stage.getChildAt(i);
currentChild.scaleX = currentChild.scaleY = currentChild.oscaleX * canvrat
currentChild.x = currentChild.ox * canvrat
currentChild.y = currentChild.oy * canvrat
} */
}
I am using the following code (in accordance with this answer) to have a responsive canvas:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
ctx.font = '52px verdana';
var text = 'Sample text';
var textWidth = ctx.measureText(text).width;
ctx.fillText(text, 50, 50);
canvas {
width: 100%;
height: auto;
}
<div class="container">
<div class="row">
<div class="col-md-12">
<canvas id="canvas" width="800">
Sorry, your browser doesn't support the <canvas> element.
</canvas>
</div>
</div>
</div>
It doesn't look well because the actual canvas element size is not 800, but 1140 (in my browser). Looks like I should detect the window size somehow and assign correct canvas size value. How should I do it? I am using the latest Twitter Bootstrap library.
Dont use css style
ctx.canvas.width = window.innerWidth || document.body.clientWidth;