Mulitple canvas-images (by Chart.js) with toDataURL() - javascript

How to handle two or more linked Canvas-Charts with Chart.js? With this script the canvas-image is linked with a bigger version to present the big file in a fancybox or just two download it (right-click -> save). Easy.
<a HREF="#" id="canvas_link_1">
<canvas id="image1"></canvas>
</a>
<a HREF="#" id="canvas_link_2">
<canvas id="image2"></canvas>
</a>
To use toDataURL() with Chart.js it's a bit tricky, cause it will render the whole chart not before the animation has ended. If we do ask for the URL too early, it will present an empty (transparent) image. That's why we need onAnimationComplete in the options:
var options = {
onAnimationComplete: done
}
Later in the script, it will fire/change the new HREF, when the animation is over.
var ct1 = document.getElementById("image1").getContext("2d");
ct1.canvas.width = document.getElementById("image1").offsetWidth;
ct1.canvas.height = document.getElementById("image1").offsetHeight;
var Chart1 = new Chart(ct1).Line(lineChartData1,options);
function done() {
var url1=document.getElementById("image1").toDataURL();
document.getElementById("canvas_link_1").href=url1;
}
var ct2 = document.getElementById("image2").getContext("2d");
ct2.canvas.width = document.getElementById("image2").offsetWidth;
ct2.canvas.height = document.getElementById("image2").offsetHeight;
var Chart2 = new Chart(ct2).Line(lineChartData2,options);
function done() {
var url2=document.getElementById("image2").toDataURL();
document.getElementById("canvas_link_2").href=url2;
}
That works. But only with one canvas-image. If I delete the 2nd function done() it will work with the 1st canvas, if I delete the 1st function, only the 2nd canvas presents the url.
I think the problem is with "done", it's not a name, just like a situation, or? The "var options" is are general for all canvas-images (for sure I can change to options1 and options2 and also to "Line(lineChartData1,options1)" but without any change)... so "done" will fire all functions and - that's bad - appearently only the last function.
On the other side I cannot rename the entry of onAnimationComplete. It's null or done, nothing else. What is to do?

You can have different callbacks with different names. "done" is just an example name (a better name would probably be "completed").
For example - first create two option objects, one for each chart:
var options1 = {
onAnimationComplete: done1
};
var options2 = {
onAnimationComplete: done2
};
Then initialize the charts with those:
...
var Chart1 = new Chart(ct1).Line(lineChartData1, options1);
...
var Chart2 = new Chart(ct2).Line(lineChartData2, options2);
And finally define the callbacks:
function done1() {
var url = document.getElementById("image1").toDataURL();
document.getElementById("canvas_link_1").href = url;
}
function done2() {
var url = document.getElementById("image2").toDataURL();
document.getElementById("canvas_link_2").href = url;
}
Hope this helps (and that I understood the problem correctly) !

Related

JavaScript last element of HTML collection not defined

I am trying to create a bookable product, where upon selected (= selectbox) a room type, the picture changes to that specific room with good old javascript.
the interesting part is that it works for the first element of the HTML collection, but the last element is giving an undefined and makes it impossible to override.
I am not getting why that is. I tried via the console log to view what I am missing, but I see nothing problematic.
HTML collection:
0: <a href="https://staging.deloftli…09/Steck-coachruimte.jpg" hidefocus="true" style="outline: currentcolor none medium;">​
1: <img class="zoomImg" role="presentation" alt="" src="https://staging.deloftli…09/Steck-coachruimte.jpg" style="position: absolute; top:…none; max-height: none;">
I have the following script:
<script id="bookingschanges">
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if(activities.value == "1949")
{
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = ""
zoompic.scr = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
});</script>
The first element (image) is correct, the second element (zoompic) gives undefined.
To see it live, go to https://staging.deloftlisse.nl/product/vergaderruimte-huren/ and check the console log.
What am I missing here?
Variable zoompic is not defined at the time the variable is declared (its called before the element is created on loading, debug the page and refresh it to see) you will need to use an onload event listener.
https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
As someone else has suggested it would be better to call the image change function in the original javascript to change the image that is selected and you will avoid any issues. This might not be easy though if it is an external library.
EDIT: Added an example of onLoad
window.addEventListener('load', (event) => {
var activities = document.getElementById("wc_bookings_field_resource");
var image = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[0].firstChild;
var zoompic = document.getElementsByClassName("woocommerce-product-gallery__image")[0].children[1];
activities.addEventListener("click", function() {
var options = activities.querySelectorAll("option");
});
activities.addEventListener("change", function() {
if (activities.value == "1949") {
image.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
image.srcset = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg 768w";
zoompic.src = "https://staging.deloftlisse.nl/wp-content/uploads/2021/09/Podkas.jpeg";
}
console.log(image);
console.log(zoompic);
})
});

