【问题标题】:How to add undo-functionality to HTML5 Canvas?如何向 HTML5 Canvas 添加撤消功能?
【发布时间】:2010-06-23 06:18:55
【问题描述】:

我有一个使用所有 HTML5 和 Javascript 编写的草图应用程序,我想知道如何创建一个撤消按钮,以便您可以撤消您最后绘制的内容。有什么想法吗?

【问题讨论】:

标签: javascript html canvas


【解决方案1】:

您必须将所有修改存储在数据结构中。然后,如果用户想要撤消它,您可以删除最新的修改。然后再次从数据结构中重新绘制所有绘图操作。

【讨论】:

  • 请参阅whiffdoc.appspot.com/tests/schema/diagram 了解如何执行此操作的示例——在这种情况下,通过将表示画布信息的 JSON 数据结构存储在不可见的 DIV 元素中。 (点击页面上的链接到源)
  • @AaronWatters,此链接将我引导至 404。您有更新的链接吗?
【解决方案2】:

http://arthurclemens.github.io/Javascript-Undo-Manager/ 我有一个使用画布元素撤消的工作示例。当您进行修改时,您向撤消管理器提供撤消和重做方法。撤消堆栈中的位置跟踪是自动完成的。源代码在 Github。

【讨论】:

  • 我已经修复了演示中的几个小绘图错误。
  • 这个demo在Chrome和Opera中画了各种颜色的圆圈,如果是FF什么都没有,在IE中画黑色。很奇怪。解决此问题后,我很乐意跟进。
  • 我已更新演示 URL,使其指向最新版本。
【解决方案3】:

如果您需要操作对象,另一种选择是使用保留 Canvas API 以防止重写的库将您的画布转换为 SVG。

目前至少存在一个这样的库(2011 年 11 月): SVGKit

拥有 SVG 后,移除对象会容易得多,而且无需重新绘制整个画布。

【讨论】:

  • 或者他可以只使用画布抽象库,例如fabric.js,以便能够以编程方式处理画布上的对象。
  • 优秀的@kangax。我很好奇,当涉及到在具有数百个节点的画布中编辑对象时,您认为 fabric.js 与 SVGKit 相比在性能方面如何?
  • 我不确定它与 SVGKit 相比如何,但当涉及到大量对象时,canvas 往往比 SVG 性能更高。看一些比较 Raphael(基于 SVG)和 fabric(基于画布)的基准——fabricjs.com/benchmarks
  • 感谢基准测试,但这些基准与 Raphaël 相比仅适用于静态图像,这显然会支持 Canvas。你有动画的比较基准吗?
  • 暂时没有。我看看能不能煮点东西。或者也许其他人会。
【解决方案4】:

这是一个适合我的解决方案。我已经在最新版本的 Firefox 和 Chrome 中进行了尝试,并且在这两个浏览器中运行良好。

var isFirefox = typeof InstallTrigger !== 'undefined';
var ctx = document.getElementById('myCanvas').getContext("2d");
var CanvasLogBook = function() {
    this.index = 0;
    this.logs = [];
    this.logDrawing();
};
CanvasLogBook.prototype.sliceAndPush = function(imageObject) {
    var array;
    if (this.index == this.logs.length-1) {
        this.logs.push(imageObject);
        array = this.logs;
    } else {
        var tempArray = this.logs.slice(0, this.index+1);
        tempArray.push(imageObject);
        array = tempArray;
    }
    if (array.length > 1) {
        this.index++;
    }
    return array;
};
CanvasLogBook.prototype.logDrawing = function() { 
    if (isFirefox) {
        var image = new Image();
        image.src = document.getElementById('myCanvas').toDataURL();
        this.logs = this.sliceAndPush(image);
    } else {
        var imageData = document.getElementById('myCanvas').toDataURL();
        this.logs = this.sliceAndPush(imageData);
    }
};
CanvasLogBook.prototype.undo = function() {
    ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height());
    if (this.index > 0) {
        this.index--;
        this.showLogAtIndex(this.index);
    }
};
CanvasLogBook.prototype.redo = function() {
    if (this.index < this.logs.length-1) {
        ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height());
        this.index++;
        this.showLogAtIndex(this.index);
    }
};
CanvasLogBook.prototype.showLogAtIndex = function(index) {
    ctx.clearRect(0, 0, $('#myCanvas').width(), $('#myCanvas').height());
    if (isFirefox) {
        var image = this.logs[index];
        ctx.drawImage(image, 0, 0);
    } else {
        var image = new Image();
        image.src = this.logs[index];
        ctx.drawImage(image, 0, 0);
    }
};
var canvasLogBook = new CanvasLogBook();

所以每次你绘制任何东西时,你都会在运行函数 canvasLogBook.logDrawing() 来存储画布的快照,然后你可以调用 canvasLogBook.undo() 来撤消和 canvasLogBook.redo() 来重做。

【讨论】:

  • 如果您不依赖 jQuery 并且摆脱了 isFirefox 布尔值,这将是一个很好的答案 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-31
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
相关资源
最近更新 更多