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
How can I create a horizontal scrolling Chart.js line chart with a locked y axis?
提问by Joshua Richards
I'd like to create a line chart with Chart.Js
but have the Y-Axis
not move when I scroll.
我想创建一个折线图,Chart.Js
但Y-Axis
滚动时不动。
I'm assuming I can use a fixed width, and put it in a container div with overflow:auto
, but then The Y-axis
info 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
预览
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/
回答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 width
dynamically based on the length of data. For example in VueJS you can do it as follow (considering 30px
for 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`;