2016-10-17 3 views
0

Я пытаюсь создать функцию отмены/повтора для jQueryUI .draggable на данный момент у меня есть функция, но не работает, как ожидают, вот мой несильно:Undo & Redo функция не работает, как ожидают

$(document).ready(function() { 
 
    $('.editable').draggable({ 
 
    stop: stopHandlerDrag, 
 
    start: startHandlerDrag 
 

 
    }); 
 
    
 
}); 
 

 
var historyApp = { 
 
    stackStyle: [], 
 
    stackId: [], 
 
    counter: -1, 
 
    add: function(style, id) { 
 

 
    ++this.counter; 
 
    this.stackStyle[this.counter] = style; 
 
    this.stackId[this.counter] = id; 
 
    this.doSomethingWith(style, id); 
 

 
    // delete anything forward of the counter 
 
    this.stackStyle.splice(this.counter + 1); 
 
    }, 
 
    undo: function() { 
 
    --this.counter; 
 
    this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]); 
 
    }, 
 
    redo: function() { 
 
    ++this.counter; 
 
    this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]); 
 

 
    }, 
 
    doSomethingWith: function(style, id) { 
 

 
    //Check if make buttons undo/redo disabled or enabled 
 
    if (this.counter <= -1) { 
 
     $('#undo').addClass('disabled'); 
 
     $('#redo').removeClass('disabled'); 
 
     return; 
 
    } else { 
 
     $('#undo').removeClass('disabled'); 
 
    } 
 

 
    if (this.counter == this.stackStyle.length) { 
 
     $('#redo').addClass('disabled'); 
 
     $('#undo').removeClass('disabled'); 
 
     return; 
 
    } else { 
 
     $('#redo').removeClass('disabled'); 
 
    } 
 

 
    console.log(style + ' - ' + id); 
 
    //Apply history style 
 
    $('#' + id).attr('style', style); 
 

 
    console.log(this.counter + ' - ' + this.stackStyle.length); 
 
    } 
 
}; 
 

 
//Stop Handler Drag 
 
function stopHandlerDrag(event, ui) { 
 
    console.log('stop drag'); 
 
    var style = $(ui.helper).attr('style'); 
 
    var id = $(ui.helper).attr('id'); 
 
    historyApp.add(style, id); 
 

 
} 
 

 
//Star Handler Drag 
 
function startHandlerDrag(event, ui) { 
 
    console.log('start drag'); 
 
    var style = $(ui.helper).attr('style'); 
 
    var id = $(ui.helper).attr('id'); 
 
    historyApp.add(style, id); 
 

 
    //Dettach all events 
 
    $('#' + id).draggable("option", "revert", false); 
 
    //reassign stop events 
 
    $('#' + id).draggable({ 
 
    stop: stopHandlerDrag, 
 
    start: '' 
 
    }); 
 

 
} 
 

 
//Click Events For Redo and Undo 
 
$(document).on('click', '#redo', function() { 
 
    historyApp.redo(); 
 
}); 
 

 
$(document).on('click', '#undo', function() { 
 
    historyApp.undo(); 
 
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.0/jquery-ui.min.js"></script> 
 

 
<span id="undo" class="btn btn-sm btn-success disable">Undo</span> 
 
<span id="redo" class="btn btn-sm btn-danger disable ">Redo</span>         
 

 
<p id="P1" class="editable" style="left: auto; top:auto;">Drag #1</<p> 
 
<p id="P2" class="editable" style="left: auto; top:auto;">Drag #2</<p> 
 
<p id="P3" class="editable" style="left: auto; top:auto;">Drag #3</<p>

а вот Working code demo

Теперь проблема в том, что, например, если вы запустите мой код и перетаскивать элементы в следующем порядке (Drag # 1, Drag # 2, 3 Drag #) , а затем нажмите на undo иногда вам придется дважды щелкнуть, а затем, если вы перетащите этот порядок и щелкните по undo, чтобы восстановить все элементы, как в начале, а затем drag elements #1 and #2 and then click on undo, вы заметите, что только элемент № 1 вернется к нему так что мой вопрос в том, что я делаю неправильно, и как я могу это решить? Я думаю, что, может быть, мой счетчик неправильно или что-то не так с моим варом historyApp

ответ

0

Вы, вероятно, необходимо добавить логику, чтобы проверить, если стиль undo или redo отличается от предыдущего/следующего состояния:

 add: function (style, id) { 
      var preIndex = this.counter; 
      if (this.counter == this.stackStyle.length - 1) 
      { 
       ++this.counter; 
      } 
      else 
      { 
       this.counter = this.stackStyle.length; 
      } 
      this.stackStyle[this.counter] = style; 
      this.stackId[this.counter] = id; 
      this.doSomethingWith(style, id); 
     }, 
     undo: function() { 
      --this.counter; 
      while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter]) 
      { 
       --this.counter; 
      } 
      this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]); 
     }, 
     redo: function() { 
      ++this.counter; 
      while ($('#' + this.stackId[this.counter]).attr('style') == this.stackStyle[this.counter]) { 
       ++this.counter; 
      } 
      this.doSomethingWith(this.stackStyle[this.counter], this.stackId[this.counter]); 

     }, 
