How to know if a font (#font-face) has already been loaded? - javascript

I'm using Font-Awesome, but while the font files are not loaded, the icons appear with .
So, I want these icons to have display:none while files are not loaded.
#font-face {
font-family: "FontAwesome";
src: url('../font/fontawesome-webfont.eot');
src: url('../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../font/fontawesome-webfont.woff') format('woff'), url('../font/fontawesome-webfont.ttf') format('truetype'), url('../font/fontawesome-webfont.svg#FontAwesome') format('svg');
font-weight: normal;
font-style: normal;
}
How do I know that these files have been loaded and I'm finally able to show the icons?
Edit:
I'm not talking when the page is loaded (onload), because the font could be loaded before the whole page.

Now on GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js
Essentially the method works by comparing the width of a string in two different fonts. We are using Comic Sans as the font to test against, because it is the most different of the web safe fonts and hopefully different enough to any custom font you will be using. Additionally we are using a very large font-size so even small differences will be apparent. When the width of the Comic Sans string has been calculated, the font-family is changed to your custom font, with a fallback to Comic Sans. When checked, if the string element width is the same, the fallback font of Comic Sans is still in use. If not, your font should be operational.
I rewrote the method of font load detection into a jQuery plugin designed to give the developer the ability to style elements based upon whether the font has been loaded or not. A fail safe timer has been added so the user isn’t left without content if the custom font fails to load. That’s just bad usability.
I have also added greater control over what happens during font loading and on fail with the inclusion of classes addition and removal. You can now do whatever you like to the font. I would only recommend modifying the fonts size, line spacing, etc to get your fall back font as close to the custom as possible so your layout stays intact, and users get an expected experience.
Here's a demo: http://patrickmarabeas.github.io/jQuery-FontSpy.js
Throw the following into a .js file and reference it.
(function($) {
$.fontSpy = function( element, conf ) {
var $element = $(element);
var defaults = {
font: $element.css("font-family"),
onLoad: '',
onFail: '',
testFont: 'Comic Sans MS',
testString: 'QW#HhsXJ',
delay: 50,
timeOut: 2500
};
var config = $.extend( defaults, conf );
var tester = document.createElement('span');
tester.style.position = 'absolute';
tester.style.top = '-9999px';
tester.style.left = '-9999px';
tester.style.visibility = 'hidden';
tester.style.fontFamily = config.testFont;
tester.style.fontSize = '250px';
tester.innerHTML = config.testString;
document.body.appendChild(tester);
var fallbackFontWidth = tester.offsetWidth;
tester.style.fontFamily = config.font + ',' + config.testFont;
function checkFont() {
var loadedFontWidth = tester.offsetWidth;
if (fallbackFontWidth === loadedFontWidth){
if(config.timeOut < 0) {
$element.removeClass(config.onLoad);
$element.addClass(config.onFail);
console.log('failure');
}
else {
$element.addClass(config.onLoad);
setTimeout(checkFont, config.delay);
config.timeOut = config.timeOut - config.delay;
}
}
else {
$element.removeClass(config.onLoad);
}
}
checkFont();
};
$.fn.fontSpy = function(config) {
return this.each(function() {
if (undefined == $(this).data('fontSpy')) {
var plugin = new $.fontSpy(this, config);
$(this).data('fontSpy', plugin);
}
});
};
})(jQuery);
Apply it to your project
.bannerTextChecked {
font-family: "Lobster";
/* don't specify fallback font here, do this in onFail class */
}
$(document).ready(function() {
$('.bannerTextChecked').fontSpy({
onLoad: 'hideMe',
onFail: 'fontFail anotherClass'
});
});
Remove that FOUC!
.hideMe {
visibility: hidden !important;
}
.fontFail {
visibility: visible !important;
/* fall back font */
/* necessary styling so fallback font doesn't break your layout */
}
EDIT: FontAwesome compatibility removed as it didn't work properly and ran into issues with different versions. A hacky fix can be found here: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

