- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试用 JavaScript 制作一个绘画程序,并且我想包含一个撤消功能(不是橡皮擦)。如何将所有事件添加到一个数组中,然后可以将它们一一删除?
我有一个工具下拉列表(到目前为止只有四个可以工作)。我添加了一个带有 id 的撤消按钮。我已经尝试了几个小时(实际上是几天)来找出如何做到这一点。我找到了一些例子,我想我必须同时使用推送和空数组才能更进一步?
这是工具选择和按钮的代码
<label>
Object type:
<select id="selectTool">
<option value="line">Linje</option>
<option value="pencil">Blyant</option>
<option value="rect">Rektangel</option>
<option value="circle">Sirkel</option>
<option value="oval">Oval</option>
<option value="polygon">Polygon</option>
</select>
Shape drawn:
<select id="shapeDrawn">
<option value=""></option>
</select>
<input type="button" id="cmbDelete" value="Undo last action">
</label>
撤消函数可能是这样的,但是这个函数
var shapes = [];
shapes.push(newShape);
function cmbDeleteClick(){
if(shapes.length > 0){
var selectedShapeIndex = selectShape.selectedIndex;
shapes.splice(selectedShapeIndex,1);
selectShape.options.remove(selectedShapeIndex);
selectShape.selectedIndex = selectShape.options.length - 1;
}
cmbDelete = document.getElementById("cmbDelete");
cmbDelete.addEventListener("click",cmbDeleteClick, false);
fillSelectShapeTypes();
drawCanvas();
}
理想情况下, Canvas 上绘制的所有内容都会添加到下拉菜单中,并且可以通过单击按钮将其删除(撤消)。这是代码 JS Bin 的“工作”版本
最佳答案
您当前的实现不使用 shapes
数组,并且无法在创建它们后重新绘制它们。
因此,将每个操作存储为位图是最简单的。因此,您需要一个位图数组,我们称之为:
var history = [];
一旦绘制了一些东西,我们就会创建当前 Canvas 的快照并将其存储在该数组中:
history.push(contextTmp.getImageData(0,0,canvasTmp.width,canvasTmp.height))
当您想要撤消操作时,弹出历史记录并在 Canvas 上绘制最后一个位图:
function cmbDeleteClick(){
history.pop()
contextTmp.putImageData(history[history.length-1],0,0)
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Paint</title>
<style type="text/css">
#content { position: relative; }
#cvs { border: 1px solid #c00; }
#cvsTmp { position: absolute; top: 1px; left: 1px; }
</style>
</head>
<body>
<p>
<label>
Object type:
<select id="selectTool">
<option value="line">Linje</option>
<option value="pencil">Blyant</option>
<option value="rect">Rektangel</option>
<option value="circle">Sirkel</option>
<option value="oval">Oval</option>
<option value="polygon">Polygon</option>
</select>
Shape drawn:
<select id="shapeDrawn">
<option value=""></option>
</select>
History:
<select id="historySelect">
</select>
<input type="button" id="cmbDelete" value="Undo last action">
</label>
</p>
<div id="content">
<canvas id="cvs" width="1024" height="512"></canvas>
</div>
<script type="text/javascript">
if(window.addEventListener) {
window.addEventListener('load', function () {
var canvas;
var context;
var canvasTmp;
var contextTmp;
var tool;
var toolDefault = 'line';
var cmbDelete = null;
var shapes = [];
var history = [];
var historySelect;
// Canvas and temp. canvas
function init () {
canvasTmp = document.getElementById('cvs');
if (!canvasTmp) {
return;
} if (!canvasTmp.getContext) {
return;
}
historySelect = document.getElementById('historySelect')
historySelect.addEventListener('change', ()=>{
restoreHistoryAction(historySelect.value)
})
contextTmp = canvasTmp.getContext('2d');
if (!contextTmp) {
return;
}
// Add the temporary canvas.
var content = canvasTmp.parentNode;
canvas = document.createElement('canvas');
if (!canvas) {
return;
}
canvas.id = 'cvsTmp';
canvas.width = canvasTmp.width;
canvas.height = canvasTmp.height;
content.appendChild(canvas);
context = canvas.getContext('2d');
// Get the tool select input.
var toolSelect = document.getElementById('selectTool');
if (!toolSelect) {
return;
}
toolSelect.addEventListener('change', ev_tool_change, false);
// Activate the default tool.
if (tools[toolDefault]) {
tool = new tools[toolDefault]();
toolSelect.value = toolDefault;
}
// Attach the mousedown, mousemove and mouseup event listeners.
canvas.addEventListener('mousedown', evMouse, false);
canvas.addEventListener('mousemove', evMouse, false);
canvas.addEventListener('mouseup', evMouse, false);
drawCanvas()
}
function evMouse (ev) {
if (ev.layerX || ev.layerX == 0) {
ev._x = ev.layerX;
ev._y = ev.layerY;
}
var evHandler = tool[ev.type];
if (evHandler) {
evHandler(ev);
}
}
// The event handler for any changes made to the tool selector.
function toolChange (ev) {
if (tools[this.value]) {
tool = new tools[this.value]();
}
}
// Updates Canvas on interval timeout
function drawCanvas() {
contextTmp.drawImage(canvas, 0, 0);
history.push(contextTmp.getImageData(0,0,canvasTmp.width,canvasTmp.height))
updateHistorySelection()
context.clearRect(0, 0, canvas.width, canvas.height);
}
function ev_tool_change (ev) {
if (tools[this.value]) {
tool = new tools[this.value]();
}
}
// Get excact position for mouse coordinates in canvas
function mouseAction (ev) {
if (ev.layerX || ev.layerX == 0) {
ev._x = ev.layerX;
ev._y = ev.layerY;
}
// Call the event handler of the tool.
var func = tool[ev.type];
if (func) {
func(ev);
}
}
function selectShapeChange(){
drawCanvas();
}
var tools = {};
// The drawing pencil.
tools.pencil = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
context.beginPath();
context.moveTo(ev._x, ev._y);
tool.started = true;
};
this.mousemove = function (ev) {
if (tool.started) {
context.lineTo(ev._x, ev._y);
context.stroke();
}
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
// The rectangle tool.
tools.rect = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
var x = Math.min(ev._x, tool.x0),
y = Math.min(ev._y, tool.y0),
w = Math.abs(ev._x - tool.x0),
h = Math.abs(ev._y - tool.y0);
context.clearRect(0, 0, canvas.width, canvas.height);
if (!w || !h) {
return;
}
context.fillRect(x, y, w, h);
context.fillStyle = 'hsl(' + 360 * Math.random() + ', 50%, 50%)';
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
// The line tool.
tools.line = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.moveTo(tool.x0, tool.y0);
context.lineTo(ev._x, ev._y);
context.stroke();
context.closePath();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
// Circle tool
tools.circle = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
var radius = Math.max(
Math.abs(ev._x - tool.x0),
Math.abs(ev._y - tool.y0)
) / 2;
var x = Math.min(ev._x, tool.x0) + radius;
var y = Math.min(ev._y, tool.y0) + radius;
context.beginPath();
context.arc(x, y, radius, 0, Math.PI*2, false);
// context.arc(x, y, 5, 0, Math.PI*2, false);
context.stroke();
context.closePath();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
// Ellipse/oval tool
// Polygon tool
// Undo button
function cmbDeleteClick(){
if(history.length<=1)
return
history.pop()
contextTmp.putImageData(history[history.length-1],0,0)
updateHistorySelection()
}
function updateHistorySelection(){
historySelect.innerHTML = ''
history.forEach((entry,index)=>{
let option = document.createElement('option')
option.value = index
option.textContent = index===0 ? 'Beginning' : 'Action '+index
historySelect.appendChild(option)
})
historySelect.selectedIndex = history.length-1
}
function restoreHistoryAction(index){
contextTmp.putImageData(history[index],0,0)
}
cmbDelete = document.getElementById("cmbDelete");
cmbDelete.addEventListener("click",cmbDeleteClick, false);
init();
}, false); }
</script>
</body>
</html>
但这并不是很有效。它将为每个操作存储 Canvas 的完整位图,因此非常消耗内存。最好让绘图工具实际创建一个形状实例,可以调用该实例在 Canvas 上按需重绘。
关于javascript - 如何使用数组添加撤消列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55871586/
我正在尝试创建一个包含 int[][] 项的数组 即 int version0Indexes[][4] = { {1,2,3,4}, {5,6,7,8} }; int version1Indexes[
我有一个整数数组: private int array[]; 如果我还有一个名为 add 的方法,那么以下有什么区别: public void add(int value) { array[va
当您尝试在 JavaScript 中将一个数组添加到另一个数组时,它会将其转换为一个字符串。通常,当以另一种语言执行此操作时,列表会合并。 JavaScript [1, 2] + [3, 4] = "
根据我正在阅读的教程,如果您想创建一个包含 5 列和 3 行的表格来表示这样的数据... 45 4 34 99 56 3 23 99 43 2 1 1 0 43 67 ...它说你可以使用下
我通常使用 python 编写脚本/程序,但最近开始使用 JavaScript 进行编程,并且在使用数组时遇到了一些问题。 在 python 中,当我创建一个数组并使用 for x in y 时,我得
我有一个这样的数组: temp = [ 'data1', ['data1_a','data1_b'], ['data2_a','data2_b','data2_c'] ]; // 我想使用 toStr
rent_property (table name) id fullName propertyName 1 A House Name1 2 B
这个问题在这里已经有了答案: 关闭13年前。 Possible Duplicate: In C arrays why is this true? a[5] == 5[a] array[index] 和
使用 Excel 2013。经过多年的寻找和适应,我的第一篇文章。 我正在尝试将当前 App 用户(即“John Smith”)与他的电子邮件地址“jsmith@work.com”进行匹配。 使用两个
当仅在一个边距上操作时,apply 似乎不会重新组装 3D 数组。考虑: arr 1),但对我来说仍然很奇怪,如果一个函数返回一个具有尺寸的对象,那么它们基本上会被忽略。 最佳答案 这是一个不太理
我有一个包含 GPS 坐标的 MySQL 数据库。这是我检索坐标的部分 PHP 代码; $sql = "SELECT lat, lon FROM gps_data"; $stmt=$db->query
我需要找到一种方法来执行这个操作,我有一个形状数组 [批量大小, 150, 1] 代表 batch_size 整数序列,每个序列有 150 个元素长,但在每个序列中都有很多添加的零,以使所有序列具有相
我必须通过 url 中的 json 获取文本。 层次结构如下: 对象>数组>对象>数组>对象。 我想用这段代码获取文本。但是我收到错误 :org.json.JSONException: No valu
enter code here- (void)viewDidLoad { NSMutableArray *imageViewArray= [[NSMutableArray alloc] init];
知道如何对二维字符串数组执行修剪操作,例如使用 Java 流 API 进行 3x3 并将其收集回相同维度的 3x3 数组? 重点是避免使用显式的 for 循环。 当前的解决方案只是简单地执行一个 fo
已关闭。此问题需要 debugging details 。目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and the
我有来自 ASP.NET Web 服务的以下 XML 输出: 1710 1711 1712 1713
如果我有一个对象todo作为您状态的一部分,并且该对象包含数组列表,则列表内部有对象,在这些对象内部还有另一个数组listItems。如何更新数组 listItems 中 id 为“poi098”的对
我想将最大长度为 8 的 bool 数组打包成一个字节,通过网络发送它,然后将其解压回 bool 数组。已经在这里尝试了一些解决方案,但没有用。我正在使用单声道。 我制作了 BitArray,然后尝试
我们的数据库中有这个字段指示一周中的每一天的真/假标志,如下所示:'1111110' 我需要将此值转换为 boolean 数组。 为此,我编写了以下代码: char[] freqs = weekday
我是一名优秀的程序员,十分优秀!