Javascript 如何创建带有锁定 y 轴的水平滚动 Chart.js 折线图?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35854244/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:17:19  来源:igfitidea点击:

How can I create a horizontal scrolling Chart.js line chart with a locked y axis?

javascriptionic-frameworkchart.js

提问by Joshua Richards

I'd like to create a line chart with Chart.Jsbut have the Y-Axisnot move when I scroll.

我想创建一个折线图,Chart.JsY-Axis滚动时不动。

I'm assuming I can use a fixed width, and put it in a container div with overflow:auto, but then The Y-axisinfo is attached to the canvas and scrolls along.

我假设我可以使用固定宽度,并将其放入容器 div 中overflow:auto,然后将Y-axis信息附加到画布并滚动。

I don't see a parameter or option for this in the docs. Any ideas?

我在文档中没有看到此参数或选项。有任何想法吗?

Thank you

谢谢

回答by potatopeelings

Scrollable Chart

可滚动图表

You're pretty much on the right track. If you add another wrapper and the y axis you are done.

你几乎走在正确的轨道上。如果您添加另一个包装器和 y 轴,您就完成了。



Preview

预览

enter image description here

在此处输入图片说明



CSS

CSS

.chartWrapper {
    position: relative;
}

.chartWrapper > canvas {
    position: absolute;
    left: 0;
    top: 0;
    pointer-events:none;
}

.chartAreaWrapper {
    width: 600px;
    overflow-x: scroll;
}

HTML

HTML

<div class="chartWrapper">
    <div class="chartAreaWrapper">
        <canvas id="myChart" height="300" width="1200"></canvas>
    </div>
    <canvas id="myChartAxis" height="300" width="0"></canvas>
</div>

Script

脚本

...

new Chart(ctx).Line(data, {
    onAnimationComplete: function () {
        var sourceCanvas = this.chart.ctx.canvas;
        // the -5 is so that we don't copy the edges of the line
        var copyWidth = this.scale.xScalePaddingLeft - 5;
        // the +5 is so that the bottommost y axis label is not clipped off
        // we could factor this in using measureText if we wanted to be generic
        var copyHeight = this.scale.endPoint + 5;
        var targetCtx = document.getElementById("myChartAxis").getContext("2d");
        targetCtx.canvas.width = copyWidth;
        targetCtx.drawImage(sourceCanvas, 0, 0, copyWidth, copyHeight, 0, 0, copyWidth, copyHeight);
    }
});


Fiddle - http://jsfiddle.net/mbhavfwm/

小提琴 - http://jsfiddle.net/mbhavfwm/

回答by Emma Louise

Chart.js 2.7.2: https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/

Chart.js 2.7.2:https://jsfiddle.net/EmmaLouise/eb1aqpx8/3/

This approach handles different DPR settings and will scale the axis to match the scaling that Chart.js applies to its charts. It also calls .clearRect() on the original Y axis that Chart.js draws, clearing the pixels in the defined area which means that there is no duplication of axes or overlaps.

这种方法处理不同的 DPR 设置,并将缩放轴以匹配 Chart.js 应用于其图表的缩放比例。它还在 Chart.js 绘制的原始 Y 轴上调用 .clearRect() ,清除定义区域中的像素,这意味着没有重复的轴或重叠。

CSS:

CSS:

.chartWrapper {
 position: relative;
}

.chartWrapper > canvas {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
}

.chartAreaWrapper {
  width: 600px;
  overflow-x: scroll;
}

HTML

HTML

<div class="chartWrapper">
    <div class="chartAreaWrapper">
        <div class="chartAreaWrapper2">
          <canvas id="chart-Test" height="300" width="1200"></canvas>
        </div>
     </div>
     <canvas id="axis-Test" height="300" width="0"></canvas>
</div>

JS:

