HTML2Canvas does not render full div, only what is visible on screen? - javascript
I'm trying to use HTML2Canvas to render the contents of a div. Here is the code:
var htmlSource = $('#potenzial-page')[0];
$('#btn').on("click", function() {
html2canvas(htmlSource).then(function(canvas) {
var img = canvas.toDataURL();
window.open(img);
});
});
I'm using v5 beta 3.
When this code runs, it only renders what is visible on the screen. The #potenzial-page div is essentially the entire page, minus the header and footer. All content in this div is visible by scrolling (there are some hidden elements, but I do not want the hidden elements visible in the image.)
I cannot find what's wrong or why it won't save the entire div. I should also note that it appears the image is as tall as the div but only partially visible.
To give an example of what I mean, here is a comparison:
The left is how HTML2Canvas should render the div. The right shows how it renders when it runs the code above. The right image is what's visible in my browsers screen.
I did try adding the height option but it doesn't make a difference.
UPDATE
If I scroll to the very top of the page then run the script it will render the entire div as it should.
How do I render the div without having to scroll to the top?
I hope thet help you
html2canvas(htmlSource, {scrollY: -window.scrollY}).then(function(canvas) {
var img = canvas.toDataURL();
window.open(img);
});
A solution that worked for me was to add the following to my css:
.html2canvas-container { width: 3000px !important; height: 3000px !important; }
It prevents html2canvas from limiting the rendering to the viewable area (which seems to be the default).
See here: https://github.com/niklasvh/html2canvas/issues/117
I used window.scrollTo()in my case and it worked for me.
Below is a sample code
$('#btn').on("click", function() {
window.scrollTo(0,0);
html2canvas(htmlSource).then(function(canvas) {
var img = canvas.toDataURL();
window.open(img);
});
window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
});
I just did something like this and it worked for me:
html2canvas(document.querySelector("#capture2image"), {
allowTaint: true,
useCORS: true,
logging: false,
height: window.outerHeight + window.innerHeight,
windowHeight: window.outerHeight + window.innerHeight,
You can add in scroll position as a variable in html2canvas which removes the need to scroll the page.
html2canvas(document.querySelector("#your-element"), {
scrollX: 0,
scrollY: 0
}).then(function(canvas) {
If you have a height set to the div you want to turn to a canvas - you need to remove that before actually taking the snapshot. Otherwise it will just cut it off because of that height.
$(".yourElemThatHasSomeHeightSet").css("height", "");
Then you will notice that scrolling down - will still cut your document.
Simply do a:
$("html, body").scrollTop(0);
before taking the snapshot.
This is how I've achieved in Reactjs.
Main problem were ratio and scale
If you do a quick window.devicePixelRatio, it's default value is 2 which was causing the half image issue.
const printDocument = () => {
const input = document.getElementById('divToPrint');
const divHeight = input.clientHeight
const divWidth = input.clientWidth
const ratio = divHeight / divWidth;
html2canvas(input, { scale: '1' }).then((canvas) => {
const imgData = canvas.toDataURL('image/jpeg');
const pdfDOC = new jsPDF("l", "mm", "a0"); // use a4 for smaller page
const width = pdfDOC.internal.pageSize.getWidth();
let height = pdfDOC.internal.pageSize.getHeight();
height = ratio * width;
pdfDOC.addImage(imgData, 'JPEG', 0, 0, width - 20, height - 10);
pdfDOC.save('summary.pdf'); //Download the rendered PDF.
});
}
window.scrollTo(0,0);
Add this works for me.
for people dont wanna hacky a way for scroll problem.: dom-to-image
you can scroll while you are snapshotting image
and its much more faster (70x according to this blog ).
blog: https://betterprogramming.pub/heres-why-i-m-replacing-html2canvas-with-html-to-image-in-our-react-app-d8da0b85eadf
in the blog it mentions html-to-image. it is fork of dom-2-image. i used dom-to-image (ancestor , original one).
var node = document.getElementById('my-node');
domtoimage.toPng(node)
.then(function (dataUrl) {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch(function (error) {
console.error('oops, something went wrong!', error);
});
The following code worked for me:
window.scrollTo(0, 0);
html2canvas(htmlRef, {
scrollX: -window.scrollX,
scrollY: -window.scrollY,
windowWidth: document.documentElement.offsetWidth,
windowHeight: htmlRef.scrollHeight,
}).then((canvas) => {
const img = new Image();
const imgData = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
const pdf = new jsPDF("p", "mm", "a4");
const imgProps = pdf.getImageProperties(imgData);
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
pdf.addImage(imgData, "JPEG", 0, 0, pdfWidth, pdfHeight);
pdf.save();
});
<div #potentialContainer id="potentialContainer" class="potentialContainer" style="height: auto;">
some large content here--------------------
</div>
import html2canvas from 'html2canvas';
downloadImage() {
html2canvas(document.querySelector('#potentialContainer'), {
logging: true,
allowTaint: false,
useCORS: true,
width: document.querySelector('#potentialContainer').scrollWidth,
height: section.scrollHeight,
scrollX: -window.scrollX,
scrollY: -window.scrollY,
}).then((canvas) => {
var imgWidth = 210;
var pageHeight = 290;
var imgHeight = canvas.height * imgWidth / canvas.width;
var heightLeft = imgHeight;
var doc = new jsPDF('p', 'mm');
var position = 0;
var pageData = canvas.toDataURL('image/jpeg', 1.0);
var imgData = encodeURIComponent(pageData);
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
doc.setLineWidth(5);
doc.setDrawColor(255, 255, 255);
doc.rect(0, 0, 210, 295);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
doc.setLineWidth(5);
doc.setDrawColor(255, 255, 255);
doc.rect(0, 0, 210, 295);
heightLeft -= pageHeight;
}
doc.save('file.pdf');
});
}
Note:- It is important to add style height:auto
The above code will convert a large image to a multipage pdf
Another React Approach...
On your submit button on click set the document height dynamically, then call html2canvas using document.body as the first argument
<button onClick={() => {
var body = document.body,
html = document.documentElement;
var height = Math.max(body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight);
document.body.style.height = `${height}px`
html2canvas(document.body).then(function (canvas) {
var imgData = canvas.toDataURL('image/pdf')
var doc = new jsPDF('p', 'mm', [canvas.width, canvas.height])
doc.addImage(imgData, 'PDF', 10, 10, canvas.width, canvas.height)
doc.save('name-of-pdf.pdf')
});
}}>Submit</button>
This will set the html height of public/index.html which html2canvas seems to render from (i.e. not the "root" div).
This works for me:
const input = document.getElementById('fragmentForPDF');
// This row fixed problem
input.parentNode.style.overflow = 'visible';
html2canvas(input)...
window.scrollTo(0, 0); // this will help to print if div hidden or on mobile screen
html2canvas(document.getElementById("my_div_img")).then(function (canvas)
{
//for give white BG
var context = canvas.getContext('2d');
context.save();
context.globalCompositeOperation = 'destination-over';
context.fillStyle = "rgb(255, 255, 255)";
context.fillRect(0, 0, canvas.width, canvas.height);
context.restore();
var imgData = canvas.toDataURL('image/jpeg', 1);
//console.log(imgData);
}
This worked for me:
html2canvas(el, {
width: el.scrollWidth,
height: el.scrollHeight,
})
See here for the source.
document.getElementById("dld_report").addEventListener("click", function() {
// Make sure the area you want to take screenshot is visible in CSS
document.querySelector('#boundary').style.overflow = 'visible';
html2canvas(document.querySelector('#boundary'), {
// Set the height of screenshot image same as page hieght
height: window.outerHeight + window.innerHeight,
}).then(function(canvas) {
saveAs(canvas.toDataURL(), 'report.png');
});
});
// dld_report -> button to trigger the screenshot
Set style overflow = "visible" and height were the key to make this happen.
General advice regarding html2canvas Try and set height of html to 100%.
Related
jsPDF Big table, headers overlap in second page
I'm using jsPDF and svg_to_pdf to export both an SVG and a table to a PDF. This is my code: function svg_to_pdf(svg, callback) { console.log("svgtopdf.js"); svgAsDataUri(svg, {}, function(svg_uri) { var image = document.createElement('img'); image.src = svg_uri; image.onload = function() { var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var doc = new jsPDF('portrait', 'pt', 'a4', true); var dataUrl; source = $('#datos_alumnos')[0]; specialElementHandlers = { '#bypassme' : function(element, renderer){ return true; } }; margins = { top: 40, bottom: 60, left: 40, width: 270 }; canvas.width = image.width; canvas.height = image.height; context.drawImage(image, 0, 0, image.width, image.height); dataUrl = canvas.toDataURL('image/jpeg'); doc.addImage(dataUrl, 'JPEG', 0, 0, 500, 263); doc.fromHTML( source, 40, 270, { 'width': 550, 'elementHandlers': specialElementHandlers }, function(dispose){ //doc.save('SVG.pdf'); } , margins); callback(doc); } }); } The result is a PDF with the image at the top and the table next. The problem comes when it enters the second page as the headers of the table overlap with the first row and it looks like this. http://i.imgur.com/vlfm4Fv.png Is it possible to remove the headers on the second page?
Ok so I've found a possible solution for this, if you are using this script for jsPDF https://raw.githubusercontent.com/MrRio/jsPDF/master/dist/jspdf.debug.js you can download it, and modify line 5201 to have a negative value, for example; this.margins.top = -50; With this there is no header on the second page of the PDF. I've tried playing around with the code without much success, if I find a better way I will update this answer (don't know if I'm allowed to do that).
When you draw table, just use: startY: doc.internal.getNumberOfPages() > 1 ? doc.autoTableEndPosY() + 20 : 200,
Try adding css classes like - thead { display: table-header-group } tfoot { display: table-row-group } tr { page-break-inside: avoid }
how to use html2canvas and jspdf to export to pdf in a proper and simple way
I am currently working on a school management software that usually requires exporting of html contents that contains data tables and div tag. I have tried all possible means to write a code that will be able to export my html data in a good way, with css preferably. After checking some question and answers here, I tried using spdf, but no luck. It keeps destroying my table alignment, then I read about html2canvas but to implement it with jspdf was my problem, i would like to capture the content if a div tag with html2canvas then send the canvas to jspdf to export the canvas as pdf. Here is my code below: <script src="assets/js/pdfconvert/jspdf.js"></script> <script src="assets/js/pdfconvert/jspdf.plugin.from_html.js"></script> <script src="assets/js/pdfconvert/jspdf.plugin.split_text_to_size.js"></script> <script src="assets/js/pdfconvert/jspdf.plugin.standard_fonts_metrics.js"> </script> <script src="assets/js/pdfconvert/jspdf.plugin.addhtml.js"></script> <script src="assets/js/pdfconvert/filesaver.js"></script> <script src="assets/js/pdfconvert/jspdf.plugin.cell.js"></script> <script src="assets/js/pdfconvert/html2canvas.js"></script> <script src="assets/js/pdfconvert/jspdf.plugin.addimage.js"></script> here is the js code var pdf = new jsPDF('p', 'pt', 'letter'); pdf.addHTML($('#ElementYouWantToConvertToPdf')[0], function () { pdf.save('Test.pdf'); });
I have made a jsfiddle for you. <canvas id="canvas" width="480" height="320"></canvas> <button id="download">Download Pdf</button> ' html2canvas($("#canvas"), { onrendered: function(canvas) { var imgData = canvas.toDataURL( 'image/png'); var doc = new jsPDF('p', 'mm'); doc.addImage(imgData, 'PNG', 10, 10); doc.save('sample-file.pdf'); } }); jsfiddle: http://jsfiddle.net/rpaul/p4s5k59s/5/ Tested in Chrome38, IE11 and Firefox 33. Seems to have issues with Safari. However, Andrew got it working in Safari 8 on Mac OSx by switching to JPEG from PNG. For details, see his comment below.
This one shows how to print only selected element on the page with dpi/resolution adjustments HTML: <html> <body> <header>This is the header</header> <div id="content"> This is the element you only want to capture </div> <button id="print">Download Pdf</button> <footer>This is the footer</footer> </body> </html> CSS: body { background: beige; } header { background: red; } footer { background: blue; } #content { background: yellow; width: 70%; height: 100px; margin: 50px auto; border: 1px solid orange; padding: 20px; } JS: $('#print').click(function() { var w = document.getElementById("content").offsetWidth; var h = document.getElementById("content").offsetHeight; html2canvas(document.getElementById("content"), { dpi: 300, // Set to 300 DPI scale: 3, // Adjusts your resolution onrendered: function(canvas) { var img = canvas.toDataURL("image/jpeg", 1); var doc = new jsPDF('L', 'px', [w, h]); doc.addImage(img, 'JPEG', 0, 0, w, h); doc.save('sample-file.pdf'); } }); }); jsfiddle: https://jsfiddle.net/marksalvania/dum8bfco/
Changing this line: var doc = new jsPDF('L', 'px', [w, h]); var doc = new jsPDF('L', 'pt', [w, h]); To fix the dimensions.
This worked when you add "useCORS: true". Grabs google map as it is rendered on the screen and puts it in a pdf function create_pdf() { return html2canvas($('#map'), { useCORS: true, background: "#ffffff", onrendered: function(canvas) { var myImage = canvas.toDataURL("image/jpeg,1.0"); // Adjust width and height var imgWidth = (canvas.width * 60) / 240; var imgHeight = (canvas.height * 70) / 240; // jspdf changes var pdf = new jsPDF('p', 'mm', 'a4'); pdf.addImage(myImage, 'png', 15, 2, imgWidth, imgHeight); // 2: 19 pdf.save(`Budgeting ${$('.pdf-title').text()}.pdf`); } });
Resizing a hidden-frame
I am trying to load a webpage into a hidden-frame (sdk/frame/hidden-frame) and then render a screenshot of it. I can get the screenshot just fine, but the frame is not properly sized. The screenshot is rendered by using the following code var hiddenFrames = require("sdk/frame/hidden-frame"); let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({ onReady: function() { this.element.contentWindow.location = "http://example.com"; let self = this; this.element.addEventListener("DOMContentLoaded", function() { var cnvs = self.element.contentDocument.createElement("canvas"); var width = self.element.contentDocument.body.clientHeight; //for some reason clientWidth is 0 for www.example.com var height = self.element.contentDocument.body.clientHeight; cnvs.width = width; cnvs.height = height; var ctx = cnvs.getContext("2d"); console.log(width+" "+height); ctx.drawWindow(self.element.contentWindow, 0, 0, width, height, "rgb(255,255,255)"); console.log(cnvs.toDataURL()); }, true, true); } })); I have tried changing the width and height of the iframe with this.element.width = "1600"; this.element.height = "900"; changing the size of element.contentWindow with resizeTo(), and changing the size of the body. None of them seem to have an impact on the final screenshot, which looks like
Try adding this on the parent page after including the iframe (or script that includes the iframe). $('iframe').load(function(){ $(this).css({ width: 1600, height: 900 }); });
Speed up canvas with webGL or webworkers?
I'm creating a blurred background from an image in canvas with this script: http://www.quasimondo.com/BoxBlurForCanvas/FastBlur2Demo.html You can see a demo on my page here: http://www.cphrecmedia.dk/musikdk/stage/prelisten.php I find the speed a little bit slow and I know I can make it faster by only make 1 iterations, but its not that pretty then. Can I somehow use WEbGL or any other kind of tech to speed up the canvas for this? This is the stackbox-code: var mul_table=[1,171,205,293,57,373,79,137,241,27,391,357,41,19,283,265,497,469,443,421,25,191,365,349,335,161,155,149,9,278,269,261,505,245,475,231,449,437,213,415,405,395,193,377,369,361,353,345,169,331,325,319,313,307,301,37,145,285,281,69,271,267,263,259,509,501,493,243,479,118,465,459,113,446,55,435,429,423,209,413,51,403,199,393,97,3,379,375,371,367,363,359,355,351,347,43,85,337,333,165,327,323,5,317,157,311,77,305,303,75,297,294,73,289,287,71,141,279,277,275,68,135,67,133,33,262,260,129,511,507,503,499,495,491,61,121,481,477,237,235,467,232,115,457,227,451,7,445,221,439,218,433,215,427,425,211,419,417,207,411,409,203,202,401,399,396,197,49,389,387,385,383,95,189,47,187,93,185,23,183,91,181,45,179,89,177,11,175,87,173,345,343,341,339,337,21,167,83,331,329,327,163,81,323,321,319,159,79,315,313,39,155,309,307,153,305,303,151,75,299,149,37,295,147,73,291,145,289,287,143,285,71,141,281,35,279,139,69,275,137,273,17,271,135,269,267,133,265,33,263,131,261,130,259,129,257,1];var shg_table=[0,9,10,11,9,12,10,11,12,9,13,13,10,9,13,13,14,14,14,14,10,13,14,14,14,13,13,13,9,14,14,14,15,14,15,14,15,15,14,15,15,15,14,15,15,15,15,15,14,15,15,15,15,15,15,12,14,15,15,13,15,15,15,15,16,16,16,15,16,14,16,16,14,16,13,16,16,16,15,16,13,16,15,16,14,9,16,16,16,16,16,16,16,16,16,13,14,16,16,15,16,16,10,16,15,16,14,16,16,14,16,16,14,16,16,14,15,16,16,16,14,15,14,15,13,16,16,15,17,17,17,17,17,17,14,15,17,17,16,16,17,16,15,17,16,17,11,17,16,17,16,17,16,17,17,16,17,17,16,17,17,16,16,17,17,17,16,14,17,17,17,17,15,16,14,16,15,16,13,16,15,16,14,16,15,16,12,16,15,16,17,17,17,17,17,13,16,15,17,17,17,16,15,17,17,17,16,15,17,17,14,16,17,17,16,17,17,16,15,17,16,14,17,16,15,17,16,17,17,16,17,15,16,17,14,17,16,15,17,16,17,13,17,16,17,17,16,17,14,17,16,17,16,17,16,17,9];function stackBoxBlurCanvasRGB(ctxt,top_x,top_y,width,height,radius,iterations){if(isNaN(radius)||radius<1)return;radius|=0;if(isNaN(iterations))iterations=1;iterations|=0;if(iterations>3)iterations=3;if(iterations<1)iterations=1;var imageData;try{try{imageData=ctxt.getImageData(top_x,top_y,width,height);}catch(e){try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");imageData=ctxt.getImageData(top_x,top_y,width,height);}catch(e){alert("Cannot access local image");throw new Error("unable to access local image data: "+e);return;}}}catch(e){alert("Cannot access image");throw new Error("unable to access image data: "+e);} var pixels=imageData.data;var x,y,i,p,yp,yi,yw,r_sum,g_sum,b_sum,r_out_sum,g_out_sum,b_out_sum,r_in_sum,g_in_sum,b_in_sum,pr,pg,pb,rbs;var div=radius+radius+1;var w4=width<<2;var widthMinus1=width-1;var heightMinus1=height-1;var radiusPlus1=radius+1;var stackStart=new BlurStack();var stack=stackStart;for(i=1;i<div;i++){stack=stack.next=new BlurStack();if(i==radiusPlus1)var stackEnd=stack;} stack.next=stackStart;var stackIn=null;var mul_sum=mul_table[radius];var shg_sum=shg_table[radius];while(iterations-->0){yw=yi=0;for(y=height;--y>-1;){r_sum=radiusPlus1*(pr=pixels[yi]);g_sum=radiusPlus1*(pg=pixels[yi+1]);b_sum=radiusPlus1*(pb=pixels[yi+2]);stack=stackStart;for(i=radiusPlus1;--i>-1;){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} for(i=1;i<radiusPlus1;i++){p=yi+((widthMinus1<i?widthMinus1:i)<<2);r_sum+=(stack.r=pixels[p++]);g_sum+=(stack.g=pixels[p++]);b_sum+=(stack.b=pixels[p]);stack=stack.next;} stackIn=stackStart;for(x=0;x<width;x++){pixels[yi++]=(r_sum*mul_sum)>>>shg_sum;pixels[yi++]=(g_sum*mul_sum)>>>shg_sum;pixels[yi++]=(b_sum*mul_sum)>>>shg_sum;yi++;p=(yw+((p=x+radius+1)<widthMinus1?p:widthMinus1))<<2;r_sum-=stackIn.r-(stackIn.r=pixels[p++]);g_sum-=stackIn.g-(stackIn.g=pixels[p++]);b_sum-=stackIn.b-(stackIn.b=pixels[p]);stackIn=stackIn.next;} yw+=width;} for(x=0;x<width;x++){yi=x<<2;r_sum=radiusPlus1*(pr=pixels[yi++]);g_sum=radiusPlus1*(pg=pixels[yi++]);b_sum=radiusPlus1*(pb=pixels[yi]);stack=stackStart;for(i=0;i<radiusPlus1;i++){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;} yp=width;for(i=1;i<=radius;i++){yi=(yp+x)<<2;r_sum+=(stack.r=pixels[yi++]);g_sum+=(stack.g=pixels[yi++]);b_sum+=(stack.b=pixels[yi]);stack=stack.next;if(i<heightMinus1)yp+=width;} yi=x;stackIn=stackStart;for(y=0;y<height;y++){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>>shg_sum;p=(x+(((p=y+radiusPlus1)<heightMinus1?p:heightMinus1)*width))<<2;r_sum-=stackIn.r-(stackIn.r=pixels[p]);g_sum-=stackIn.g-(stackIn.g=pixels[p+1]);b_sum-=stackIn.b-(stackIn.b=pixels[p+2]);stackIn=stackIn.next;yi+=width;}}} ctxt.putImageData(imageData,top_x,top_y);} function BlurStack(){this.r=0;this.g=0;this.b=0;this.a=0;this.next=null;} And this is how its activated on each page: <script type='text/javascript'>window.onload=function(){ //create image object var imageObj = new Image(); //when image is finished loading imageObj.onload = function() { //get size var w = imageObj.naturalWidth; var h = imageObj.naturalHeight; var canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; //create virtual canvas var ctx = canvas.getContext('2d'); //draw the image on it ctx.drawImage(imageObj, 0, 0); //apply the blur stackBoxBlurCanvasRGB(ctx, 0, 0, w, h, 11, 2); //add grey filter ctx.fillStyle='rgba(64,64,64,0.4)'; ctx.fillRect(0,0,w,h); //and display it in 1 second using a fade var $canvas = $(canvas) $("#bgc").append(canvas); $canvas.show(); var canvasRatio = $canvas.width()/$canvas.height(); var windowRatio = $(window).width()/$(window).height(); if (canvasRatio > windowRatio){ $canvas.css({ "height": "100%", "width" : "auto" }); } else { $canvas.css({ "width": "100%", "height": "auto" }); } $canvas.css({ "marginLeft" : -$canvas.width()/2+"px", "marginTop" : -$canvas.height()/2+"px" }); window.onresize = function(){ var canvasRatio = $canvas.width()/$canvas.height(); var windowRatio = $(window).width()/$(window).height(); if (canvasRatio > windowRatio){ $canvas.css({ "height": "100%", "width" : "auto" }); } else { $canvas.css({ "width": "100%", "height": "auto" }); } $canvas.css({ "marginLeft" : -$canvas.width()/2+"px", "marginTop" : -$canvas.height()/2+"px" }); } }; //set the source of the image from the data source imageObj.src = $('#bgc').data("src"); } </script>
Maybe have a look at Pixi.js it is a library that wraps canvas and will use either the 2D context or the WebGL context based on browser capabilities. It has a built in Blur Filter that could possibly achieve the effect you are after http://www.goodboydigital.com/pixijs/docs/classes/BlurFilter.html If you are working with WebKit based browsers there is a draft implementation of CSS filters that should be fast https://developer.mozilla.org/en-US/docs/Web/CSS/filter
Resize HTML5 canvas to fit window
How can I automatically scale the HTML5 <canvas> element to fit the page? For example, I can get a <div> to scale by setting the height and width properties to 100%, but a <canvas> won't scale, will it?
I believe I have found an elegant solution to this: JavaScript /* important! for alignment, you should make things * relative to the canvas' current width/height. */ function draw() { var ctx = (a canvas context); ctx.canvas.width = window.innerWidth; ctx.canvas.height = window.innerHeight; //...drawing code... } CSS html, body { width: 100%; height: 100%; margin: 0; } Hasn't had any large negative performance impact for me, so far.
The following solution worked for me the best. Since I'm relatively new to coding, I like to have visual confirmation that something is working the way I expect it to. I found it at the following site: http://htmlcheats.com/html/resize-the-html5-canvas-dyamically/ Here's the code: (function() { var // Obtain a reference to the canvas element using its id. htmlCanvas = document.getElementById('c'), // Obtain a graphics context on the canvas element for drawing. context = htmlCanvas.getContext('2d'); // Start listening to resize events and draw canvas. initialize(); function initialize() { // Register an event listener to call the resizeCanvas() function // each time the window is resized. window.addEventListener('resize', resizeCanvas, false); // Draw canvas border for the first time. resizeCanvas(); } // Display custom canvas. In this case it's a blue, 5 pixel // border that resizes along with the browser window. function redraw() { context.strokeStyle = 'blue'; context.lineWidth = '5'; context.strokeRect(0, 0, window.innerWidth, window.innerHeight); } // Runs each time the DOM window resize event fires. // Resets the canvas dimensions to match window, // then draws the new borders accordingly. function resizeCanvas() { htmlCanvas.width = window.innerWidth; htmlCanvas.height = window.innerHeight; redraw(); } })(); html, body { width: 100%; height: 100%; margin: 0px; border: 0; overflow: hidden; /* Disable scrollbars */ display: block; /* No floating content on sides */ } <canvas id='c' style='position:absolute; left:0px; top:0px;'></canvas> The blue border shows you the edge of the resizing canvas, and is always along the edge of the window, visible on all 4 sides, which was NOT the case for some of the other above answers. Hope it helps.
Basically what you have to do is to bind the onresize event to your body, once you catch the event you just need to resize the canvas using window.innerWidth and window.innerHeight. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Canvas Resize</title> <script type="text/javascript"> function resize_canvas(){ canvas = document.getElementById("canvas"); if (canvas.width < window.innerWidth) { canvas.width = window.innerWidth; } if (canvas.height < window.innerHeight) { canvas.height = window.innerHeight; } } </script> </head> <body onresize="resize_canvas()"> <canvas id="canvas">Your browser doesn't support canvas</canvas> </body> </html>
Setting the canvas coordinate space width and height based on the browser client's dimensions requires you to resize and redraw whenever the browser is resized. A less convoluted solution is to maintain the drawable dimensions in Javascript variables, but set the canvas dimensions based on the screen.width, screen.height dimensions. Use CSS to fit: #containingDiv { overflow: hidden; } #myCanvas { position: absolute; top: 0px; left: 0px; } The browser window generally won't ever be larger than the screen itself (except where the screen resolution is misreported, as it could be with non-matching dual monitors), so the background won't show and pixel proportions won't vary. The canvas pixels will be directly proportional to the screen resolution unless you use CSS to scale the canvas.
A pure CSS approach adding to solution of #jerseyboy above. Works in Firefox (tested in v29), Chrome (tested in v34) and Internet Explorer (tested in v11). <!DOCTYPE html> <html> <head> <style> html, body { width: 100%; height: 100%; margin: 0; } canvas { background-color: #ccc; display: block; position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%; } </style> </head> <body> <canvas id="canvas" width="500" height="500"></canvas> <script> var canvas = document.getElementById('canvas'); if (canvas.getContext) { var ctx = canvas.getContext('2d'); ctx.fillRect(25,25,100,100); ctx.clearRect(45,45,60,60); ctx.strokeRect(50,50,50,50); } </script> </body> </html> Link to the example: http://temporaer.net/open/so/140502_canvas-fit-to-window.html But take care, as #jerseyboy states in his comment: Rescaling canvas with CSS is troublesome. At least on Chrome and Safari, mouse/touch event positions will not correspond 1:1 with canvas pixel positions, and you'll have to transform the coordinate systems.
function resize() { var canvas = document.getElementById('game'); 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.style.width = width + 'px'; canvas.style.height = height + 'px'; }; window.addEventListener('resize', resize, false);
Set initial size. const canvas = document.getElementById('canvas'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; Update size on window resize. function windowResize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }; window.addEventListener('resize', windowResize);
2022 answer The recommended way in 2022 to check if an element resized is to use ResizeObserver const observer = new ResizeObserver(myResizeTheCanvasFn); observer.observe(someCanvasElement); It's better than window.addEventListener('resize', myResizeTheCanvasFn) or onresize = myResizeTheCanvasFn because it handles EVERY case of the canvas resizing, even when it's not related to the window resizing. Similarly it makes no sense to use window.innerWidth, window.innerHeight. You want the size of the canvas itself, not the size of the window. That way, no matter where you put the canvas you'll get the correct size for the situation and won't have to re-write your sizing code. As for getting the canvas to fill the window html, body { height: 100%; } canvas { width: 100%; height: 100%; display: block; /* this is IMPORTANT! */ } The reason you need display: block is because by default the canvas is inline which means it includes extra space at the end. Without display: block you'll get a scrollbar. Many people fix the scrollbar issue by adding overflow: hidden to the body of the document but that's just hiding the fact that the canvas's CSS was not set correctly. It's better to fix the bug (set the canvas to display: block than to hide the bug with overflow: hidden Full example const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); const observer = new ResizeObserver((entries) => { canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; }); observer.observe(canvas) // not import but draw something just to showcase const hsla = (h, s, l, a) => `hsla(${h * 360}, ${s * 100}%, ${l * 100}%, ${a})`; function render(time) { const {width, height} = canvas; ctx.clearRect(0, 0, width, height); ctx.save(); ctx.translate(width / 2, height / 2); ctx.rotate(time * 0.0001); const range = Math.max(width, height) * 0.8; const size = 64 + Math.sin(time * 0.001) * 50; for (let i = 0; i < range; i += size) { ctx.fillStyle = hsla(i / range * 0.3 + time * 0.0001, 1, 0.5, 1); ctx.fillRect( i, -range, size, range * 2); ctx.fillRect(-i, -range, size, range * 2); } ctx.restore(); requestAnimationFrame(render) } requestAnimationFrame(render) html, body { height: 100%; margin: 0; } canvas { width: 100%; height: 100%; display: block; } <canvas></canvas> Note: there are other issues related to resizing the canvas. Specifically if you want to deal with different devicePixelRatio settings. See this article for more.
Unless you want the canvas to upscale your image data automatically (that's what James Black's answer talks about, but it won't look pretty), you have to resize it yourself and redraw the image. Centering a canvas
If your div completely filled the webpage then you can fill up that div and so have a canvas that fills up the div. You may find this interesting, as you may need to use a css to use percentage, but, it depends on which browser you are using, and how much it is in agreement with the spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#the-canvas-element The intrinsic dimensions of the canvas element equal the size of the coordinate space, with the numbers interpreted in CSS pixels. However, the element can be sized arbitrarily by a style sheet. During rendering, the image is scaled to fit this layout size. You may need to get the offsetWidth and height of the div, or get the window height/width and set that as the pixel value.
CSS body { margin: 0; } canvas { display: block; } JavaScript window.addEventListener("load", function() { var canvas = document.createElement('canvas'); document.body.appendChild(canvas); var context = canvas.getContext('2d'); function draw() { context.clearRect(0, 0, canvas.width, canvas.height); context.beginPath(); context.moveTo(0, 0); context.lineTo(canvas.width, canvas.height); context.moveTo(canvas.width, 0); context.lineTo(0, canvas.height); context.stroke(); } function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; draw(); } window.addEventListener("resize", resize); resize(); });
If you're interested in preserving aspect ratios and doing so in pure CSS (given the aspect ratio) you can do something like below. The key is the padding-bottom on the ::content element that sizes the container element. This is sized relative to its parent's width, which is 100% by default. The ratio specified here has to match up with the ratio of the sizes on the canvas element. // Javascript var canvas = document.querySelector('canvas'), context = canvas.getContext('2d'); context.fillStyle = '#ff0000'; context.fillRect(500, 200, 200, 200); context.fillStyle = '#000000'; context.font = '30px serif'; context.fillText('This is some text that should not be distorted, just scaled', 10, 40); /*CSS*/ .container { position: relative; background-color: green; } .container::after { content: ' '; display: block; padding: 0 0 50%; } .wrapper { position: absolute; top: 0; right: 0; left: 0; bottom: 0; } canvas { width: 100%; height: 100%; } <!-- HTML --> <div class=container> <div class=wrapper> <canvas width=1200 height=600></canvas> </div> </div>
Using jQuery you can track the window resize and change the width of your canvas using jQuery as well. Something like that $( window ).resize(function() { $("#myCanvas").width($( window ).width()) }); <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000;">
Here's a tiny, complete Code Snippet that combines all the answers. Press: "Run Code Snippet" then press "Full Page" and resize the window to see it in action: function refresh(referenceWidth, referenceHeight, drawFunction) { const myCanvas = document.getElementById("myCanvas"); myCanvas.width = myCanvas.clientWidth; myCanvas.height = myCanvas.clientHeight; const ratio = Math.min( myCanvas.width / referenceWidth, myCanvas.height / referenceHeight ); const ctx = myCanvas.getContext("2d"); ctx.scale(ratio, ratio); drawFunction(ctx, ratio); window.requestAnimationFrame(() => { refresh(referenceWidth, referenceHeight, drawFunction); }); } //100, 100 is the "reference" size. Choose whatever you want. refresh(100, 100, (ctx, ratio) => { //Custom drawing code! Draw whatever you want here. const referenceLineWidth = 1; ctx.lineWidth = referenceLineWidth / ratio; ctx.beginPath(); ctx.strokeStyle = "blue"; ctx.arc(50, 50, 49, 0, 2 * Math.PI); ctx.stroke(); }); div { width: 90vw; height: 90vh; } canvas { width: 100%; height: 100%; object-fit: contain; } <div> <canvas id="myCanvas"></canvas> </div> This snippet uses canvas.clientWidth and canvas.clientHeight rather than window.innerWidth and window.innerHeight to make the snippet run inside a complex layout correctly. However, it works for full window too if you just put it in a div that uses full window. It's more flexible this way. The snippet uses the newish window.requestAnimationFrame to repeatedly resize the canvas every frame. If you can't use this, use setTimeout instead. Also, this is inefficient. To make it more efficient, store the clientWidth and clientHeight and only recalculate and redraw when clientWidth and clientHeight change. The idea of a "reference" resolution lets you write all of your draw commands using one resolution... and it will automatically adjust to the client size without you having to change the drawing code. The snippet is self explanatory, but if you prefer it explained in English: https://medium.com/#doomgoober/resizing-canvas-vector-graphics-without-aliasing-7a1f9e684e4d
A bare minimum setup HTML <canvas></canvas> CSS html, body { height: 100%; margin: 0; } canvas { width: 100%; height: 100%; display: block; } JavaScript const canvas = document.querySelector('canvas'); const resizeObserver = new ResizeObserver(() => { canvas.width = Math.round(canvas.clientWidth * devicePixelRatio); canvas.height = Math.round(canvas.clientHeight * devicePixelRatio); }); resizeObserver.observe(canvas); For WebGL const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); const resizeObserver = new ResizeObserver(() => { canvas.width = Math.round(canvas.clientWidth * devicePixelRatio); canvas.height = Math.round(canvas.clientHeight * devicePixelRatio); gl.viewport(0, 0, canvas.width, canvas.height); }); resizeObserver.observe(canvas); Notice that we should take device pixel ratio into account, especially for HD-DPI display.
I think this is what should we exactly do: http://www.html5rocks.com/en/tutorials/casestudies/gopherwoord-studios-resizing-html5-games/ function resizeGame() { var gameArea = document.getElementById('gameArea'); var widthToHeight = 4 / 3; var newWidth = window.innerWidth; var newHeight = window.innerHeight; var newWidthToHeight = newWidth / newHeight; if (newWidthToHeight > widthToHeight) { newWidth = newHeight * widthToHeight; gameArea.style.height = newHeight + 'px'; gameArea.style.width = newWidth + 'px'; } else { newHeight = newWidth / widthToHeight; gameArea.style.width = newWidth + 'px'; gameArea.style.height = newHeight + 'px'; } gameArea.style.marginTop = (-newHeight / 2) + 'px'; gameArea.style.marginLeft = (-newWidth / 2) + 'px'; var gameCanvas = document.getElementById('gameCanvas'); gameCanvas.width = newWidth; gameCanvas.height = newHeight; } window.addEventListener('resize', resizeGame, false); window.addEventListener('orientationchange', resizeGame, false);
(function() { // get viewport size getViewportSize = function() { return { height: window.innerHeight, width: window.innerWidth }; }; // update canvas size updateSizes = function() { var viewportSize = getViewportSize(); $('#myCanvas').width(viewportSize.width).height(viewportSize.height); $('#myCanvas').attr('width', viewportSize.width).attr('height', viewportSize.height); }; // run on load updateSizes(); // handle window resizing $(window).on('resize', function() { updateSizes(); }); }());
This worked for me. Pseudocode: // screen width and height scr = {w:document.documentElement.clientWidth,h:document.documentElement.clientHeight} canvas.width = scr.w canvas.height = scr.h Also, like devyn said, you can replace "document.documentElement.client" with "inner" for both the width and height: **document.documentElement.client**Width **inner**Width **document.documentElement.client**Height **inner**Height and it still works.