context.putImageData() 在 React 中不起作用

context.putImageData() Not working in React

我正在尝试在 React 中创建一个绘图应用程序,它的大部分工作正常。但是当我尝试添加一个撤消按钮时它不起作用。

我尝试使用此 finishDrawing() 函数制作撤消按钮,我在其中使用 getImageData 并将其存储在名为 restore_array

的数组中
    const finishDrawing = () => {
        //console.log(contextRef.current)
        contextRef.current.closePath()
        setIsDrawing(false)
        //restore_array.push(contextRef.current.getImageData(0, 0,400,256+80))
        setIndex_array(index_array + 1)
        setRestore_Array((prevState) => { return [...prevState, contextRef.current.getImageData(0, 0,400,256+80)]})
        console.log(restore_array, restore_array.length)
        
    }

然后,当单击“撤消”按钮时,我尝试使用 useEffectrestore_array 中重新加载此图像,这应该使用之前的图像 [=] 重新渲染主菜 canvas 20=]

    function undoLast() {
        console.log(restore_array[index_array], index_array, restore_array)
        if (index_array > -1) {
            setIndex_array(index_array-1)
            restore_array.pop()
            setLines(!lines)

        }
        //contextRef.current.putImageData(restore_array[index_array-1], 0, 0)   
    }

    useEffect(() => {
        console.log('use effect 3c')
        console.log(restore_array)
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d")
        context.lineWidth = thick
        context.lineCap = "round"
        context.strokeStyle = colour
        if (restore_array.length === 0) {
            console.log('empty')
            return
        }
        //context.clearRect(0,0,400,256+80)
        //context.fillRect(0,0,400,256+80)
        context.putImageData(restore_array[index_array], 0, 0, 0, 0, 400, 256+80)
        contextRef.current = context
    }, [lines])

有谁知道为什么我的 putImageData 在这种情况下不起作用?

这是我的全部代码供参考

import React, { useEffect, useRef, useState } from "react";