JS:

    $(function () {
    var rectangleSet = false;

    var canvasTest = $('#chart-Test');
    var chartTest = new Chart(canvasTest, {
        type: 'bar',
        data: chartData,
        maintainAspectRatio: false,
        responsive: true,
        options: {
            tooltips: {
                titleFontSize: 0,
                titleMarginBottom: 0,
                bodyFontSize: 12
            },
            legend: {
                display: false
            },
            scales: {
                xAxes: [{
                    ticks: {
                        fontSize: 12,
                        display: false
                    }
                }],
                yAxes: [{
                    ticks: {
                        fontSize: 12,
                        beginAtZero: true
                    }
                }]
            },
            animation: {
                onComplete: function () {
                    if (!rectangleSet) {
                        var scale = window.devicePixelRatio;                       

                        var sourceCanvas = chartTest.chart.canvas;
                        var copyWidth = chartTest.scales['y-axis-0'].width - 10;
                        var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;

                        var targetCtx = document.getElementById("axis-Test").getContext("2d");

                        targetCtx.scale(scale, scale);
                        targetCtx.canvas.width = copyWidth * scale;
                        targetCtx.canvas.height = copyHeight * scale;

                        targetCtx.canvas.style.width = `${copyWidth}px`;
                        targetCtx.canvas.style.height = `${copyHeight}px`;
                        targetCtx.drawImage(sourceCanvas, 0, 0, copyWidth * scale, copyHeight * scale, 0, 0, copyWidth * scale, copyHeight * scale);

                        var sourceCtx = sourceCanvas.getContext('2d');

                        // Normalize coordinate system to use css pixels.

                        sourceCtx.clearRect(0, 0, copyWidth * scale, copyHeight * scale);
                        rectangleSet = true;
                    }
                },
                onProgress: function () {
                    if (rectangleSet === true) {
                        var copyWidth = chartTest.scales['y-axis-0'].width;
                        var copyHeight = chartTest.scales['y-axis-0'].height + chartTest.scales['y-axis-0'].top + 10;

                        var sourceCtx = chartTest.chart.canvas.getContext('2d');
                        sourceCtx.clearRect(0, 0, copyWidth, copyHeight);
                    }
                }
            }
        }
    });

回答by Majid

with the latest version (2.4.0) this worked for me:

使用最新版本(2.4.0)这对我有用:

HTML

HTML

<div style="width: 100%; overflow-x: auto;overflow-y:hidden">
  <div style="width: 3000px, height: 300px">
    <canvas id="chart1" height="300" width="0"></canvas>
  </div>
</div>

You can also calculate the widthdynamically based on the length of data. For example in VueJS you can do it as follow (considering 30pxfor each entry):

您还width可以根据数据的长度动态计算。例如在 VueJS 中,您可以按如下方式执行(考虑30px每个条目):

VueJS

VueJS

<div style="width: 100%; overflow-x: auto;">
  <div :style="{width: (data.length * 30) + 'px', height: '300px'}">
    <canvas id="chart1" height="300" width="0"></canvas>
  </div>
</div>

回答by Sumama Waheed

Chart.js 2.x:

Chart.js 2.x:

CSS:

CSS:

    .graph {
        padding: 10px;
        position: relative;
        overflow-x: scroll;
        width: 100%;
        .graph-container {
            height: 500px;
            width: 100%;
            min-width: 100%
        }
    }

HTML : Ignore the angular ngFor

HTML : 忽略角度 ngFor

  <div *ngFor="let graph of graphs; let i = index" class="graph">
        <div class="graph-container">
            <canvas #graphCanvas></canvas>
        </div>
  </div>

JS

JS

Set these options:

设置这些选项:

        options: {
            responsive: true,
            maintainAspectRatio: false
        }

Set the width of the "graph-container"

设置“图形容器”的宽度

I'm setting the width based on the number of elements I have X a zoom that the user chooses

我根据元素的数量设置宽度我有 X 用户选择的缩放

graphCanvasElement.nativeElement.parentElement
    .setAttribute('style', `width: ${chartData.data.labels.length*graph.zoom}px;min-width: 100%`);

graph.chartObj = new Chart(graphCanvasElement.nativeElement.getContext('2d'), chartData);

回答by Gurbaksh Singh

<ion-content > <div scrollX="true" scrollY="true" style="overflow:scroll; position:fixed; display:inline; top:0; right:0; bottom:0; left:0; white-space: nowrap;"> <canvas baseChart [data]="barChartData" [labels]="barChartLabels" [options]="barChartOptions" [colors]="lineChartColors" [legend]="barChartLegend" [chartType]="barChartType" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"> </canvas> </div> <button (click)="randomize()">Update</button> </ion-content>

<ion-content > <div scrollX="true" scrollY="true" style="overflow:scroll; position:fixed; display:inline; top:0; right:0; bottom:0; left:0; white-space: nowrap;"> <canvas baseChart [data]="barChartData" [labels]="barChartLabels" [options]="barChartOptions" [colors]="lineChartColors" [legend]="barChartLegend" [chartType]="barChartType" (chartHover)="chartHovered($event)" (chartClick)="chartClicked($event)"> </canvas> </div> <button (click)="randomize()">Update</button> </ion-content>

It works for me as I set canvas width 700 px and scroll works smoothly.

它对我有用,因为我将画布宽度设置为 700 像素并且滚动工作顺利。

回答by justFatLard

All these answers are rather muddy and complex.. Heres what I went with for my project. I just call fitChart() whenever the dataset changes or the container size changes.

所有这些答案都相当混乱和复杂。这是我为我的项目所用​​的。每当数据集更改或容器大小更改时,我都会调用 fitChart()。

HTML:

HTML:

<div class="chartWrapper">
    <div class="chartContainer">
        <canvas id="chart"></canvas>
    </div>
</div>

CSS: (Set the width and height to whatever you want)

CSS:(将宽度和高度设置为您想要的任何值)

div.chartWrapper {
    position: relative;
    overflow: auto;
    width: 100%;
}

div.chartContainer {
    position: relative;
    height: 300px;
}

JS:

JS:

var xAxisLabelMinWidth = 15; // Replace this with whatever value you like
var myChart = new Chart(document.getElementById('chart').getContext('2d'), {
    type: 'line',
    data: {},
    options: {
        responsive: true,
        maintainAspectRatio: false
    }
});

function fitChart(){
    var chartCanvas = document.getElementById('chart');
    var maxWidth = chartCanvas.parentElement.parentElement.clientWidth;
    var width = Math.max(mayChart.data.labels.length * xAxisLabelMinWidth, maxWidth);

    chartCanvas.parentElement.style.width = width +'px';
}

回答by KGlickman

To add to Emma Louise's solution above (I don't have the reputation to comment), I found that if I filled the rectangle with a color before drawing the image, the overlay canvas is opaque. Then you don't need to worry about clearing the rectangle, either onProgress or onComplete.

添加到上面 Emma Louise 的解决方案(我没有评论的声誉),我发现如果在绘制图像之前用颜色填充矩形,覆盖画布是不透明的。然后您无需担心清除矩形,无论是 onProgress 还是 onComplete。

    targetCtx.fillStyle = 'white'
    targetCtx.fillRect(0, 0, copyWidth * scale, copyHeight * scale);

Also, maybe it's because my devicePixelRatio is one, but these two lines caused the text in my chart to be all fuzzy. It looks fine when I remove them.

另外,可能是因为我的 devicePixelRatio 是一,但是这两行导致我图表中的文本全部模糊。当我删除它们时看起来很好。

    targetCtx.canvas.style.width = `${copyWidth}px`;
    targetCtx.canvas.style.height = `${copyHeight}px`;