Try WebFont Loader (github repo), developed by Google and Typekit.
This example first displays the text in the default serif font; then after the fonts have loaded it displays the text in the specified font. (This code reproduces Firefox's default behavior in all other modern browsers.)

Actually, there is a good way to understand all fonts begin to download or loaded completely or not and fall into some errors, but it is not just for a specific font, pay attention to the following code:
document.fonts.onloading = () => {
// do someting when fonts begin to download
};
document.fonts.onloadingdone = () => {
// do someting when fonts are loaded completely
};
document.fonts.onloading = () => {
// do someting when fonts fall into some error
};
And also there is an option that returns Promise and it could handle with .then function:
document.fonts.ready
.then(() => console.log('do someting at the final with each status'))

Here is a different approach to the solutions from others.
I'm using FontAwesome 4.1.0 to build WebGL textures. That gave me the idea to use a tiny canvas to render a fa-square to, then check a pixel in that canvas to test whether it has loaded:
function waitForFontAwesome( callback ) {
var retries = 5;
var checkReady = function() {
var canvas, context;
retries -= 1;
canvas = document.createElement('canvas');
canvas.width = 20;
canvas.height = 20;
context = canvas.getContext('2d');
context.fillStyle = 'rgba(0,0,0,1.0)';
context.fillRect( 0, 0, 20, 20 );
context.font = '16pt FontAwesome';
context.textAlign = 'center';
context.fillStyle = 'rgba(255,255,255,1.0)';
context.fillText( '\uf0c8', 10, 18 );
var data = context.getImageData( 2, 10, 1, 1 ).data;
if ( data[0] !== 255 && data[1] !== 255 && data[2] !== 255 ) {
console.log( "FontAwesome is not yet available, retrying ..." );
if ( retries > 0 ) {
setTimeout( checkReady, 200 );
}
} else {
console.log( "FontAwesome is loaded" );
if ( typeof callback === 'function' ) {
callback();
}
}
}
checkReady();
};
As it uses a canvas it requires a fairly modern browser, but it might work on IE8 as well with the polyfill.

Here's another way of knowing if a #font-face has already been loaded without having to use timers at all: utilize a "scroll" event to receive an instantaneous event when the size of a carefully crafted element is changed.
I wrote a blog post about how it's done and have published the library on Github.

Try something like
$(window).bind("load", function() {
$('#text').addClass('shown');
});
and then do
#text {visibility: hidden;}
#text.shown {visibility: visible;}
The load event should fire after the fonts are loaded.

alternatively, you could add font-display: block to your #font-face declaration.
this instructs browsers to render the fallback font as invisible until your font is loaded, no need for display: none or any javascript load font detection

Solution for Typescript, Angular.
If you are working with Angular, you can use this module in order to do a font check.
// document.fonts.check extension
import type {} from 'css-font-loading-module';
ngOnInit() {
this.onFontLoad();
}
public onFontLoad() {
let myTimer = setInterval(() => {
if (document.fonts.check('14px MyFont')) {
console.log('Font is loaded!');
clearInterval(myTimer);
} else {
console.log('Font is loading');
}
}, 1);
}
Also, some fonts are extremely heavy. Therefore, you can add a loading screen while the font is loading and remove the loading screen when the font is loaded. I believe this is a better approach rather than changing your CSS class to display: none, merely because it might take 3-4+ seconds to download some fonts if the user has slow internet.

This is an alternate approach that will at least ensure that font-awesome is loaded, NOT a complete solution to the OP. Original code found in the wordpress forums here https://wordpress.stackexchange.com/a/165358/40636.
It's agnostic and will work with any font style resource like font-awesome where a font-family can be checked. With a little more thought I bet this could be applied to much more...
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script>
(function($){
var faSpan = $('<span class="fa" style="display:none"></span>').appendTo('body');
if (faSpan .css('fontFamily') !== 'FontAwesome' ) {
// Fallback Link
$('head').append('<link href="/css/font-awesome.min.css" rel="stylesheet">');
}
faSpan.remove();
})(jQuery);
</script>

Use the below code:
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<canvas id="canvasFont" width="40px" height="40px" style="position: absolute; display: none;"></canvas>
<script>
function IsLoadedFonts()
{
var Args = arguments;
var obj = document.getElementById('canvasFont');
var ctx = obj.getContext("2d");
var baseFont = (/chrome/i.test(navigator.userAgent))?'tims new roman':'arial';
//................
function getImg(fon)
{
ctx.clearRect(0, 0, (obj).width, (obj).height);
ctx.fillStyle = 'rgba(0,0,0,1.0)';
ctx.fillRect( 0, 0, 40, 40 );
ctx.font = '20px '+ fon;
ctx.textBaseline = "top";
ctx.fillStyle = 'rgba(255,255,255,1.0)';
ctx.fillText( '\u0630', 18, 5 );
return ctx.getImageData( 0, 0, 40, 40 );
};
//..............
for(var i1=0; i1<Args.length; i1++)
{
data1 = getImg(Args[i1]);
data2 = getImg(baseFont);
var isLoaded = false;
//...........
for (var i=0; i<data1.data.length; i++)
{
if(data1.data[i] != data2.data[i])
{isLoaded = true; break;}
}
//..........
if(!isLoaded)
return false;
}
return true;
};
setTimeout(function(){alert(IsLoadedFonts('myfont'));},100);
</script>
</body>
Can check many fonts:
setTimeout(function(){alert(IsLoadedFonts('font1','font2','font3'));},100);
The below code works in opera only but is easy:
if(!document.defaultView.getComputedStyle(document.getElementById('mydiv'))['fontFamily'].match(/myfont/i))
alert("font do not loaded ");

Related

Can't get a canvas to display a custom font

Canvas.filltext won't render custom font (though body text will, and canvas.filltext will render local fonts)
I want to paint some text on a canvas, using a custom font called jelleebold. Here's an abbreviated version of the function that I hoped would do that:
function paintLinkNameOnCanvas(linkName, canvas){
let context = canvas.getContext('2d');
context.font = "25px jelleebold";
context.fillText(linkName, 50, 50);
return canvas;
}
However, the font that gets used is whatever the browser (Chrome) uses as its fallback (Times New Roman, I think). Here's the html link
<link href="./CSS/stylesheet.css" rel="stylesheet" type="text/css">
and the css, downloaded from fontsquirrel, and modified to suit my local directory structure:
#font-face {
font-family: jelleebold;
src: url('/fonts/jellee-roman-webfont.woff2') format('woff2'),
url('/fonts/jellee-roman-webfont.woff') format('woff');
font-weight: normal;
font-style: normal;
}
I believe that this has worked, and Jelleebold has loaded successfully, because index.php contains:
<body style ="font-size: 50px; font-family: 'jelleebold'">
<div id= "output" >
test
</div>
etc.
and the word 'test' gets printed in jelleebold.
In contrast, if the paintLinkNameOnCanvas function specifies a font that is installed on the local machine (such as context.font = "25px 'Balford Base'"), the linkname does get painted in that font.
So why isn't the custom font being used to paint the linkname on the canvas?
And after a very helpful suggestion from Renato Bibiano, I've now produced an updated version of the function (shown below). The commented lines are a hint about the next problem; how do I get it to work with both woff- and woff2-formatted fonts? It works with either one, but not the other.
function paintLinkNameOnCanvas(linkName, canvas){
let earl = "";
// earl += "url('/fonts/jellee-roman-webfont.woff' ) format('woff' ";
// earl += ", ";
earl += "url('/fonts/jellee-roman-webfont.woff2')format('woff2')";
let f = new FontFace("jelleebold", earl);
f.load().then(function() {
let context = canvas.getContext('2d');
context.font = '23px jelleebold';
context.fillText('Hey, world', 0, 100);
});
return canvas;
}
I feel that taking the comment slashes out should produce something sensible, but if both specifications (woff and woff2) are included, the ouput reverts to the default font, TNR.

Panel tranparency firefox addon

I'm programming a firefox addon and using a panel to display info on a video, everything works fine althought I can't make the panel transparent. I define the panel styling in the html file as follow:
<html>
<head>
<meta charset="utf-8" />
<style type="text/css" media="all">
html
{
opacity:0.1;
border-style:none;
resize:none;
}
textarea
{
background-color:transparent;
resize: none;
border-style:none;
}
</style>
</head>
<body>
<textarea id="text" readonly=true rows="3" cols="60"></textarea>
</panel>
</body>
</html>
Except the panel is not transparent only the text area is. I tried with:
opacity:1 for textarea
It doesn't work either way. What am I doing wrong? Is this even possible?
From what I understand :
html
{
opacity:0.1;
border-style:none;
resize:none;
}
only applies to the panel content not to the panel itself. I found a post on this subject but it is outdated since the sdk/panel.js mentionned in the post is not the same anymore.
Anyway I tried downloading the panel.js and replacing the current one, but it doesn't seem to affect the panel I display at all. The panel is still white and the border-radius option does not work either. (I should say that I replaced all the "./" with "sdk/" as mentionned in that post).
Ok here is a pure addon sdk solution:
let myPanel = Panel({
width: 180,
height: 180,
contentURL: 'data:text/html,<textarea style="width:120px; height:80px;">this is my textarea</textarea>'
})
let { getActiveView }=require("sdk/view/core");
getActiveView(myPanel).setAttribute("noautohide", true);
getActiveView(myPanel).setAttribute("level", 'top');
getActiveView(myPanel).setAttribute("style", 'background-color:rgba(0, 0, 0, 0.2);');
You can't style the panel provided in the SDK, only the content but you can definitely follow the procedure you mention and provide your modified panel.
I had to solve this same problem today (transparent panel in SDK). The trick is getting at the anonymous content:
function makePanelTransparent() {
// Get the panel element in the XUL DOM and make its background transparent.
const { getActiveView } = require('sdk/view/core');
const el = getActiveView(panel);
el.style.background = 'rgba(0,0,0,0)';
// Go up the XUL DOM till you hit the Document (nodeType 9).
let parentNode = el;
while (parentNode !== null && parentNode.nodeType !== 9) {
parentNode = parentNode.parentNode;
}
if (!parentNode) {
console.error('unable to find the document parent; giving up');
return;
}
// Now that we've found it, call the document a document.
const xulDocument = parentNode;
// Use the document pointer to access and style 'anonymous' content.
const xulContainer = xulDocument.getAnonymousElementByAttribute(el, 'class', 'panel-arrowcontent')
xulContainer.style.background = 'rgba(0,0,0,0)';
xulContainer.style.boxShadow = 'none';
}
This works for me. Hope it helps some other person in the next 1-5 years ;-)
I found out that you could create a panel with transparency this way:
var win = Services.wm.getMostRecentWindow('navigator:browser');
var panel = win.document.createElement('panel');
var screen = Services.appShell.hiddenDOMWindow.screen;
var props = {
noautohide: true,
noautofocus: false,
level: 'top',
style: 'padding:15px; margin:0; width:' + screen.width + 'px; height:' + screen.height + 'px; background-color:rgba(180,180,180,.5);'
}
for (var p in props) {
panel.setAttribute(p, props[p]);
}
win.document.querySelector('#mainPopupSet').appendChild(panel);
panel.addEventListener('dblclick', function () {
panel.parentNode.removeChild(panel)
}, false);
panel.openPopup(null, 'overlap', screen.availLeft, screen.availTop);
To embed an iframe remember to set the path to your ".html" as:
"resource://"id of your addon"-at-jetpack/data/custom_panel.html".
Here is my code :
var win = Services.wm.getMostRecentWindow('navigator:browser');
var panel = win.document.createElement('panel');
var screen = Services.appShell.hiddenDOMWindow.screen;
var props = {
noautohide: true,
noautofocus: false,
backdrag: true,
level: 'top',
style: 'padding:10px; margin:0; width:530px; height:90px; background-color:rgba(180,180,180,.5);'
}
for (var p in props) {
panel.setAttribute(p, props[p]);
}
var iframe = win.document.createElement('iframe');
iframe.setAttribute('src','resource://"id of your addon"-at-jetpack/data/custom_panel.html');
panel.appendChild(iframe);
win.document.querySelector('#mainPopupSet').appendChild(panel);
panel.addEventListener('dblclick', function () {
panel.parentNode.removeChild(panel)
}, false);
panel.openPopup(null, 'overlap', screen.availLeft+screen.width/2-256, screen.availTop+760);
Thanks Noitidart for the help.

