撤销和恢复

         在GIS地图数据编辑中,撤销和恢复功能也算是基本功能了,那么如何设计一个简单高效的编辑撤销和恢复模块也显得比较重要,这里本人也是用C#写了一个简单的撤销和恢复功能供大家参考,有更好的方法请不吝指教。

        设计接口:IDoable,凡是需要具有撤销和恢复功能的类都必须要实现此接口,代码如下:

    public interface IDoable
    {
        bool CanUndo();
        bool CanRedo();
        void Undo();
        void Redo();
    }

       设计泛型接口:IDoManager<T>,这个类作为需要有撤销和恢复功能的类的一个成员变量,主要用来辅助管理撤销和恢复的一些数据存储和读取,代码如下:

    interface IDoManager<T>
    {
        void PushData(T data);
        T PopData();
        void ClearDatas();
        void SetUndoLen(int len);
        bool CanUndo();
        bool CanRedo();
    }

       IDoManager<T>接口的实现类DoManager<T>:

    public class DoManager<T> : IDoManager<T>
    {

        private Queue<T> _datas;
        private int _len = 8;
        private int _cur = -1;

        public DoManager()
        {
            _datas = new Queue<T>(_len);
        }

        #region IDoManager<T> 成员

        public void PushData(T data)
        {
            if (_cur < _len)
            {
                _cur++;
                _datas.Enqueue(data);
            }
            else
            {
                _datas.Dequeue();
                _datas.Enqueue(data);
            }
        }

        public void ClearDatas()
        {
            _datas.Clear();
            _cur = -1;
        }

        public void SetUndoLen(int len)
        {
            _datas.Clear();
            _len = len;
        }

        public T PopData()
        {
            return _datas.ToArray()[_cur--];
        }

        public bool CanUndo()
        {
            if (_cur < _len)
                return true;
            return false;
        }

        public bool CanRedo()
        {
            if (_cur > -1)
                return true;
            else return false;
        }

        #endregion
    }

   

       如上几行简单的代码就实现了一个具有撤销和恢复功能的基本接口和基础类的实现。

       这里写一个简单的小例子来作为测试,如地图中,我们需要编辑数据,假如是采集面数据,采集面的时候可以支持撤销和恢复,废话不多说,具体见代码:

    public class Polygon:IDoable
    {
        private List<Point> _points = new List<Point>();
        private DoManager<Point> _doManager = new DoManager<Point>();

        public List<Point> Points
        {
            get { return _points; }
            set { _points = value; }
        }

        public Polygon()
        {
            _doManager.SetUndoLen(100);
        }

        public void AddPoint(Point pt)
        {
            _points.Add(pt);
        }



        #region IDoable 成员

        public bool CanUndo()
        {
            if (_doManager.CanUndo() && _points.Count > 1)
                return true;
            return false;
        }

        public bool CanRedo()
        {
            if (_doManager.CanRedo())
                return true;
            return false;
        }

        public void Undo()
        {
            if (CanUndo())
            {
                _doManager.PushData(_points[_points.Count - 1]);
                _points.RemoveAt(_points.Count - 1);
            }
        }

        public void Redo()
        {
            if (CanRedo())
            {
                _points.Add(_doManager.PopData());
            }
        }

        #endregion
    }


       测试方法:运行工程中demo,如下图所示,点击界面白颜色部分采集点,点击上方的撤销按钮撤销,点击上方的恢复按钮恢复撤销。

       效果如下:



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现canvas涂鸦的撤销恢复上一步功能,可以采用以下步骤: 1. 定义一个数组,用于存储每一步的绘制数据; 2. 监听canvas的touchstart、touchmove和touchend事件,将绘制数据存储到数组中; 3. 撤销操作时,从数组中取出最后一次绘制数据并删除,然后重新绘制整个canvas; 4. 恢复上一步操作时,将最后一次撤销的绘制数据重新添加到数组中,并重新绘制整个canvas。 以下是示例代码: ```javascript // 定义一个数组,用于存储每一步的绘制数据 let drawData = []; // 监听canvas的touchstart、touchmove和touchend事件 canvas.addEventListener('touchstart', function(e) { // 记录起始坐标点 let startX = e.touches[0].clientX; let startY = e.touches[0].clientY; // 绘制线条 ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(startX, startY); ctx.stroke(); // 将绘制数据添加到数组中 drawData.push({ type: 'start', x: startX, y: startY }); }); canvas.addEventListener('touchmove', function(e) { // 获取当前坐标点 let currentX = e.touches[0].clientX; let currentY = e.touches[0].clientY; // 绘制线条 ctx.lineTo(currentX, currentY); ctx.stroke(); // 将绘制数据添加到数组中 drawData.push({ type: 'move', x: currentX, y: currentY }); }); canvas.addEventListener('touchend', function(e) { // 将绘制数据添加到数组中 drawData.push({ type: 'end' }); }); // 撤销操作 function undo() { // 判断数组是否为空 if(drawData.length > 0) { // 取出最后一次绘制数据 let lastData = drawData.pop(); // 判断绘制数据类型 if(lastData.type === 'start') { // 如果是起始点,则需要删除之前的绘制数据 while(drawData.length > 0 && drawData[drawData.length - 1].type !== 'start') { drawData.pop(); } } // 重新绘制整个canvas redraw(); } } // 恢复上一步操作 function redo() { // 判断数组是否为空 if(drawData.length > 0) { // 取出最后一次撤销的绘制数据 let lastData = drawData[drawData.length - 1]; // 判断绘制数据类型 if(lastData.type === 'start') { // 如果是起始点,则需要重新添加之前删除的绘制数据 for(let i = drawData.length - 2; i >= 0; i--) { if(drawData[i].type === 'start') { break; } drawData.push(drawData[i]); } } // 重新绘制整个canvas redraw(); } } // 重新绘制整个canvas function redraw() { // 清空canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // 循环绘制数据,重新绘制整个canvas for(let i = 0; i < drawData.length; i++) { let data = drawData[i]; if(data.type === 'start') { // 绘制起始点 ctx.beginPath(); ctx.moveTo(data.x, data.y); } else if(data.type === 'move') { // 绘制线条 ctx.lineTo(data.x, data.y); ctx.stroke(); } } } ``` 以上代码仅供参考,具体实现方式还需根据具体需求进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值