Fabric.js+Canvas实现拖拽着色时大量对象导致画布更新延迟问题
React+Fabric.js 网格拖拽着色延迟问题
我正在使用React和HTML5 Canvas开发应用,采用Fabric矩形对象绘制简单网格,点击或拖拽可修改单元格颜色,且网格支持尺寸调整。20x20规格的网格着色功能运行正常,但升级到50x50规格后出现延迟,拖拽路径上的部分单元格未更新。我推测是颜色变更的渲染/处理逻辑存在问题。
问题代码
class Grid extends Component { constructor(props){ super(props); this.state = { height: 500, width:500, gaugeFactor: 1.4, mousedown: false } } componentDidMount(){ this.init(); } init = () => { var canvas = new fabric.Canvas('canvas', {renderOnAddRemove: false}); var grid = []; canvas.selection = false; fabric.Object.prototype.objectCaching = false; this.generateGrid(canvas, grid); this.allowColoring(canvas); canvas.renderAll(); this.setState({ canvas }); this.setState({ grid: grid }); }; generateGrid = (canvas, grid) => { var xPos = -1 var yPos = -1 var x = 0, y = 0; while(y < this.props.rows){ var row = []; while(x < this.props.stitches){ var rect = new fabric.Rect({ left: xPos, top: yPos, fill: 'white', width: this.state.height/this.props.rows * this.props.gaugeFactor, height: this.state.height/this.props.rows, stroke: 'black', strokeWidth: 1, selectable: false, hoverCursor: 'default', }); row.push(rect); canvas.add(rect); xPos += this.state.height/this.props.rows * this.props.gaugeFactor; x += 1; } yPos += this.state.height/this.props.rows; xPos = -1; y += 1; x = 0; grid.push(row); } } allowColoring = (canvas) => { canvas.on('mouse:down', (options) => { this.setState({mousedown: true}) if(options.target){ options.target.set('fill', this.props.activeColor); } }); canvas.on('mouse:up', () => { this.setState({mousedown: false}) }); canvas.on('mouse:over', (options)=>{ if(options.target){ if(this.state.mousedown){ options.target.set('fill', this.props.activeColor); canvas.renderAll(); } } }); } updateGauge = () => { var grid = this.state.grid; var canvas = this.state.canvas; var x = 0, y = 0; var xPos = -1, yPos = -1; while(y < this.props.rows){ while(x < this.props.stitches){ grid[y][x].set({ left: xPos, top: yPos, width: this.state.height/this.props.rows * this.props.gaugeFactor, height: this.state.height/this.props.rows, }); grid[y][x].setCoords(); xPos += this.state.height/this.props.rows * this.props.gaugeFactor; x += 1; } yPos += this.state.height/this.props.rows; xPos = -1; y += 1; x = 0; } canvas.renderAll(); } clearCanvas = () => { var grid = this.state.grid; var canvas = this.state.canvas; var x = 0, y = 0; while(y < this.props.rows){ while(x < this.props.stitches){ grid[y][x].set({ fill: '#ffffff', }); x += 1; } y += 1; x = 0; } canvas.renderAll(); } render() { return ( <div id="canvasContainer" style = {containerStyle}> <canvas id="canvas" width="1000" height="1000" style = {canvasStyle}></canvas> </div> ) } } const containerStyle = { width: '800px', height: '500px', overflow: 'auto', border: '1px solid' } const canvasStyle = { border: '1px solid #000000' }
优化建议
- 替换全量重绘为单对象重绘:
mouse:over事件中修改单元格颜色后,不要调用全量重绘方法canvas.renderAll(),改为仅重绘当前修改的矩形canvas.renderObject(options.target),可大幅降低渲染开销。 - 移除不必要的React状态:将
mousedown状态从React state改为类实例属性存储(直接赋值this.mousedown = true/false),避免高频触发React状态更新带来的额外开销。 - 开启Fabric对象缓存:删除
fabric.Object.prototype.objectCaching = false配置,Fabric的对象缓存机制会将静态对象缓存为位图,避免每次重绘都重新计算渲染属性,大幅提升大数量级对象的渲染性能。 - 提前计算重复变量:在循环外提前计算好单元格的宽高、步长等固定值,避免循环内重复计算。
- 批量合并渲染操作:如果拖拽速度过快触发大量着色请求,可通过
requestAnimationFrame将多次修改合并到同一帧渲染,避免掉帧。
已验证的解决方案
通过自定义构建Fabric包解决了该问题,由于仅需要用到fabric.Rect相关功能,裁剪掉Fabric其他冗余模块后性能大幅提升。注意如果采用该方案,需要勾选Animation选项,避免功能异常。
内容的提问来源于stack exchange,提问作者nreynoldson
相关产品推荐
相关产品推荐

