HTML canvas dimensions are right but the context uses the default ones - javascript

I have a canvas element inside a div in an HTML page. I've set its width and height via CSS to 100% of the parent div, and it sets them correctly.
I defined a "draw" function, and I use it like this: <body onload="draw();"> . And it's implemented like this:
function draw() {
var cm = 6; // canvas margin
var canvases = document.getElementsByClassName('container');
for (var i=0; i<canvases.length; i++) {
var c = canvases[i];
var w = c.offsetWidth;
var h = c.offsetHeight;
console.log('w: ' + w + '; h: ' + h);
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#561a04';
ctx.moveTo(cm, cm);
ctx.lineTo(w-cm, cm);
ctx.lineTo(w-cm, h-cm);
ctx.lineTo(cm, h-cm);
ctx.lineTo(cm, cm);
ctx.stroke();
}
}
Now: the console prints the real and correct values of the width and height of the canvas (specifically 381 and 188); but it seems that the context draws as if the canvas was of its default dimensions... in fact if I simply set w = 330; h = 150; it works as expected, but then the canvas stretches the drawing and I not satisfied with the result.
What can I do to make the context use the right dimensions of the canvas? I wanted to redraw the image every time the canvas is resized, but I believe there's no event on the resize of any div, am I right?
Thank you.

Both the canvas itself and the canvas element have a width and height, they're separate things. If they're not the same, the content of the canvas is automatically scaled to fit the element. If you want a 1:1 relation, you need to set the actual width and height of the canvas.
Just after:
var w = c.offsetWidth;
var h = c.offsetHeight;
add
c.width = w;
c.height = h;
Example:
function draw() {
var cm = 6; // canvas margin
var canvases = document.getElementsByClassName('container');
for (var i=0; i<canvases.length; i++) {
var c = canvases[i];
var w = c.offsetWidth;
var h = c.offsetHeight;
c.width = w;
c.height = h;
console.log('w: ' + w + '; h: ' + h);
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.lineWidth = 2;
ctx.strokeStyle = '#561a04';
ctx.moveTo(cm, cm);
ctx.lineTo(w-cm, cm);
ctx.lineTo(w-cm, h-cm);
ctx.lineTo(cm, h-cm);
ctx.lineTo(cm, cm);
ctx.stroke();
}
}
draw();
.parent {
width: 200px;
height: 200px;
position: relative;
}
.parent canvas {
width: 100%;
height: 100%;
}
<div class="parent">
<canvas class="container"></canvas>
</div>
<div id="status"></div>

I've been using canvas for a while, instead of setting it to 100% in the css, set it to 100% with Javascript
EDIT:
I didn't notice you want to set it to 100% of parents div, this should do it
Please try this:
var canvas = document.getElementById("canvas");
canvas.width = canvas.parentElement.offsetWidth; //set canvas width to its parent width
canvas.height = canvas.parentElement.offsetHeight;//set canvas height to its parent height
var ctx = canvas.getContext("2d");
this will set canvas width and height to match it's parrents width & height
Please let me know if that solves your problem!

Related

Draw an rectangle between html elements

I'm trying to draw a rectangle between 2 tag by javascript but i couldn't have any achievement. The things i´m trying to do is something like this:
Screenshot
i tried to draw a rectangle by giving X, Y but the size changes when the page size changes:
var x = 100;
var y = 111;
var width = 200;
var height = 40
var canvas = document.createElement('canvas'); //Create a canvas element
//Set canvas width/height
canvas.style.width = '100%';
canvas.style.height = '100%';
//Set canvas drawing area width/height
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//Position canvas
canvas.style.position = 'absolute';
canvas.style.left = 0;
canvas.style.top = 0;
canvas.style.zIndex = 100000;
canvas.style.pointerEvents = 'none'; //Make sure you can click 'through' the canvas
document.body.appendChild(canvas); //Append canvas to body element
var context = canvas.getContext('2d');
//Draw rectangle
context.rect(x, y, width, height);
context.fillStyle = 'yellow';
context.fill();
function getOffset(el) {
el = el.getBoundingClientRect();
return {
left: el.left + window.scrollX,
top: el.top + window.scrollY
}
}

Make canvas transparent

