Exporting dynamic chartjs to jspdf - javascript

I'm having a problem with the code below:
var reportPageHeight = 16500;
var reportPageWidth = 12400;
// create a new canvas object that we will populate with all other canvas objects
var pdfCanvas = $('<canvas />').attr({
id: "pcanvaspdf",
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 = 4000
var canvasWidth = 6500;
// 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;
}
});
// download the pdf
pdf.save('ChartPDF.pdf');
The problem is that I lose the transparency of the chart and instead I get a black background when the chart is exported to PDF. Is there any way to change it to white when exporting to PDF?
Thanks in advance.

Set the canvas backgrounds to white:
canvas {
background-color: #fff;
}
If that doesn't work, draw the canvas background in your chart's plugins object:
plugins: [
{
id: 'background',
beforeDraw: function(chartInstance) {
var chart = chartInstance.chart;
var ctx = chart.ctx;
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, chart.width, chart.height);
}
}
],

Related

How to add a new canvas with animation in openlayers3 or openlayers4

I had drawn an animation in canvas like this and rendered a map using openlayers4. I want to add this canvas to the map[openlayers canvas] in next step.
I had used ol.source.ImageCanvas add a boundary to openlayers, so I try to add the canvas with animation using ImageCanvas, but failed.
What's more, openlayers API said ol.source.ImageCanvas method only the image canvas can be added. I didn't know whether the animate canvas so does.
Should I insit on using ImageCanvas method or try others?
Can someone give me an example if I abandon the ImageCanvas method?
After some tries, I got a solution! Haha!
First: the ol.source.ImageCanvas can still use, but you will get a stopped animate just like a screenshot.
Second: must know the ol.map.render() in openlayers3 or openlayers4, whose description is:
Request a map rendering (at the next animation frame).
Thus, you can use it to refresh the map and get the next animation of canvas.
The following is snippets of my code:
var topoCanvas = function(extent, resolution, pixelRatio, size, projection) {
// topo features;
var features = topojson.feature(tokyo, tokyo.objects.counties);
var canvasWidth = size[0];
var canvasHeight = size[1];
var canvas = d3.select(document.createElement('canvas'));
canvas.attr('width', canvasWidth).attr('height', canvasHeight);
var context = canvas.node().getContext('2d');
var d3Projection = d3.geo.mercator().scale(1).translate([0, 0]);
var d3Path = d3.geo.path().projection(d3Projection);
var pixelBounds = d3Path.bounds(features);
var pixelBoundsWidth = pixelBounds[1][0] - pixelBounds[0][0];
var pixelBoundsHeight = pixelBounds[1][1] - pixelBounds[0][1];
var geoBounds = d3.geo.bounds(features);
var geoBoundsLeftBottom = ol.proj.transform(geoBounds[0], 'EPSG:4326', projection);
var geoBoundsRightTop = ol.proj.transform(geoBounds[1], 'EPSG:4326', projection);
var geoBoundsWidth = geoBoundsRightTop[0] - geoBoundsLeftBottom[0];
if (geoBoundsWidth < 0) {
geoBoundsWidth += ol.extent.getWidth(projection.getExtent());
}
var geoBoundsHeight = geoBoundsRightTop[1] - geoBoundsLeftBottom[1];
var widthResolution = geoBoundsWidth / pixelBoundsWidth;
var heightResolution = geoBoundsHeight / pixelBoundsHeight;
var r = Math.max(widthResolution, heightResolution);
var scale = r / (resolution / pixelRatio);
var center = ol.proj.transform(ol.extent.getCenter(extent), projection, 'EPSG:4326');
d3Projection.scale(scale).center(center).translate([canvasWidth / 2, canvasHeight / 2]);
d3Path = d3Path.projection(d3Projection).context(context);
d3Path(features);
context.stroke();
// above code is add a topoJson boundary to canvas
// below code is add an animation to canvas
var settings = createSettings(tokyo, {
width: canvasWidth,
height: canvasHeight
});
// reset the projection and bounds for animation canvas
settings.projection = d3Projection;
settings.bounds = geoBounds;
var mesh = buildMeshes(tokyo, settings);
when(render(settings, mesh, {
width: canvasWidth,
height: canvasHeight
})).then(function(masks) {
when(interpolateField(stations, data, settings, masks)).then(function(field) {
// wind moving animation
animate(settings, field, canvas);
// refresh the map to get animation
window.setInterval(function() {
map.render();
}, 50);
});
});
return canvas[0][0];
}

Write from one fixed canvas to another floating

I have an imageCanvas with it's Transform matrix
Image = new image();
image.src = //stuff;
imageContext.drawImage(Image, 0, 0);
var t = imageContext.currentTransform;
that is drawn on another canvas
context.drawImage(imageCanvas, 0, 0);
on which I draw lines
context.moveTo(mousePos.X, mousePos.Y);
context.lineTo(currentPos.X, currentPos.Y);
context.stroke();
I save my lines into a vector of objects 'lines'.
I would like to join the two layers into the 'Image', but this function works only for traslation but not for rotation and scaling.
var saveCanvas = document.createElement('canvas');
var saveContext = saveCanvas.getContext('2d');
saveCanvas.width = Image.width;
saveCanvas.height = Image.height;
saveContext.drawImage(Image,0,0);
saveContext.save();
var t = imageContext.currentTransform;
saveContext.setTransform(1,0,0,1,-t.e,-t.f);
for(var i = 0; i < lines.length; i++){
saveContext.beginPath();
saveContext.moveTo(lines[i].from.X, lines[i].from.Y);
saveContext.lineTo(lines[i].to.X, lines[i].to.Y);
saveContext.stroke();
}
saveContext.restore();
Image.src = saveCanvas.toDataURL();
how can I modify the arguments of 'setTransform' to solve the problem ?
Just use the rest of the transforms attributes.
var t = imageContext.currentTransform;
saveContext.setTransform(
t.a,
t.b,
t.c,
t.d,
-t.e,
-t.f);

globalCompositeOperation drawing white over background

I am having trouble with globalCompositeOperation in javascript for the html5 canvas.
https: //jsfiddle.net/6j51kxeh/
I am using "destination-in" do draw a black square in a sprite of a heart, so when the the player is missing that heart, it shows. It draws the black heart alright, but it draws white over top of everything else. If you comment out the globalCompositeOperation line you will see it is drawn on top of other things (just a random group of sprites from the same sprite sheet). Thanks for any help.
I am also having trouble just linking to jsfiddle and I'm done playing with it.
var canvas = document.createElement("canvas");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
var scale = 4;
document.body.appendChild(canvas);
var draw = canvas.getContext("2d");
draw.imageSmoothingEnabled = false;
draw.mozImageSmoothingEnabled = false;
draw.oImageSmoothingEnabled = false;
draw.webkitImageSmoothingEnabled = false;
var assetsToLoad = [];
var assetsLoaded = 0;
var sprites = new Image();
sprites.onload = loadHandler;
sprites.src = "https://dl.dropboxusercontent.com/u/38825680/Site/images/sprites.png";
assetsToLoad.push(sprites);
function loadHandler() {
assetsLoaded++;
if(assetsLoaded === assetsToLoad.length) {
console.log(assetsLoaded + " asset[s] loaded");
Draw();
}
};
function Draw() {
draw.save();
draw.scale(scale,scale);
draw.drawImage(sprites,0,64,64,64,0,0,64,64);
// draw.drawImage(sprites,112,80,16,16,0,0,16,16);
draw.fillStyle = "black";
draw.fillRect(0,0,16,16);
//draw.globalCompositeOperation = "destination-in";
draw.drawImage(sprites,96,48,16,16,0,0,16,16);
draw.restore();
}
destination-in compositing is destructive in the sense that it will erase everything on the canvas except where the new (heart) overlaps any existing non-transparent pixels. So this form of compositing is not likely what you need.
To clarify: You want to draw the heart sprite over another sprite image and then later clear that sprite from the other sprite image that the heart was added to?
If yes, then:
To add the heart, draw the heart sprite over the other sprite.
To clear the heart, just redraw the other sprite.
Example code and a Demo:
var canvas = document.createElement("canvas");
canvas.height = 640;//window.innerHeight;
canvas.width = 960;//window.innerWidth;
var scale = 4;
document.body.appendChild(canvas);
var draw = canvas.getContext("2d");
draw.imageSmoothingEnabled = false;
draw.mozImageSmoothingEnabled = false;
draw.oImageSmoothingEnabled = false;
draw.webkitImageSmoothingEnabled = false;
var assetsToLoad = [];
var assetsLoaded = 0;
var sprites = new Image();
sprites.onload = loadHandler;
sprites.src = "https://dl.dropboxusercontent.com/u/38825680/Site/images/sprites.png";
assetsToLoad.push(sprites);
function loadHandler() {
assetsLoaded++;
if(assetsLoaded === assetsToLoad.length) {
console.log(assetsLoaded + " asset[s] loaded");
draw.scale(scale,scale);
Draw();
}
};
function Draw() {
draw.drawImage(sprites,0,64,64,64,0,0,64,64);
}
function addHeart(x,y){
draw.fillStyle = "black";
draw.fillRect(x,y,16,16);
draw.drawImage(sprites,96,48,16,16,x,y,16,16);
}
function removeHeart(x,y,spriteX,spriteY){
draw.clearRect(x,y,16,16);
draw.drawImage(sprites,spriteX,spriteY+64,16,16,x,y,16,16)
}
$('#add').click(function(){
addHeart(0,0);
});
$('#remove').click(function(){
removeHeart(0,0,0,0);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=add>Add heart</button>
<button id=remove>Remove heart</button>

Resizing Images to display in HTML5 Canvas

I'm trying to get an image to be displayed on an HTML5 Canvas while keeping the aspect ratio and having the image reduced (if larger than the canvas). I've look around at some answers but seem to be missing something and wondered if you guys can help.
I'm using the function "calculateAspectRatioFit" suggested by one of the Stackoverflow answers, but it seems to not resize the image for me, as it did in the answer - so I may be doing something wrong :)
Here's my code:
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
var rtnWidth = srcWidth*ratio;
var rtnHeight = srcHeight*ratio;
return { width: rtnWidth, height: rtnHeight };
}
var canvasImage = new Image();
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
var ctx = this.getContext('2d');
var parentWidth = self._widgetSize[0];
var parentHeight = self._widgetSize[1];
canvasImage.onload = function() {
var imgSize = calculateAspectRatioFit(canvasImage.width, canvasImage.height, parentWidth, parentHeight);
ctx.clearRect(0, 0, parentWidth, parentHeight);
ctx.drawImage(canvasImage, 0, 0,imgSize.width, imgSize.height);
};
The image is displayed but is larger than the HTML5 Canvas. What I am after is to have the image the same width as the Canvas, and if the height is larger than the height of the canvas then it overflows and is hidden...I just want to fill the width and keep the aspect ratio.
Can anyone help point out what I am missing?
Appreciate your help :)
---Update 30/11---
I've just added the answer to my code and I get the following: (this is the 2nd answer)
var canvasImage = new Image();
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
var ctx = this.getContext('2d');
canvasImage.onload = scaleAndDraw(this, ctx, canvasImage);
function scaleAndDraw(canvas, ctx, srcImage) {
// Image is 2560x1600 - so we need to scale down to canvas size...
// does this code actually 'scale' the image? Image of result suggests it doesn't.
var aspect = getAspect(canvas, srcImage);
var canvasWidth = (srcImage.width * aspect);
var canvasHeight = (srcImage.height * aspect);
ctx.drawImage(srcImage, 0, 0, canvasWidth|0, canvasHeight|0);
}
function getAspect(canvas, image) {
return canvas.size[0] / image.width;
}
So the code for displaying the image works, but the image is keeping it's dimensions and is not resizing to the dimensions of the canvas.
Hopefully the image will help you see the problem I am having. Larger images do not seem to rescale to fit in the canvas while keeping aspect ratio.
Any thoughts? :)
Your code works for me as long as I supply sane parentWidth,parentHeight values:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
var ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
var rtnWidth = srcWidth*ratio;
var rtnHeight = srcHeight*ratio;
return { width: rtnWidth, height: rtnHeight };
}
var parentWidth = 100; //self._widgetSize[0];
var parentHeight = 50; //self._widgetSize[1];
var canvasImage = new Image();
canvasImage.onload = function() {
var imgSize = calculateAspectRatioFit(canvasImage.width, canvasImage.height, parentWidth, parentHeight);
ctx.clearRect(0, 0, parentWidth, parentHeight);
ctx.drawImage(canvasImage,30,30,imgSize.width, imgSize.height);
};
canvasImage.src = "http://www.greenwallpaper.org/backgrounds/simply-green-502085.jpeg";
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Save images after using CSS filter