+0

С этим мне нужно нажать только один раз, чтобы выполнить мою функцию отмены, но другая проблема есть, перетащите элементы в следующем порядке: # 1, # 2, # 3, нажмите кнопку отмены, чтобы поместить элементы в его начало, а затем перетащите элементы # 1 и # 2, а затем нажмите кнопку отмены, вы увидите, что только элемент # 1 перемещен в его место –

+0

@MiguelFlores, я отредактировал функции 'add',' undo' и 'redo 'для лучшей обработки на' counter' в массивах состояний. –

0

Что-то не так с вашей логикой. Вот я сделал StateMaker. Не копируйте это письмо для письма, но оно показывает вам логику.

function StateMaker(initialState){ 
    var o = initialState; 
    if(o){ 
    this.initialState = o; this.states = [o]; 
    } 
    else{ 
    this.states = []; 
    } 
    this.savedStates = []; this.canUndo = this.canRedo = false; this.undoneStates = []; 
    this.addState = function(state){ 
    this.states.push(state); this.undoneStates = []; this.canUndo = true; this.canRedo = false; 
    return this; 
    } 
    this.undo = function(){ 
    var sl = this.states.length; 
    if(this.initialState){ 
     if(sl > 1){ 
     this.undoneStates.push(this.states.pop()); this.canRedo = true; 
     if(this.states.length < 2){ 
      this.canUndo = false; 
     } 
     } 
     else{ 
     this.canUndo = false; 
     } 
    } 
    else if(sl > 0){ 
     this.undoneStates.push(this.states.pop()); this.canRedo = true; 
    } 
    else{ 
     this.canUndo = false; 
    } 
    return this; 
    } 
    this.redo = function(){ 
    if(this.undoneStates.length > 0){ 
     this.states.push(this.undoneStates.pop()); this.canUndo = true; 
     if(this.undoneStates.length < 1){ 
     this.canRedo = false; 
     } 
    } 
    else{ 
     this.canRedo = false; 
    } 
    return this; 
    } 
    this.save = function(){ 
    this.savedStates = this.states.slice(); 
    return this; 
    } 
    this.isSavedState = function(){ 
    var st = this.states, l = st.length; sv = this.savedStates; 
    if(l !== sv.length){ 
     return false; 
    } 
    function rec(o, c){ 
     var x, z; 
     if(typeof o !== typeof c){ 
     return false; 
     } 
     else if(o instanceof Array){ 
     for(var n=0,q=o.length; n<q; n++){ 
      x = o[n]; z = c[n]; 
      if(x && typeof x === 'object'){ 
      return rec(x, z); 
      } 
      else if(o !== c){ 
      return false; 
      } 
     } 
     } 
     else if(o && typeof o === 'object'){ 
     for(var n in o){ 
      x = o[n]; z = c[n]; 
      if(x && typeof x === 'object'){ 
      return rec(x, z); 
      } 
      else if(o !== c){ 
      return false; 
      } 
     } 
     } 
     else if(o !== c){ 
     return false; 
     } 
     return true; 
    } 
    for(var i=0; i<l; i++){ 
     if(!rec(st[i], sv[i])){ 
     return false; 
     } 
    } 
    return true; 
    } 
} 

Реализация с JQuery UI будет выглядеть примерно так:

var dragging; 
function DragMaster(yourDraggable){ 
    var d = yourDraggable, p = d.offset(), sm = new StateMaker({left:p.left, top:p.top})), states = sm.states, t = this; 
    function ls(){ 
    if(states.length){ 
     return states.slice(-1)[0]; 
    } 
    return false; 
    } 
    this.update = function(){ 
    var cp = d.offset(); 
    sm.addState({left:cp.left, top:cp.top}); 
    return this; 
    } 
    this.undo = function(){ 
    sm.undo(); d.offset(ls()); 
    return this; 
    } 
    this.redo = function(){ 
    sm.redo(); d.offset(ls()); 
    return this; 
    } 
    this.save = function(){ 
    sm.save(); 
    return this; 
    } 
    this.getStates = function(){ 
    return states; 
    } 
    this.getSavedStates = function(){ 
    return sm.savedStates; 
    } 
    this.init = function(){ 
    return { 
     start: function(){ 
     dragging = d; 
     }, 
     stop: function(){ 
     t.update(); 
     } 
    } 
    } 
} 
var yd = $('#yourDraggable'), dm = new DragMaster(yd); 
yd.draggable(dm.init()); 
$('#undo').click(function(){ 
    if(dragging === yd)dm.undo(); 
}); 
$('#redo').click(function(){ 
    if(dragging === yd)dm.redo(); 
}); 

Теперь вы можете создать new DragMaster() с и отправлять .init() в перетаскиваемым.

Примечание:

Если вы хотите увидеть, если текущее состояние является сохраненное состояние, чтобы изменить цвет вашей экономии кнопки, то StateMakerInstance.isSavedState() является то, что вы можете использовать.