代码展示
const items = [];
const poppedItems = [];
const add = () => {
items.push(1);
};
const undo = () => {
const poppedItem = items.pop();
poppedItems.push(poppedItem);
};
const redo = () => {
const poppedItem = poppedItems.pop();
items.push(poppedItem);
};
原理分析
通过上面代码我们发现,撤销和重做本质上就是对数组的入栈和出栈操作。要注意的一点是,实现重做的前提是在撤销的时候,需要将移除的元素有序的存起来,这样在重做的时候才能用数据可用。
撤销:
将元素按照加入数组的顺序反向移除(最后加入的元素先出)。
重做:
将数组移除的元素按照移除的顺序反向加入(最后移除的元素先入)。
react版本实现
import { useState } from 'react';
const style = {
height: '100vh',
};
const pointStyle = {
position: 'absolute',
width: '10px',
height: '10px',
background: 'blue',
};
function App() {
const [points, setPoints] = useState([]);
const [popped, setPopped] = useState([]);
const onClick = (e) => {
setPoints((points) => [
...points,
{
x: e.pageX,
y: e.pageY,
},
]);
};
const onUndo = (e) => {
// 避免按钮上的点击事件冒泡到容器上
e.stopPropagation();
// 生成副本
const nextPoints = [...points];
// 取出数组中最前面的元素,并且保存到poppedPoint中
const poppedPoint = nextPoints.pop();
// 将取出的元素保存到popped中
setPopped((popped) => [...popped, poppedPoint]);
// 保存更新后的数组
setPoints(nextPoints);
};
const onRedo = (e) => {
// 避免按钮上的点击事件冒泡到容器上
e.stopPropagation();
// 生成副本
const nextPopped = [...popped];
// 取出数组中最前面的元素,并且保存到poppedPoint中
const poppedPoint = nextPopped.pop();
// 保存更新后的数组
setPopped(nextPopped);
// 将取出的元素保存到popped中
setPoints((points) => [...points, poppedPoint]);
};
return (
<div style={style} onClick={onClick}>
<button disabled={points.length < 1} onClick={onUndo}>
undo
</button>
<button disabled={popped.length < 1} onClick={onRedo}>
redo
</button>
{points.map((item) => (
<div style={{ ...pointStyle, top: item.y, left: item.x }} />
))}
</div>
);
}
export default App;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。