如何制作响应式签名板?

问题描述 投票:0回答:1

如何在 signature-pad.js 中使签名板画布响应式工作?

我的挑战如下:

  1. 我要么在正确绘制时获得光标定位,但在调整大小时丢失输入

    // 或 //

  2. 我在调整大小时保存输入,但画布计算不正确,并且不再跟踪光标位置

现在我确信问题来自我的代码中的这一行:

signaturePad.fromData(signaturePad.toData());

评论与否决定了我观察到的两个行为的结果。

有人有一个简单的解决方案吗?我已经为之疯狂了。我还创建了一个 JS Fiddle 供您查看到目前为止我所拥有的内容: https://jsfiddle.net/xetrzi9/hort1ubj/3/

附加信息: 当在手机上滚动时,它还会由于某种原因重置输入,这真的很奇怪。

function initializeSignaturePad() {
  const wrapper = document.querySelector('.form_signature-wrapper');
  const canvas = wrapper.querySelector('#signature-pad');
  const clearButton = wrapper.querySelector('[data-action=clear]');

  let signaturePad;

  function resizeCanvas() {
    const ratio = Math.max(window.devicePixelRatio || 1, 1);
    const rect = wrapper.getBoundingClientRect();

    canvas.width = rect.width * ratio;
    canvas.height = rect.height * ratio;
    canvas.getContext('2d').scale(ratio, ratio);

    // signaturePad.fromData(signaturePad.toData());
  }

  function initPad() {
    signaturePad = new SignaturePad(canvas, {
      penColor: 'rgb(255, 255, 255)',
      minWidth: 0.5,
      maxWidth: 2.5,
      throttle: 0,
      minDistance: 0,
      velocityFilterWeight: 0.4,
    });

    updateMousePosition();
  }

  function updateMousePosition() {
    const rect = canvas.getBoundingClientRect();
    const scaleX = canvas.width / rect.width;
    const scaleY = canvas.height / rect.height;

    function getPointFromEvent(event) {
      let x, y;
      if (event.touches && event.touches.length > 0) {
        const touch = event.touches[0];
        x = touch.clientX;
        y = touch.clientY;
      } else {
        x = event.clientX;
        y = event.clientY;
      }
      x = (x - rect.left) * scaleX;
      y = (y - rect.top) * scaleY;
      return new SignaturePad.Point(x, y);
    }

    const originalOnMouseDown = signaturePad._handleMouseDown;
    signaturePad._handleMouseDown = function(event) {
      const point = getPointFromEvent(event);
      originalOnMouseDown.call(signaturePad, event, point);
    };

    const originalOnMouseMove = signaturePad._handleMouseMove;
    signaturePad._handleMouseMove = function(event) {
      const point = getPointFromEvent(event);
      originalOnMouseMove.call(signaturePad, event, point);
    };

    const originalOnTouchStart = signaturePad._handleTouchStart;
    signaturePad._handleTouchStart = function(event) {
      const point = getPointFromEvent(event);
      originalOnTouchStart.call(signaturePad, event, point);
    };

    const originalOnTouchMove = signaturePad._handleTouchMove;
    signaturePad._handleTouchMove = function(event) {
      const point = getPointFromEvent(event);
      originalOnTouchMove.call(signaturePad, event, point);
    };
  }

  initPad();
  resizeCanvas();

  function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  window.addEventListener(
    'resize',
    debounce(() => {
      resizeCanvas();
    }, 250)
  );

  clearButton.addEventListener('click', function(event) {
    event.preventDefault();
    event.stopPropagation();
    signaturePad.clear();
  });

  return signaturePad;
}

document.addEventListener('DOMContentLoaded', function() {
  initializeSignaturePad();
});
html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: #0c0c0c;
  font-family: sans-serif;
}

.form_field-wrapper {
  max-width: 600px;
  margin: 0 auto;
  padding: 2rem 0;
}

.form_label {
  color: #fff;
  margin-bottom: 0.5rem;
}

.form_signature-wrapper {
  width: 100%;
  height: auto;
  aspect-ratio: 5/3;
}

.form_signature-canvas {
  height: 100%;
  width: 100%;
  display: block;
  box-sizing: border-box;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 0.25rem 0.25rem 0 0;
}

.form_signature-wrapper {
  position: relative;
}

.form_signature-wrapper button {
  appearance: none;
  outline: none;
  border: none;
  box-shadow: none;
  font-weight: 600;
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  padding: 0.5rem;
  border-radius: 0.25rem;
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  transition: 100ms ease-in-out background;
}

.form_signature-wrapper button:hover {
  background: rgba(255, 255, 255, 0.2);
  cursor: pointer;
}

.form_info-wrapper {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 0 0 0.25rem 0.25rem;
  font-size: 0.875rem;
}

.form_info-icon {
  height: 1rem;
  width: 1rem;
}

