Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render off-screen elements #117

Closed
itsravenous opened this issue Aug 21, 2012 · 47 comments
Closed

Render off-screen elements #117

itsravenous opened this issue Aug 21, 2012 · 47 comments

Comments

@itsravenous
Copy link

First - thanks for your hard work - this is an extremely useful library :)

I'm looking to render an offscreen element onto canvas, but this just results in a blank image. I assume this is because html2canvas renders the whole (visible) page and then crops down to the specified element, meaning anything that's outside the page boundaries is missed.

I suspect this requires a rewrite of how html2canvas handles rendering a specific element as opposed to the whole page, so I appreciate it's a big ask.

Cheers
Tom

@niklasvh
Copy link
Owner

Is the element visible by scrolling on the page? What element are you passing to html2canvas as the element to render?

@itsravenous
Copy link
Author

No, it's absolutely positioned and off-screened with left: -9999px. The element in question is a bar chart I created with some divs. It renders to canvas fine when on-screen.

So, the reason I'm off-screening it: The image generated is going to (at some point) be printed off, and so needs to be reasonably high-res. Thus I clone the chart, put it offscreen and enlarge it by a factor of 2 (using font-size and settings widhts/heights - I found using a CSS3 scale transform resulted in a blank image too, even if it was on-screen), and then call html2canvas on it.

@niklasvh
Copy link
Owner

Think a quick solution would be to position it on the page right before you call html2canvas on it, and then move it away right after. I'll have to look through the code to see where exactly it stops the off-screen elements to be rendered, but if you were calling html2canvas on the body element, the expected result would be to not have the item visible.

As for the scale CSS property, none of the CSS3 transorms work currently, but will hopefully be implemented, at least in part, at some point.

@itsravenous
Copy link
Author

Aah, thanks - I'll try that out! Thanks for the info :)

@wallacecDundas
Copy link

Will you be fixing the problem?
http://jsfiddle.net/makubex/DZDGw/2/

Or perhaps at least add a way to handle the error?

@niklasvh
Copy link
Owner

What exactly is the issue in your example? Seems to be doing what it should (tested with Chrome)

@wallacecDundas
Copy link

When the element is off-screen on the left, html2canvas just crashes, and provides no means of error handling.
Chrome lets the exception go and onrendered is still fired, but in firefox it fails, it just throws the exception and stop executing the code.

The example is to demonstrate that :

  1. there is a problem rendering DOM elements that are left-off-screen and top-off-screen(not shown) but not right-off-screen and bottom-off-screen
  2. although 1. can be worked around by moving the elements into view and then take a snapshot, there is no way to know if the snapshot has failed. In my cases i need to know which ones have failed to take a snapshot, remember it, and when it becomes visible on-screen, it takes a snap shot.

@yojoe26
Copy link

yojoe26 commented Jul 24, 2014

I'm having a similar issue...I have a scrollable modal that I want to convert to canvas. When running html2canvas, it only captures the visible portion of the screen, truncating everything hidden from the scroll. Setting the height attribute has no impact.

@parnelle89
Copy link

@yojoe26, try using the scrollIntoView function to move the screen around as html2canvas is parsing it. I had a similar problem, and found a solution. Very hacky solution, and would not recommend if "scrolling wildly around the page" is going to be a user experience issue!

I added "options.elements[0].scrollIntoView(false);" as the first line in the "renderElement" function right before the variable declarations.

@yojoe26
Copy link

yojoe26 commented Jul 30, 2014

@parnelle89 - thanks for the tip!

@toannguyen
Copy link

@parnelle89 thanks very much!
"options.elements[0].scrollIntoView(false);"

@zzxian
Copy link

zzxian commented Nov 18, 2014

@parnelle89
"options.elements[0].scrollIntoView(false);" ?
Can you give me an example with a whole js snippet?
I am new to html2canvas.
Thanks!

@herringtown
Copy link

@niklasvh regarding the question above, can you tell me where in the code you specify the stopping point for off-screen elements to be rendered ?

@herringtown
Copy link

by default, html2canvas uses the current viewport as the size of the canvas to be rendered (for obvious reasons). If, for instance, you want to render a particular element on the page that extends pass the fold -- or is entirely below the fold -- you can utilize the width/height options in the html2canvas call to render the element in it's entirety (not just the part that's within the viewport).

As long as the item is ulimately scrollable (and not, for example, positioned absolutely at something like left: -9999px) this should work :

var useWidth = document.getElementById("myElementThatExtendsBeyondTheViewport").style.width;
var useHeight = document.getElementById("myElementThatExtendsBeyondTheViewport").style.height;

html2canvas(element, {
    width: useWidth,
    height: useHeight,
    onrendered: function(canvas) {
        // you should get a canvas that includes the entire element -- not just the visible portion
    }
});

If the element IS absolutely positioned offscreen -- as in @itsravenous 's case -- I would temporarily add the following CSS to make it renderable by html2canvas (but still invisible -- just past the viewport):

var myOffscreenEl = document.getElementById("myOffscreenEl"),
    useWidth      = myOffscreenEl.style.width,
    useHeight     = myOffscreenEl.style.height;

