撤消/重做不正常,缩放后绘画也不能正常工作 [英] Undo / redo not working properly and painting after zoom not working properly too

查看:129
本文介绍了撤消/重做不正常,缩放后绘画也不能正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用撤消和重做功能实现一个paint bucket工具。问题是undo和redo第一次正常工作,但是当我多次撤消重做时,代码失败了。任何人都可以帮我解决这个问题吗?此外,缩放功能正常,但缩放后绘画无法正常工作。这是我的完整代码。您可以复制粘贴,它将在您的最后工作。

I am trying to implement a paint bucket tool with undo and redo functionality. The issue is that undo and redo are working properly the first time, but when I do undo redo multiple times, the code fails. Can anyone help me figure the issue out? Also zoom is working, but painting after zoom does not work correctly. This is my complete code. You can just copy paste and it will work at your end.

<!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 onclick="undo()">Undo</button>
        <button onclick="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();
            var uc = 0;
            var rc = 0;

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

            function undo() {
                if (undoarr.length <= 0)
                    return;

                if (uc==0) {
                    redoarr.push(undoarr.pop());
                    uc = 1;
                }
                var a = undoarr.pop();
                colorLayerData = a;
                redoarr.push(a);
                clearCanvas();
                context.putImageData(a, 0, 0);
                context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
                context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
                console.log(undoarr);
            }

            function redo() {
                if (redoarr.length <= 0)
                    return;
                if (rc==0) {
                    undoarr.push(redoarr.pop());
                    rc = 1;
                }
                var a = redoarr.pop();
                colorLayerData = a;
                undoarr.push(a);
                clearCanvas();
                context.putImageData(a, 0, 0);
                context.drawImage(backgroundImage, 0, 0, canvasWidth, canvasHeight);
                context.drawImage(outlineImage, 0, 0, drawingAreaWidth, drawingAreaHeight);
                console.log(redoarr);
            }
            // 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);

                undoarr.push(context.getImageData(0, 0, canvasWidth, canvasHeight));
                console.log(undoarr);
                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;

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

            resourceLoaded = function () {

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

            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";
            }
            ;

            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;
                console.log(curColor);
            }
        </script>
    </body>
</html>


推荐答案

画布尺寸&州历史

画布尺寸

如果你曾经有过在DOM中环顾四周你会注意到许多元素都有高度和宽度作为属性,高度和宽度都作为样式属性。

If you have ever had a look around in the DOM you will notice that many element have both a height and width as an attribute and a height and a width as a style attribute.

对于画布这些有两个不同的含义。所以让我们创建一个画布。

For the canvas these have two different meanings. So lets create a canvas.

var canvas = document.createElement("canvas");

现在可以设置画布元素的宽度和高度。这定义了画布图像中的像素数(分辨率)

Now the canvas element width and height can be set. This defines then number of pixels in the canvas image (the resolution)

canvas.width = 500;
canvas.height = 500;

默认情况下,当DOM中显示图像(画布只是图像)时,它会显示为一对一像素大小。这意味着对于图像中的每个像素,页面上都有一个像素。

By default when an image (canvas is just an image) is displayed in the DOM it is displayed with a one to one pixel size. That means that for each pixel in the image there is one pixel on the page.

您可以通过设置画布样式宽度和高度来更改此值

You can change this by setting the canvas style width and height

canvas.style.width = "1000px"; // Note you must add the unit type "px" in this case
canvas.style.width = "1000px";

这不会改变画布分辨率,只会改变显示尺寸。现在,对于画布中的每个像素,它占用页面上的4个像素。

This does not change the canvas resolution, just the display size. Now for each pixel in the canvas it takes up 4 pixels on the page.

当您使用鼠标绘制到画布时,当鼠标坐标成为问题时屏幕像素不再与画布分辨率匹配。

This becomes a problem when you are using the mouse to draw to the canvas as the mouse coordinates are in screen pixels that no longer match the canvas resolution.

要解决此问题。并作为OP代码的一个例子。您需要重新缩放鼠标坐标以匹配画布分辨率。这已添加到OP mousedown事件侦听器中。它首先获得显示宽度/高度,然后是分辨率宽度和高度。它通过除以显示宽度/高度来标准化鼠标坐标。这使得鼠标坐标在0 <=鼠标<0的范围内。然后我们乘以1得到画布像素坐标。由于像素需要位于整数位置(整数),因此必须对结果进行处理。

To fix this. And as an example from the OP code. You need to rescale the mouse coordinates to match the canvas resolution. This has been added to the OP mousedown event listener. It first gets the display width/height then the resolution width and height. It normalises the mouse coords by dividing by the display width/height. This brings the mouse coords to a range of 0 <= mouse < 1 which then we multiply to get the canvas pixel coordinates. As the pixels need to be at integer locations (whole numbers) you must floor the result.