This is what my body looks like:
body
{
background-image:url('../images/bg.png');
background-repeat:no-repeat;
background-size:fixed 100vw;
background-position:center;
}
The issue is, the canvas is white instead of being transparent. Is there a way to make it transparent so I can place the dna wave on top of a background?
Codepen example
One easy way, is using an offscreen canvas.
First set its context's globalAlpha value to something between 0 and 1, this will determine how fast your previous drawings will disappear.
Then, in the animation loop, before doing the new drawings,
clear the offscreen context,
draw the visible canvas on the offscreen one,
clear the visible canvas
draw back the offscreen one on the visible one
In the process, your image will have lost opacity.
var clear = function(){
// clear the clone canvas
cloneCtx.clearRect(0,0,canvasWidth, canvasHeight)
// this should be needed at init and when canvas is resized but for demo I leave it here
cloneCtx.globalAlpha = '.8';
// draw ou visible canvas, a bit less opaque
cloneCtx.drawImage(context.canvas, 0,0)
// clear the visible canvas
context.clearRect(0,0,canvasWidth, canvasHeight)
// draw back our saved less-opaque image
context.drawImage(clone, 0,0)
}
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
// create an offscreen clone
clone = canvas.cloneNode(),
cloneCtx = clone.getContext('2d'),
canvasWidth = canvas.width =
clone.width =window.innerWidth,
canvasHeight = canvas.height = clone.height = window.innerHeight,
globalTick = 0,
points = [],
pointCount = 12,
pointSpeed = 6,
spacing = canvasWidth / pointCount,
pointCount = pointCount + 2,
verticalPointRange = 60,
randomRange = function(min, max){
return Math.floor( (Math.random() * (max - min + 1) ) + min);
},
iPath,
iPoints;
var Point = function(x, y, alt){
this.x = x;
this.y = y;
this.yStart = y;
this.alt = alt;
}
Point.prototype.update = function(i){
var range = (this.alt) ? verticalPointRange : -verticalPointRange;
this.x += pointSpeed;
this.y = (this.yStart) + Math.sin(globalTick/14) * -range;
if(this.x > (canvasWidth + spacing)){
this.x = -spacing;
var moved = points.splice(i, 1);
points.unshift(moved[0]);
}
}
var updatePoints = function(){
var i = points.length;
while(i--){
points[i].update(i);
}
}
for(iPoints = 0; iPoints < pointCount; iPoints++){
var alt = (iPoints % 2 === 0);
var offset = (alt) ? verticalPointRange : -verticalPointRange;
points.push(new Point(spacing * (iPoints-1), canvasHeight/2, alt));
}
var renderPath = function(){
context.beginPath();
context.moveTo(points[0].x, points[0].y);
for(iPath = 1; iPath < pointCount; iPath++){
context.lineTo(points[iPath].x, points[iPath].y);
}
context.stroke();
}
var loop = function(){
requestAnimationFrame(loop, canvas);
clear();
updatePoints();
renderPath();
globalTick++;
};
loop();
canvas { display: block; }
body{
background-color: ivory;
}
<canvas id="canvas"></canvas>
Canvases are transparent by default.
Try setting a page background image, and then put a canvas over it. If nothing is drawn on the canvas, you can fully see the page background.
you should try
context.clearRect(0,0,width,height);
for more you can refer How do I make a transparent canvas in html5?

RequestAnimationFrame not working properly

