How to destroy / reload the canvas in html 5? - javascript

I am working on an ebook application, I draw each page on canvas using PDF.js , the problem is , when I click on the button and turn to other page, I tried simply render on the same canvas again , but the canvas seems move to a wrong location or wrong size .
function renderPage(url) {
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
//clearCanvasGrid('canvas');
PDFJS.getDocument(url).then(function (pdf) {
// Using promise to fetch the page
pdf.getPage(1).then(function(page) {
var viewport = page.getViewport(5); //scale 5
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
page.render(renderContext).then(function() {
initialZoomPage(viewport.height,viewport.width);
});
});
});
}
So, are there any necessary step I need to do before redraw the page? Also , how can I destroy it if I would like to close the page? Thanks
Update:
function clearCanvasGrid(canvasID){
canvas = document.getElementById(canvasID); //because we are looping //each location has its own canvas ID
context = canvas.getContext('2d');
//context.beginPath();
// Store the current transformation matrix
context.save();
// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
context.restore(); //CLEARS THE SPECIFIC CANVAS COMPLETELY FOR NEW DRAWING
}
I found a function to clear the canvas but it has .save , .setTransform and .restore besides clearRect, are they necessary? thanks

One way is to clear out the canvas using context.clearRect(0,0, width, height) (Reference).
Alternatively, you can append a new canvas element (and possibly remove the old one, depending on whether you will want to display it again) each time you want a new page. Something like this should do it:
var oldcanv = document.getElementById('canvas');
document.removeChild(oldcanv)
var canv = document.createElement('canvas');
canv.id = 'canvas';
document.body.appendChild(canv);
Just note that if you plan to keep more than one, each one must have a unique id instead of just id="canvas" (perhaps based on the page number - something like canvas-1).
Answer to updated question:
The save, setTransform, and restore are only necessary if you are doing (or somehow allowing users to do) transformation. I don't know if the PDF.js library does any transformation behind the scenes, so it may be best to leave it there.

try using clearRect(), like:
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);

In Lightning Aura components, a different approach is required:
The chart object, once created, is stored in an attribute.
<aura:attribute name="chartObject" type="Object"/>
<canvas id="chartId" aura:id="chartCanvas" </canvas>
let theChart = component.get("v.chartObject");
if (theChart != undefined) {
//alternative
//let chartStatus = Chart.getChart("chartId");
//if (chartStatus != undefined) {
//update data if necessary
//theChart.data.datasets[0].data[i] = etc
theChart.update();
}
else{
var ctx = component.find("chartCanvas").getElement();
var chart = new Chart(ctx, {
type: 'line',
data: myData,
options: {
tension: 0.25,
responsive: false,
maintainAspectRatio :false,
fill: true,
}
});
component.set("v.chartObject", chart);
}//endif chart exists

Related

How to capture what is visible through a div?

Suppose I have an image and a div whose position is absolute and is above that image (z-index of div more than z-index of image).Something like this :
I want to take screenshot of what is visible through the div using JavaScript. At first I thought of changing the div to a canvas and then I wrote this code:
<div class="utility-btn">
<button class="enquiry-btn" onclick="openEnquiry()">?</button>
</div>
<div id="enquiry">
<button id="close" onclick="closeEnquiry()">X</button>
<div class="cover">
<canvas id="capture"></canvas>
<button class="btn" onclick="takeScreenshot()">
Click to enquiry
</button>
</div>
</div>
Function to take screenshot:
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
var backCanvas = document.createElement('canvas');
backCanvas.width = canvas.width;
backCanvas.height = canvas.height;
var backCtx = backCanvas.getContext('2d');
backCtx.drawImage(canvas, 0, 0);
ctx.drawImage(backCanvas, 0, 0);
var dataURL = backCanvas.toDataURL();
console.log(dataURL);
}
But the image of dataURL was not what I expected it was just a blank image:
How can I implement this feature. How can I do it without using any external library?
There are two problems:
#1
If we look at your bit of code responsible for actually taking the screenshot
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
var backCanvas = document.createElement('canvas');
backCanvas.width = canvas.width;
backCanvas.height = canvas.height;
var backCtx = backCanvas.getContext('2d');
backCtx.drawImage(canvas, 0, 0);
ctx.drawImage(backCanvas, 0, 0);
var dataURL = backCanvas.toDataURL();
console.log(dataURL);
}
we can see that canvas is the element you want to have the screenshot taken onto. Later on you're creating an empty new canvas backCanvas and make it the size of the first canvas. Afterwards you're drawing the empty first canvas on the second and finally the empty second canvas back to the first.
So that does not make too much sense.
Instead you need to take the actual canvas generated by threeJS. These lines of your code append a <canvas> element to the container <div>, threeJS is using:
const container = document.getElementById('container');
container.appendChild(renderer.domElement);
We can reference it using:
document.getElementById("container").children[0]
#2
As threeJS uses WebGL to draw stuff, it's rendering context is not the regular and for performance reasons the browser is clearing the drawing buffer after something has been drawn onto - so your screenshot would come up empty all the time. There is an option you need to pass to the THREE.WebGLRenderer constructor to keep the drawingbuffer called preserveDrawingBuffer.
So change this line:
renderer = new THREE.WebGLRenderer();
to this
renderer = new THREE.WebGLRenderer({preserveDrawingBuffer: true});
and your screenshot function to this:
function takeScreenshot() {
var canvas = document.getElementById('capture');
ctx = canvas.getContext('2d');
ctx.drawImage(document.getElementById("container").children[0], 0, 0);
var dataURL = canvas.toDataURL();
console.log(dataURL);
}