ExtJS 4 - Printing Forms Programatically sometimes gives "Print Preview Failed" in Chrome

Good day. I am developing a Web Application and there's a part where I print the form on button click. To achieve this, I overrode the definition of my Form Panel so that I can call form.print() anywhere in my code when I need to. Here is how I overrode my form:
Ext.define('my_app_name.override.form.Panel', {
override: 'Ext.form.Panel',
print: function(pnl) {
if (!pnl) {
pnl = this;
}
// instantiate hidden iframe
var iFrameId = "printerFrame";
var printFrame = Ext.get(iFrameId);
if (printFrame === null) {
printFrame = Ext.getBody().appendChild({
id: iFrameId,
tag: 'iframe',
cls: 'x-hidden',
style: {
display: "none"
}
});
}
var cw = printFrame.dom.contentWindow;
// instantiate application stylesheets in the hidden iframe
var stylesheets = "";
for (var i = 0; i < document.styleSheets.length; i++) {
stylesheets += Ext.String.format('<link rel="stylesheet" href="{0}" />', document.styleSheets[i].href);
}
// various style overrides
stylesheets += ''.concat(
"<style>",
".x-panel-body {overflow: visible !important;}",
// experimental - page break after embedded panels
// .x-panel {page-break-after: always; margin-top: 10px}",
"</style>"
);
// get the contents of the panel and remove hardcoded overflow properties
var markup = pnl.getEl().dom.innerHTML;
while (markup.indexOf('overflow: auto;') >= 0) {
markup = markup.replace('overflow: auto;', '');
}
var str = Ext.String.format('<html><head>{0}</head><body>{1}</body></html>',stylesheets,markup);
// output to the iframe
cw.document.open();
cw.document.write(str);
cw.document.close();
// remove style attrib that has hardcoded height property
cw.document.getElementsByTagName('DIV')[0].removeAttribute('style');
// print the iframe
cw.print();
// destroy the iframe
Ext.fly(iFrameId).destroy();
}
});
Then on a click of a button in my Web App, I do something like:
var form = Ext.getCmp('formIDHere');
form.print();
However, this code is rather inconsistent at times. There are times that I can print the form no problem and there are times that it gives the "Print Preview Error" message. I can't replicate the issue consistently and the logs aren't showing anything so I'm in the dark.
What I've noticed however, is that when I save my project (I'm using Sencha Architect), preview it (or refresh the current window where I'm previewing my Web App), stay with the web app all throughout the process (meaning I don't shift tabs or windows), hit the print button, the print preview appears and I don't have problems with it.
So far I haven't tested in other Web Browsers. Any ideas anyone? I'll be really thankful for anyone who can point out what I'm doing wrong. Thanks in advance.
Sorry I forgot to update this. Thanks to whoever upvoted my question.
The concept is simple. Since ExtJS4 is asynchronous, I placed my code in "blocks" and then I delayed my calls to those functions to ensure that they finish constructing what they need to construct before moving on to the next part.
print: function(pnl) {
if (!pnl) {
pnl = this;
}
// instantiate hidden iframe
var iFrameId = "printerFrame";
var printFrame = Ext.get(iFrameId);
if (printFrame === null) {
printFrame = Ext.getBody().appendChild({
id: iFrameId,
tag: 'iframe',
cls: 'x-hidden',
style: {
display: "none"
}
});
}
var cw = printFrame.dom.contentWindow;
var stylesheets = "";
var markup;
// instantiate application stylesheets in the hidden iframe
var printTask = new Ext.util.DelayedTask(function(){
// print the iframe
cw.print();
// destroy the iframe
Ext.fly(iFrameId).destroy();
});
var strTask = new Ext.util.DelayedTask(function(){
var str = Ext.String.format('<html><head>{0}</head><body>{1}</body></html>',stylesheets,markup);
// output to the iframe
cw.document.open();
cw.document.write(str);
cw.document.close();
// remove style attrib that has hardcoded height property
// cw.document.getElementsByTagName('DIV')[0].removeAttribute('style');
printTask.delay(500);
});
var markUpTask = new Ext.util.DelayedTask(function(){
// get the contents of the panel and remove hardcoded overflow properties
markup = pnl.getEl().dom.innerHTML;
while (markup.indexOf('overflow: auto;') >= 0) {
markup = markup.replace('overflow: auto;', '');
}
while (markup.indexOf('background: rgb(255, 192, 203) !important;') >= 0) {
markup = markup.replace('background: rgb(255, 192, 203) !important;', 'background: pink !important;');
}
strTask.delay(500);
});
var styleSheetConcatTask = new Ext.util.DelayedTask(function(){
// various style overrides
stylesheets += ''.concat(
"<style>",
".x-panel-body {overflow: visible !important;}",
// experimental - page break after embedded panels
// .x-panel {page-break-after: always; margin-top: 10px}",
"</style>"
);
markUpTask.delay(500);
});
var styleSheetCreateTask = new Ext.util.DelayedTask(function(){
for (var i = 0; i < document.styleSheets.length; i++) {
stylesheets += Ext.String.format('<link rel="stylesheet" href="{0}" />', document.styleSheets[i].href);
}
styleSheetConcatTask.delay(500);
});
styleSheetCreateTask.delay(500);
}

