I'm filling canvas with some dynamic texts in ArrayList. And I set the height as length of ArrayList like ArrayList.length * 20 or something like that.
HTML:
<canvas id="myCanvas" width="272px" style="border: 1px solid black"></canvas>
JS :
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var myheight = 10;
for(var i = 0; mylist.length > i; i++){
context.fillText("my text", 10, myheight);
myheight += 10;
}
loop works fine, texts are filled.. and I'm trying to calculate the canvas.height after the loop.
When I set canvas.height = myheight and all filled texts are gone.. canvas gone blank.
How can I set height after canvas filled by texts dynamically?
Help me..
Thank you.. And sorry for bad English..
Resizing window or canvas using canvas.height = myheight clears the canvas
you need to sava the content of canvas and redraw it again on the resized canvas
If you want to save the content of the canvas and redraw it,here is few options
Use context.getImageData to grab the whole canvas, resize the
canvas, then use context.putImageData to redraw it at the new scale.
Create a new canvas with the updated size and call
context.drawImage(oldCanvas, ...) to copy the old canvas onto the
new one.
call context.setScale(xscale, yscale) and call whatever function you
used to draw the canvas originally. Assuming you set up xscale and
yscale correctly, it will automatically scale everything to the new
size.
A quick solution to your problem is to calculate canvas height before drawing the text, because as you use canvas.height the canvas will be erased
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var myheight = 10;
for(var i = 0; 10 > i; i++){
myheight += 10;
}
canvas.height = myheight;
myheight = 10;
for(var i = 0; 10 > i; i++){
context.fillText("my text", 10, myheight);
myheight += 10;
}
http://jsfiddle.net/borja204/66dn06tq/1/
Related
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>
Ok, I've noticed this bug happen not only in the following code(which is just to illustrate), but every code I've written for canvas;
basically, in chrome(I didn't test other browsers),
once you shift your current tab to either a different pc screen, or simply if you have a few tabs in a row and decide to make a new window out of your current tab,
the canvas itself fails to draw. it is stuck and no reason is given in the console.
Here is a GIF of it happening:
http://gifmaker.cc/PlayGIFAnimation.php?folder=2016081601ZEuvXaZnxC2T9fHNNqSW3l&file=output_f72O1H.gif
Just a simple canvas code if you want to try it out:
<canvas id="canvas" width="500" height="400" style="border:1px solid #000000;"></canvas>
<script>
// grab the canvas and context
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// inital coordinates of the black square
var x = 0;
var y = 200;
// speed of the movement in x- and y-direction
// actual no movement in y-direction
var vX = 1;
var vY = 0;
// width and height of the square
var width = 10;
var height = 10;
function animate() {
// clear
// comment this clear function to see the plot of the sine movement
ctx.clearRect(0, 0, canvas.width, canvas.height);
x += vX;
y += vY;
// if the block leaves the canvas on the right side
// bring it back to the left side
if(x>500) {
x = 0;
}
ctx.fillRect(x, y, width, height);
setTimeout(animate, 33);
}
// call the animate function manually for the first time
animate();
</script>
Some sites fixed this bug but I don't know if I am allowed to link them here.
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'm experimenting with HTML5 canvas, and am having some issues with drawing Circle / Arc, when ever I make a circle, it appears to be more of a distorted oval. I'm not sure what I am doing wrong.
Here is my js, fiddle below the code.
<script>
$(document).ready(function drawOnCanvas() {
$("a#draw").click(function(){
var canvas = document.getElementById("canvas_1");
if ( canvas.getContext ) {
var canvas_context = canvas.getContext("2d");
console.log("2D context supported");
// Drawing circles
var start_degrees = 0;
var end_degrees = 360;
var start_angle = ( Math.PI/180 ) * start_degrees;
var end_angle = ( Math.PI/180 ) * end_degrees;
canvas_context.beginPath();
canvas_context.arc(100,100,25,start_angle, end_angle, true);
canvas_context.strokeStyle="rgb(0,222,0)";
canvas_context.fill();
}
else{
alert("Not Working");
}
});
});
</script>
Fiddle:
http://jsfiddle.net/hLfHy/
The problem is that the canvas inner size isn't accorded to the viewport in which it's rendered.
Add this in the initialization :
var canvas = document.getElementById('canvas_1');
canvas.height = canvas.clientHeight;
canvas.width = canvas.clientWidth
Demonstration
You should set the canvas size to have the correct aspect ratio
Example
<canvas id="canvas_1" width="500" height="500>
You need to set your canvas width and height so you get your calculations right. Try the below it seems to be working now in your fiddle http://jsfiddle.net/hLfHy/10/
canvas.height = $("#canvas_1").clientHeight;
canvas.width = $("#canvas_1").clientWidth;
Basically, I'm trying to create a grid spanning multiple canvases, but I get strange behaviour on the first and last one. The stroke color and spacing is changed. I don't see how it could happen. Here is the relevant code, follow the link to see it in action. (the site is work in progress)
http://www.gjar-po.sk/~hudak9c/test3/
var canvasCount = document.getElementsByTagName("canvas").length;
if (canvasCount > 0) {
for (var i = 0; i < canvasCount; i++) {
var canvas = document.getElementsByTagName("canvas")[i];
if (canvas.getContext("2d")) {
var can = canvas.getContext("2d");
can.beginPath();
for (var x = 5; x < 640; x += 20) {
can.moveTo(x, 0);
can.lineTo(x, canvas.height);
}
for (var y = 5; y < canvas.height; y += 20) {
can.moveTo(0, y);
can.lineTo(canvas.width, y);
}
can.lineWidth = 1;
can.strokeStyle = "#000";
can.stroke();
} else {
alert("getContext fail");
}
}
}
Edit: I managed to fix the problem. It was caused by me (obviously) setting the width and height of the canvas through style.width and style.height, not through canvas.width and canvas.height, which made them stretch/shrink from their default dimensions instead of resize.
Try to set width and height to the canvases by tag attributes width and height like this:
<canvas width="640" height="50">
Not by style attribute. It is very important. Because when you set style attribute and point there width and height properties, then your canvas with default real width and height just stretches to your new sizes.