function DrawCanvas() {

    const canvasRef = useRef(null)
    const contextRef = useRef(null)
    const [isDrawing, setIsDrawing] = useState(false)
    const [colour, setColour] = useState('black')
    const [prcolour, setPrColour] = useState(null)
    const [thick, setThick] = useState(3)
    const [image, setImage] = useState(null)
    const [restore_array, setRestore_Array] = useState([])
    const [index_array, setIndex_array] = useState(-1)
    const [clear_, setClear_] = useState(true)
    const [lines, setLines] = useState(true)
    //let restore_array = []





    useEffect(() => {
        const canvas = canvasRef.current;
        canvas.width = window.innerWidth/1.5 ;
        canvas.height = window.innerHeight/1.5 ;
        canvas.style.width = `${window.innerWidth/3}px`;
        canvas.style.height = `${window.innerHeight/3}px`;
        const context = canvas.getContext("2d")
        context.scale(2,2)
        context.lineCap = "round"
        context.lineWidth = 3
        context.strokeStyle = "black"
        //const img = DogImage
        context.fillStyle = 'gray'
        context.fillRect(0,0,400,256+80)
        contextRef.current = context
        
    }, [])




    useEffect(() => {
        console.log('use effect 1a')
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d")
        context.lineWidth = thick
        context.lineCap = "round"
        context.strokeStyle = colour
        contextRef.current = context
        document.getElementById(colour).style.borderColor = 'gray'
        if(prcolour === null) {
            return
        }
        document.getElementById(prcolour).style.borderColor = 'black'
        document.getElementById(prcolour).style.borderWidth = '3px'

    }, [colour])

    useEffect(() => {
        console.log('use effect 2b')
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d")
        context.lineWidth = thick
        context.lineCap = "round"
        context.strokeStyle = colour
        const Dogimage = new Image();
        Dogimage.src = '../../images/dog-without-labels.png'
        Dogimage.onLoad = () => {
            console.log('load image')
            context.current.drawImage(Dogimage, 0, 0,400,256+80);
        }
        contextRef.current = context
    }, [thick])

    useEffect(() => {
        console.log('use effect 3c')
        console.log(restore_array)
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d")
        context.lineWidth = thick
        context.lineCap = "round"
        context.strokeStyle = colour
        if (restore_array.length === 0) {
            console.log('empty')
            return
        }
        //context.clearRect(0,0,400,256+80)
        //context.fillRect(0,0,400,256+80)
        context.putImageData(restore_array[index_array], 0, 0, 0, 0, 400, 256+80)
        contextRef.current = context
    }, [lines])

    useEffect(() => {
        console.log('use effect 4d')
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d")
        //contextRef.fillStyle = 'gray'
        context.lineWidth = thick
        context.lineCap = "round"
        context.strokeStyle = colour
        context.clearRect(0,0,400,256+80)
        context.fillRect(0,0,400,256+80)
        contextRef.current = context
    }, [clear_])

    


    function undoLast() {
        console.log(restore_array[index_array], index_array, restore_array)
        if (index_array > -1) {
            setIndex_array(index_array-1)
            restore_array.pop()
            setLines(!lines)

        }
        //contextRef.current.putImageData(restore_array[index_array-1], 0, 0)   
    }

    const clearCanvas = () => {
        setClear_(!clear_)
    }



    const startDrawing = ({nativeEvent}) => {
        const {offsetX, offsetY} = nativeEvent
        contextRef.current.beginPath()
        contextRef.current.moveTo(offsetX, offsetY)
        setIsDrawing(true)

    }

    const finishDrawing = () => {
        //console.log(contextRef.current)
        contextRef.current.closePath()
        setIsDrawing(false)
        //restore_array.push(contextRef.current.getImageData(0, 0,400,256+80))
        setIndex_array(index_array + 1)
        setRestore_Array((prevState) => { return [...prevState, contextRef.current.getImageData(0, 0,400,256+80)]})
        console.log(restore_array, restore_array.length)
        
    }

    const draw = ({nativeEvent}) => {
        if (!isDrawing) {
            return
        }
        const {offsetX, offsetY} = nativeEvent;
        contextRef.current.lineTo(offsetX, offsetY)
        contextRef.current.stroke()
    }

    const colourChange = (value) => {
        setPrColour(colour)
        setColour(value)
    }

    const thickChange = (event) => {
        setThick(event)
    }
      
      
    return (
    <div>
        <div className="left">
            
            
                <canvas className="canvas" onMouseDown={startDrawing}
                        onMouseUp={finishDrawing}
                        onMouseMove={draw}
                        ref={canvasRef}/>
        </div>
        <div className="right">
            <h5><dt>Chart Draw</dt></h5>
            <p>Use your finger or your mouse to draw directly on the chart opposite.</p>

            <button className="draw">Draw</button>
            <button className="select">Select</button>

            <p><dt>Line thickness: {thick} </dt> </p>
            <input className="slider" type="range" min="1" max="19" value={thick} class="slider" id="myRange" step="1" onChange={(event)=>thickChange(event.target.value)}></input>
            
            <button className='red' id='red'onClick={() => colourChange('red')}>  </button>
            <button className='black' id='black'  onClick={() => colourChange('black')}>  </button>
            <button className='yellow' id='rgba(204,153,0,255)' onClick={() => colourChange('rgba(204,153,0,255)')}>  </button>
            <button className='blue' id='rgba(1,102,255,255)' onClick={() => colourChange('rgba(1,102,255,255)')}>  </button>
            <button className="undoButton" onClick={()=>undoLast()}> <dt> Undo/Back a step</dt></button>
            <button className="undoButton" onClick={()=>clearCanvas()}> <dt> Clear canvas</dt></button>
            <button className="savecloseButton"> <dt>Save and Close</dt></button>

        </div>
    </div>
    )

}

export default DrawCanvas;

你的代码对我来说“没问题”...
我认为很明显你的 putImageData 函数 undoLast 被注释了。
我假设这是故意的,而不是实际问题。


我们可以看到它在这里工作:

是的,只有一部分是撤消。
那是因为您在 getImageData:

中有硬编码值
setRestore_Array((prevState) => { 
  return [...prevState, contextRef.current.getImageData(0, 0,400,256+80)]
})

那只获得了 canvas 的一部分,所以只有那部分正在恢复...
如果您需要整个 canvas,请将其更改为使用 canvas 维度。
类似于:

setRestore_Array((prevState) => { 
  return [...prevState, contextRef.current.getImageData(0, 0, canvasRef.current.width, canvasRef.current.height)]
})

另外你应该看看提到的,他有一个很好的观点:

storing an Array of drawing commands will cause your memory to explode

对于使用图像数据的方法,我的建议是将撤消操作限制在一个固定数量,例如 10 次并丢弃旧的,这将保证内存消耗上限并防止任何内存不足错误。