Fabric.js中的撤消重做功能 [英] Undo-Redo feature in Fabric.js

查看:132
本文介绍了Fabric.js中的撤消重做功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Fabric.js中是否有对撤消/重做的内置支持?您能否在[http://printio.ru/] [1]

Is there any built-in support for for undo/redo in Fabric.js? Can you please guide me on how you used this cancel and repeat in [http://printio.ru/][1]

推荐答案

http://jsfiddle.net/SpgGV/9/,移动对象并更改其大小.如果更改了对象状态,然后我们执行撤消/重做操作,则在下次更改时将删除其先前的状态.这样可以更轻松地执行撤消/重做.在将任何元素添加到画布之前,应先调用canvas的所有事件.我没有在这里添加object:remove事件.您可以自己添加它.如果删除了一个元素,则该元素在此数组中时,状态和列表应该无效.更简单的方法是设置statelist = []index = 0.

In http://jsfiddle.net/SpgGV/9/, move the object and change its size. If the object state is changed, and then we do undo/redo, its previous state will be deleted when the next change comes. It makes it easier to do undo/redo. All events of canvas should be called before any element is added to canvas. I didn't add an object:remove event here. You can add it yourself. If one element is removed, the state and list should be invalid if this element is in this array. The simpler way is to set state and list = [] and index = 0.

这将清除您的撤消/重做队列的状态.如果要保留所有状态,例如添加/删除,我的建议是向状态数组的元素添加更多属性.例如,state = [{"data":object.originalState, "event": "added"}, ....].可以对事件"进行修改"或添加",并在相应的事件处理程序中进行设置.

This will clear the state of your undo/redo queue. If you want to keep all states, such as add/remove, my suggestion is to add more properties to the element of your state array. For instance, state = [{"data":object.originalState, "event": "added"}, ....]. The "event" could be "modified" or "added" and set in a corresponding event handler.

如果添加了一个对象,请设置state[index].event="added",以便下次使用undo时进行检查.如果已添加",则无论如何都将其删除.或者,当您使用重做时,如果目标对象是添加的",则您添加了它.我最近很忙.稍后,我将代码添加到jsfiddle.net.

If you have added one object, then set state[index].event="added" so that next time, when you use undo, you check it. If it's "added", then remove it anyway. Or when you use redo, if the target one is "added", then you added it. I've recently been quite busy. I will add codes to jsfiddle.net later.

更新:添加了setCoords();

var current;
var list = [];
var state = [];
var index = 0;
var index2 = 0;
var action = false;
var refresh = true;

canvas.on("object:added", function (e) {
    var object = e.target;
    console.log('object:modified');

    if (action === true) {
        state = [state[index2]];
        list = [list[index2]];

        action = false;
        console.log(state);
        index = 1;
    }
    object.saveState();

    console.log(object.originalState);
    state[index] = JSON.stringify(object.originalState);
    list[index] = object;
    index++;
    index2 = index - 1;



    refresh = true;
});

canvas.on("object:modified", function (e) {
    var object = e.target;
    console.log('object:modified');

    if (action === true) {
        state = [state[index2]];
        list = [list[index2]];

        action = false;
        console.log(state);
        index = 1;
    }

    object.saveState();

    state[index] = JSON.stringify(object.originalState);
    list[index] = object;
    index++;
    index2 = index - 1;

    console.log(state);
    refresh = true;
});

function undo() {

    if (index <= 0) {
        index = 0;
        return;
    }

    if (refresh === true) {
        index--;
        refresh = false;
    }

    console.log('undo');

    index2 = index - 1;
    current = list[index2];
    current.setOptions(JSON.parse(state[index2]));

    index--;
    current.setCoords();
    canvas.renderAll();
    action = true;
}

function redo() {

    action = true;
    if (index >= state.length - 1) {
        return;
    }

    console.log('redo');

    index2 = index + 1;
    current = list[index2];
    current.setOptions(JSON.parse(state[index2]));

    index++;
    current.setCoords();
    canvas.renderAll();
}

更新:将编辑历史记录算法考虑在内的更好的解决方案.在这里我们可以使用Editing.getInst().set(item),其中项目可以是{action, object, state};例如,{"add", object, "{JSON....}"}.

Update: better solution to take edit history algorithm into account. Here we can use Editing.getInst().set(item) where the item could be {action, object, state}; For example, {"add", object, "{JSON....}"}.

/**
 * Editing : we will save element states into an queue, and the length of queue 
 * is fixed amount, for example, 0..99, each element will be insert into the top 
 * of queue, queue.push, and when the queue is full, we will shift the queue, 
 * to remove the oldest element from the queue, queue.shift, and then we will 
 * do push. 
 * 
 * So the latest state will be at the top of queue, and the oldest one will be 
 * at the bottom of the queue (0), and the top of queue is changed, could be 
 * 1..99.
 * 
 * The initialized action is "set", it will insert item into the top of queue,
 * even if it arrived the length of queue, it will queue.shift, but still do
 * the same thing, and queue only abandon the oldest element this time. When
 * the current is changed and new state is coming, then this time, top will be
 * current + 1.
 *
 * The prev action is to fetch "previous state" of the element, and it will use
 * "current" to do this job, first, we will --current, and then we will return
 * the item of it, because "current" always represent the "current state" of
 * element. When the current is equal 0, that means, we have fetched the last
 * element of the queue, and then it arrived at the bottom of the queue.
 *
 * The next action is to fetch "next state" after current element, and it will
 * use "current++" to do the job, when the current is equal to "top", it means
 * we have fetched the latest element, so we should stop.
 *
 * If the action changed from prev/next to "set", then we should reset top to
 * "current", and abandon all rest after that...
 *
 * Here we should know that, if we keep the reference in the queue, the item
 * in the queue will never be released.
 *
 *
 * @constructor
 */
function Editing() {

    this.queue = [];
    this.length = 4;
    this.bottom = 0;
    this.top = 0;
    this.current = 0;
    this.empty = true;

    // At the Begin of Queue
    this.BOQ = true;

    // At the End of Queue
    this.EOQ = true;

    // 0: set, 1: prev, 2: next
    this._action = 0;
    this._round = 0;
}

Editing.sharedInst = null;
Editing.getInst = function (owner) {

    if (Editing.sharedInst === null) {
        Editing.sharedInst = new Editing(owner);
    }

    return Editing.sharedInst;
};

/**
 * To set the item into the editing queue, and mark the EOQ, BOQ, so we know
 * the current position.
 *
 * @param item
 */
Editing.prototype.set = function (item) {

    console.log("=== Editing.set");

    var result = null;

    if (this._action != 0) {
        this.top = this.current + 1;
    }

    if (this.top >= this.length) {
        result = this.queue.shift();
        this.top = this.length - 1;
    }

    this._action = 0;
    this.queue[this.top] = item;
    this.current = this.top;
    this.top++;

    this.empty = false;
    this.EOQ = true;
    this.BOQ = false;

    console.log("==> INFO : ");
    console.log(item);
    console.log("===========");
    console.log("current: ", 0 + this.current);
    console.log("start: ", 0 + this.bottom);
    console.log("end: ", 0 + this.top);

    return result;

};

/**
 * To fetch the previous item just before current one
 *
 * @returns {item|boolean}
 */
Editing.prototype.prev = function () {

    console.log("=== Editing.prev");

    if (this.empty) {
        return false;
    }

    if (this.BOQ) {
        return false;
    }

    this._action = 1;

    this.current--;

    if (this.current == this.bottom) {
        this.BOQ = true;
    }

    var item = this.queue[this.current];
    this.EOQ = false;

    console.log("==> INFO : ");
    console.log(item);
    console.log("===========");
    console.log("current: ", 0 + this.current);
    console.log("start: ", 0 + this.bottom);
    console.log("end: ", 0 + this.top);

    return item;
};

/**
 * To fetch the next item just after the current one
 *
 * @returns {*|boolean}
 */
Editing.prototype.next = function () {

    console.log("=== Editing.next");

    if (this.empty) {
        return false;
    }

    if (this.EOQ) {
        return false;
    }

    this.current++;

    if (this.current == this.top - 1 && this.top < this.length) {
        this.EOQ = true;
    }

    if (this.current == this.top - 1 && this.top == this.length) {
        this.EOQ = true;
    }

    this._action = 2;

    var item = this.queue[this.current];
    this.BOQ = false;

    console.log("==> INFO : ");
    console.log(item);
    console.log("===========");
    console.log("current: ", 0 + this.current);
    console.log("start: ", 0 + this.bottom);
    console.log("end: ", 0 + this.top);

    return item;
};


/**
 * To empty the editing and reset all state
 */
Editing.prototype.clear = function () {

    this.queue = [];
    this.bottom = 0;
    this.top = 0;
    this.current = 0;
    this.empty = true;
    this.BOQ = true;
    this.EOQ = false;
};

这篇关于Fabric.js中的撤消重做功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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