0

I am designing a paint bucket app. My code is working fine. Just need a little help. On zoom in and zoom out buttons, I am changing the height and width of canvas. How can I apply animation to it? This is my complete code. The zoom part is at the end. You can simply copy paste it inside a file. It will work.

<!DOCTYPE html>
<html>
        <head>
        <title>Painitng</title>
        <style>
body {
    width: 100%;
    height: auto;
    text-align: center;
}
.colorpick {
    widh: 100%;
    height: atuo;
}
.pick {
    display: inline-block;
    width: 30px;
    height: 30px;
    margin: 5px;
    cursor: pointer;
}
canvas {
    border: 2px solid silver;
}
</style>
        </head>
        <body>
<button id="zoomin">Zoom In</button>
<button id="zoomout">Zoom Out</button>
<button id = "undo-button" onclick="history('undo')">Undo</button>
<button id = "redo-button" onclick="history('redo')">Redo</button>
<div id="canvasDiv"></div>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script> 
<script type="text/javascript">
            var colorYellow = {
                r: 255,
                g: 207,
                b: 51
            };
            var context;
            var canvasWidth = 500;
            var canvasHeight = 500;
            var myColor = colorYellow;
            var curColor = myColor;
            var outlineImage = new Image();
            var backgroundImage = new Image();
            var drawingAreaX = 0;
            var drawingAreaY = 0;
            var drawingAreaWidth = 500;
            var drawingAreaHeight = 500;
            var colorLayerData;
            var outlineLayerData;
            var totalLoadResources = 2;
            var curLoadResNum = 0;
            var undoarr = new Array();
            var redoarr = new Array();

            function history(command){ // handles undo/redo button events.
            var data;
            if(command === "redo"){
                data = historyManager.redo(); // get data for redo
            }else
            if(command === "undo"){
                data = historyManager.undo(); // get data for undo
            }
            if(data !== undefined){ // if data has been found
                setColorLayer(data); // set the data
            }
        }

        // sets colour layer and creates copy into colorLayerData
        function setColorLayer(data){
            context.putImageData(data, 0, 0);  
            colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
            context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
            context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);            
        }

            // Clears the canvas.
            function clearCanvas() {
                context.clearRect(0, 0, context.canvas.width, context.canvas.height);
            }



            // Draw the elements on the canvas
            function redraw() {
                uc = 0;
                rc = 0;
                var locX,
                        locY;

                // Make sure required resources are loaded before redrawing
                if (curLoadResNum < totalLoadResources) {
                    return; // To check if images are loaded successfully or not.
                }

                clearCanvas();
                // Draw the current state of the color layer to the canvas
                context.putImageData(colorLayerData, 0, 0);

                historyManager.push(context.getImageData(0, 0, canvasWidth, canvasHeight));
                redoarr = new Array();
                // Draw the background
                context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);

                // Draw the outline image on top of everything. We could move this to a separate 
                //   canvas so we did not have to redraw this everyime.
                context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);


            }
            ;

            function matchOutlineColor(r, g, b, a) {

                return (r + g + b < 100 && a === 255);
            }
            ;

            function matchStartColor(pixelPos, startR, startG, startB) {

                var r = outlineLayerData.data[pixelPos],
                        g = outlineLayerData.data[pixelPos + 1],
                        b = outlineLayerData.data[pixelPos + 2],
                        a = outlineLayerData.data[pixelPos + 3];

                // If current pixel of the outline image is black
                if (matchOutlineColor(r, g, b, a)) {
                    return false;
                }

                r = colorLayerData.data[pixelPos];
                g = colorLayerData.data[pixelPos + 1];
                b = colorLayerData.data[pixelPos + 2];

                // If the current pixel matches the clicked color
                if (r === startR && g === startG && b === startB) {
                    return true;
                }

                // If current pixel matches the new color
                if (r === curColor.r && g === curColor.g && b === curColor.b) {
                    return false;
                }

                return true;
            }
            ;

            function colorPixel(pixelPos, r, g, b, a) {
                colorLayerData.data[pixelPos] = r;
                colorLayerData.data[pixelPos + 1] = g;
                colorLayerData.data[pixelPos + 2] = b;
                colorLayerData.data[pixelPos + 3] = a !== undefined ? a : 255;
            }
            ;

            function floodFill(startX, startY, startR, startG, startB) {
                var newPos,
                        x,
                        y,
                        pixelPos,
                        reachLeft,
                        reachRight,
                        drawingBoundLeft = drawingAreaX,
                        drawingBoundTop = drawingAreaY,
                        drawingBoundRight = drawingAreaX + drawingAreaWidth - 1,
                        drawingBoundBottom = drawingAreaY + drawingAreaHeight - 1,
                        pixelStack = [[startX, startY]];

                while (pixelStack.length) {

                    newPos = pixelStack.pop();
                    x = newPos[0];
                    y = newPos[1];

                    // Get current pixel position
                    pixelPos = (y * canvasWidth + x) * 4;

                    // Go up as long as the color matches and are inside the canvas
                    while (y >= drawingBoundTop && matchStartColor(pixelPos, startR, startG, startB)) {
                        y -= 1;
                        pixelPos -= canvasWidth * 4;
                    }

                    pixelPos += canvasWidth * 4;
                    y += 1;
                    reachLeft = false;
                    reachRight = false;

                    // Go down as long as the color matches and in inside the canvas
                    while (y <= drawingBoundBottom && matchStartColor(pixelPos, startR, startG, startB)) {
                        y += 1;

                        colorPixel(pixelPos, curColor.r, curColor.g, curColor.b);

                        if (x > drawingBoundLeft) {
                            if (matchStartColor(pixelPos - 4, startR, startG, startB)) {
                                if (!reachLeft) {
                                    // Add pixel to stack
                                    pixelStack.push([x - 1, y]);
                                    reachLeft = true;
                                }

                            } else if (reachLeft) {
                                reachLeft = false;
                            }
                        }

                        if (x < drawingBoundRight) {
                            if (matchStartColor(pixelPos + 4, startR, startG, startB)) {
                                if (!reachRight) {
                                    // Add pixel to stack
                                    pixelStack.push([x + 1, y]);
                                    reachRight = true;
                                }
                            } else if (reachRight) {
                                reachRight = false;
                            }
                        }

                        pixelPos += canvasWidth * 4;
                    }
                }
            }
            ;

            // Start painting with paint bucket tool starting from pixel specified by startX and startY
            function paintAt(startX, startY) {

                var pixelPos = (startY * canvasWidth + startX) * 4,
                        r = colorLayerData.data[pixelPos],
                        g = colorLayerData.data[pixelPos + 1],
                        b = colorLayerData.data[pixelPos + 2],
                        a = colorLayerData.data[pixelPos + 3];

                if (r === curColor.r && g === curColor.g && b === curColor.b) {
                    // Return because trying to fill with the same color
                    return;
                }

                if (matchOutlineColor(r, g, b, a)) {
                    // Return because clicked outline
                    return;
                }

                floodFill(startX, startY, r, g, b);

                redraw();
            }
            ;

            // Add mouse event listeners to the canvas
            function createMouseEvents() {

                $('#canvas').mousedown(function (e) {

                    // Mouse down location
                    var mouseX = e.pageX - this.offsetLeft,
                            mouseY = e.pageY - this.offsetTop;

                    // assuming that the mouseX and mouseY are the mouse coords.
                    if(this.style.width){   // make sure there is a width in the style 
                    // (assumes if width is there then height will be too
                        var w = Number(this.style.width.replace("px",""));  // warning this will not work if size is not in pixels
                        var h = Number(this.style.height.replace("px","")); // convert the height to a number
                        var pixelW = this.width;  // get  the canvas resolution
                        var pixelH = this.height;
                        mouseX = Math.floor((mouseX / w) * pixelW); // convert the mouse coords to pixel coords
                        mouseY = Math.floor((mouseY / h) * pixelH);
                    }

                    if ((mouseY > drawingAreaY && mouseY < drawingAreaY + drawingAreaHeight) && (mouseX <= drawingAreaX + drawingAreaWidth)) {
                        paintAt(mouseX, mouseY);
                    }
                });
            }
            ;

            resourceLoaded = function () {

                curLoadResNum += 1;
                //if (curLoadResNum === totalLoadResources) {
                createMouseEvents();
                redraw();
                //}
            };

            var historyManager = (function (){  // Anon for private (closure) scope
            var uBuffer = []; // this is undo buff
            var rBuffer = []; // this is redo buff
            var currentState = undefined; // this holds the current history state
            var undoElement = undefined;
            var redoElement = undefined;
            var manager = {
                UI : {  // UI interface just for disable and enabling redo undo buttons
                    assignUndoButton : function(element){
                        undoElement = element;
                        this.update();
                    },
                    assignRedoButton : function(element){
                        redoElement = element;
                        this.update();
                    },
                    update : function(){
                        if(redoElement !== undefined){
                            redoElement.disabled = (rBuffer.length === 0);
                        }
                        if(undoElement !== undefined){
                            undoElement.disabled = (uBuffer.length === 0);                                
                        }
                    }
                },
                reset : function(){
                    uBuffer.length = 0;
                    rBuffer.length = 0;
                    currentState = undefined;
                    this.UI.update();
                },
                push : function(data){

                    if(currentState !== undefined){
                        var same=true
                        for(i=0;i<data.data.length;i++){
                            if(data.data[i] !== currentState.data[i]){
                                same = false;break;
                            }
                        } if(same){
                            return;
                        }
                    }

                    if(currentState !== undefined){
                        uBuffer.push(currentState);                        
                    }
                    currentState = data;
                    rBuffer.length = 0;
                    this.UI.update();
                },
                undo : function(){
                   if(uBuffer.length > 0){
                       if(currentState !== undefined){
                            rBuffer.push(currentState);                        
                        }
                        currentState = uBuffer.pop();
                    }
                    this.UI.update();
                    return currentState; // return data or unfefined
                },
                redo : function(){
                    if(rBuffer.length > 0){
                       if(currentState !== undefined){
                            uBuffer.push(currentState);                        
                        }
                        currentState = rBuffer.pop();
                    }
                    this.UI.update();    
                    return currentState;
                },
            }
            return manager;
        })();

            function start() {

                var canvas = document.createElement('canvas');
                canvas.setAttribute('width', canvasWidth);
                canvas.setAttribute('height', canvasHeight);
                canvas.setAttribute('id', 'canvas');
                document.getElementById('canvasDiv').appendChild(canvas);

                if (typeof G_vmlCanvasManager !== "undefined") {
                    canvas = G_vmlCanvasManager.initElement(canvas);
                }
                context = canvas.getContext("2d");
                backgroundImage.onload = resourceLoaded();
                backgroundImage.src = "images/t1.png";

                outlineImage.onload = function () {
                    context.drawImage(outlineImage, drawingAreaX, drawingAreaY, drawingAreaWidth, drawingAreaHeight);

                    try {
                        outlineLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
                    } catch (ex) {
                        window.alert("Application cannot be run locally. Please run on a server.");
                        return;
                    }
                    clearCanvas();
                    colorLayerData = context.getImageData(0, 0, canvasWidth, canvasHeight);
                    resourceLoaded();
                };
                outlineImage.src = "images/d.png";
            }
            ;

            if(historyManager !== undefined){
                // only for visual feedback and not required for the history manager to function.
                historyManager.UI.assignUndoButton(document.querySelector("#undo-button"));
                historyManager.UI.assignRedoButton(document.querySelector("#redo-button"));
            }

            getColor = function () {

            };

        </script> 