// 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);
}

这将解决您的扩展问题。但是查看您的代码,它是一团糟,您不应该每次都在搜索nodetree ,重新获取上下文。我很惊讶它有效,但那可能是Jquery(我不知道,因为我从来没有使用它)或者可能是你在其他地方渲染。

That will fix your scaling problem. But looking at your code, its is a mess and you should not be searching the nodetree each time, re getting the context. I am surprised it works, but that might be Jquery (I don't know as I never use it) or might be that you are rendering elsewhere.

州历史

计算机程序的当前状态是定义当前状态的所有条件和数据。当您保存某些状态时,保存状态,并在加载时恢复状态。

The current state of a computer program is all the conditions and data that define the current state.. When you save something you are saving a state, and when you load you restore the state.

历史只是一种保存和加载状态的方法,而不会在文件系统中乱七八糟。它有一些约定,表明统计数据存储为堆栈。第一个是最后一个,它有一个重做堆栈,允许你重做以前的撤消,但保持正确的状态,因为状态依赖于以前的状态,重做只能从关联状态重做。因此,如果您撤消然后绘制某些内容,则会使任何现有重做状态无效,并且应该将它们转储。

History is just a way of saving and loading states without the messing around in the file system. It has a few conventions that say that the stats are stored as a stack. The first in is the last out, it has a redo stack that allows you to redo previous undos but to maintain the correct state and because states are dependent on previous states the redo can only redo from associated states. Hence if you undo and then draw something you invalidate any existing redo states and they should be dumped.

此外,保存的状态,无论是磁盘还是撤消堆栈,都必须与当前状态分离。如果您对当前状态进行了更改,则不希望这些更改影响已保存的状态。

Also the saved state, be it on disk, or undo stack must be dissociated from the current state. IF you make changes to the current state you do not want those changes to effect the saved state.

我认为这是您出错的地方OP,因为您使用的是 colorLayerData 在你撤消或重做时填充(绘画)使用撤消/重做缓冲区中保留的引用数据,因此在绘制时你实际上正在更改数据仍然在撤消缓冲区中。

This I think is where you went wrong OP, as you were using the colorLayerData to fill (paint) when you got a undo or redo you where using the referenced data that remained in the undo/redo buffers thus when you painted you actually were changing the data still in the undo buffer.

历史记录管理器

这是一个通用目的状态管理器并且可以满足任何撤销/重做需求,您所要做的就是确保将当前状态收集到一个对象中。

This is a general purpose state manager and will work for any undo/redo needs, all you have to do is ensure that you gather the current state into a single object.

帮助我写一个简单的历史经理。它有两个缓冲区作为堆栈,一个用于undos,另一个用于redos。它还保持当前状态,这是它所知道的最新状态。

To help I have written a simple history manager. It has two buffers as stacks one for undos and one for redos. It also holds the current state, which is the most recent state it knows about.

当您推送到历史记录管理器时,它将获取它所知道的当前状态并将其推送到撤消堆栈,保存当前状态,并使任何重做数据无效(使重做数组长度为0)

When you push to the history manager it will take the current state it knows about and push it to the undo stack, save the current state, and invalidate any redo data (making the redo array length 0)

撤消时将当前状态推送到重做堆栈,从撤消堆栈弹出状态并将其置于当前状态,然后它将返回当前状态。

When you undo it will push the current state onto the redo stack, pop a state from the undo stack and put it in current state, then it will return that current state.

当你重做它会将当前状态推送到撤销堆栈,从重做堆栈弹出状态并将其置于当前状态,然后它将返回当前状态。

When you redo it will push the current state onto the undo stack, pop a state from the redo stack and put it in current state, then it will return that current state.

重要的是你要复制状态管理器返回的状态,这样你就不会无意中更改存储的数据在缓冲区。

It is important that you make a copy of the state returned from the state managers so that you do not inadvertently change the data stored in the buffers.

你可能会问。 为什么州政府经理不能确保数据是副本?一个很好的问题,但这不是一个州经理的角色,它保存了州,它必须这样做,无论它需要保存什么,它本质上完全没有意识到它存储的数据的含义。这样它就可以用于图像,文本,游戏状态,任何东西,就像文件系统一样,它不能(不应该)意识到其含义,因此知道如何创建有意义的副本。你推送到状态管理器的数据只是像素数据的一个参考(64位长),或者你可以推动像素数据的每个字节,它不知道差异。

You may ask. "why cant the state manager ensure that the data is a copy?" A good question but this is not the role of a state manager, it saves states and it must do so no matter what it has to save, it is by nature completely unaware of the meaning of the data it stores. This way it can be used for images, text, game states, anything, just as the file system can, it can not (should not) be aware of the meaning and thus know how to create meaningful copies. The data you push to the state manager is just a single referance (64bits long) to the pixel data or you could push each byte of the pixel data, it does not know the difference.

也是OP我已经向状态管理器添加了一些UI控件。这允许它显示其当前状态即禁用并启用撤消重做按钮。对于良好的UI设计来说,提供反馈总是很重要。

Also OP I have added some UI control to the state manager. This allows it to display its current state Ie disables and enables the undo redo buttons. Its always important for good UI design to provide feedback.

代码

您需要对代码进行以下所有更改才能使用历史记录管理器。您可以这样做,或者只是将其作为指南并编写自己的指南。我在检测到你的错误之前写了这个。如果这是唯一的错误,那么您可能只需要更改。

You will need to make all the following changes to your code to use the history manager. You can do that or just use this as a guide and write your own. I wrote this before I detected your error. If that is the only error then you may only need to change.

   // your old code (from memory)
   colorLayerData = undoArr.pop(); 
   context.putImageData(colorLayerData, 0, 0);  


   // the fix same applies to redo and just makes a copy rather than use 
   // the reference that is still stored in the undoe buff
   context.putImageData(undoArr, 0, 0);   // put the undo onto the canvas
   colorLayerData = context.getImageData(0, 0, canvasWidth, canvaHeight); 

删除撤消/重做的所有代码。

Remove all the code you have for the undo/redo.

将页面顶部的撤消/重做按钮更改为,只需一个函数即可处理这两个事件。

Change the undo/redo buttons at top of page to, with a single function to handle both events.

    <button id = "undo-button" onclick="history('undo')">Undo</button>
    <button id = "redo-button" onclick="history('redo')">Redo</button>

为您的代码添加以下两个函数

Add the following two functions to you code

        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);            
        }

