首页 > 解决方案 > 以编程方式拖动 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])
       }
   }

上面的代码有效,但每个轴的移动方式不同,我无法获得所有轴平移在一起的原生效果。如何使用自定义函数实现平滑平移。谢谢。

标签: lightningchart

解决方案


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

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

const offsetX = 1 // offset should be -1,0,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, 2, 3, 4, 1, 2], 1, 1)

        const pointSeries = chart.addPointSeries({
            xAxis: axisX2,
            yAxis: axisY2
        })
        pointSeries.addArrayY([2, 4, 1, 2, 7, 3, 4, 5, 1, 2], 1, 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', function (ev) {
            keymap.set(ev.code, 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>

推荐阅读