<script type="text/javascript"> $(document).ready(function () {
                start();
            });</script> 
<script language="javascript">
            $('#zoomin').click(function () {
                if ($("#canvas").width()==500){
                $("#canvas").width(750);
                $("#canvas").height(750);
                var ctx = canvas.getContext("2d");
                ctx.drawImage(backgroundImage, 0, 0, 749, 749);
                ctx.drawImage(outlineImage, 0, 0, 749, 749);
                redraw();
                 } else if ($("#canvas").width()==750){

                $("#canvas").width(1000);
                $("#canvas").height(1000);
                var ctx = canvas.getContext("2d");
                ctx.drawImage(backgroundImage, 0, 0, 999, 999);
                ctx.drawImage(outlineImage, 0, 0, 999, 999);
                redraw();
                 }
            });
            $('#zoomout').click(function () {
                if ($("#canvas").width() == 1000) {

                $("#canvas").width(750);
                $("#canvas").height(750);
                var ctx = canvas.getContext("2d");
                ctx.drawImage(backgroundImage, 0, 0, 749, 749);
                ctx.drawImage(outlineImage, 0, 0, 749, 749);
                redraw();
                } else if ($("#canvas").width() == 750) {

                $("#canvas").width(500);
                $("#canvas").height(500);
                var ctx = canvas.getContext("2d");
                ctx.drawImage(backgroundImage, 0, 0, 499, 499);
                ctx.drawImage(outlineImage, 0, 0, 499, 499);
                redraw();
                }
            });
        </script>