在重绘功能中,您已经替换了用于撤消的内容并添加了此项排在同一地点。这会将当前状态保存在历史记录管理器中。

In the redraw function you have replace the stuff you had for undo and add this line at the same spot. This saves the current state in the history manager.

           historyManager.push(context.getImageData(0, 0, canvasWidth, canvasHeight));

在启动功能中,您必须将UI元素添加到状态管理器。这取决于你,如果没有定义,可以忽略统计经理将忽略它们。

In the start function you have to add the UI elements to he state manager. This is up to you and can be ignored the stat manager will just ignore them if they are not defined.

            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"));
            }

当然还有历史经理自己的。它封装了数据,因此除非通过接口提供,否则无法访问其内部状态。

And off course the historyManager its self. It encapsulates the data so that you can not access its internal state except via the interface provides.

historyManager(hM)API

The historyManager (hM) API


  • hM.UI ui经理只更新并分配按钮
    禁用/启用状态

  • hM.UI.assignUndoButton(element)设置撤消元素

  • hM.UI .assignRedoButton(元素)设置重做元素

  • nM.UI.update()更新按钮声明反映当前的
    内部状态。所有内部状态都会自动调用,因此只有在您更改重做/撤消按钮统计信息时才需要

  • hM.reset()重置历史管理器清除所有堆栈和当前保存的状态。加载或创建新项目时调用此方法。

  • nM.push(data)将提供的数据添加到历史记录中。

  • nM.undo()获取上一个历史状态并返回存储的数据。如果没有数据,那么这将返回undefined。

  • nM.redo()获取下一个历史状态并返回存储的数据。如果没有数据,那么这将返回undefined。

  • hM.UI The ui manager just updates and assigns button disabled/enabled states
  • hM.UI.assignUndoButton(element) set the undo element
  • hM.UI.assignRedoButton(element) set the redo element
  • nM.UI.update() Updates the button states to reflect the current internal state. All internal states are automatically call this so only needed if you are changing the redo/undo buttons stats your self
  • hM.reset() Resets the history manager clearing all the stacks and current saved states. Call this when you load or create a new project.
  • nM.push(data) Add the provided data to the history.
  • nM.undo() get the previous history state and return the data stored. If no data then this will return undefined.
  • nM.redo() get the next history state and return the data stored. If no data then this will return undefined.

自调用函数创建历史管理器,通过变量historyManager访问接口

The self invoking function creates the history manager, the interface is accessed via the variable historyManager

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){
                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;
})();

That will fix your Zoom problem and the undo problem. Best of luck with your project.

That will fix your Zoom problem and the undo problem. Best of luck with your project.

这篇关于撤消/重做不正常,缩放后绘画也不能正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