html canvas中的goBack()函数无法正常工作

g0czyy6m  于 2023-04-27  发布在  Go
关注(0)|答案(1)|浏览(123)

我正在尝试在JavaScript中创建一个goBack()函数,它基本上是一个撤销按钮。例如:用户拖放1个图像并绘制2条线。goBack()函数应该一次删除一条线,然后删除图像。我的函数删除线和图像,但不是按顺序。它首先删除图像,即使它是第一个拖动的东西。为了更好地理解,下面是代码。

//Javascript per la creazione di cirucuiti elettrici

const NUMERO_CICLO = 990;
const NUMERO_CICLO_INTERNO = 60;

const resistor = document.getElementById('component_circuit_resistor');
const condensator = document.getElementById('component_circuit_condensator');
const tranistor = document.getElementById('component_circuit_tranistor');
const alimentator = document.getElementById('component_circuit_alimentator');
const circuit = document.getElementById('components_circuit');
const back_button = document.getElementById('back-button');
const clear_button = document.getElementById('clear-button');
const draggable = document.querySelectorAll('.draggable');
const container = document.querySelectorAll('.container');
const canvas = document.getElementById('canvas');
const foward_button = document.getElementById('foward-button');

/** EDIT START */
const draggableImages = document.querySelectorAll('img[draggable]');

for (let i = 0; i < draggableImages.length; i++)
  draggableImages[i].ondragstart = (ev) => {
    ev.dataTransfer.setData('text/plain', i.toString());
  };

canvas.ondragover = (ev) => ev.preventDefault(); // IMPORTANT

const drawnImageData = [];
const deletedImageData = [];

canvas.ondrop = (ev) => {
  const index = parseInt(ev.dataTransfer.getData('text/plain'));
  const img = draggableImages[index];
  drawnImageData.push({ img, x: ev.offsetX, y: ev.offsetY });
};
clear_button.disabled = true;
clear_button.style.cursor = 'not-allowed';
foward_button.disabled = true;
foward_button.style.cursor = 'not-allowed';
back_button.disabled = true;
back_button.style.cursor = 'not-allowed';
/** EDIT END */

canvas.width = 1500;
canvas.height = 855;
canvas.style.backgroundColor = 'lightgrey';
circuit.appendChild(canvas);
canvas.style.borderRadius = '10px';
canvas.style.marginLeft = 'auto';
canvas.style.marginRight = 'auto';
canvas.style.display = 'block';
const ctx = canvas.getContext('2d');

const circles = [];
const lines = [];
const lines_c = [];
var deletedLines = [];

for (let y = 20; y <= NUMERO_CICLO; y += 20) {
  for (let i = 0; i < NUMERO_CICLO_INTERNO; i++) {
    circles.push({
      x: 13 + i * 25,
      y,
      radius: 5,
      color: 'grey',
    });
  }
}

let startCircle = null;
let endCircle = null;
var priorita = null;

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  circles.forEach((circle) => {
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    ctx.fillStyle = circle.color;
    ctx.fill();
  });
  lines.forEach((line) => {
    ctx.beginPath();
    ctx.lineWidth = 4;
    ctx.moveTo(line.start.x, line.start.y);
    ctx.lineTo(line.end.x, line.end.y);
    ctx.stroke();
  });
  if (startCircle && endCircle) {
    ctx.beginPath();
    ctx.lineWidth = 4;
    ctx.moveTo(startCircle.x, startCircle.y);
    ctx.lineTo(endCircle.x, endCircle.y);
    ctx.stroke();
  }
  /** EDIT START */
  for (const data of drawnImageData)
    ctx.drawImage(data.img, data.x, data.y, data.img.width, data.img.height);

  /** EDIT END */
  requestAnimationFrame(draw);
  if(drawnImageData.length > 0 || lines.length > 0){
    clear_button.disabled = false;
    clear_button.style.cursor = 'pointer';
    back_button.disabled = false;
    back_button.style.cursor = 'pointer';
  }
}
draw();

function goBack() {
  if (drawnImageData.length > 0) {
    deletedImageData.push(drawnImageData.pop());
  }
  else if (lines.length > 0) {
    deletedLines.push(lines.pop());
  }
  // Check if there are any items left to undo
  if (drawnImageData.length === 0 && lines.length === 0) {
    back_button.disabled = true;
    back_button.style.cursor = 'not-allowed';
    clear_button.disabled = true;
    clear_button.style.cursor = 'not-allowed';
  }
  // Enable the forward button
  foward_button.disabled = false;
  foward_button.style.cursor = 'pointer';
}

