Skip to content Skip to sidebar Skip to footer

Optimize Output Of Html2canvas

I created a chart with amchart. I will later have to export it as PDF. As suggested here, I need to convert the chart first into SVG with html2canvas. It works, but the chart looks

Solution 1:

I have tried a lot of solution, but the most i have found is to use a script for convert SVG (Thanks @Kaiido) and use html2canvas plugin.

I generated your chart and onclick event i assign ID to the SVG tag created.

After click on the button the SVG transform this to Canvas. Now you can just make what you want with this.

Please try bellow in full screen mode for show your label totally

$(document).ready(function(){
	var chart = AmCharts.makeChart("chartdiv1", {
	  "type": "serial",
	  "theme": "light",
	  "dataProvider": [{
		"country": "USA",
		"visits": 2025
	  }, {
		"country": "China",
		"visits": 1882
	  }, {
		"country": "Japan",
		"visits": 1809
	  }, {
		"country": "Germany",
		"visits": 1322
	  }, {
		"country": "UK",
		"visits": 1122
	  }, {
		"country": "France",
		"visits": 1114
	  }, {
		"country": "India",
		"visits": 984
	  }, {
		"country": "Spain",
		"visits": 711
	  }, {
		"country": "Netherlands",
		"visits": 665
	  }, {
		"country": "Russia",
		"visits": 580
	  }, {
		"country": "South Korea",
		"visits": 443
	  }, {
		"country": "Canada",
		"visits": 441
	  }, {
		"country": "Brazil",
		"visits": 395
	  }],
	  "valueAxes": [{
		"gridColor": "#FFFFFF",
		"gridAlpha": 0.2,
		"dashLength": 0
	  }],
	  "gridAboveGraphs": true,
	  "startDuration": 1,
	  "graphs": [{
		"balloonText": "[[category]]: <b>[[value]]</b>",
		"fillAlphas": 0.8,
		"lineAlpha": 0.2,
		"type": "column",
		"valueField": "visits"
	  }],
	  "chartCursor": {
		"categoryBalloonEnabled": false,
		"cursorAlpha": 0,
		"zoomable": false
	  },
	  "categoryField": "country",
	  "categoryAxis": {
		"gridPosition": "start",
		"gridAlpha": 0,
		"tickPosition": "start",
		"tickLength": 20
	  },
	  "export": {
		"enabled": true
	  }

	});

	$('#cmd').click(function() {
		$("svg").attr("id","svg") //Assign ID to SCG tag// without converting the svg to pnghtml2canvas(chartdiv1, {         // chartdiv1 is your divonrendered: function(can) {
			  //dirty.appendChild(can);
			}
		  });
		  
		// first convert your svg to pngexportInlineSVG(svg, function(data, canvas) {
		  svg.parentNode.replaceChild(canvas, svg);
		  // then call html2canvashtml2canvas(chartdiv1, {       // chartdiv1 is your divonrendered: function(can) {
			  can.id = 'canvas';
			 // clean.appendChild(can);
			}
		  });
		})


		functionexportInlineSVG(svg, receiver, params, quality) {
		  if (!svg || !svg.nodeName || svg.nodeName !== 'svg') {
			console.error('Wrong arguments : should be \n exportSVG(SVGElement, function([dataURL],[canvasElement]) || IMGElement || CanvasElement [, String_toDataURL_Params, Float_Params_quality])')
			return;
		  }

		  var xlinkNS = "http://www.w3.org/1999/xlink";
		  var clone;
		  // This will convert an external image to a dataURLvar toDataURL = function(image) {

			var img = newImage();
			// CORS workaround, this won't work in IE<11// If you are sure you don't need it, remove the next line and the double onerror handler// First try with crossorigin set, it should fire an error if not needed
			img.crossOrigin = 'Anonymous';

			img.onload = function() {
			  // we should now be able to draw it without tainting the canvasvar canvas = document.createElement('canvas');
			  var bbox = image.getBBox();
			  canvas.width = bbox.width;
			  canvas.height = bbox.height;
			  // draw the loaded image
			  canvas.getContext('2d').drawImage(this, 0, 0, bbox.width, bbox.height);
			  // set our original <image>'s href attribute to the dataURL of our canvas
			  image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
			  // that was the last oneif (++encoded === total) exportDoc()
			}

			// No CORS set in the response		
			img.onerror = function() {
				// save the srcvar oldSrc = this.src;
				// there is an other problemthis.onerror = function() {
					console.warn('failed to load an image at : ', this.src);
					if (--total === encoded && encoded > 0) exportDoc();
				  }
				  // remove the crossorigin attributethis.removeAttribute('crossorigin');
				// retrythis.src = '';
				this.src = oldSrc;
			  }
			  // load our external image into our img
			img.src = image.getAttributeNS(xlinkNS, 'href');
		  }

		  // The final function that will export our svgNode to our receivervar exportDoc = function() {
			  // check if our svgNode has width and height properties set to absolute values// otherwise, canvas won't be able to draw itvar bbox = svg.getBBox();
			  // avoid modifying the original one
			  clone = svg.cloneNode(true);
			  if (svg.width.baseVal.unitType !== 1) clone.setAttribute('width', bbox.width);
			  if (svg.height.baseVal.unitType !== 1) clone.setAttribute('height', bbox.height);

			  parseStyles();

			  // serialize our nodevar svgData = (newXMLSerializer()).serializeToString(clone);
			  // remember to encode special charsvar svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);

			  var svgImg = newImage();

			  svgImg.onload = function() {
				// if we set a canvas as receiver, then use it// otherwise create a new onevar canvas = (receiver && receiver.nodeName === 'CANVAS') ? receiver : document.createElement('canvas');
				// IE11 doesn't set a width on svg images...
				canvas.width = this.width || bbox.width;
				canvas.height = this.height || bbox.height;
				canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);

				// try to catch IEtry {
				  // if we set an <img> as receiverif (receiver.nodeName === 'IMG') {
					// make the img looks like the svg
					receiver.setAttribute('style', getSVGStyles(receiver));
					receiver.src = canvas.toDataURL(params, quality);
				  } else {
					// make the canvas looks like the canvas
					canvas.setAttribute('style', getSVGStyles(canvas));
					// a container elementif (receiver.appendChild && receiver !== canvas)
					  receiver.appendChild(canvas);
					// if we set a functionelseif (typeof receiver === 'function')
					  receiver(canvas.toDataURL(params, quality), canvas);
				  }
				} catch (ie) {
				  console.warn("Your ~browser~ has tainted the canvas.\n The canvas is returned");
				  if (receiver.nodeName === 'IMG') receiver.parentNode.replaceChild(canvas, receiver);
				  elsereceiver(null, canvas);
				}
			  }
			  svgImg.onerror = function(e) {
				if (svg._cleanedNS) {
				  console.error("Couldn't export svg, please check that the svgElement passed is a valid svg document.");
				  return;
				}
				// Some non-standard NameSpaces can cause this issues// This will remove them allfunctioncleanNS(el) {
				  var attr = el.attributes;
				  for (var i = 0; i < attr.length; i++) {
					if (attr[i].name.indexOf(':') > -1) el.removeAttribute(attr[i].name)
				  }
				}
				cleanNS(svg);
				for (var i = 0; i < svg.children.length; i++)
				  cleanNS(svg.children[i]);
				svg._cleanedNS = true;
				// retry the exportexportDoc();
			  }
			  svgImg.src = svgURL;
			}
			// ToDo : find a way to get only usefull rulesvar parseStyles = function() {
			var styleS = [],i;
			// transform the live StyleSheetList to an array to avoid endless loopfor (i = 0; i < document.styleSheets.length; i++)
			  styleS.push(document.styleSheets[i]);
			// Do we have a `<defs>` element already ?var defs = clone.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
			if (!defs.parentNode)
			  clone.insertBefore(defs, clone.firstElementChild);

			// iterate through all document's stylesheetsfor (i = 0; i < styleS.length; i++) {
			  var style = document.createElement('style');
			  var rules = styleS[i].cssRules,
				l = rules.length;
			  for (var j = 0; j < l; j++)
				style.innerHTML += rules[j].cssText + '\n';

			  defs.appendChild(style);
			}
			// small hack to avoid border and margins being applied inside the <img>var s = clone.style;
			s.border = s.padding = s.margin = 0;
			s.transform = 'initial';
		  }
		  var getSVGStyles = function(node) {
			var dest = node.cloneNode(true);
			svg.parentNode.insertBefore(dest, svg);
			var dest_comp = getComputedStyle(dest);
			var svg_comp = getComputedStyle(svg);
			var mods = "";
			for (var i = 0; i < svg_comp.length; i++) {
			  if (svg_comp[svg_comp[i]] !== dest_comp[svg_comp[i]])
				mods += svg_comp[i] + ':' + svg_comp[svg_comp[i]] + ';';
			}
			svg.parentNode.removeChild(dest);
			return mods;
		  }

		  var images = svg.querySelectorAll('image'),
			total = images.length,
			encoded = 0;
		  // Loop through all our <images> elementsfor (var i = 0; i < images.length; i++) {
			// check if the image is externalif (images[i].getAttributeNS(xlinkNS, 'href').indexOf('data:image') < 0)
			  toDataURL(images[i]);
			// else increment our counterelseif (++encoded === total) exportDoc()
		  }
		  // if there were no <image> elementif (total === 0) exportDoc();
		}
	})
})
#chartdiv1 {
  width: 100%;
  height: 500px;
}
.amcharts-export-menu {
  display:none;
}
<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><scriptsrc="https://www.amcharts.com/lib/3/amcharts.js"></script><scriptsrc="https://www.amcharts.com/lib/3/serial.js"></script><scriptsrc="https://www.amcharts.com/lib/3/themes/light.js"></script><scriptsrc="https://www.amcharts.com/lib/3/plugins/export/export.min.js"></script><scriptsrc="https://github.com/niklasvh/html2canvas/releases/download/0.5.0-alpha1/html2canvas.js"></script><buttonid="cmd">HTML2Canvas</button><divid="output"><divid="chartdiv1"></div></div>

Solution 2:

On click of html2Canvas button add jQuery code to add height and width as attribute to your svg element (add as attribute and not in style) and it will export in exact format

Solution 3:

I have tried the same, and fix this by changing the SVG width and height. Here is the code used to fix the issue.

functionprintOptions(type = 'before') {
    var svg = chart.div.querySelector('svg');
    if (svg) {
       if (type == 'after') { // remove the attributes after generating canvas
          svg.removeAttribute('width');
          svg.removeAttribute('height');
       } else { // set width and height according to parent container
          svg.setAttribute('width', chart.div.clientWidth);
          svg.setAttribute('height', chart.div.clientHeight);
       }
       chart.validateNow(); // validating am chart again.
     }
}    
// code for download imagedownloadImage() {
   printOptions();
   html2canvas(document.getElementById('chart-div')).then(canvas => {
      printOptions('after');
      let a = document.createElement('a');
      a.href = canvas.toDataURL("image/png")
      a.download = 'Report.png';
      a.click();
    });
 }

Working Demo, you can comment printOption before converting the image for the exported image changes.

Post a Comment for "Optimize Output Of Html2canvas"