You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React.js中渲染Canvas?解决渲染空白问题

React中Canvas血氧波形显示空白的修复方案

问题分析与修复步骤

1. React中DOM元素引用方式错误

直接使用document.getElementById('canvas')获取元素存在潜在风险,React更推荐用useRef引用DOM元素,避免DOM查询的不确定性,尤其适配组件的生命周期逻辑。

2. 数据索引递增逻辑错误

原代码中py=(data[++i>=data.length? i=0 : i++]/300+10);的逻辑会导致i每次循环被递增两次(++i和i++),跳过大量数据点,直接导致波形无法正常绘制。

3. React内联style格式错误

React要求内联style必须是对象形式,原代码的字符串格式style="background-color: black"不符合语法规范,会导致样式失效。

4. 未清理动画循环导致内存泄漏

组件卸载时,requestAnimationFrame的循环不会自动停止,会造成内存泄漏,需要在组件卸载时主动取消动画帧。


修复后的完整代码

import React, { useEffect, useRef } from 'react';

function Spo2() {
  const canvasRef = useRef(null);
  const animationRef = useRef(null);

  const EcgData = [
    "3700,4800,5900,6700,7400,7700,7800,7700,7600,7400,7200,7100,7000,7000,7000,7000,7100,7100,7100,7000,6800,6600,6300,6000,5800,5500,5300,5100,4900,4800,4600,4500,4300,4100,3900,3700,3500,3300,3100,2900,2800,2600,2500,2300,2300,2500,3000,3600,4600,5600,6700,7600,8200,8600,8800,8800,8600,8400,8200,7900,7700,7500,7400,7400,7400,7500,7500,7400,7300,7100,6800,6500,6200,5800,5500,5200,4900,4700,4500,4400,4200,4100,3900,3800,3600,3400,3200,3200,3300,3800,4500,5500,6700,7800,8700,9400,9500,9500,9400,9200,8900,8600,8400,8200,8000,7900,7900,7900,7900,7800,7700,7500,7200,6800,6400,6100,5700,5400,5100,4800,4600,4500,4300,4100,3900,3800,3600,3300,3100,2900,2600,2300,2100,1900,1900,2200,2800,3800,5000,6300,7600,8600,9300,9500,9500,9400,9100,8900,8600,8300,8100,8000,7900,7900,7900,7900,7800,7700,7500,7200,6800,6400,6100,5700,5400,5100,4900,4600,4500,4300,4100,3900,3700,3500,3200,3000,2700,2400,2100,1900,1700,1400,1200,1000,900,700,800,1100,1700,2600,3800,5100,6300,7400,8100,8400,8500,8400,8200,7900,7600,7400,7200,7000,7000,7000,7000,7000,6800,6300,5900,4800,4400,4200,3900,3700,3500,3300,3200,3000,2800,2500,2300,2000,1800,1500,1200,1000,700,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,800,1400,2200,3300,4400,5500,6400,7100,7500,7600,7600,7500,7400,7300,7200,7100,7000,7000,7100,7100,7100,7100,7100,6900,6800,6500,6300,6000,5700,5500,5300,5100,4900,4800,4600,4500,4400,4200,4000,3800,3600,3400,3200,3000,2800,2600,2400,2300,2100,1900,1800,1600,1500,1400,1200,1100,1000,900,800,700,800,1200,1800,2800,3900,5100,6300,7200,7800,8100,8200,8100,7900,7700,7500,7300,7200,7200,7300,7400,7500,7500,7600,7500,7400,7200,6900,6600,6200,5900,5600,5400,5200,5100,4900,4800,4700,4600,4500,4300,4100,3800,3600,3300,3100"
  ];

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext("2d");
    const w = canvas.width;
    const h = canvas.height;
    const speed = 3;
    const scanBarWidth = 20;
    let i = 0;
    const data = EcgData[0].split(',').map(Number); // 转换为数字类型,避免字符串运算错误
    const color = '#00ff00';
    let px = 0;
    let opx = 0;
    let py = h / 5;
    let opy = py;

    ctx.strokeStyle = color;
    ctx.lineWidth = 2;
    ctx.setTransform(1, 0, 0, -3, 0, h);

    function drawWave() {
      px += speed;
      ctx.clearRect(px, 0, scanBarWidth, h);
      ctx.beginPath();
      ctx.moveTo(opx, opy);
      ctx.lineJoin = 'round';

      // 修正索引递增逻辑,每次只递增一次
      i = (i + 1) % data.length;
      py = (data[i] / 300) + 10;

      ctx.lineTo(px, py);
      ctx.stroke();
      opx = px;
      opy = py;

      if (opx > w) {
        px = opx = -speed;
      }

      animationRef.current = requestAnimationFrame(drawWave);
    }

    animationRef.current = requestAnimationFrame(drawWave);

    // 组件卸载时清理动画
    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, []);

  return (
    <div>
      <h1>hello world</h1>
      <canvas
        ref={canvasRef}
        width="500"
        height="160"
        style={{ backgroundColor: 'black' }}
      >
        你的浏览器不支持Canvas
      </canvas>
    </div>
  );
}

export default Spo2;

关键修复说明

  1. 使用useRef引用Canvas:通过canvasRef.current直接获取DOM元素,符合React组件模型,避免DOM查询的不确定性。
  2. 修正索引逻辑:用i = (i + 1) % data.length替代错误的三元表达式,确保每次循环i只递增一次,到达数组末尾后重置为0,完整遍历所有数据点。
  3. 数据类型转换:将分割后的字符串数组转换为数字数组,避免字符串除法导致的计算错误。
  4. 修复style格式:改用React内联样式对象{{ backgroundColor: 'black' }},确保样式正常生效。
  5. 添加动画清理:在useEffect的返回函数中调用cancelAnimationFrame,避免组件卸载后动画继续执行造成内存泄漏。

内容的提问来源于stack exchange,提问作者Mallikarjun S Modi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:15:43