.form_info-icon svg {
  display: flex;
  height: auto;
  width: 100%;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/signature_pad.umd.min.js"></script>

<div class="form_field-wrapper">
  <div class="form_label">Signature</div>
  <div class="form_signature-wrapper">
    <canvas id="signature-pad" class="form_signature-canvas" width="" height="" style="touch-action: none"></canvas
      ><button
               data-action="clear"
               type="button"
               class="form_signature-btn is-clear">
    Delete
    </button>
  </div>
  <div class="form_info-wrapper is-signature form_label">
    <div class="form_info-icon">
      <svg
           fill="none"
           xmlns="http://www.w3.org/2000/svg"
           viewBox="0 0 14 14">
        <path
              d="M7 14a7 7 0 1 1 7-7 7.007 7.007 0 0 1-7 7ZM7 1.167A5.833 5.833 0 1 0 12.833 7 5.84 5.84 0 0 0 7 1.167Z"
              fill="currentColor"></path>
        <path
              d="M7.583 8.75H6.416v-.434a2.306 2.306 0 0 1 1.146-2.041A1.167 1.167 0 1 0 5.833 5.25H4.666a2.333 2.333 0 1 1 3.46 2.044 1.156 1.156 0 0 0-.543 1.022v.434ZM7.583 9.916H6.416v1.167h1.167V9.916Z"
              fill="currentColor"></path>
      </svg>
    </div>
    <div class="form_info">Please add your signature</div>
  </div>
</div>

javascript canvas responsive signature signaturepad
1个回答
0
投票

似乎在调整大小时清除画布是图书馆的意图。

当然,这是一个有问题的设计决策,但您可以通过创建自己的点计算添加缩放逻辑来绕过此行为。

基本上,我们仅在初始化时设置画布尺寸(根据 CSS 规则),并缩放/投影鼠标/指针坐标以满足当前画布缩放比例。因此画布保持其尺寸并仅通过其 CSS 布局属性进行缩放。

const wrapper = document.querySelector('.form_signature-wrapper'); const canvas = wrapper.querySelector('#signature-pad'); const clearButton = wrapper.querySelector('[data-action=clear]'); function initializeSignaturePad() { //set initial width and height let ratio = Math.max(window.devicePixelRatio || 1, 1); let bb = wrapper.getBoundingClientRect(); let w = bb.width * ratio; let h = bb.height * ratio; canvas.width = w; canvas.height = h; const signaturePad = new SignaturePad(canvas, { penColor: 'rgb(255, 255, 255)', minWidth: 0.5, maxWidth: 2.5, throttle: 0, minDistance: 0, velocityFilterWeight: 0.4, }); // add scaling to point creation signaturePad._createPoint = (x, y, pressure) => { let bb = canvas.getBoundingClientRect(); let scale = w / bb.width; return new Point((x - bb.left) * scale, (y - bb.top) * scale, pressure, new Date().getTime()); } class Point { constructor(x, y, pressure, time) { if (isNaN(x) || isNaN(y)) { throw new Error(`Point is invalid: (${x}, ${y})`); } this.x = +x; this.y = +y; this.pressure = pressure || 0; this.time = time || Date.now(); } distanceTo(start) { return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2)); } equals(other) { return (this.x === other.x && this.y === other.y && this.pressure === other.pressure && this.time === other.time); } velocityFrom(start) { return this.time !== start.time ? this.distanceTo(start) / (this.time - start.time) : 0; } } clearButton.addEventListener('click', function(event) { event.preventDefault(); event.stopPropagation(); signaturePad.clear(); }); return signaturePad; } document.addEventListener('DOMContentLoaded', function() { initializeSignaturePad(); });
html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: #0c0c0c;
  font-family: sans-serif;
}

.form_field-wrapper {
  max-width: 100%;
  margin: 0 auto;
  padding: 2rem 0;
}

.form_label {
  color: #fff;
  margin-bottom: 0.5rem;
}

.form_signature-wrapper {
  width: 100%;
  height: auto;
  aspect-ratio: 5/3;
  resize: horizontal;
  overflow: auto;
  outline: 1px solid red;
}

.form_signature-canvas {
  height: 100%;
  width: 100%;
  display: block;
  box-sizing: border-box;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 0.25rem 0.25rem 0 0;
}

.form_signature-wrapper {
  position: relative;
}

.form_signature-wrapper button {
  appearance: none;
  outline: none;
  border: none;
  box-shadow: none;
  font-weight: 600;
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  padding: 0.5rem;
  border-radius: 0.25rem;
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  transition: 100ms ease-in-out background;
}

.form_signature-wrapper button:hover {
  background: rgba(255, 255, 255, 0.2);
  cursor: pointer;
}

.form_info-wrapper {
  display: flex;
  gap: 0.5rem;
  padding: 0.5rem;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 0 0 0.25rem 0.25rem;
  font-size: 0.875rem;
}

.form_info-icon {
  height: 1rem;
  width: 1rem;
}

.form_info-icon svg {
  display: flex;
  height: auto;
  width: 100%;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/signature_pad.umd.min.js"></script>


<div class="form_field-wrapper">
  <div class="form_label">Signature</div>
  <div class="form_signature-wrapper">
    <canvas id="signature-pad" class="form_signature-canvas" width="" height="" style="touch-action: none"></canvas
      ><button
               data-action="clear"
               type="button"
               class="form_signature-btn is-clear">
    Delete
    </button>
  </div>
  <div class="form_info-wrapper is-signature form_label">

    <div class="form_info">Please add your signature</div>
  </div>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.