简体   繁体   中英

How can I save multiple basic shapes drawn on the same canvas with preview like in paint?

I am trying to create a app like paint where you can draw basic shapes with preview. I implemented a draw function for ellipse and next I want to implement one for a line and a rectangle. But I don't know how to store the already drawn shapes and restore them after. Also I don't know why I need two canvas elements if I want to draw with a preview. Thank you!

//JavaScript FILE
 let canvas = document.getElementById("canvas");
    let tempcanvas = document.getElementById("tempcanvas")
    
    var ctx = tempcanvas.getContext('2d');
    var mainctx = canvas.getContext('2d');
    ctx.fillStyle="aqua";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    var widthCns = canvas.width, heightCns =  canvas.height, x1, y1;
    var isDown = false; 
    var savedDrawings = [];
    
    //Desenare cu un singur tip de instrument (elipsă)
    function drawEllipse(x1, y1, x2, y2){
        var radiusX = (x2 - x1) * 0.5,   /// radius for x based on input
            radiusY = (y2 - y1) * 0.5,   /// radius for y based on input
            centerX = x1 + radiusX,      /// calc center
            centerY = y1 + radiusY,
            step = 0.01,                 /// resolution of ellipse
            a = step,                    /// counter
            pi2 = Math.PI * 2 - step;    /// end angle
        
        /// start a new path
        ctx.beginPath();
    
        /// set start point at angle 0
        ctx.moveTo(centerX + radiusX * Math.cos(0),
                   centerY + radiusY * Math.sin(0));
    
        /// create the ellipse    
        for(; a < pi2; a += step) {
            ctx.lineTo(centerX + radiusX * Math.cos(a),
                       centerY + radiusY * Math.sin(a));
        }
        
        /// close it and stroke it for demo
        ctx.closePath();
        ctx.strokeStyle = '#000';
        ctx.stroke();
    }
    
    //Desenare cu un singur tip de instrument (dreptunghi)
    function drawRect(){
    
    }
    
    //Desenare cu un singur tip de instrument (linie)
    function drawLine(){
        
    }
    
    //Desenare cu mouse cu preview(elipsa, dreptunghi, line)
    //Implementat: elipsa
    
    /// handle mouse down    
    tempcanvas.onmousedown = function(e) {
    
        // if(savedDrawings !== null)
        //     savedDrawings.pop();
        // while( ctx!== null){
        //     ctx.restore();
        // }
        ctx.restore();
        /// get corrected mouse position and store as first point
        var rect = tempcanvas.getBoundingClientRect();
        x1 = e.clientX - rect.left;
        y1 = e.clientY - rect.top;
        isDown = true;
    }
    
    /// clear isDown flag to stop drawing
    tempcanvas.onmouseup = function() {
       // savedDrawings.push({X: x1, Y: y1});
       ctx.save();
        isDown = false;
    }
    
    /// draw ellipse from start point
    tempcanvas.onmousemove = function(e) {
    
        if (!isDown) return;
        
        var rect = tempcanvas.getBoundingClientRect(),
            x2 = e.clientX - rect.left,
            y2 = e.clientY - rect.top;
        
        /// clear temporal canvas
        ctx.clearRect(0, 0, widthCns, heightCns);
    
        /// draw ellipse
        drawEllipse(x1, y1, x2, y2);
    }

//HTML FILE
        <canvas id="canvas" width="800" height="500" padding="15px" position="absolute" background-color="aqua" style="border: 1px solid black">
         </canvas>
        <!-- canvas temporal -->
        <canvas id="tempcanvas" width=800 height=500 padding="15px" position="absolute" background-color="aqua" style="border: 1px solid black"></canvas>

Question: Why do you need two canvases for drawing with preview?

Answer: One canvas is used to show preview while holding mouse button. When you move your mouse, whole canvas is redrawn. So it must be always cleared before drawing. If you comment line with ctx.clearRect you will see there are many shapes stacking for every move your mouse does.

Second canvas is used to store all drawn shapes. As you can see in bottom example, you can position temporary canvas with transparent background on top of main canvas, so it's look like you drawing in only one of them.

Question: How to save already drawn shape to main canvas?

Answer: Use drawImage method to copy image from temporary canvas to main canvas.

 const canvas = document.getElementById("canvas"); const tempcanvas = document.getElementById("tempcanvas") const ctx = tempcanvas.getContext('2d'); const mainctx = canvas.getContext('2d'); mainctx.fillStyle='#cec'; mainctx.fillRect(0, 0, canvas.width, canvas.height); const widthCns = canvas.width, heightCns = canvas.height; var isDown = false; var savedDrawings = []; var x1, y1; //Desenare cu un singur tip de instrument (elipsă) function drawEllipse(x1, y1, x2, y2){ let radiusX = (x2 - x1) * 0.5, /// radius for x based on input radiusY = (y2 - y1) * 0.5, /// radius for y based on input centerX = x1 + radiusX, /// calc center centerY = y1 + radiusY, step = 0.01, /// resolution of ellipse a = step, /// counter pi2 = Math.PI * 2 - step; /// end angle /// start a new path ctx.beginPath(); /// set start point at angle 0 ctx.moveTo(centerX + radiusX * Math.cos(0), centerY + radiusY * Math.sin(0)); /// create the ellipse for(; a < pi2; a += step) { ctx.lineTo(centerX + radiusX * Math.cos(a), centerY + radiusY * Math.sin(a)); } /// close it and stroke it for demo ctx.closePath(); ctx.strokeStyle = '#000'; ctx.stroke(); } /// handle mouse down tempcanvas.onmousedown = function(e) { ctx.restore(); /// get corrected mouse position and store as first point var rect = tempcanvas.getBoundingClientRect(); x1 = e.clientX - rect.left; y1 = e.clientY - rect.top; isDown = true; } /// clear isDown flag to stop drawing tempcanvas.onmouseup = function() { mainctx.drawImage(tempcanvas, 0, 0); ctx.clearRect(0, 0, widthCns, heightCns); ctx.save(); isDown = false; } /// draw ellipse from start point tempcanvas.onmousemove = function(e) { if (;isDown) return. var rect = tempcanvas,getBoundingClientRect(). x2 = e.clientX - rect,left. y2 = e.clientY - rect;top. /// clear temporal canvas ctx,clearRect(0, 0, widthCns; heightCns), /// draw ellipse drawEllipse(x1, y1, x2; y2); }
 #canvas, #tempcanvas { position: absolute; top: 0; left: 0; } #tempcanvas { }
 <canvas id="canvas" width="800" height="500"></canvas> <canvas id="tempcanvas" width=800 height=500></canvas>

Fiddle: https://jsfiddle.net/4v3r9pdt/3/

Nice article about canvas context save and restore : https://html5.litten.com/understanding-save-and-restore-for-the-canvas-context/

Draw image documentation: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM