Показать сообщение отдельно
  #2 (permalink)  
Старый 16.10.2022, 02:14
Профессор
Отправить личное сообщение для Nexus Посмотреть профиль Найти все сообщения от Nexus
 
Регистрация: 04.12.2012
Сообщений: 3,791

Ваша задача показалась мне интересной и я написал... (даже не знаю, как это обозвать) "нечто", что может послужить вам либо основой, либо подтолкнуть вас к решению задачи.
Тоже самое, что и ниже, только на codesandbox.
<!DOCTYPE html>
<html>
  <head>
    <title>Vanila js. Line on canvas with Marker</title>
    <meta charset="UTF-8" />
    <style>
        #canvas {
          background: #eee;
        }
    </style>
  </head>

  <body>
    <div>
      <lable>
        <span>Completeness</span>
        <div>
          <span>0%</span>
          <input
            type="range"
            name="completeness"
            min="0"
            max="100"
            step="1"
            value="0"
          />
          <span>100%</span>
        </div>
      </lable>
    </div>

    <div id="canvas-container">
      <div>
        <div style="margin-top: 3px; color: gray;">
          Try to draw a line on the canvas below
        </div>
        <canvas id="canvas"></canvas>
      </div>
      <button type="button" id="set-new-route">Set a new route</button>
    </div>

    <script>
(() => {
  const getElement = (selector) => {
    const element = document.querySelector(selector);
    if (!element) {
      throw new Error(`Element with selector [${selector}] not found`);
    }

    return element;
  };

  const getRoutePoints = (canvas, externalListeners = {}) => {
    return new Promise((resolve) => {
      const canvasRect = canvas.getBoundingClientRect();
      const getPoint = (e) => ({
        x: e.clientX - canvasRect.left,
        y: e.clientY - canvasRect.top
      });

      let pointsBuffer = [];
      let shouldRecordPoints = false;

      const done = () => {
        for (const eventName of Object.keys(listeners)) {
          canvas.removeEventListener(eventName, listeners[eventName]);
        }

        resolve(pointsBuffer);
      };

      const listeners = {
        mousedown(e) {
          if (shouldRecordPoints) {
            return;
          }

          pointsBuffer = [];
          shouldRecordPoints = true;

          if (externalListeners.onmousedown) {
            externalListeners.onmousedown(getPoint(e));
          }
        },

        mousemove(e) {
          if (!shouldRecordPoints) {
            return;
          }

          pointsBuffer.push(getPoint(e));
          if (externalListeners.onmousemove) {
            externalListeners.onmousemove(getPoint(e));
          }
        },

        mouseup(e) {
          if (!shouldRecordPoints) {
            return;
          }

          shouldRecordPoints = false;

          if (externalListeners.onmouseup) {
            externalListeners.onmouseup(getPoint(e));
          }

          done();
        }
      };

      for (const eventName of Object.keys(listeners)) {
        canvas.addEventListener(eventName, listeners[eventName]);
      }
    });
  };

  const listenCompletenesChanges = (() => {
    let listener = null;

    const node = getElement('[name="completeness"]');
    node.addEventListener("input", function () {
      try {
        if (listener) {
          listener(+this.value);
        }
      } catch (_) {}
    });

    return (callable) => {
      try {
        (listener = callable)(+node.value);
      } catch (_) {}
    };
  })();

  const canvas = getElement("#canvas");
  canvas.width = window.innerWidth;
  const ctx = canvas.getContext("2d");
  ctx.lineWidth = 3;
  ctx.setLineDash([15, 5]);

  const setNewRouteButton = getElement("#set-new-route");

  setNewRouteButton.addEventListener("click", async function () {
    listenCompletenesChanges(() => {}); // remove last listener
    setNewRouteButton.disabled = true;

    const ctx = canvas.getContext("2d");

    // clear canvas
    ctx.beginPath();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.stroke();

    // await route points
    const points = await getRoutePoints(canvas, {
      onmousedown({ x, y }) {
        ctx.moveTo(x, y);
        ctx.lineTo(x, y);
        ctx.stroke();
      },
      onmousemove({ x, y }) {
        ctx.lineTo(x, y);
        ctx.stroke();
      }
    }).then((points) => {
      setNewRouteButton.disabled = false;

      return points;
    });

    if (!points) {
      return;
    }

    const cache = document.createElement("canvas");
    cache.width = canvas.width;
    cache.height = canvas.height;
    cache.getContext("2d").drawImage(canvas, 0, 0);

    listenCompletenesChanges((completeness) => {
      const ctx = canvas.getContext("2d");

      ctx.beginPath();
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(cache, 0, 0);

      const lastCompletedPointIndex = Math.max(
        0,
        Math.min(
          points.length,
          Math.round((points.length / 100) * completeness)
        ) - 1
      );

      const cursorSize = 10;
      const halfOfCursorSize = cursorSize / 2;
      const { x, y } = points[lastCompletedPointIndex];

      ctx.save();
      ctx.strokeStyle = "green";
      ctx.setLineDash([]);
      ctx.roundRect(
        x - halfOfCursorSize,
        y - halfOfCursorSize,
        cursorSize,
        cursorSize,
        halfOfCursorSize
      );
      ctx.stroke();
      ctx.restore();
    });
  });

  setNewRouteButton.dispatchEvent(new Event("click"));
})();
    </script>
  </body>
</html>

Последний раз редактировалось Nexus, 16.10.2022 в 02:17.
Ответить с цитированием