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

时间:2016-03-07 21:19:17

标签: javascript ionic-framework chart.js

我想创建一个包含Chart.Js的折线图,但滚动时Y-Axis不会移动。

我假设我可以使用固定宽度,并将其放在带有overflow:auto的容器div中,但随后将Y-axis信息附加到画布并向上滚动。

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

谢谢

7 个答案:

答案 0 :(得分:27)

可滚动图表

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

预览

enter image description here

<强> CSS

.chartWrapper {
    position: relative;
}

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

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

<强> 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>

<强>脚本

...

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);
    }
});

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

答案 1 :(得分:6)

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

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

CSS:

.chartWrapper {
 position: relative;
}

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

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

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:

    $(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);
                    }
                }
            }
        }
    });

答案 2 :(得分:2)

这是完整的工作示例,包含 5000 个数据点,并根据需要添加尽可能多的数据。 滚动条代码取自互联网,因此您可以根据需要对其进行修改。 此示例还包含一个带有多个系列的滚动条。 (代码有注释)

Chart with Scrollbar

    <!DOCTYPE html>
<html lang="en">
<head> </head>
<body>
    <div style="width: 500pt;">
    <canvas id="myChart" style="display: block;width: 1333px;height: 369px;"></canvas>
    <section class="range-slider">
        <span class="rangeValues"></span>
        <input value="1" min="1" max="" type="range" />
        <input value="10" min="1" max="" type="range" />
    </section>
    </div>
</body>
<style>
    section.range-slider {
    position: relative;
    width: 700px;
    height: 300px;
    float: left;
    text-align: center;
    }
    section.range-slider input[type="range"] {
    pointer-events: none;
    position: absolute;
    -webkit-appearance: none;
    -webkit-tap-highlight-color: rgba(255, 255, 255, 0);
    border: none;
    border-radius: 14px;
    background: #f1efef;
    box-shadow: inset 0 1px 0 0 #cdc6c6, inset 0 -1px 0 0 #d9d4d4;
    -webkit-box-shadow: inset 0 1px 0 0 #cdc6c6, inset 0 -1px 0 0 #d9d4d4;
    overflow: hidden;
    left: 0;
    top: 50px;
    width: 700px;
    outline: none;
    height: 20px;
    margin: 0;
    padding: 0;
    }
    section.range-slider input[type="range"]::-webkit-slider-thumb {
    pointer-events: all;
    position: relative;
    z-index: 1;
    outline: 0;
    -webkit-appearance: none;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 14px;
    background-image: -webkit-gradient(
        linear,
        left top,
        left bottom,
        color-stop(0%, #dad8da),
        color-stop(100%, #413f41)
    );
    /* android <= 2.2 */
    background-image: -webkit-linear-gradient(top, #dad8da 0, #413f41 100%);
    /* older mobile safari and android > 2.2 */
    background-image: linear-gradient(to bottom, #dad8da 0, #413f41 100%);
    /* W3C */
    }
    section.range-slider input[type="range"]::-moz-range-thumb {
    pointer-events: all;
    position: relative;
    z-index: 10;
    -moz-appearance: none;
    width: 20px;
    height: 20px;
    border: none;
    border-radius: 14px;
    background-image: linear-gradient(to bottom, #dad8da 0, #413f41 100%);
    /* W3C */
    }
    section.range-slider input[type="range"]::-ms-thumb {
    pointer-events: all;
    position: relative;
    z-index: 10;
    -ms-appearance: none;
    width: 20px;
    height: 20px;
    border-radius: 14px;
    border: 0;
    background-image: linear-gradient(to bottom, #dad8da 0, #413f41 100%);
    /* W3C */
    }
    section.range-slider input[type="range"]::-moz-range-track {
    position: relative;
    z-index: -1;
    background-color: black;
    border: 0;
    }
    section.range-slider input[type="range"]:last-of-type::-moz-range-track {
    -moz-appearance: none;
    background: none transparent;
    border: 0;
    }
    section.range-slider input[type="range"]::-moz-focus-outer {
    border: 0;
    }
</style>
</html>

<script src="https://cdn.jsdelivr.net/npm/chart.js@2.8.0"></script>
<script>



var ctx = document.getElementById("myChart").getContext("2d");
    // create dummt data

    var labels = [];
    var values = [];

    for(var i=0;i<5000;i++){
        labels.push("Label"+i)
        values.push(Math.random()*30)
    }  
var chart = new Chart(ctx, {
    // The type of chart we want to create
    type: "bar",
    // The data for our dataset
    data: {
    labels: labels,
    datasets: [
        {
        label: "My First dataset",
        backgroundColor: "rgb(255, 99, 132)",
        borderColor: "rgb(255, 99, 132)",
        data: values,
        },
    ],
    },
    // Configuration options go here
    options: {},
});


function getVals() {
    // Get slider values
    var parent = this.parentNode;
    var slides = parent.getElementsByTagName("input");
    var min = parseFloat(slides[0].value);
    var max = parseFloat(slides[1].value);
    // Neither slider will clip the other, so make sure we determine which is larger
    if (min > max) {
    var tmp = max;
    max = min;
    min = tmp;}

        var label = [];
        var value = [];

        label = JSON.parse(JSON.stringify(labels)).slice(min, max);

        //var datasets = Data.datasets;
        // IF YOU HAVE MULTIPLE SERIESES

        // ChartObj.data.labels = label;
        // for (var i = 0; i < datasets.length; i++) {
        //     values = datasets[i].data.slice(min, max);
        //     ChartObj.data.datasets[i].data = values;
        // }
        // ChartObj.update();
        value = JSON.parse(JSON.stringify(values)).slice(min, max);
        chart.data.labels = label;
        chart.data.datasets[0].data = value;
        chart.update();

    var displayElement = parent.getElementsByClassName("rangeValues")[0];
    displayElement.innerHTML = "Min : " + min + " Max : " + max;
}

// Initialize Sliders
var sliderSections = document.getElementsByClassName("range-slider");
for (var x = 0; x < sliderSections.length; x++) {
    var sliders = sliderSections[x].getElementsByTagName("input");
    for (var y = 0; y < sliders.length; y++) {
    if (sliders[y].type === "range") {
        sliders[y].oninput = getVals;
        sliders[y].max=JSON.parse(JSON.stringify(labels)).length;
        // Manually trigger event first time to display values
        sliders[y].oninput();
    }
    }
}




</script>

答案 3 :(得分:1)

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

HTML:

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

CSS :(将宽度和高度设置为任意值)

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

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

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';
}

答案 4 :(得分:0)

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

HTML

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

您还可以根据数据长度动态计算width。例如,在VueJS中,您可以按照以下步骤进行操作(对于每个条目,请考虑30px):

VueJS

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

答案 5 :(得分:0)

<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>

当我将画布宽度设置为700 px且滚动工作顺利时,它对我有用。

答案 6 :(得分:0)

要添加到以上Emma Louise的解决方案中(我不敢发表评论),我发现如果在绘制图像之前用某种颜色填充矩形,则覆盖画布是不透明的。这样,您就不必担心清除onProgress或onComplete矩形了。

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

另外,也许是因为我的devicePixelRatio是1,但是这两行使我图表中的文本变得模糊。删除它们时看起来还不错。

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