【发布时间】:2023-03-18 22:33:01
【问题描述】:
我正在使用@media print { 自定义我的打印视图。我的图表的问题在于它们的打印大小取决于浏览器窗口的大小。所以当我的浏览器很大时,画布对于页面来说太大了(我使用的是响应式图表)
我尝试在@media print { 中重新定义画布的大小,但没有成功。
在打印时获得一致的图表大小(不影响浏览器视图)的解决方案是什么?
【问题讨论】:
我正在使用@media print { 自定义我的打印视图。我的图表的问题在于它们的打印大小取决于浏览器窗口的大小。所以当我的浏览器很大时,画布对于页面来说太大了(我使用的是响应式图表)
我尝试在@media print { 中重新定义画布的大小,但没有成功。
在打印时获得一致的图表大小(不影响浏览器视图)的解决方案是什么?
【问题讨论】:
如果您的图表很简单,请尝试仅使用 css
@media print {
canvas.chart-canvas {
min-height: 100%;
max-width: 100%;
max-height: 100%;
height: auto!important;
width: auto!important;
}
}
【讨论】:
问题是图表不适合新的打印宽度尺寸。幸运的是,我们可以在打印开始时执行重绘。
解决方案是在调用打印时使用canvas.toDataURL() 方法将图表呈现为图像,然后在打印后将其切换回画布图表。 More on toDataURL().
要检测何时调用您的函数,webkit 提供方法 window.matchMedia('print')(在打印期间将是 true 或之后再次为 false),而其他浏览器使用 window.onbeforeprint 和 window.onafterprint。 More on print methods.
剩下要做的就是使用 CSS 确保图像是响应式的,即缩放到 100% 宽度。
【讨论】:
我为画布使用了自定义打印样式和自定义右填充:
@media print {
.chartCanvas {
padding-right: 1cm;
}
}
【讨论】:
这是一个基于Reggie's answer 的简单修复,它比斯蒂芬的回答更容易实现:
//Fix chartjs printing:
window.onbeforeprint = (ev) => {
for (var id in Chart.instances) {
let instance = Chart.instances[id];
let b64 = instance.toBase64Image();
let i = new Image();
//Instead of the below, you could save something on the
//chart that decides what to change this to. This worked for me
//however:
i.style.maxWidth = "100vw";
i.src = b64;
let parent = instance.canvas.parentNode;
instance.tempImage = i;
instance.oldDisplay = instance.canvas.style.display;
instance.canvas.style.display = "none";
parent.appendChild(i);
}
};
window.onafterprint = (ev) => {
for (var id in Chart.instances) {
let instance = Chart.instances[id];
if (instance.tempImage) {
let parent = instance.canvas.parentNode;
parent.removeChild(instance.tempImage);
instance.canvas.style.display = instance.oldDisplay;
delete instance.oldDisplay;
delete instance.tempImage;
}
}
};
【讨论】:
按照链接和 Reggie Pinkham 的示例,我能够在 Angular 2 中完成这项工作。该示例使用打字稿,但读者应该能够相当容易地将其适应常规 JavaScript 以在其他项目中工作。请注意,我只在 Linux 机器上的最新版 Chrome 中对此进行了测试,因为这对于我的内部公司项目来说很好。
// imports left out here
export class ScaleChartComponent implements OnInit {
private chart:Chart;
private chartNode:HTMLCanvasElement;
private chartImage:HTMLImageElement;
private mediaQueryListener:MediaQueryListListener;
// el is a reference to the root node of my angular html component think of it as just a div container.
constructor(private el:ElementRef) { }
ngOnDestroy() {
this.chart.clear();
this.chart.destroy();
this.chart = null;
this.cleanupPrint();
}
ngAfterViewInit() {
this.drawChart();
this.setupPrint();
}
// need to setup the event listeners here and hold a reference to cleanup afterwards.
public setupPrint() {
if (!window.matchMedia) {
return;
}
var mediaQueryList = window.matchMedia('print');
this.mediaQueryListener = this.handlePrintMediaChange.bind(this);
mediaQueryList.addListener(this.mediaQueryListener);
}
// make sure to cleanup the reference after the fact.
public cleanupPrint() {
if (!window.matchMedia) {
return;
}
var mediaQueryList = window.matchMedia('print');
mediaQueryList.removeListener(this.mediaQueryListener);
this.mediaQueryListener = null;
}
// here's where the magic happens. I first grab the image
// then
public handlePrintMediaChange(mql) {
if (mql.matches) {
let dataUrl = this.chartNode.toDataURL('image/png');
if (this.chartNode && this.chartNode.parentNode) {
this.chartImage = new Image();
this.chartImage.src = dataUrl;
this.chartNode.parentNode.appendChild(this.chartImage);
this.chartService.destroyChart(this.chart);
this.chartNode.parentNode.removeChild(this.chartNode);
}
} else {
// here is where we switch back to non print mode.
if (this.chartImage) {
if (this.chartImage.parentNode) {
this.chartImage.parentNode.removeChild(this.chartImage);
}
this.chartImage = null;
}
// go through and recreate the entire chart again.
this.drawChart();
}
}
public drawChart() {
var chartData = {}; // setup your chart data here.
this.chartNode = this.createChartNode();
if (this.chartNode) {
this.chart = ; // execute your chart.js draw commands here.
}
}
private createChartNode() {
let componentElement = this.el.nativeElement as Element;
let element = componentElement.querySelector(".scale-chart-container");
if (!element) {
console.error("could not find element with class .scale-chart-container");
return null;
}
let chartNode = document.createElement("canvas") as HTMLCanvasElement;
element.appendChild(chartNode);
chartNode = chartNode;
return chartNode;
}
}
请注意,我省略了 Chart.js 命令和数据,因为它们会因人而异。我有一个处理我遗漏的外部服务。
我希望这可以帮助那些对如何摆脱雷吉的回答感到头疼的人。
【讨论】: