I would like to be able to create a div and give it a height, width, and class. Then add the div to a Cesium map as a billboard.
I can create billboards with images and labels and also found this link about how to use an svg, but I am having trouble figuring out how to make the billboard contain dynamically produced html. The project this is in uses class names to apply font icons to divs.
Is there a way to insert the html into a billboard? Or is there another class that would be better suited for this? I am new to Cesium and am open to suggestions.
Billboards are intended to display rasterized data such as images. If you want to render html to one you must create an canvas element, draw the dom elements to it, then pass that canvas to the billboard as it's image property.
If you actually only need to have an icon/image visible and then display some HTML when the user clicks on it, than you should use Entity API to create your Billboard. You are provided with additional properties such as 'description' when you use the Entity API. The description can be a static HTML string or a Callback Property which can be updated as often as necessary. Descriptions are shown when the user picks the entity, usually via mouse click, but can be done programmatically via the viewer.trackedEntity property.
You can run this in Sandcastle or Codepen
var viewer = new Cesium.Viewer('cesiumContainer');
var canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var svgString = '<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; color: #FF0">' +
'<em>I</em> like' +
'<span style="color:white; text-shadow:0 0 2px blue;">' +
'Cupcakes</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var image = new Image();
image.src = 'data:image/svg+xml;base64,' + window.btoa(svgString);
//Need to wait for image to load before proceeding to draw
image.onload = function() {
canvas.getContext('2d').drawImage(image, 0, 0);
viewer.entities.add({
id: 'Cupcake SVG',
position: Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
billboard: {
image: canvas
},
description: '<p>This is a cupcake that can be modified.</p>'
});
};
Related
I have used chart.js to generate a report page that has multiple charts. I need to export this report to PDF. There are many solutions available via search, but I cannot find one which has multiple canvas elements.
The only available solution seems to be to loop through all the images, and recreate the report using the images, and then download that as a pdf.
Is there any simpler/more efficient way to accomplish this?
<body>
<h1> Chart 1 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_1" width="50" height="50"></canvas>
</div>
<h1> Chart 2 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_2" width="50" height="50"></canvas>
</div>
<h1> Chart 3 </h1>
<div style="width:800px; height:400px;">
<canvas id="chart_3" width="50" height="50"></canvas>
</div>
</body>
Honestly, it seems like the easiest approach would be to just provide a "download to PDF" link that pops up the browser's print page and instruct to user to select "print as pdf".
If that approach doesn't work for you (or your users), then here is a rough way to do it.
Basically, we create a new canvas element that is the size of your report page and incrementally paint the pixels from your existing chart.js canvas charts into the new canvas. Once that is done, then you can use jsPDF to add the new canvas to a pdf document as an image and download the file.
Here is an example implementation that does just that.
$('#downloadPdf').click(function(event) {
// get size of report page
var reportPageHeight = $('#reportPage').innerHeight();
var reportPageWidth = $('#reportPage').innerWidth();
// create a new canvas object that we will populate with all other canvas objects
var pdfCanvas = $('<canvas />').attr({
id: "canvaspdf",
width: reportPageWidth,
height: reportPageHeight
});
// keep track canvas position
var pdfctx = $(pdfCanvas)[0].getContext('2d');
var pdfctxX = 0;
var pdfctxY = 0;
var buffer = 100;
// for each chart.js chart
$("canvas").each(function(index) {
// get the chart height/width
var canvasHeight = $(this).innerHeight();
var canvasWidth = $(this).innerWidth();
// draw the chart into the new canvas
pdfctx.drawImage($(this)[0], pdfctxX, pdfctxY, canvasWidth, canvasHeight);
pdfctxX += canvasWidth + buffer;
// our report page is in a grid pattern so replicate that in the new canvas
if (index % 2 === 1) {
pdfctxX = 0;
pdfctxY += canvasHeight + buffer;
}
});
// create new pdf and add our new canvas as an image
var pdf = new jsPDF('l', 'pt', [reportPageWidth, reportPageHeight]);
pdf.addImage($(pdfCanvas)[0], 'PNG', 0, 0);
// download the pdf
pdf.save('filename.pdf');
});
You can see it in action at this codepen.
Now let's talk about some gotchas with this approach. First, you have to control the position of each chart.js canvas in the new canvas object. The only way to do that is to have an understanding of how your report page is structured and implement that same structure. In my example, my charts are in a 2x2 grid and the logic handles this accordingly. If you had a 3x2 grid or something different then you would have to change the positioning logic.
Lastly, the final pdf output file dimensions are much larger than the original chart page (from the web). I think the reason is because my chart "container" div stretches across the full page. Therefore, you probably want to use a different approach for setting the size of your new canvas.
So long story short, the above example is meant to demonstrate an approach and not be your final solution.
Good luck!
I have a working solution in vanilla javascript(although I used ts typing) and using the lib jsPdf, where you need a plot per pdf page:
let index = 1;
// create new pdf object
// if don't choose compress as true you will end up with a large pdf file
let pdf = new jsPDF({
orientation: 'landscape',
unit: 'px',
format: 'a4',
compress: true,
})
// search for the html element(s) you need
const canvas = document.querySelectorAll("canvas");
// here my size are in pixels since I configured that in the obj instance
let pageWidth = 400;
let pageHeight = 400;
let index = 1;
// traverse the array of canvas
canvas.forEach( (canva:HTMLCanvasElement) => {
// I added some options among others I added the type of the compression
// method: FAST
pdf.addImage(canva, 'PNG', 10, 10, pageWidth, pageHeight, `img${index}`, "FAST");
// so as to not end up with an extra pdf page at the end of the iteration
if (index < canvas.length) {
pdf.addPage();
}
index++;
});
// download the pdf
pdf.save('Reporte.pdf');
Some issues using Javascript library html2canvas. The problem is that html2canvas is getting "div2" with a transparent (sometimes white) background, I want to include the body background (or is behind this div) on "div2".
<div id="div2" style="width: 150px; height: 50px;"></div>
html2canvas(document.getElementById("div2"), {
onrendered: function(canvas) {
var photostring = canvas.toDataURL("image/png");
console.log(photostring);
}
});
Logically the rendering works from the element you select.
So will not get the background, unless you select the "parent element" or "root element".
I have two solutions:
1 - Using Javascript/Jquery you can capture X and Y position from div#target
then you set background-image and background-position in div#target by X/Y position
2 - You capture <body> with html2canvas, then using canvas/javascript api you crop image by X/Y position and Width/Height from div#target, see example: #242 (comment)
Note: set width/height/x/y in these variables (see example):
var targetDiv = $("#target");
var sourceX = targetDiv.position().left;/*X position from div#target*/
var sourceY = targetDiv.position().top;/*Y position from div#target*/
var sourceWidth = targetDiv.width();/*clientWidth/offsetWidth from div#target*/
var sourceHeight = targetDiv.height();/*clientHeight/offsetHeight from div#target*/
Get background-image in parent/root element:
https://github.com/niklasvh/html2canvas/commit/281e6bbedf9f611846eba3af4d256eb97f608aa2
Crop canvas:
https://github.com/niklasvh/html2canvas/issues/242#issuecomment-20875688
Using this JS Fiddle I am able to press a button to add new canvases to the screen...
var next = 4
function addCanvas() {
// create a new canvas element
var newCanvas = document.createElement("canvas");
newCanvas.id = "addedcanvases" + next; //added this to give each new canvas a unique id
next++;
newCanvas.width = canvasWidth;
newCanvas.height = canvasHeight;
// add a context for the new canvas to the contexts[] array
contexts.push(newCanvas.getContext("2d"));
// link the new canvas to its context in the contexts[] array
newCanvas.contextIndex = contexts.length;
// wire up the click handler
newCanvas.onclick = function (e) {
handleClick(e, this.contextIndex);
};
// wire up the mousemove handler
newCanvas.onmousemove = function (e) {
handleMousemove(e, this.contextIndex);
};
// add the new canvas to the page
document.body.appendChild(newCanvas);
}
The problem:
What is the best way to go about grouping / attaching a static image to the top of a canvas (as shown in the image below) so that whenever a new canvas is created in JS Fiddle an image is automatically created with it that is grouped / attached to the top of the new canvas.
This is so that where-ever a new canvas is dynamically created on the page an image is put above that canvas?
There may be an obvious way to do this that I am overlooking? but googling has not thrown up much as all 'image' and 'canvas' searches inevitably relate to actually adding an image to the canvas - which is not what I want to do in this instance.
Here's taking #KaliedaRik's answer and creating your groups using javascript:
http://jsfiddle.net/m1erickson/3EUnc/
The code to create a new group could be something like this:
function newGroup(){
// create a new wrapper div
var div=document.createElement("div");
div.className="wrapper";
// create an img and a canvas element
var img=document.createElement("img");
var br=document.createElement("br");
img.style.width="50px";
img.src="houseicon.png";
var canvas=document.createElement("canvas");
canvas.width=300;
canvas.height=55;
// add the img and canvas elements to the wrapper div
div.appendChild(img);
div.appendChild(br);
div.appendChild(canvas);
// add the wrapper div with its contained img + canvas to the page
document.body.appendChild(div);
}
One possible solution: when you create the canvas element, create a new div at the same time and put the canvas and img tags inside it. By making the div position style relative and the contained canvas and img position styles absolute, you'll be able to place the image wherever it needs to go.
<div style="position: relative">
<canvas style="position: absolute; top: 0px; left: 0px;"></canvas>
<img src="whatever" alt="whatever" style="position: absolute; top: -20px; left: 0px" />
</div>
I'm trying to save my HTML canvas to file which I can successfully do, but it's not saving any objects I've dragged into the canvas.
So, by using the Draggable JQuery I can happily move my object around screen and place it ontop of my canvas. When I save the canvas using the Canvas.ToDataURL() it does not save my dragged objects (and also does something strange to my canvas in the jsFiddle, it appears to change the colour of my canvas?).
To see a "working" example, please visit my jsFiddle http://jsfiddle.net/JVSFS/74/
Please simply drag the green box over the blue box and click the save button. The result will be shown underneath (just an orange box).
HTML
<canvas id="MyCanvas" class="canvas"></canvas>
<div class="popup_click">
<div id="popup_title">Drag</div>
</div>
<asp:HiddenField ID="hideMe" runat="server" />
<asp:Button runat="server" OnClick="ClickMe" Text="Click" OnClientClick="SaveMe()" />
<button onclick="SaveMe()">Try it</button>
<p>Results: </p>
<img id="myImage" />
JavaScript
$(document).ready(function () {
$('.popup_click').show(0).draggable();
});
function SaveMe() {
var canvas = document.getElementById("MyCanvas");
var context = canvas.getContext("2d");
context.fillStyle = "orange";
context.fillRect(0, 0, 100, 100);
var image = canvas.toDataURL("image/png");
document.getElementById("myImage").src = image;
document.getElementById("hideMe").value = image;
}
CSS
.popup_click {
background: #80FF80;
width: 50px; }
.canvas {
width: 100px;
height: 100px;
background-color: #0FC;
}
How can I get the dragged object to save? I assume I have to tell the Canvas that the object is part of it's context but no idea how and my own searches came up with nothing.
From https://developer.mozilla.org/en-US/docs/HTML/Canvas/Drawing_DOM_objects_into_a_canvas
You can't just draw HTML into a canvas. Instead, you need to use an SVG image containing the content you want to render. To draw HTML content, you'd use a element containing the HTML, then draw that SVG image into your canvas.
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 = self.URL || self.webkitURL || self;
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's because your draggable object isn't in the canves.
They are simple html elements.
It'll save only the objects whose created with canvas methods.
Any way to create html elements on canvas you have to use svg.
Mozilla show nice way to to this but you need to get all the css to inline css before.
mozilla explanation
Anyway with using svg on you canvas you won't be able to use toDataUrl because of security policy.
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();
}
}