I'm building a new website that will let users apply filters to images (just like Instagram). I will use -webkit-filter for that.
The user must be able to save the filtered image. There is any way I can do that using JavaScript?
You can't save images directly, but you can render them in Canvas, then save from there.
See: Save HTML5 canvas with images as an image
There is no direct/straight forward method to export an image with CSS Filter.
Follow the below steps for Saving/Exporting an Image with -webkit-filter applied on it:
1. Render the image to a canvas:
var canvas = document.createElement('canvas');
canvas.id="canvasPhoto";
canvas.width = imageContaainer.width;
canvas.height = imageContaainer.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(imageContaainer, 0, 0, canvas.width, canvas.height);
Get the ImageData from canvas and apply the filter. Eg: I will apply grayscale filter to the ImageData below:
function grayscale(ctx) {
var pixels = ctx.getImageData(0,0,canvas.width, canvas.height);
var d = pixels.data;
for (var i=0; i<d.length; i+=4) {
var r = d[i];
var g = d[i+1];
var b = d[i+2];
var v = 0.2126*r + 0.7152*g + 0.0722*b;
d[i] = d[i+1] = d[i+2] = v
}
context.putImageData(pixels, 0, 0);
}
Add an event and use the below code to trigger download
function download(canvas) {
var data = canvas.toDataURL("image/png");
if (!window.open(data))
{
document.location.href = data;
}
}

Categories