// position it relatively, just below the fold..
myOffscreenEl.style.position = 'relative';
myOffscreenEl.style.top = window.innerHeight + 'px';
myOffscreenEl.style.left = 0;

html2canvas(myOffscreenEl, {
    width: useWidth,
    height: useHeight,
    onrendered: function(canvas) {

       // restore the old offscreen position
      myOffscreenEl.style.position = 'absolute';
      myOffscreenEl.style.top = 0;
      myOffscreenEl.style.left = "-9999px"

    }

});

^^ seems potentially simpler/safer than @parnelle89's solution, but I might be missing something here...

EDIT: I'm encountering some scenarios with my solution where some extremely tall elements get cropped unless if I first clone the element with element.cloneNode and then append it to body. Not sure why..

@ejlocop
Copy link

ejlocop commented Apr 30, 2015

hi im facing the same problem, but the above solution didnt work for me..
i have a scrollable div content and i want to capture the whole content. not the one in the viewport only.. can someone help me ?

@ejlocop
Copy link

ejlocop commented Apr 30, 2015

1
2

why is mine is like this ? T_T

@eworksmedia
Copy link

We solved this by changing line 604 from

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {

to

return renderDocument(node.ownerDocument, options, options.width != undefined ? options.width : node.ownerDocument.defaultView.innerWidth, options.height != undefined ? options.height : node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {

Basically force it to respect the width/height arguments set during setup rather than getting the viewable area according to the document.. No weird positioning hacks necessary..

@ioRekz
Copy link

ioRekz commented May 16, 2015

Hello, I'm trying to render an element that is offscreen (need to scroll to see it).
I don't capture the whole page, only one element.
Should it work or I need to tweak some things ?

@btm1
Copy link

btm1 commented Jun 30, 2015

+1 this is the issue i'm having

@ghost
Copy link

ghost commented Oct 8, 2015

@eworksmedia solution above worked for me. Great stuff thanks

@alana314
Copy link

I had to put my canvas in an iframe, run html2canvas inside the iframe, run toDataURL(), and pass the image back to the parent.

@OscarAgreda
Copy link

@eworksmedia
Thank you , good code. works like a charm.

@gregoryduckworth
Copy link

@eworksmedia your fix worked for me as well.

@Trexology
Copy link

@eworksmedia MAGIC! it works perfectly!!

@codebyuma
Copy link

Is the fix that @eworksmedia mentioned (to line 604) in any of the available releases? Or is this a manual change everyone's making to get this to work?

@gururajmca
Copy link

Thanks @eworksmedia for solution, I am able to see the canvas getting rendered, but it's very small in size, I have tried giving width and height params but still image is very small, is there any way we could scale the image?

@gururajmca
Copy link

@codebyuma this change we need to update manually.

@AlexGrump
Copy link

AlexGrump commented Mar 9, 2017

My fix is strange, but it work
CSS:
.html2canvas-container { width: 3000px !important; height: 3000px !important; }

@Brieg
Copy link

Brieg commented Mar 23, 2017

+1

@Romerski
Copy link

Romerski commented Mar 23, 2017

@eworksmedia not working for me :( Still not rendering off-screen width gantt chart who im wanting to render.

Any help?

@gururajmca
Copy link

@Romerski make sure your container is set visibility: visible;

@Romerski
Copy link

Romerski commented Mar 24, 2017

@gururajmca thanks but still not working :( Here is an image: http://i.imgur.com/EE8GyHD.png

Green: Popup when I click print button on Gantt chart.
Blue: HTML gantt chart who I want to render.
Red: What is rendering html2canvas from html (only screen visible part of html)

I tried to set visibility and overflow attributes to html gantt's div and to canvas div but nothing happens :(

Thanks again!!

@gururajmca
Copy link

@Romerski Can you try this one, which worked me.

//Default css
#hidden_div {
display: none;
visibility: hidden;
position: absolute;
top: 0;
left: 0;
}

function HtmltoImage() {
$('#hidden_div').css("display" , "block");
$('#hidden_div').css("visibility" , "visible");

var defer = $q.defer();
html2canvas($('#hidden_div'), 
{
    useCORS: true,
    allowTaint: true,
    letterRendering: true,
    logging:true,
    onrendered: function (canvas) {
        $('#hidden_div').css("overflow" , "hidden");
        var quality =[0.0,1.0];
        img = canvas.toDataURL('image/png',quality);
        var data = {
            "pngdata" : img,
        };
        defer.resolve(data);
    }
});
return defer.promise;

}

@Romerski
Copy link

Romerski commented Mar 24, 2017

@gururajmca thanks for your help but unfortunately still not working... I have no problem with the height, is rendering well but the problem is with the width, which is not rendering off-screen width of gantt chart.

Im pretty sure that is something related to the gantt chart overflow, This gantt chartt which im rendring is via JQuery.gantt(). I'll keep investigating.

Appreciate it any help.

@battlesteel
Copy link

@eworksmedia solution didn't work for me.

I fixed mine with this code.

function generateToPdf(element, fileName, method) {
    var c = document.getElementById(element);
    // overwrite owner doc inner height with your div clientHeight
    c.ownerDocument.defaultView.innerHeight = c.clientHeight;
    c.ownerDocument.defaultView.innerWidth = c.clientWidth;
    if (method === 'canvas') {
        html2canvas(c, {
            onrendered: function (canvas) {
                document.body.appendChild(canvas);
                // scale paper height based on ratio new canvas height and width
                var paperHeight = 210 * (canvas.height / canvas.width);
                var paperFormatInMm = [210, paperHeight];
                var doc = new jsPDF('p', 'mm', paperFormatInMm);
                doc.addImage(canvas, 'PNG', 2, 2);
                doc.save(fileName + '.pdf');
            }
        });
    }
}

@wotomas
Copy link

wotomas commented Aug 17, 2017

Its a closed issue, but some people like me could searching for the same issue, so i'll just add an comment in here. Solution as suggested by @eworksmedia, I created an npm module for personal use, but feel free use.

npm install --save html2canvas-render-offscreen

and use it just like html2canvas

@baylock
Copy link

baylock commented Nov 9, 2017

I'm not a developper or anything but I had the same issue and none of your fixes worked for me.
But then I tried something very simple: instead of putting the container off screen, I just "z-indexed" it below everything else.
This way, it was technically on screen for the script to work, even though it was not visible to the final user.
I'm pretty sure there are some side effects to this, according to your own layout but in my case, it worked just fine.

@rickli1989
Copy link

@eworksmedia You are legend !!!!!!

@DougHenrique
Copy link

The solution below worked for me, but it works only in the web browser, if I try to download the pdf through the mobile application (using WebView) does not work, someone else has gone through something similar and know a way to correct?

Note: sorry my English I am using Google Translate

@foxhehehe
Copy link

foxhehehe commented Feb 27, 2019

@battlesteel Your solution worked for me, others didn't
Thks !

@eworksmedia solution didn't work for me.

I fixed mine with this code.

function generateToPdf(element, fileName, method) {
    var c = document.getElementById(element);
    // overwrite owner doc inner height with your div clientHeight
    c.ownerDocument.defaultView.innerHeight = c.clientHeight;
    c.ownerDocument.defaultView.innerWidth = c.clientWidth;
    if (method === 'canvas') {
        html2canvas(c, {
            onrendered: function (canvas) {
                document.body.appendChild(canvas);
                // scale paper height based on ratio new canvas height and width
                var paperHeight = 210 * (canvas.height / canvas.width);
                var paperFormatInMm = [210, paperHeight];
                var doc = new jsPDF('p', 'mm', paperFormatInMm);
                doc.addImage(canvas, 'PNG', 2, 2);
                doc.save(fileName + '.pdf');
            }
        });
    }
}

@andylighthouse
Copy link

We solved this by changing line 604 from

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {

to

return renderDocument(node.ownerDocument, options, options.width != undefined ? options.width : node.ownerDocument.defaultView.innerWidth, options.height != undefined ? options.height : node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) {

Basically force it to respect the width/height arguments set during setup rather than getting the viewable area according to the document.. No weird positioning hacks necessary..

What is line 604? can you give some context? I can't find line 604 anywhere

@andreasvirkus
Copy link

@andylighthouse see 5 comments above: #117 (comment)

@ProteanDev
Copy link

Still having this issue

@eljeffeg
Copy link

eljeffeg commented Nov 11, 2020

Was having this issue still on version 1.0.0-rc.7. My element to convert is at the top of the page. If html2canvas is run when the user has scrolled down, it would crop the top of the image off. I tried @wotomas / @eworksmedia solution and while it fixed the cropping, the captured text was improperly positioned (as if it was forced down similar to the crop). In conjunction with html2canvas-render-offscreen window.scrollTo(0, 0) did the trick to fix the text, but I don't want to jump the user back up to the top. Hope this gets a solution soon. Honestly, I'd like to get the entire thing working without any visibility as I'm using it in an Electron app, generating thumbnail images on the fly based on changes made.

Ultimately, I ended up using html2canvas-render-offscreen and floating it top, left of 0, 0 with a z-index that placed it under everything. Thanks for the great tool.

@HenrijsS
Copy link

This has been an issue for 6-8 years and still isn't fixed.
Gosh. There are multiple solutions posted above. Why aren't they being implemented?

@nishantgupta11
Copy link

-> Add CSS overflow-y: scroll; to HTML element pdfHTML
-> Add attribute width & windowWidth to html2canvas method, which should be equal to width of pdfHTML Div

html2canvas(document.querySelector(".pdfHTML"), {
    width: 1350, windowWidth: 1350
}).then(canvas => {
    base64stringpdf = canvas.toDataURL("image/jpeg");
});

1 similar comment
@nishantgupta11
Copy link

-> Add CSS overflow-y: scroll; to HTML element pdfHTML
-> Add attribute width & windowWidth to html2canvas method, which should be equal to width of pdfHTML Div

html2canvas(document.querySelector(".pdfHTML"), {
    width: 1350, windowWidth: 1350
}).then(canvas => {
    base64stringpdf = canvas.toDataURL("image/jpeg");
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests