在重绘k线图的实时行情数据的candlestick时,如何不重绘整个k线图?

新手上路,请多包涵

在重绘k线图的实时行情数据的candlestick时,如何不重绘整个k线图。

本地加载历史数据,实时行情数据通过ws从网络上获取。当没有行情时,chart显示的是历史数据;当有行情时,历史数据+实时数据,然后显示在chart里。我想要的chart的样子是这样:当有行情数据时,chart里原有的candlesticks向左平移,给新数据留出最右边空间来显示。此时,左右拖拽或缩放等不会受到新行情数据的刷新而回到chart拖拽或缩放前的样子,或者说只更新实时数据对应的那条candlestick,其它的不受影响,或者干脆一点就像行情软件里一样,缩放等操作不会被新行情数据刷新到操作前的样子。

现在的问题是,不能实现上述要求。下面我给出相关代码,请帮忙诊断一下!

<body>
    <div id="chart_container">
      <div id="text_show">
        <textarea id="info_textarea" readonly></textarea>
      </div>
      <div id="candlesticks">
        <div id="real_chart">"real_chart"</div>
        <div id="real_text">
          <div id="real">实时行情</div>
          <div id="kdj">kdj指标</div>
        </div>
      </div>
    </div>
    <div id="chart2">
      <div id="table_container">"table_container"</div>
      <div id="input_container">
        <input id="search-box" type="text" placeholder="输入股票代码或名称" />
        <div id="suggestions" class="suggestions"></div>
      </div>
    </div>

    <script>
      // 全局变量
      let g_his_data = null; // 历史数据
      let g_his_data_len = 0; // 历史数据长度
      let g_stk_code = "000001.SH"; // 默认股票代码

      const chart = echarts.init(document.getElementById("real_chart"));

      const option = {
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "cross",
            link: { xAxisIndex: "all" },
            label: {
              show: true,
              position: "outside",
              offset: [0, -10],
            },
          },
          backgroundColor: "rgba(245, 245, 145, 1)",
          position: function (point, params, dom, rect, size) {
            // 将 tooltip 放在 cross 十字线交点的右下方
            return [point[0] + 30, point[1] + 10];
          },

        },
        axisPointer: {
          link: [
            {
              xAxisIndex: "all",
            },
          ],

          label: {
            backgroundColor: "#777",
          },
        },
        grid: [
          {
            top: "1%",
            height: "75%",
            left: "2%",
            right: "1%",
            containLabel: true,
          },
          {
            top: "75%",
            height: "21%",
            left: "2%",
            right: "1%",
            containLabel: true,
          },
        ],
        xAxis: [
          {
            type: "category",
            data: [], // 日期,
            boundaryGap: true,
            gridIndex: 0,
          },
          {
            type: "category",
            data: [], // 日期,
            boundaryGap: true,
            axisTick: { show: false },
            splitLine: { show: false },
            axisLabel: { show: false },
            gridIndex: 1,
          },
        ],
        yAxis: [
          {
            scale: true,
            axisLine: { onZero: false },
            position: "left",
            gridIndex: 0,
            splitLine: {
              show: false,
              lineStyle: {
                type: "dashed", // 设置为虚线
              },
            },
            min: "dataMin",
            max: "dataMax",
            axisLabel: { show: false },
          },
          {
            scale: true,
            axisLine: { onZero: false },
            position: "left",
            gridIndex: 1,
            splitLine: {
              show: false,
              lineStyle: {
                type: "dashed", // 设置为虚线
              },
            },

            axisLabel: { show: false },
          },
        ],
        dataZoom: [
          {
            type: "inside",
            xAxisIndex: [0, 1],
            start: 70,
            end: 100,
          },
          {
            type: "slider",
            xAxisIndex: [0, 1],
            start: 70,
            end: 100,
            top: "97.5%",
          },
        ],
        series: [
          {
            name: "Candlestick",
            type: "candlestick",
            data: [], // 初始为空,后续通过历史数据填充
            itemStyle: {
              color: "#ef232a",
              color0: "#14b143",
              borderColor: "#ef232a",
              borderColor0: "#14b143",
            },
            xAxisIndex: 0,
            yAxisIndex: 0,
          },
        ],
      };
      // 在loadHisData函数中,使用initChart函数初始化图表
      function initChart(his_data) {
        const dates = his_data.dates;
        const candlestick = his_data.candlestick;

        option.xAxis[0].data = dates;
        option.xAxis[1].data = dates;
        option.series[0].data = candlestick;
        chart.setOption(option, false);
      }
    </script>

    <script>
      // 加载股票数据,并绘制图表
      async function loadHisData(code) {
        try {
          // 获取历史数据
          g_stk_code = code;
          const hisResponse = await fetch(`/stock/${code}`);
          g_his_data = await hisResponse.json();
          g_his_data_len = g_his_data.dates.length;

          // 绘制图表
          initChart(g_his_data);
        } catch (error) {
          console.error("Error loading initial data:", error);
        }
      }
    </script>

    <script>
      // 建立ws连接,处理实时数据。
      let socket = new WebSocket("ws://localhost:8000/ws");

      // 发送股票代码到后端
      function sendStockCode() {
        // 将 g_stk_code 从 "000001.SZ" 格式转换为 "sz000001",这样符合easyquotation的要求
        const formattedCode = g_stk_code
          .split(".")
          .reverse()
          .join("")
          .toLowerCase();
        socket.send(JSON.stringify({ stock_code: formattedCode }));
      }
      // 定时发送股票代码
      setInterval(sendStockCode, 2000);

      socket.onmessage = function (event) {
        let data = JSON.parse(event.data);
        if ((data.high > 0) && (data.date > data.old_date)) {
          if (g_his_data.length < g_his_data_len) {

            g_his_data.dates.push(data.date);
            g_his_data.candlestick.push([data.open, data.now, data.low, data.high,]);
          } else {
            g_his_data.dates[g_his_data_len] = data.date;
            g_his_data.candlestick[g_his_data_len] = [data.open, data.now, data.low, data.high,];
          }
          initChart(g_his_data);
        }
      };

      socket.onopen = function (event) {
        console.log("WebSocket connection opened");
        sendStockCode();
      };

      socket.onclose = function (event) {
        console.log("WebSocket connection closed");
      };

      socket.onerror = function (error) {
        console.error("WebSocket error:", error);
      };
    </script>
</body>

问过各个AI,其建议是在websocket中把新数据push到g_his_data上,然后单独设置option的series。这些建议没有解决我的问题。

阅读 3.2k
1 个回答

socket.onmessage中直接更新g_his_datadatescandlestick,不调用 initChart重绘。
使用chart.setOption更新图表只更新需要变更的K线和X轴,并设第二个参数为false避免重绘。
下面是稍微改动了下的代码↓

<body>
    <div id="chart_container">
        <div id="text_show">
            <textarea id="info_textarea" readonly></textarea>
        </div>
        <div id="candlesticks">
            <div id="real_chart">"real_chart"</div>
            <div id="real_text">
                <div id="real">实时行情</div>
                <div id="kdj">kdj指标</div>
            </div>
        </div>
    </div>
    <div id="chart2">
        <div id="table_container">"table_container"</div>
        <div id="input_container">
            <input id="search-box" type="text" placeholder="输入股票代码或名称" />
            <div id="suggestions" class="suggestions"></div>
        </div>
    </div>

    <script>
        // 全局变量
        let g_his_data = null; // 历史数据
        let g_his_data_len = 0; // 历史数据长度
        let g_stk_code = "000001.SH"; // 默认股票代码
        const visibleCount = 100; // 可见K线数量

        const chart = echarts.init(document.getElementById("real_chart"));

        const option = {
            tooltip: {
                trigger: "axis",
                axisPointer: {
                    type: "cross",
                    link: { xAxisIndex: "all" },
                    label: {
                        show: true,
                        position: "outside",
                        offset: [0, -10],
                    },
                },
                backgroundColor: "rgba(245, 245, 145, 1)",
                position: function (point) {
                    return [point[0] + 30, point[1] + 10];
                },
            },
            axisPointer: {
                link: [{ xAxisIndex: "all" }],
                label: { backgroundColor: "#777" },
            },
            grid: [{ top: "1%", height: "75%", left: "2%", right: "1%", containLabel: true }],
            xAxis: [{
                type: "category",
                data: [],
                boundaryGap: true,
            }],
            yAxis: [{
                scale: true,
                axisLine: { onZero: false },
                position: "left",
                splitLine: { show: false },
            }],
            dataZoom: [
                { type: "inside", xAxisIndex: [0], start: 70, end: 100 },
                { type: "slider", xAxisIndex: [0], start: 70, end: 100, top: "97.5%" }
            ],
            series: [{
                name: "Candlestick",
                type: "candlestick",
                data: [],
                itemStyle: {
                    color: "#ef232a",
                    color0: "#14b143",
                    borderColor: "#ef232a",
                    borderColor0: "#14b143",
                },
            }],
        };

        function initChart(his_data) {
            const dates = his_data.dates;
            const candlestick = his_data.candlestick;

            option.xAxis[0].data = dates;
            option.series[0].data = candlestick;
            chart.setOption(option, false);
        }

        async function loadHisData(code) {
            try {
                g_stk_code = code;
                const hisResponse = await fetch(`/stock/${code}`);
                g_his_data = await hisResponse.json();
                g_his_data_len = g_his_data.dates.length;

                initChart(g_his_data);
            } catch (error) {
                console.error("Error loading initial data:", error);
            }
        }

        let socket = new WebSocket("ws://localhost:8000/ws");

        function sendStockCode() {
            const formattedCode = g_stk_code.split(".").reverse().join("").toLowerCase();
            socket.send(JSON.stringify({ stock_code: formattedCode }));
        }

        setInterval(sendStockCode, 2000);

        socket.onmessage = function (event) {
            let data = JSON.parse(event.data);
            if (data.high > 0 && data.date > data.old_date) {
                
                // 更新历史数据
                if (g_his_data.dates.length < g_his_data_len) {
                    g_his_data.dates.push(data.date);
                    g_his_data.candlestick.push([data.open, data.now, data.low, data.high]);
                } else {
                    g_his_data.dates[g_his_data_len] = data.date;
                    g_his_data.candlestick[g_his_data_len] = [data.open, data.now, data.low, data.high];
                }

                // 更新图表的K线数据
                chart.setOption({
                    series: [{
                        data: g_his_data.candlestick,
                    }],
                    xAxis: [{
                        data: g_his_data.dates,
                        min: Math.max(0, g_his_data.dates.length - visibleCount), // 控制X轴最小值
                        max: g_his_data.dates.length - 1,
                    }]
                }, false); // false表示不重绘整个图表
            }
        };

        socket.onopen = function () {
            console.log("WebSocket connection opened");
            sendStockCode();
        };

        socket.onclose = function () {
            console.log("WebSocket connection closed");
        };

        socket.onerror = function (error) {
            console.error("WebSocket error:", error);
        };
    </script>
</body>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