p5.js loadFont function?

How can I change the font in p5.js? It does not recognize the Processing term "loadFont," does not carry over a font from CSS, nor does it let me put in a .vlw file or link to a GoogleFont. At least, not in any way I have tried.
The references page only contains "text" and "textFont" options (in the Typography section at the end of the p5.js references page), neither of which allow for actually specifying a font.
I have also tried the
text.style('font-family', 'Walter Turncoat');
option listed here (https://github.com/lmccart/p5.js/wiki/Beyond-the-canvas) to no avail. It actually broke the whole page. In CSS:
#font-face {
font-family: 'Walter Turncoat';
src: url('http://fonts.googleapis.com/css?family=Walter+Turncoat');
}
Processing version did not work:
var type = loadFont("AmericanTypewriter-48.vlw");
var smallType = loadFont("AmericanTypewriter-14.vlw");
Also,
var type = "Helvetica";
which they have in the examples for text and textFont does not work.
There has to be a way to have another font. Please help!
The examples given in the reference work fine. Run code snippet below for results. What do you mean when you say it doesn't work for you?
function setup() {
createCanvas(640, 480);
}
function draw() {
fill(0);
textSize(36);
textFont("Georgia");
text("Hello World! in Georgia.", 12, 40);
textFont("Arial");
text("Hello World! in Arial.", 12, 100);
textFont("Walter Turncoat");
text("Hello World! in Walter Turncoat.", 12, 160);
}
<link href="http://fonts.googleapis.com/css?family=Walter+Turncoat&.css" rel="stylesheet"/>
<script src="http://cdn.jsdelivr.net/p5.js/0.3.8/p5.min.js"></script>
To load a font in p5.js you need a .ttf or .otf file, p5 doesn't work with .vlw files. So to use a font in p5 you need to:
Get a .ttf or .otf font file. This font file will be loaded on execution time to your app.
Declare a global variable to keep the font.
Load the font with loadFont in a preload function.
After the font is loaded you must use textFont() to tell p5 that this is the font to be used.
Print someting with text().
Here is an example:
var myFont, fontReady = false;
function fontRead(){
fontReady = true;
}
function preload() {
myFont = loadFont("./fonts/MyfontFile.ttf", fontRead);
}
function setup() {
createCanvas(720, 400);
doyourSetup();
}
function draw() {
background(255);
if (fontReady) {
textFont(myFont);
text("Hello World!", 10, 30);
}
}
You need to load the font in preload:
var font;
function preload() {
font = loadFont('thefont.ttf');
}
function setup() {
createCanvas(600, 400);
textFont(font);
}
function draw() {
background(255);
text('The Text', 280, 300);
}
According to the docs, if you have a font file that p5 recognizes (such as otf, ttf ect...), you can load that font file and than use it with the following 2 lines of code:
var myFont = loadFont('customfont.ttf');
textFont(myFont);
and then write with the font like this:
text('Stack overflow', 2,2);
var myfont;
function preload() {
font = loadFont('font.ttf)
}
function setup{
createCanvas(400, 400)
}
function draw{
textFont(myfont)
text("Hello", 200, 200)
}
There is no need for a ready function because newer versions of
p5.js will not display the project until it is finshed loading if it is in the preload function.

converting Div to Canvas options [duplicate]

It would be incredibly useful to be able to temporarily convert a regular element into a canvas. For example, say I have a styled div that I want to flip. I want to dynamically create a canvas, "render" the HTMLElement into the canvas, hide the original element and animate the canvas.
Can it be done?
There is a library that try to do what you say.
See this examples and get the code
http://hertzen.com/experiments/jsfeedback/
http://html2canvas.hertzen.com/
Reads the DOM, from the html and render it to a canvas, fail on some, but in general works.
Take a look at this tutorial on MDN: https://developer.mozilla.org/en/HTML/Canvas/Drawing_DOM_objects_into_a_canvas (archived)
Its key trick was:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like ' +
'<span style="color:white; text-shadow:0 0 2px blue;">' +
'cheese</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
}
img.src = url;
That is, it used a temporary SVG image to include the HTML content as a "foreign element", then renders said SVG image into a canvas element. There are significant restrictions on what you can include in an SVG image in this way, however. (See the "Security" section for details — basically it's a lot more limited than an iframe or AJAX due to privacy and cross-domain concerns.)
Sorry, the browser won't render HTML into a canvas.
It would be a potential security risk if you could, as HTML can include content (in particular images and iframes) from third-party sites. If canvas could turn HTML content into an image and then you read the image data, you could potentially extract privileged content from other sites.
To get a canvas from HTML, you'd have to basically write your own HTML renderer from scratch using drawImage and fillText, which is a potentially huge task. There's one such attempt here but it's a bit dodgy and a long way from complete. (It even attempts to parse the HTML/CSS from scratch, which I think is crazy! It'd be easier to start from a real DOM node with styles applied, and read the styling using getComputedStyle and relative positions of parts of it using offsetTop et al.)
You can use dom-to-image library (I'm the maintainer).
Here's how you could approach your problem:
var parent = document.getElementById('my-node-parent');
var node = document.getElementById('my-node');
var canvas = document.createElement('canvas');
canvas.width = node.scrollWidth;
canvas.height = node.scrollHeight;
domtoimage.toPng(node).then(function (pngDataUrl) {
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
context.translate(canvas.width, 0);
context.scale(-1, 1);
context.drawImage(img, 0, 0);
parent.removeChild(node);
parent.appendChild(canvas);
};
img.src = pngDataUrl;
});
And here is jsfiddle
Building on top of the Mozdev post that natevw references I've started a small project to render HTML to canvas in Firefox, Chrome & Safari. So for example you can simply do:
rasterizeHTML.drawHTML('<span class="color: green">This is HTML</span>'
+ '<img src="local_img.png"/>', canvas);
Source code and a more extensive example is here.
No such thing, sorry.
Though the spec states:
A future version of the 2D context API may provide a way to render fragments of documents, rendered using CSS, straight to the canvas.
Which may be as close as you'll get.
A lot of people want a ctx.drawArbitraryHTML/Element kind of deal but there's nothing built in like that.
The only exception is Mozilla's exclusive drawWindow, which draws a snapshot of the contents of a DOM window into the canvas. This feature is only available for code running with Chrome ("local only") privileges. It is not allowed in normal HTML pages. So you can use it for writing FireFox extensions like this one does but that's it.
You could spare yourself the transformations, you could use CSS3 Transitions to flip <div>'s and <ol>'s and any HTML tag you want. Here are some demos with source code explain to see and learn: http://www.webdesignerwall.com/trends/47-amazing-css3-animation-demos/
the next code can be used in 2 modes, mode 1 save the html code to a image, mode 2 save the html code to a canvas.
this code work with the library: https://github.com/tsayen/dom-to-image
*the "id_div" is the id of the element html that you want to transform.
**the "canvas_out" is the id of the div that will contain the canvas
so try this code.
:
function Guardardiv(id_div){
var mode = 2 // default 1 (save to image), mode 2 = save to canvas
console.log("Process start");
var node = document.getElementById(id_div);
// get the div that will contain the canvas
var canvas_out = document.getElementById('canvas_out');
var canvas = document.createElement('canvas');
canvas.width = node.scrollWidth;
canvas.height = node.scrollHeight;
domtoimage.toPng(node).then(function (pngDataUrl) {
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0);
};
if (mode == 1){ // save to image
downloadURI(pngDataUrl, "salida.png");
}else if (mode == 2){ // save to canvas
img.src = pngDataUrl;
canvas_out.appendChild(img);
}
console.log("Process finish");
});
}
so, if you want to save to image just add this function:
function downloadURI(uri, name) {
var link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
}
Example of use:
<html>
<head>
</script src="/dom-to-image.js"></script>
</head>
<body>
<div id="container">
All content that want to transform
</div>
<button onclick="Guardardiv('container');">Convert<button>
<!-- if use mode 2 -->
<div id="canvas_out"></div>
</html>
Comment if that work.
Comenten si les sirvio :)
The easiest solution to animate the DOM elements is using CSS transitions/animations but I think you already know that and you try to use canvas to do stuff CSS doesn't let you to do. What about CSS custom filters? you can transform your elements in any imaginable way if you know how to write shaders. Some other link and don't forget to check the CSS filter lab.
Note: As you can probably imagine browser support is bad.
function convert() {
dom = document.getElementById('divname');
var script,
$this = this,
options = this.options,
runH2c = function(){
try {
var canvas = window.html2canvas([ document.getElementById('divname') ], {
onrendered: function( canvas ) {
window.open(canvas.toDataURL());
}
});
} catch( e ) {
$this.h2cDone = true;
log("Error in html2canvas: " + e.message);
}
};
if ( window.html2canvas === undefined && script === undefined ) {
} else {.
// html2canvas already loaded, just run it then
runH2c();
}
}

Categories

Resources