【问题标题】:Resize chart.js canvas for printing调整 chart.js 画布的大小以进行打印
【发布时间】:2023-03-18 22:33:01
【问题描述】:

我正在使用@media print { 自定义我的打印视图。我的图表的问题在于它们的打印大小取决于浏览器窗口的大小。所以当我的浏览器很大时,画布对于页面来说太大了(我使用的是响应式图表)

我尝试在@media print { 中重新定义画布的大小,但没有成功。

在打印时获得一致的图表大小(不影响浏览器视图)的解决方案是什么?

【问题讨论】:

    标签: canvas chart.js


    【解决方案1】:

    如果您的图表很简单,请尝试仅使用 css

    @media print {
        canvas.chart-canvas {
            min-height: 100%;
            max-width: 100%;
            max-height: 100%;
            height: auto!important;
            width: auto!important;
        }
    }
    

    【讨论】:

    • 此解决方案与我的 react UI 中的 react-chartjs-2 包装器组件一起使用。比我迄今为止看到的任何其他解决方案都简单得多。我已经在 IE、Chrome、MS Edge 中进行了测试。在 Firefox 中没有得到很好的预览,但我认为 Firefox 设置有问题。浏览器没有为我正确打印任何页面。
    • 我们是在谈论简单地向画布添加一个类吗? @BrianTatum 你能分享你的解决方案吗?
    • 这是神一样的答案。谢谢!
    • 不幸的是,这对我在 Chart.js 的 2.9.4 版上不起作用(在 Firefox 84 和 Chrome 87 上测试)。
    【解决方案2】:

    问题是图表不适合新的打印宽度尺寸。幸运的是,我们可以在打印开始时执行重绘。

    解决方案是在调用打印时使用canvas.toDataURL() 方法将图表呈现为图像,然后在打印后将其切换回画布图表。 More on toDataURL().

    要检测何时调用您的函数,webkit 提供方法 window.matchMedia('print')(在打印期间将是 true 或之后再次为 false),而其他浏览器使用 window.onbeforeprintwindow.onafterprintMore on print methods.

    剩下要做的就是使用 CSS 确保图像是响应式的,即缩放到 100% 宽度。

    【讨论】:

    • 有人知道哪里有这样的工作示例吗?
    • 这个问题stackoverflow.com/questions/52754926/… 提供了一个在 Chrome 中工作的示例,但显然不是在 Firefox/IE 中。我确认它适用于 Chrome。
    【解决方案3】:

    我为画布使用了自定义打印样式和自定义右填充:

    @media print {
      .chartCanvas {
        padding-right: 1cm;
      }
    }
    

    【讨论】:

    • 不确定这如何解决问题。画布仍未调整大小,因为您添加了填充。
    【解决方案4】:

    这是一个基于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;
            }
        }
    };
    

    【讨论】:

      【解决方案5】:

      按照链接和 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 命令和数据,因为它们会因人而异。我有一个处理我遗漏的外部服务。

      我希望这可以帮助那些对如何摆脱雷吉的回答感到头疼的人。

      【讨论】:

        猜你喜欢
        • 2013-11-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-06
        • 2011-03-14
        • 2013-12-15
        相关资源
        最近更新 更多