Get the Trim Area of a PDF with PDF.js?

I'm playing about with PDF.js, and can't seem to find a solution to my problem.
I have a PDF with a trim area and bleed, I need to get the trim area so that I can crop the PDF image data in HTML canvas.
I see that Acrobat has javascript that can return the Trim based on getPageBox("Trim"). Is there any equivalent in PDF.js?
I cant seem to find a reference when inspecting the Javascript PDF Object in the console.
I was able to get it after editing pdf.worker.js. Tested with 1.7.225. First, Add get trimBox() after get cropBox() like this:
get trimBox() {
var trimBox = this.getInheritedPageProp('TrimBox', true);
if (!isArray(trimBox) || trimBox.length !== 4) {
return shadow(this, 'trimBox', this.mediaBox);
}
return shadow(this, 'trimBox', trimBox);
},
Now, in handler.on('GetPage', function ... of WorkerMessageHandler, add a few lines like this:
handler.on('GetPage', function wphSetupGetPage(data) {
return pdfManager.getPage(data.pageIndex).then(function (page) {
var rotatePromise = pdfManager.ensure(page, 'rotate');
var refPromise = pdfManager.ensure(page, 'ref');
var userUnitPromise = pdfManager.ensure(page, 'userUnit');
var viewPromise = pdfManager.ensure(page, 'view');
var trimBoxPromise = pdfManager.ensure(page, 'trimBox'); //added
return Promise.all([
rotatePromise,
refPromise,
userUnitPromise,
viewPromise,
trimBoxPromise //added
]).then(function (results) {
return {
rotate: results[0],
ref: results[1],
userUnit: results[2],
view: results[3],
trimBox: results[4] //added
That's it. Now you can get the trim box in your app by page.pageInfo.trimBox.
In addition to the excellent answer from #Sangbok Lee,
If you use the latest PDF.js version, the this.getInheritedPageProp function has changed to
this._getInheritableProperty('TrimBox', true)
Figured it out.
For anyone else who may be interested in pdf.worker.js on line 2654 I added the following that exposed the trimBox.
tboxX = this.pageDict.map.TrimBox[0];
tboxY = this.pageDict.map.TrimBox[1];
tboxW = this.pageDict.map.TrimBox[2];
tboxH = this.pageDict.map.TrimBox[3];
I'm sure there is a neater way, but it works.

Dynamically add pictures using javascript.

I've been working on a flowchart type program that changes various parts of the html each time a new question is posed (this question is represented by the 'state' variable below), things like Title, description and images.
Currently, my HTML for the images is:
<a id="imageLink" target="_blank" href=""><img class= 'right' id= 'imageBox' style ='max-height:50%;' src='' /></a>
<h3 id ='imageBoxText' ></h3>
and the image link and the legend for the image are found in imageArray which looks something like this:
imageArray = {'questionOne': ['www.link to image.com,'Legend for image']}
In this case 'questionOne' would be 'state'.
So far, I can load a single image asynchronously using:
document.getElementById("imageBox").src = "http://www.ktcagency.com/img/loader.gif"; // load spinner
var img = new Image(); // load image asynchronously
var newsrc = imageArray[state][0];
img.onload = function () { // onload handler
document.getElementById("imageBox").src = newsrc;
};
img.src = newsrc;
I also add the description and link to the image while I'm at it:
document.getElementById("imageLink").setAttribute('href',imageArray[state][0])
document.getElementById('imageBoxText').innerHTML = imageArray[state][1];
OK, so now what I need to do is write a function that can do this for multiple images, side by side, reading from a modified imageArray that looks like:
imageArray = {'questionOne': [['www.linktoimage1.com,'Legend for image1'],['www.linktoimage2.com,'Legend for image2']]}
The code should be able to handle up to three images, but I certainly wouldn't mind if it could do any more.
I tried to do it using a table to represent the images and used a for loop with the code from earlier. The image would never load, stuck on the spinner for ever and the images would all bunch up on one side of the screen.
Any help appreciated, thanks very much.
Well, I think I see what you're trying to do.
It sounds like you're trying to make a slideshow of some kind.
This is an example of how I would have done it.
Here's the jsfiddle so you can see what's going on. http://jsfiddle.net/VodkaTonic/4KWLr/
(function () {
var imageArray = {
link: [['http://i.imgur.com/7OfqRbF.jpg', 'http://i.imgur.com/AHbc6zO.jpg','http://i.imgur.com/gW144OQ.jpg'],['http://i.imgur.com/v0V8hrB.jpg', 'http://i.imgur.com/9l40vIT.png','http://i.imgur.com/ZXYUttz.png']],
label: [['Legend for image1','Legend for image2', 'Legend for image3'],['Legend for image4','Legend for image5', 'Legend for image6']]
},
doc = document,
question = 0,
img = doc.getElementsByClassName('images'),
links = doc.getElementsByClassName('links'),
label = doc.getElementsByClassName('label'),
button = doc.getElementById('button');
function add (state) {
var arr = imageArray.link[state],
arr2 = imageArray.label[state];
arr.forEach(function(value,index,array) {
img[index]['src'] = value;
links[index]['href'] = value;
});
arr2.forEach(function(value,index,array) {
label[index]['innerHTML'] = value;
});
question++;
}
window.addEventListener('load', add(question), false);
button.addEventListener('click', function () {
add(question);
}, false)
}());

Need help related to EXT JS tooltip functionality

We are using EXTJS 3.1.0
There is a functionality where we have to add tooltip to an image, using code:
function loadToolTip(objImageId,entity_id,entity_type){
var objImage = document.getElementById(objImageId);
var tp = new Ext.ToolTip({
target: objImage,
autoLoad: {url: ‘some URL’},
showDelay:500
});
var extObjImage = Ext.get(objImageId);
tp.showAt([extObjImage.getX()+25,extObjImage.getY()+25]);
if(tp.anchorEl){
tp.anchorEl.hide();
}
}
This function is called on the mouseover event of a image.
It is possible that the user does mouseover on the image after changing some data and every time we have to get the tool-tip contents from the server.
The problem we are facing is, it creates multiple tool tips every time we do an mouse over.
Ideally we would want it to be displayed only once. We tried giving id to the tool tip object and then calling .destroy() method on that object when mouseout event occurs. This also does not work and at times gives JS error.
This or something similar should work for you:
function loadToolTip(objImageId,entity_id,entity_type){
var extObjImage = Ext.get(objImageId);
var tp = objImage.tp;
if(!tp)
{
var objImage = document.getElementById(objImageId);
tp = new Ext.ToolTip({
target: objImage,
autoLoad: {url: ‘some URL’},
showDelay:500
});
extObjImage.tp = tp;
}
tp.showAt([extObjImage.getX()+25,extObjImage.getY()+25]);
if(tp.anchorEl){
tp.anchorEl.hide();
}
}

Change the code of a javascript function

Actually my idea is the following: i have a div tag that displays the result of a function called "afficher_general()" inside a script tag, and a button that calls a first function called "Ajouter_general()" that's supposed to change a string inside the function "afficher_general()"
var general = document.getElementById("general");
general.innerHTML = general.innerHTML.replace('//WRITE', ch);
the button is as follows:
<input type="button" value="ajouter au graphe general" id="id1" onClick="Ajouter_general();afficher_general();" />
But this doesn't seem to work because i've been told that a script is loaded only once and it cannot be changed. So is there any solution ? Thanks in advance.
So i'll add what contain the functions. Actually i'm working with Amcharts . the function afficher_generalis as follows:
<script name="general" id="general" type="text/javascript">
function afficher_general()
{
chart = new AmCharts.AmSerialChart();
chart.dataProvider = chartData;
chart.categoryField = "country";
chart.startDuration = 1;
// WRITE
chart.write("chartContainer");
}
</script>
instead of the comment //WRITE i wish to add the a code everytime the button is pressed. the function is as follows:
function Ajouter_general(ind){
var ch="";
var couleur = getSelectValueId("background-color"+ind);
var transparence = getSelectValueId("transparence"+ind);
var type = get_radio_value_type(ind);
var balloon = getSelectValueId("balloon-color"+ind);
if(type == "column")
{
ch=ch+"var graph"+ind+" = new AmCharts.AmGraph();";
ch=ch+"graph"+ind+".valueField = "+"visits"+";"
ch=ch+"graph"+ind+".balloonColor = "+balloon+";";
ch=ch+"graph"+ind+".type = "+type+";";
ch=ch+"graph"+ind+".fillAlphas = "+transparence+";";
ch=ch+"graph"+ind+".fillColors = ["+couleur+"];";
ch=ch+"graph"+ind+".lineColor = ["+couleur+"];";
ch=ch+"chart.addGraph(graph"+ind+");";
ch=ch+"// WRITE";
}
var general = document.getElementById("general");
general.innerHTML = general.innerHTML.replace('//WRITE', ch);
}
since Amcharts uses the script to draw graphs inside a chart, i would like to be able to dynamically add those graphs everythime the button is pressed.
If you really need to change the definition of a function, you could always just redefine it (functions are just objects assigned to variables):
var Ajouter_general = function () {
// redefine the afficher_general function
afficher_general = function () {
// change it to whatever you want
};
};
That being said, this is generally a bad practice and the same functionality can be better accomplished by using function parameters.

Categories