Cannot export Canvas to PDF

I'm trying to print a canvas in a PDF file that I'm creating. The issue is that I always get a black rectangle in my PDF instead of the graph that I can see on my web page.
I generate the canvas like this:
<div class="col-md-6 featurePanel" style="display: none;">
<div id="canvasContainer_#featureID"></div>
</div>
It's filled by JS:
function SetFeatureValues(measID, measIndex, measCount, featureID, canvasID, name, humL, humR, femL, femR, thor, lumb, reference, type, color, ResTable) {
if (ResTable.length == measCount) {
var element = document.createElement("canvas");
element.id = canvasID;
element.classList.add("ThreeDDogGraph");
document.getElementById('canvasContainer_' + featureID).appendChild(element);
_3ddogvis.scene(0.0, true, ResTable, canvasID);
}
}
Then I export it to a PDF file:
function RenderFeature(pdf, currentTopPosition, currentLeftPasition, canvas) {
var newCanvas = document.createElement('canvas');
let context = newCanvas.getContext('2d');
newCanvas.width = width;
newCanvas.height = height;
context.drawImage(document.querySelector('#' + canvas.id), 0, 0, width, height);
var newCanvasImg = newCanvas.toDataURL("image/png");
pdf.addImage(newCanvasImg, 'png', currentLeftPasition, currentTopPosition, graphWidth, graphHeight, undefined, 'FAST');
}
After downloading the file I get a black rectangle in my PDF with the dimensions of my canvas.
EDIT:
If I try to copy the canvas (as propose obscure) I also get a black picture and the error message:
Unable to clone WebGL context as it has preserveDrawingBuffer=false
As I use a library done by someone else, I cannot set the preserveDrawingBuffer to true.
Is there a solution to get a picture from a canvas with preserveDrawingBuffer set to false?

How to wait for the multiple images to load in the canvas and convert to base64?

I'm trying to get a base64 version of a canvas in HTML5.
Currently, the base64 image that I get from the canvas is blank.
I have found similar questions for example this one:
HTML Canvas image to Base64 problem
However, the issue that I have is that because I am adding 2 images in the canvas, I cannot use the example provided in those answers as most of them are using single image.
I understand that I have to wait until the canvas image is loaded properly before trying to get the base64 image. But I don't know how in my case.
This is my code:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
var imageObj1 = new Image();
imageObj1.src = "http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg";
imageObj1.onload = function() {
context.drawImage(imageObj1, 0, 180, canvas.width, canvas.height);
};
var imageObj2 = new Image();
imageObj2.src = "http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"
imageObj2.onload = function() {
context.drawImage(imageObj2, 0, 0, canvas.width, 180);
};
// get png data url
//var pngUrl = canvas.toDataURL();
var pngUrl = canvas.toDataURL('image/png');
// get jpeg data url
var jpegUrl = canvas.toDataURL('image/jpeg');
$('#base').val(pngUrl);
<div class="contents" style="overflow-x:hidden; overflow-y:scroll;">
<div style="width:100%; height:90%;">
<canvas id="myCanvas" class="snap" style="width:100%; height:100%;" onclick="takephoto()"></canvas>
</div>
</div>
<p>
This is the base64 image
</p>
<textarea id="base">
</textarea>
and this is a working FIDDLE:
https://jsfiddle.net/3p3e6Ldu/1/
Can someone please advice on this issue?
Thanks in advance.
EDIT:
As suggested in the comments bellow, i tried to use a counter and when the counter reaches a specific number, I convert the canvas to base64.
Like so:https://jsfiddle.net/3p3e6Ldu/4/
In both your examples (the one from the question and the one from the comments), the order of commands does not really respect the async nature of the task at hand.
In your later example, you should put the if( count == 2 ) block inside the onload callbacks to make it work.
However, even then you will run into the next problem: You are loading the images from different domains. You can still draw them (either into the canvas or using an <img> tag), but you are not able to access their contents directly. Not even with the detour of using the <canvas> element.
I changed to code so it would work, if the images are hosted on the same domain. I also used a function to load the image and promises to handle the callbacks. The direct way of using callbacks and a counting variable, seem error-prone to me. If you check out the respective fiddle, you will notice the SecurityError shown. This is the result of the aforementioned problem with the Same-Origin-Policy I mentioned.
A previous question of mine in a similar direction was about how to detect, if I can still read the contents of a <canvas> after adding some images.
const canvas = document.getElementById('myCanvas');
const context = canvas.getContext('2d');
context.canvas.width = 1000;
context.canvas.height = 1000;
// function to retrieve an image
function loadImage(url) {
return new Promise((fulfill, reject) => {
let imageObj = new Image();
imageObj.onload = () => fulfill(imageObj);
imageObj.src = url;
});
}
// get images
Promise.all([
loadImage("http://www.offthegridnews.com/wp-content/uploads/2017/01/selfie-psuDOTedu.jpg"),
loadImage("http://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg"),
])
.then((images) => {
// draw images to canvas
context.drawImage(images[0], 0, 180, canvas.width, canvas.height);
context.drawImage(images[1], 0, 0, canvas.width, 180);
// export to png/jpg
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg');
// show in textarea
$('#base').val(pngUrl);
})
.catch( (e) => alert(e) );

not able to draw rect on existing <canvas> which rendered PDF using pdfjs

Task
Have a canvas in your page
<canvas id="pdfCanvas">
create a fabric canvas for existing canvas
new fabric.Canvas("pdfCanvas");
have mouse.down, mouse.up, mouse.move methods to enable drawing rectangles
render pdf in the above canvas "pdfCanvas" using PDF.js
browser now shows the rendered PDF
now draw rectangles on the pdf, it hides the rendered canvas but it does draw objects
Problem
Here is the fiddle to see the issue:
- run the above https://jsfiddle.net/hiitskiran/wgz8qore/2/
- you can see the fabric rectangle is hiding behind the rendered pdf
- click on the pdf canvas area to see the fabric objects
What I see from your fiddle is that you don't wait the result of page.render method, which is asyncronous, then, unfortunately, if you wait the page.render, the fabric.canvas instance will clean your previously filled canvas, and we can't do nothing for this.
So I tried another approach: First of all, I wait the pdf.js page.render method, then I invoke the .toDataURL of your previously filled canvas so that I can have a copy of the old content, then I set your previously filled canvas as background of your new fabric.canvas and finish to draw your rectangle.
var url = 'http://www.africau.edu/images/default/sample.pdf';
var canvas = document.getElementById('pdfcanvas');
var context = canvas.getContext('2d');
PDFJS.disableWorker = true;
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(1).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({
canvasContext: context,
viewport: viewport
}).then(function() {
var bg = canvas.toDataURL("image/png");
var fcanvas = new fabric.Canvas("pdfcanvas", {
selection: false
});
fcanvas.setBackgroundImage(bg,fcanvas.renderAll.bind(fcanvas));
fcanvas.setWidth(300);
fcanvas.setHeight(300);
var rect = new fabric.Rect({
left: 100,
top: 100,
width: 50,
height: 50,
fill: '#FF454F',
opacity: 0.5,
transparentCorners: true,
borderColor: "gray",
cornerColor: "gray",
cornerSize: 5
});
fcanvas.add(rect);
fcanvas.renderAll();
});
});
});
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://seikichi.github.io/tmp/PDFJS.0.8.715/pdf.min.js"></script>
<canvas id="pdfcanvas" style="border:1px solid black"></canvas>

swap graphs on same canvas

Colleagues,
I have designed a dashboard with HTML canvas and want to show on same page a flot graph, toggling between them with a button.
The problem that I am facing is that when I click to get the flot, the graph shows up under the canvas, and not in the same place (as I want).
This is the relevant code :
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas" width="800" height="400" style="background-color:#ffffff"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var radio = canvas.height / 2;
var ent_direc={{ent_direc}};
var ent_vel={{ent_vel}};
ctx.translate(radio,radio);
radio_direc = radio * 0.9;
......
function show_graph is called when I click the button
function show_graph(){
// create dynamic DIV to hold graph
var div = document.createElement('div');
div.id = "placeholder";
document.body.appendChild(div) document.getElementById("placeholder").setAttribute("style","width:600px;height:300px");
ctx.save();
// Use the identity matrix while clearing the canvas
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Restore the transform
ctx.restore();
data_g =[ [0,2],[1,4],[2,5]];
$(document).ready(function(){
$.plot($("#placeholder"), [
{
data: data_g ,
bars:
{
show: true
}
}
]);
});
Any idea on how to show the flot graph on the same canvas area?
What you try is not that simple. Flot takes a div element and creates its own canvas elements in this div (and some stuff around the canvas like axis labels).
The easiest solution would be to hide your original canvas when you create the Flot graph:
$('#canvas').hide();

Categories

Resources