以编程方式拖动 Lightningchart 图表

问题描述

我想使用键盘键(例如向上箭头向上平移图表,向下箭头向下平移图表等)拖动图表。

const lcjs = lightningChart({
        overrideInteractionMouseButtons: {
            chartXYPanMouseButton: 0,chartXYRectangleZoomFitMouseButton: 2,},})

const chart = lcjs.ChartXY()

上面的代码用鼠标没问题,但我使用 javascript 尝试了类似下面的代码,我尝试根据键盘中的箭头键移动图表。

   function upArrowPress() {
       move = 2;
       lastpos_y1 = lastpos_y1 + move;
       lastpos_y2 = lastpos_y2 + move;
       for (var key in axis) {                                 
         axis[key].setScrollStrategy(undefined).setInterval(lastpos_y1[key],lastpos_y2[key])
       }
   }

以上代码有效,但每个轴的移动方式不同,我无法获得所有轴一起平移的本机效果。如何使用自定义功能实现平滑平移。谢谢。

解决方法

无法保证 X 轴和 Y 轴间隔在视觉上具有相同的大小。这就是为什么当您将轴间隔设置为相同时,仍然会导致视觉上不同的大小变化。

您需要根据每个系列尺度方向上的像素大小调整间隔变化。为此,您首先需要获取每个缩放方向(X 和 Y)series.scale.x.getPixelSize()series.scale.y.getPixelSize() 的像素大小。这两个值是乘数,然后您可以使用它们将间隔更改为在视觉上相同的变化。

const offsetX = 1 // offset should be -1,1 based on what keys are pressed
const origXInterval = series.axisX.getInterval();
const xIntervalSize = origXInterval.end - origXInterval.start
const pixelSizeX = series.scale.x.getPixelSize()
const newXIntervalStart = origXInterval.start + offsetX * pixelSizeX * 10
axisX.setInterval(newXIntervalStart,newXIntervalStart + xIntervalSize)

这里的关键是+ offsetX * pixelSizeX * 10。这会将 X 间隔向左 (-1) 或向右 (1) 偏移,即 10 个像素将表示的间隔值。

要对所有轴和系列执行此操作,您需要遍历每个系列 chart.getSeries().forEach(series=>{}) 并偏移为任何系列找到的所有轴一次。一个轴可能被多个系列使用,因此如果您已经偏移了轴,则需要跟踪,否则平移将不同步。您可以使用 JS Set 轻松做到这一点。检查轴是否已经在集合 processedAxisSet.has(series.axisX) 中,如果不存在,则按照之前的方式进行偏移并将轴添加到集合 processedAxisSet.add(series.axisX) 中。

请参阅下面的完整示例。这是一个现成的 html 文件,您可以将其复制并粘贴到本地文件系统,然后在浏览器中打开以查看它的工作情况。我这样做是因为 iframe 会导致将事件附加到窗口时出现一些问题。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />

    <!-- Flexbox styling to have the chart and header fill the page.
        Chart will take as much space as possible. -->
    <style>
        html,body {
            height: 100%;
            margin: 0;
        }

        .box {
            display: flex;
            flex-flow: column;
            height: 100%;
        }

        .box .row.header {
            flex: 0 1 auto;
        }

        .box .row.content {
            flex: 1 1 auto;
        }
    </style>
</head>

<body class="box">
    <!-- Create div to render the chart into-->
    <div id="target" class="row content"></div>

    <!--IIFE assembly (lcjs.iife.js) is a standalone JS file,which does not need any build tools,such as NPM,to be installed-->
    <!--Script source must be defined in it's own script tag-->
    <script src="https://unpkg.com/@arction/lcjs@3.0.1/dist/lcjs.iife.js"></script>

    <!--Actual chart related script tag-->
    <script>
        // Replace the contents of this script tag if you want to test code from our examples:
        // https://www.arction.com/lightningchart-js-interactive-examples/

        // Extract required parts from LightningChartJS.
        const {
            lightningChart
        } = lcjs //Note: @arction/lcjs is not needed here,when using IIFE assembly

        // Create a XY Chart.
        const chart = lightningChart().ChartXY({
            // Set the chart into a div with id,'target'. 
            // Chart's size will automatically adjust to div's size. 
            container: 'target'
        })

        const axisX = chart.getDefaultAxisX();
        const axisY = chart.getDefaultAxisY();
        const axisX2 = chart.addAxisX({ opposite: true })
        const axisY2 = chart.addAxisY({ opposite: true })

        axisX.setScrollStrategy(undefined)
        axisY.setScrollStrategy(undefined)
        axisX2.setScrollStrategy(undefined)
        axisY2.setScrollStrategy(undefined)

        const lineSeries = chart.addLineSeries()
        lineSeries.addArrayY([1,2,1,3,4,2],1)

        const pointSeries = chart.addPointSeries({
            xAxis: axisX2,yAxis: axisY2
        })
        pointSeries.addArrayY([2,7,5,1)

        // Create a keymap to track key states
        const keymap = new Map();
        keymap.set('ArrowUp',0);
        keymap.set('ArrowLeft',0);
        keymap.set('ArrowRight',0);
        keymap.set('ArrowDown',0);

        // attach listeners to keydown and keyup events to keep track of key states
        // keydown is also used to trigger update to pan the chart based on keyboard input
        document.addEventListener('keydown',function (ev) {
            keymap.set(ev.code,1);
            updateKeyboardPan();
        });

        document.addEventListener('keyup',0);
        });

        function updateKeyboardPan() {
            // update offsets based on keyboard state
            let offsetX = 0
            let offsetY = 0
            if (keymap.get('ArrowUp') === 1) {
                offsetY -= 1
            }
            if (keymap.get('ArrowDown') === 1) {
                offsetY += 1
            }
            if (keymap.get('ArrowLeft') === 1) {
                offsetX += 1
            }
            if (keymap.get('ArrowRight') === 1) {
                offsetX -= 1
            }

            // set for storing what axes have already been processed
            const processedAxisSet = new Set()

            chart.getSeries().forEach(series => {
                // offset based on pixels only if the axis hasn't been processed this loop
                if (!processedAxisSet.has(series.axisX)) {
                    // Get original state info
                    const origXInterval = series.axisX.getInterval();
                    const xIntervalSize = origXInterval.end - origXInterval.start
                    // get the pixel size for axis 
                    const pixelSizeX = series.scale.x.getPixelSize()
                    const newXIntervalStart = origXInterval.start + offsetX * pixelSizeX * 10
                    // pixel size info is used to scale the change to be visually same size for both X and Y axis
                    series.axisX.setInterval(newXIntervalStart,newXIntervalStart + xIntervalSize)
                    // add the axis to the processed axis set
                    processedAxisSet.add(series.axisX)
                }

                // Do same for Y axis as was done for X axis
                if (!processedAxisSet.has(series.axisY)) {
                    const origYInterval = series.axisY.getInterval();
                    const yIntervalSize = origYInterval.end - origYInterval.start
                    const pixelSizeY = series.scale.y.getPixelSize()
                    const newYIntervalStart = origYInterval.start + offsetY * pixelSizeY * 10
                    series.axisY.setInterval(newYIntervalStart,newYIntervalStart + yIntervalSize)
                    processedAxisSet.add(series.axisY)
                }
            })
        }
    </script>
</body>

</html>