<div class="colorpick">
          <div class="pick" style="background-color:rgb(150, 0, 0);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(0, 0, 152);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(0, 151, 0);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(255, 0, 5);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(255, 255, 0);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(0, 255, 255);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(255, 0, 255);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(255, 150, 0);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(255, 0, 150);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(0, 255, 150);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(150, 0, 255);" onclick="hello(this.style.backgroundColor);"></div>
          <div class="pick" style="background-color:rgb(0, 150, 255);" onclick="hello(this.style.backgroundColor);"></div>
        </div>
<script>
            function hello(e) {
                var rgb = e.replace(/^(rgb|rgba)\(/, '').replace(/\)$/, '').replace(/\s/g, '').split(',');
                myColor.r = parseInt(rgb[0]);
                myColor.g = parseInt(rgb[1]);
                myColor.b = parseInt(rgb[2]);
                curColor = myColor;
            }
        </script>
</body>
</html>
Ali Zia
  • 3,825
  • 5
  • 29
  • 77
  • Note you can make this into a stack snippet or fiddle so we can see and modify the code immediately. Also what about using jquery's [`animate()`](http://api.jquery.com/animate/) method? – Spencer Wieczorek May 05 '16 at 08:20
  • I don't know how to create a fiddle. Can you help me with that? Yes I tried to animate() but it didn't worked as expected. – Ali Zia May 05 '16 at 08:21
  • Go to: https://jsfiddle.net/. Copy your CSS code into the CSS portion (top right), and the same for your HTML and JS. Then to include jQuery select "Javascript (*gear*)" then under *FRAMEWORKS & EXTENSIONS* select jQuery such as *"jQuery 1.12.2"*. Then hit *Run* in the top left corner. – Spencer Wieczorek May 05 '16 at 08:46

1 Answers1

0

You could continuously redraw the canvas content as you're resizing, but there is a more efficient way to do it.

Alternatively...

Continuously redrawing the canvas content is resource intensive. A more efficient way to handle the zoom-animation is to use CSS to do it. When the animation completes, only then resize the canvas and redraw the content at its final zoomed scale. It's true that while animating the transition the canvas content may temporarily appear stretched or squished, but it all happens so quickly that it will not be noticeable.

  1. Save the unzoomed canvas to another in-memory canvas: var memCanvas = canvas.cloneNode() & drawImage the visible canvas to the memCanvas.

  2. Use CSS animations to resize the canvas element to a zoom size. Here's an example on a previous SO Q&A. The animation completes so quickly it probably won't be noticeable if the content resizes disproportionally.

  3. When the animation is complete (the canvas is at its full-zoomed size) you can scale the saved drawing to the new size with drawImage( savedCanvas, 0,0, originalWidth*zoomFactor, originalHeight*zoomFactor) and reset the CSS width and height to the original size.

Community
  • 1
  • 1
markE
  • 102,905
  • 11
  • 164
  • 176