vue2实现元素拖拽新增删除操作后的撤销和恢复功能

需求是页面新增删除元素操作或拖拽元素操作后,通过点击撤销和恢复按钮撤销上一步操作或恢复被撤销的操作,下面是在html中引入vue2实现的demo,可直接复制到本地查看

<html>

<head>
  <title>元素操作撤销恢复</title>
  <style>
    .container {
      position: relative;
      width: 500px;
      height: 300px;
      border: 1px solid #ccc;
      margin-bottom: 10px;
    }

    .draggable {
      position: absolute;
      background-color: #f0f0f0;
      border: 1px solid #999;
      padding: 10px;
      cursor: move;
      z-index: 99;
    }

    .actions {
      margin-top: 10px;
    }

    .delete {
      position: fixed;
      left: 550px;
      top: 10px;
      background-color: red;
      width: 150px;
      height: 150px;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="container">
      <!-- 拖拽区域 -->
      <div v-for="item in items" :key="item.id" :ref="item.id" class="draggable"
        :style="`top: ${item.top}px; left: ${item.left}px`" @mousedown="startDrag(item, $event)">
        {{ item.text }}
      </div>
      <div class="delete" v-show="items.length > 0">拖拽至此删除</div>
    </div>
    <div class="actions">
      <!-- 操作按钮 -->
      <button @click="addItem">新增</button>
      <button :disabled="!canUndo" @click="undo">撤销</button>
      <button :disabled="!canRedo" @click="redo">恢复</button>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

  <script>
    new Vue({
      el: '#app',
      data: {
        items: [], // 存储拖拽元素的数组
        undoStack: [], // 撤销操作的栈
        redoStack: [], // 恢复操作的栈
      },
      computed: {
        canUndo() {
          return this.undoStack.length > 0;
        },
        canRedo() {
          return this.redoStack.length > 0;
        }
      },
      methods: {
        // 开始拖拽元素
        startDrag(item, event) {
          const startX = item.left;
          const startY = item.top;
          const container = this.$el.querySelector('.container');
          const rect = container.getBoundingClientRect();

          const offsetX = event.clientX - item.left - rect.left;
          const offsetY = event.clientY - item.top - rect.top;

          const moveHandler = (event) => {
            const left = event.clientX - rect.left - offsetX;
            const top = event.clientY - rect.top - offsetY;

            item.left = left;
            item.top = top;
          };

          const endHandler = () => {
            document.removeEventListener('mousemove', moveHandler);
            document.removeEventListener('mouseup', endHandler);

            // 判断鼠标松开时的位置是否在删除区域内
            const deleteContainer = document.querySelector('.delete');
            const deleteRect = deleteContainer.getBoundingClientRect();
            if (
              event.clientX > deleteRect.left &&
              event.clientX < deleteRect.right &&
              event.clientY > deleteRect.top &&
              event.clientY < deleteRect.bottom
            ) {
              // 执行删除操作
              const index = this.items.findIndex((i) => i.id === item.id);
              if (index > -1) {
                const deletedItem = this.items.splice(index, 1)[0];
                this.undoStack.push({ type: 'delete', item: deletedItem, index });
                this.redoStack = []; // 删除元素后清空恢复栈
              }
            } else {
              // 添加拖拽操作记录到undoStack栈
              this.undoStack.push({ type: 'drag', item, startPos: { left: startX, top: startY }, endPos: { left: item.left, top: item.top } });
              this.redoStack = []; // 修改元素后清空恢复栈
            }
          };

          // 监听鼠标移动和松开事件
          document.addEventListener('mousemove', moveHandler);
          document.addEventListener('mouseup', endHandler);
        },

        // 撤销操作
        undo() {
          const lastAction = this.undoStack.pop();
          if (lastAction.type === 'add') {
            const index = this.items.findIndex((item) => item.id === lastAction.item.id);
            if (index > -1) {
              this.items.splice(index, 1);
              this.redoStack.push(lastAction);
            }
          } else if (lastAction.type === 'delete') {
            this.items.push(lastAction.item);
            this.redoStack.push(lastAction);
          } else if (lastAction.type === 'drag') {
            // 将拖拽操作记录添加到redoStack栈
            this.redoStack.push(lastAction);
            // 还原元素拖拽后的位置
            lastAction.item.left = lastAction.startPos.left;
            lastAction.item.top = lastAction.startPos.top;
          }
        },

        // 恢复操作
        redo() {
          const lastAction = this.redoStack.pop();
          if (lastAction.type === 'add') {
            this.items.push(lastAction.item);
            this.undoStack.push(lastAction);
          } else if (lastAction.type === 'delete') {
            const index = this.items.findIndex((item) => item.id === lastAction.item.id);
            if (index > -1) {
              this.items.splice(index, 1);
              this.undoStack.push(lastAction);
            }
          } else if (lastAction.type === 'drag') {
            // 将拖拽操作记录添加到undoStack栈
            this.undoStack.push(lastAction);
            // 还原元素拖拽后的位置
            lastAction.item.left = lastAction.endPos.left;
            lastAction.item.top = lastAction.endPos.top;
          }
        },
        // 新增元素
        addItem() {
          const newItem = {
            id: Date.now(),
            text: `元素 ${this.items.length + 1}`,
            top: 0,
            left: 0
          };
          this.items.push(newItem);
          this.undoStack.push({ type: 'add', item: newItem });
          this.redoStack = []; // 新增元素后清空恢复栈
        },
      }
    });
  </script>
</body>

</html>

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值