function goFoward() {
  if (deletedImageData.length > 0) {
    drawnImageData.push(deletedImageData.pop());
  } else if (deletedLines.length > 0) {
    lines.push(deletedLines.pop());
  }
  // Check if there are any items left to redo
  if (deletedImageData.length === 0 && deletedLines.length === 0) {
    foward_button.disabled = true;
    foward_button.style.cursor = 'not-allowed';
  }

  // Enable the back button
  back_button.disabled = false;
  back_button.style.cursor = 'pointer';
}

function clearCanvas() {
  if (confirm('Are you sure you want to delete the circuit?')) {
    lines.length = 0;
    deletedLines.length = 0;
    lastDeleted = null;

    // clear drawn images data
    drawnImageData.length = 0;

    clear_button.disabled = true;
    clear_button.style.cursor = 'not-allowed';
    foward_button.disabled = true;
    foward_button.style.cursor = 'not-allowed';
    back_button.disabled = true;
    back_button.style.cursor = 'not-allowed';
  }
}

function getNearestCircle(x, y) {
  let nearestCircle = null;
  let nearestDistance = Infinity;
  circles.forEach((circle) => {
    const distance = Math.sqrt((circle.x - x) ** 2 + (circle.y - y) ** 2);
    if (distance < nearestDistance && distance < 30) {
      nearestCircle = circle;
      nearestDistance = distance;
    }
  });
  return nearestCircle;
}

let isDrawing = false;

canvas.addEventListener('mousedown', (event) => {
  const x = event.offsetX;
  const y = event.offsetY;
  const circle = getNearestCircle(x, y);
  if (circle) {
    startCircle = circle;
    endCircle = { x, y };
    isDrawing = true;
  }
});

canvas.addEventListener('mousemove', (event) => {
  if (isDrawing) {
    endCircle.x = event.offsetX;
    endCircle.y = event.offsetY;
  } else {
    const x = event.offsetX;
    const y = event.offsetY;
    const circle = getNearestCircle(x, y);
    if (circle) {
      circles.forEach((circle) => {
        circle.color = 'grey';
      });
      circle.color = 'red';
    } else {
      circles.forEach((circle) => {
        circle.color = 'grey';
      });
    }
  }
});

canvas.addEventListener('mouseup', () => {
  if (isDrawing) {
    const x = endCircle.x;
    const y = endCircle.y;
    const circle = getNearestCircle(x, y);
    if (circle) {
      lines.push({
        start: startCircle,
        end: circle,
      });
    }
    isDrawing = false;
    startCircle = null;
    endCircle = null;
  }
});

//back_button.addEventListener("click", goBack);
//foward_button.addEventListener("click", goBack);
clear_button.addEventListener('click', clearCanvas);

var back_clicked = false;
back_button.addEventListener('click', function () {
  back_clicked = true;
  goBack();
  back_clicked = false;
});

var foward_clicked = false;
foward_button.addEventListener('click', function () {
  foward_clicked = true;
  goFoward();
  foward_clicked = false;
});
<h1 id="h1_titolo">From Circuit to Breadboard</h1>
<div class="container">
  <div class="components">
    <div id="components_circuit">
      <h3 id="h3_componenti_circuit">Componenti:</h3>
      <ul id="components_circuit_border">
        <li>
          <img
            id="component_circuit_resistor"
            src="https://i.kym-cdn.com/entries/icons/original/000/006/428/637738.jpg"
            height="50"
            draggable
          />
        </li>
        <br /><br />
        <li>
          <img
            id="component_circuit_condensator"
            src="https://ftw.usatoday.com/wp-content/uploads/sites/90/2017/05/spongebob.jpg"
            height="50"
            draggable
          />
        </li>
        <br /><br />
        <li>
          <img
            id="component_circuit_transistor"
            src="https://pyxis.nymag.com/v1/imgs/0f9/f96/029acbf4c6d8c67e138e1eb06a277204bf-05-patrick.rsquare.w700.jpg"
            height="50"
            draggable
          />
        </li>
        <br /><br />
      </ul>
      <div class="elementi_disegno">
        <h1 id="h1_disegna">Disegna il tuo circuito!</h1>
        <button id="back-button">
          Indietro
          <span class="material-symbols-outlined">undo</span>
        </button>
        <button id="foward-button">
          Avanti
          <span class="material-symbols-outlined">redo</span>
        </button>
        <button id="clear-button">
          Clear All
          <span class="material-symbols-outlined">delete</span>
        </button>
        <canvas id="canvas" class="dropzone"></canvas>
      </div>
    </div>
m3eecexj

m3eecexj1#

这是因为你没有保存执行的顺序。在goBack()函数中,如果数组不为空,你总是首先尝试从drawnImageData数组中删除,即使最近的执行是绘制线条。我会通过创建另一个数组来保持执行顺序,如下所示:

const orderStack = [];
const deletedOrderStack = [];

以下是更新后的片段:

//Javascript per la creazione di cirucuiti elettrici

const NUMERO_CICLO = 990;
const NUMERO_CICLO_INTERNO = 60;

const resistor = document.getElementById('component_circuit_resistor');
const condensator = document.getElementById('component_circuit_condensator');
const tranistor = document.getElementById('component_circuit_tranistor');
const alimentator = document.getElementById('component_circuit_alimentator');
const circuit = document.getElementById('components_circuit');
const back_button = document.getElementById('back-button');
const clear_button = document.getElementById('clear-button');
const draggable = document.querySelectorAll('.draggable');
const container = document.querySelectorAll('.container');
const canvas = document.getElementById('canvas');
const foward_button = document.getElementById('foward-button');

/** EDIT START */
const draggableImages = document.querySelectorAll('img[draggable]');

for (let i = 0; i < draggableImages.length; i++)
  draggableImages[i].ondragstart = (ev) => {
    ev.dataTransfer.setData('text/plain', i.toString());
  };

canvas.ondragover = (ev) => ev.preventDefault(); // IMPORTANT

const orderStack = [];
const deletedOrderStack = [];

const drawnImageData = [];
const deletedImageData = [];

canvas.ondrop = (ev) => {
  const index = parseInt(ev.dataTransfer.getData('text/plain'));
  const img = draggableImages[index];
  drawnImageData.push({
    img,
    x: ev.offsetX,
    y: ev.offsetY
  });
  orderStack.push(1);
};
clear_button.disabled = true;
clear_button.style.cursor = 'not-allowed';
foward_button.disabled = true;
foward_button.style.cursor = 'not-allowed';
back_button.disabled = true;
back_button.style.cursor = 'not-allowed';
/** EDIT END */

canvas.width = 1500;
canvas.height = 855;
canvas.style.backgroundColor = 'lightgrey';
circuit.appendChild(canvas);
canvas.style.borderRadius = '10px';
canvas.style.marginLeft = 'auto';
canvas.style.marginRight = 'auto';
canvas.style.display = 'block';
const ctx = canvas.getContext('2d');

const circles = [];
const lines = [];
const lines_c = [];
var deletedLines = [];

for (let y = 20; y <= NUMERO_CICLO; y += 20) {
  for (let i = 0; i < NUMERO_CICLO_INTERNO; i++) {
    circles.push({
      x: 13 + i * 25,
      y,
      radius: 5,
      color: 'grey',
    });
  }
}

let startCircle = null;
let endCircle = null;
var priorita = null;

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  circles.forEach((circle) => {
    ctx.beginPath();
    ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
    ctx.fillStyle = circle.color;
    ctx.fill();
  });
  lines.forEach((line) => {
    ctx.beginPath();
    ctx.lineWidth = 4;
    ctx.moveTo(line.start.x, line.start.y);
    ctx.lineTo(line.end.x, line.end.y);
    ctx.stroke();
  });
  if (startCircle && endCircle) {
    ctx.beginPath();
    ctx.lineWidth = 4;
    ctx.moveTo(startCircle.x, startCircle.y);
    ctx.lineTo(endCircle.x, endCircle.y);
    ctx.stroke();
  }
  /** EDIT START */
  for (const data of drawnImageData)
    ctx.drawImage(data.img, data.x, data.y, data.img.width, data.img.height);

  /** EDIT END */
  requestAnimationFrame(draw);
  if (drawnImageData.length > 0 || lines.length > 0) {
    clear_button.disabled = false;
    clear_button.style.cursor = 'pointer';
    back_button.disabled = false;
    back_button.style.cursor = 'pointer';
  }
}
draw();

function goBack() {
  if (orderStack.length === 0) {
    return
  }

  if (orderStack[orderStack.length - 1] === 1 && drawnImageData.length > 0) {
    deletedImageData.push(drawnImageData.pop());
  } else if (orderStack[orderStack.length - 1] === 0 && lines.length > 0) {
    deletedLines.push(lines.pop());
  }

  deletedOrderStack.push(orderStack.pop())

  // Check if there are any items left to undo
  if (drawnImageData.length === 0 && lines.length === 0) {
    back_button.disabled = true;
    back_button.style.cursor = 'not-allowed';
    clear_button.disabled = true;
    clear_button.style.cursor = 'not-allowed';
  }
  // Enable the forward button
  foward_button.disabled = false;
  foward_button.style.cursor = 'pointer';
}

