如何在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;
关键修复说明
- 使用useRef引用Canvas:通过
canvasRef.current直接获取DOM元素,符合React组件模型,避免DOM查询的不确定性。 - 修正索引逻辑:用
i = (i + 1) % data.length替代错误的三元表达式,确保每次循环i只递增一次,到达数组末尾后重置为0,完整遍历所有数据点。 - 数据类型转换:将分割后的字符串数组转换为数字数组,避免字符串除法导致的计算错误。
- 修复style格式:改用React内联样式对象
{{ backgroundColor: 'black' }},确保样式正常生效。 - 添加动画清理:在useEffect的返回函数中调用
cancelAnimationFrame,避免组件卸载后动画继续执行造成内存泄漏。
内容的提问来源于stack exchange,提问作者Mallikarjun S Modi
相关产品推荐
相关产品推荐