After the requestAnimationFrame is called, the square does not move.
The square is expected to move though.
What is wrong with this code?
http://jsfiddle.net/o3qbesy6/1/
// Check if canvas exists and get the context of the canvas
var canvas = document.getElementsByTagName('canvas')[0];
var ctx = canvas.getContext('2d');
// Add width and height of the canvas
var width = 640, height = 480;
// Make the canvas width and height as the variables
canvas.width = width, canvas.height = height;
rect = {
x: 0,
y: 0,
width: 100,
height: 100
}
ctx.fillStyle = 'red';
ctx.fillRect(rect.x,rect.y,rect.width,rect.height);
function animate(){
if(rect.x <= 200){
rect.x += 5;
}
requestID = requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

Canvas saving and restoring

I have a canvas that i need to change the height for after every bit of content is added - the only way for me to get a dynamic canvas height. I'm trying to use save and restore so i don't lose all the styles, settings etc.
I can't get save and restore to work. I must be doing something wrong or is this the wrong approach?
function DrawLeftText(text) {
var canvas = document.getElementById('canvasPaper');
if (canvas.getContext) {
var context = canvas.getContext('2d');
context.textAlign = 'left';
context.font = 'normal 20px Monkton';
context.fillText(text, leftPosition, cursor);
context.textAlign = 'start';
context.save();
}
}
function restoreSettings(){
var canvas = document.getElementById('canvasPaper');
if (canvas.getContext) {
var context = canvas.getContext('2d');
context.restore();
}
}
function onDrawReceipt() {
var canvas = document.getElementById('canvasPaper');
if (canvas.getContext) {
var context = canvas.getContext('2d');
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;
context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
DrawCenterText(company['name']); cursor += lineSpace;
DrawCenterText(company['address']['address_line_1']); cursor += lineSpace;
DrawCenterText(company['address']['city'].toUpperCase()); cursor += lineSpace;
DrawCenterText(company['address']['postcode']); cursor += lineSpace;
context.clearRect(0, 0, canvas.width, canvas.height += 20);
**//increasing the height and then trying to restore**
restoreSettings();
}
}
////// Additional attempt. I've tried the following too without luck.
////// None of the content appears despite setting the height before adding the content??
var header = function headerSection(){
var height = cursor;
canvas.height += height;
context.textBaseline = 'top';
lineSpace = 20;
leftPosition = 0;
centerPosition = (canvas.width + 26) / 2;
rightPosition = (canvas.width - 0);
console.log(height);
console.log(context);
context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
context.fillRect(25, cursor - 2, 526, 2); cursor += lineSpace;
DrawCenterText(company['name']); cursor += lineSpace;
DrawCenterText(company['address']['address_line_1']); cursor += lineSpace;
DrawCenterText(company['address']['city'].toUpperCase()); cursor += lineSpace;
DrawCenterText(company['address']['postcode']); cursor += lineSpace;
console.log(canvas.height);
return;
}
header()
I would suggest the following approach at the cost of a little more memory (if you're dealing with very large canvases you will of course have to weight in memory concern) -
Use an internal larger canvas representing a max size (or block size1)
Keep a value representing the current height (not actual)
Create a visual canvas in the document at the height stored in the value
For every draw operation, draw to the internal canvas
When done, set the visual canvas height using the current height value, draw internal canvas to visual one
Example 1
This example uses this technique to draw text at random color. As you can see we do not need to redraw the text when we change the visual canvas' size, we just copy everything from the internal canvas which also contains the current styles (font, font size, fill style).
var vcanvas = document.querySelector("canvas"),
vctx = vcanvas.getContext("2d"),
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
line = 25,
maxHeight = 7 * line,
currentHeight = line,
chars = "abcdefghijklmnowxyzABCD#/&%/(#)=!LMNOPQRSTUVWXYZ".split(""),
x = 0, y = 0, ch;
vcanvas.height = currentHeight;
// internal canvas setup
ctx.font = "20px sans-serif";
ctx.textBaseline = "top";
// draw someting to internal canvas:
(function loop() {
ch = chars[(Math.random() * chars.length)|0];
ctx.fillStyle = "hsl(" + (360*Math.random()) + ",75%,50%)";
ctx.fillText(ch, x, y);
x += ctx.measureText(ch).width;
if (x > canvas.width) {
x = 0; y += line; currentHeight += line;
}
// copy to visual canvas:
if (currentHeight < maxHeight) vcanvas.height = currentHeight;
vctx.drawImage(canvas, 0, 0, canvas.width, currentHeight,
0, 0, canvas.width, currentHeight);
if (currentHeight < maxHeight) setTimeout(loop, 50);
})();
body{background:#eee} canvas{background:#fff}
canvas{border:1px solid #000}
<canvas></canvas>
1) If you are dealing with "infinite" height you want to split the process into blocks. Set internal canvas to block size, when exceeded enlarge internal canvas with new block size - but here you will have to do full redraw and setup again.
And this leads to option 2: The latter technique can be used directly with the visual canvas as well, and you can use CSS to clip it putting it inside a div element which you style with height and overflow:hidden.
Example 2
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
div = document.querySelector("div"),
line = 25,
maxHeight = 7 * line,
currentHeight = line,
chars = "abcdefghijklmnowxyzABCD#/&%/(#)=!LMNOPQRSTUVWXYZ".split(""),
x = 0, y = 0, ch;
// set canvas to max height, div to clip height
canvas.height = maxHeight;
div.style.height = currentHeight + "px";
// canvas setup
ctx.font = "20px sans-serif";
ctx.textBaseline = "top";
// draw someting to the canvas:
(function loop() {
ch = chars[(Math.random() * chars.length)|0];
ctx.fillStyle = "hsl(" + (360*Math.random()) + ",75%,50%)";
ctx.fillText(ch, x, y);
x += ctx.measureText(ch).width;
if (x > canvas.width) {
x = 0; y += line; currentHeight += line;
}
// copy to visual canvas:
if (currentHeight < maxHeight) {
div.style.height = currentHeight + "px"; // increase clipping height
setTimeout(loop, 50)
}
})();
body{background:#eee} canvas{background:#fff}
div{border:1px solid #000; overflow:hidden; width:302px}
<div><canvas></canvas></div>
Resizing the canvas element will always reset its context to the default style states. (.save & .restore will not let the context styles survive a resizing)
The common canvas pattern to deal with changes to canvas content is:
Save data (eg your company)
Either save context styles as javascript variables or embed the style changes in functions (eg a specialized function to set appropriate styles and redraw the company heading).
Resize (or clear) the canvas
Redraw all the content using the saved data and saved styles/functions.
Example code:
Click "Full page" to see full receipt
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var lineSpace=20;
var leftOfForm=25;
var topOfForm=80;
var testingItems=110;
var company={
name:'MyCompany, Inc',
address:{
address_line_1: '123 Main Street',
city:'Anytown, Anywhere',
postcode:'12345'
}
}
var lineItems=[];
addLineItem(testingItems,'Description of sku#'+testingItems,testingItems);
$('#test').click(function(){
testingItems++;
addLineItem(testingItems,'Description of sku#'+testingItems,testingItems);
draw();
});
draw();
function addLineItem(sku,description,price){
lineItems.push({
sku:sku,
description:description,
price:price
});
}
function draw(){
// note: changing canvas.height auto-erases the content also
canvas.height=topOfForm+(6+2+lineItems.length)*lineSpace;
ctx.strokeRect(0,0,canvas.width,canvas.height);
drawHeader(leftOfForm,topOfForm);
for(var i=0;i<lineItems.length;i++){
drawLineItem(lineItems[i],leftOfForm,topOfForm+(6+2+i)*lineSpace);
}
}
function drawHeader(left,cursor){
var cx=canvas.width/2;
var line=function(linecount){ return(cursor+lineSpace*linecount); }
ctx.save();
ctx.beginPath();
ctx.moveTo(left,line(0)-2);
ctx.lineTo(526,line(0)-2);
ctx.moveTo(left,line(1)-2);
ctx.lineTo(526,line(1)-2);
ctx.lineWidth=2;
ctx.stroke();
ctx.font='18px verdana';
ctx.textAlign='center';
ctx.textBaseline='top';
ctx.fillText(company.name,cx,line(2));
ctx.fillText(company.address.address_line_1,cx,line(3));
ctx.fillText(company.address.city,cx,line(4));
ctx.fillText(company.address.postcode,cx,line(5));
ctx.restore();
}
function drawLineItem(item,left,cursor){
ctx.save();
ctx.font='14px verdana';
ctx.textAlign='left';
ctx.textBaseline='top';
ctx.fillText(item.sku,left,cursor);
ctx.fillText(item.description,left+40,cursor);
ctx.textAlign='right';
ctx.fillText(item.price,left+450,cursor);
ctx.restore();
}
body{ background-color: ivory; padding:10px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=test>Add another line</button>
<br>
<canvas id="canvas" width=550 height=300></canvas>

Javascript canvas stroke opacity not applied to each lineto but rather continually over lines

It seems that my understanding of the javascript canvas strokestyle/line drawing is not up to par. I want to draw lines with a bit of transparency, but I fail to get the transparency applied to each and every line (segment) and instead get it sort of applied as a gradient over all the lines I've drawn...
Example code:
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
WIDTH = 400;
HEIGHT = 400;
c.width = WIDTH;
c.height = HEIGHT;;
function applyGrid()
{
a.fillStyle = "000000";
a.lineWidth = 2;
a.fillRect(0, 0, WIDTH, HEIGHT);
a.strokeStyle = "rgba(0,200,200,0.1)";
for (var x = 0; x <= WIDTH; x += 50)
{
a.moveTo(x, 0);
a.lineTo(x, HEIGHT);
a.stroke();
}
}
applyGrid();​
See example code and (wrong) result here: http://jsfiddle.net/zKWaT/3/ I want all vertical lines to have the transparency of the rightmost line, but instead they gradually fade.
I guess I have missed something basic about how lineTo and stroke works, but having looked at examples here and there I haven't found something revealing... tt.
Thanks for your input!
inside your for loop add a.beginPath() to start a new line on each loop. Otherwise you're just extending and stroking the same line over and over.
var c = document.getElementsByTagName('canvas')[0];
var a = c.getContext('2d');
WIDTH = 400;
HEIGHT = 400;
c.width = WIDTH;
c.height = HEIGHT;;
function applyGrid()
{
a.fillStyle = "000000";
a.lineWidth = 2;
a.fillRect(0, 0, WIDTH, HEIGHT);
a.strokeStyle = "rgba(0,200,200,0.1)";
for (var x = 0; x <= WIDTH; x += 50)
{
a.beginPath(); //ADD THIS
a.moveTo(x, 0);
a.lineTo(x, HEIGHT);
a.stroke();
}
}
applyGrid();​

Categories

Resources