function goFoward() {
  if (deletedOrderStack.length === 0) {
    return
  }

  if (deletedOrderStack[deletedOrderStack.length - 1] === 1 && deletedImageData.length > 0) {
    drawnImageData.push(deletedImageData.pop());
    orderStack.push(deletedOrderStack.pop());
  } else if (deletedOrderStack[deletedOrderStack.length - 1] === 0 && deletedLines.length > 0) {
    lines.push(deletedLines.pop());
    orderStack.push(deletedOrderStack.pop());
  }
  // Check if there are any items left to redo
  if (deletedImageData.length === 0 && deletedLines.length === 0) {
    foward_button.disabled = true;
    foward_button.style.cursor = 'not-allowed';
  }

  // Enable the back button
  back_button.disabled = false;
  back_button.style.cursor = 'pointer';
}

function clearCanvas() {
  if (confirm('Are you sure you want to delete the circuit?')) {
    lines.length = 0;
    deletedLines.length = 0;
    lastDeleted = null;

    // clear drawn images data
    drawnImageData.length = 0;

    orderStack.length = 0
    deletedOrderStack.length = 0

    clear_button.disabled = true;
    clear_button.style.cursor = 'not-allowed';
    foward_button.disabled = true;
    foward_button.style.cursor = 'not-allowed';
    back_button.disabled = true;
    back_button.style.cursor = 'not-allowed';
  }
}

function getNearestCircle(x, y) {
  let nearestCircle = null;
  let nearestDistance = Infinity;
  circles.forEach((circle) => {
    const distance = Math.sqrt((circle.x - x) ** 2 + (circle.y - y) ** 2);
    if (distance < nearestDistance && distance < 30) {
      nearestCircle = circle;
      nearestDistance = distance;
    }
  });
  return nearestCircle;
}

let isDrawing = false;

canvas.addEventListener('mousedown', (event) => {
  const x = event.offsetX;
  const y = event.offsetY;
  const circle = getNearestCircle(x, y);
  if (circle) {
    startCircle = circle;
    endCircle = {
      x,
      y
    };
    isDrawing = true;
  }
});

canvas.addEventListener('mousemove', (event) => {
  if (isDrawing) {
    endCircle.x = event.offsetX;
    endCircle.y = event.offsetY;
  } else {
    const x = event.offsetX;
    const y = event.offsetY;
    const circle = getNearestCircle(x, y);
    if (circle) {
      circles.forEach((circle) => {
        circle.color = 'grey';
      });
      circle.color = 'red';
    } else {
      circles.forEach((circle) => {
        circle.color = 'grey';
      });
    }
  }
});

canvas.addEventListener('mouseup', () => {
  if (isDrawing) {
    const x = endCircle.x;
    const y = endCircle.y;
    const circle = getNearestCircle(x, y);
    if (circle) {
      lines.push({
        start: startCircle,
        end: circle,
      });

      orderStack.push(0);
    }
    isDrawing = false;
    startCircle = null;
    endCircle = null;
  }
});

//back_button.addEventListener("click", goBack);
//foward_button.addEventListener("click", goBack);
clear_button.addEventListener('click', clearCanvas);

var back_clicked = false;
back_button.addEventListener('click', function() {
  back_clicked = true;
  goBack();
  back_clicked = false;
});

var foward_clicked = false;
foward_button.addEventListener('click', function() {
  foward_clicked = true;
  goFoward();
  foward_clicked = false;
});
<h1 id="h1_titolo">From Circuit to Breadboard</h1>
<div class="container">
  <div class="components">
    <div id="components_circuit">
      <h3 id="h3_componenti_circuit">Componenti:</h3>
      <ul id="components_circuit_border">
        <li>
          <img id="component_circuit_resistor" src="https://i.kym-cdn.com/entries/icons/original/000/006/428/637738.jpg" height="50" draggable />
        </li>
        <br /><br />
        <li>
          <img id="component_circuit_condensator" src="https://ftw.usatoday.com/wp-content/uploads/sites/90/2017/05/spongebob.jpg" height="50" draggable />
        </li>
        <br /><br />
        <li>
          <img id="component_circuit_transistor" src="https://pyxis.nymag.com/v1/imgs/0f9/f96/029acbf4c6d8c67e138e1eb06a277204bf-05-patrick.rsquare.w700.jpg" height="50" draggable />
        </li>
        <br /><br />
      </ul>
      <div class="elementi_disegno">
        <h1 id="h1_disegna">Disegna il tuo circuito!</h1>
        <button id="back-button">
          Indietro
          <span class="material-symbols-outlined">undo</span>
        </button>
        <button id="foward-button">
          Avanti
          <span class="material-symbols-outlined">redo</span>
        </button>
        <button id="clear-button">
          Clear All
          <span class="material-symbols-outlined">delete</span>
        </button>
        <canvas id="canvas" class="dropzone"></canvas>
      </div>
    </div>

相关问题