1
votes

i'm using the signature-pad plugin and i'm having some issues whith the resize event: - Multiple resizes lead to a loss in quality and the signature "moves" at each resize of the browser window ending with no signature in canvas. - In some cases, the isEmpty() function wont work and i'll be able to save the empty signature.

Optional question : how can i detect an empty signature on php side ?

Thank you :)

Below my code :

$(window).resize(function() {
            resizeCanvas();
        });



        var wrapper1 = document.getElementById("signature-pad"),
            clearButton1 = wrapper1.querySelector("[data-action=clear]"),
            canvas1 = wrapper1.querySelector("canvas"),
            signaturePad1;

        var wrapper2 = document.getElementById("signature-pad-paraphe"),
            clearButton2 = wrapper2.querySelector("[data-action=clear]"),
            canvas2 = wrapper2.querySelector("canvas"),
            signaturePad2;

        // Adjust canvas coordinate space taking into account pixel ratio,
        // to make it look crisp on mobile devices.
        // This also causes canvas to be cleared.

        signaturePad1 = new SignaturePad(canvas1);
        signaturePad2 = new SignaturePad(canvas2);

        function resizeCanvas() {

            //Sauvegarde sig / par
            var sig = signaturePad1.toDataURL(); 
            var par = signaturePad2.toDataURL(); 


            var ratio =  Math.max(window.devicePixelRatio || 1, 1);
            canvas1.width = canvas1.offsetWidth * ratio;
            canvas1.height = canvas1.offsetHeight * ratio;
            canvas1.getContext("2d").scale(ratio, ratio);           
            canvas2.width = canvas2.offsetWidth * ratio;
            canvas2.height = canvas2.offsetHeight * ratio;
            canvas2.getContext("2d").scale(ratio, ratio);

            // redraw
            signaturePad1.fromDataURL(sig); 
            signaturePad2.fromDataURL(par); 

        }

        window.onresize = resizeCanvas;
        resizeCanvas();

        // Init -> retourne la bonne valeur de isEmpty -> !!? Not sure if needed
        signaturePad1.clear();
        signaturePad2.clear();

        var signature = $('#confirm_delete_signature').val();
        if(signature){
            signaturePad1.fromDataURL(signature);
        }

        var paraphe = $('#confirm_delete_paraphe').val();
        if(paraphe){
            signaturePad2.fromDataURL(paraphe);
        }

        clearButton1.addEventListener("click", function (event) {
            signaturePad1.clear();
        });

        clearButton2.addEventListener("click", function (event) {
            signaturePad2.clear();
        });
1

1 Answers

0
votes

Here is i developed a little solution; Here are two key DOM elements:

  • div#id_wrapper
  • canvas#id

Considered it may be applied at devices with different devicePixelRatio and on screens changins theirs width (f.i.: portrait-landscape orientation).

export class FlexSignatureComponent extends React.Component {
  state = {
     width: 0,
     lines: [],
     storedValue: undefined,
     validationClass: '', // toggles between 'is-invalid'/'is-valid'
     validationMessage: ''
  }

The lib initiation is right after the component got loaded:

  componentDidMount = () => {  
    this.signPad = new SignaturePad(document.getElementById(this.props.htmlid), {
      onEnd: this.onChangeSignaturePad,
      backgroundColor: '#fff'
    });

    if (this.valueHolder.current.value) {
      const data = JSON.parse(this.valueHolder.current.value);

      this.state.lines = data.value;
      this.state.width = 100;
    }

    //you need the next workarounds if you have other onWidnowResize handlers manarging screen width
    //setTimeout-0 workaround to move windowResizeHandling at the end of v8-enging procedures queue
    // otherwise omit setTimeout and envoke func as it is
    setTimeout(this.handleWindowResize, 0);
    window.addEventListener("resize", () => setTimeout(this.handleWindowResize, 0));
  }

First handle window resize change


  handleWindowResize = () => {
    if (this.state.storedValue) {

      const prevWrapperWidth = this.state.width;
      const currentWrapperWidth = $(`#${this.props.htmlid}_wrapper`).width();
      const scale = prevWrapperWidth / currentWrapperWidth;

      this.state.width = currentWrapperWidth;

      this.setRescaledSignature(this.state.lines, scale);
      this.resetCanvasSize();
      this.signPad.fromData(this.state.lines)

    } else
      this.resetCanvasSize()
  }

Second rescaleSignature to another width


  setRescaledSignature = (lines, scale) => {
    lines.forEach(line => {
      line.points.forEach(point => {
        point.x /= scale;
        point.y /= scale;
      });
    });
  }

Finally updated canvas size

  resetCanvasSize = () => {
    const canvas = document.getElementById(this.props.htmlid);

    canvas.style.width = '100%';
    canvas.style.height = canvas.offsetWidth / 1.75 + "px";

    canvas.width = canvas.offsetWidth * devicePixelRatio;
    canvas.height = canvas.offsetHeight * devicePixelRatio;

    canvas.getContext("2d").scale(devicePixelRatio, devicePixelRatio);
  }

Here we on every change add new drawn line to this.state.lines and prepare the lines to be submited as json. But before the submission they need to create deepCopy and to be rescaled to conventional size (its width is equal 100px and DPR is 1)


  onChangeSignaturePad = () => {
    const value = this.signPad.toData();
    this.state.lines = value;

    const currentWrapperWidth = $(`#${this.props.htmlid}_wrapper`).width();

    const scale = currentWrapperWidth / 100;
    const ratio = 1 / devicePixelRatio;

    const linesCopy = JSON.parse(JSON.stringify(value));
    this.setRescaledSignature(linesCopy, scale, ratio);

    const data = {
      signature_configs: {
        devicePixelRatio: 1,
        wrapper_width: 100
      },
      value: linesCopy
    };
    this.state.storedValue = JSON.stringify(data);

    this.validate()
  }

One more thing is the red button to swipe the previous signatures

  onClickClear = (e) => {
    e.stopPropagation();
    this.signPad.clear();
    this.valueHolder.current.value = null;
    this.validate()
  }

  render() {
    let {label, htmlid} = this.props;
    const {validationClass = ''} = this.state;
    return (
      <div className="form-group fs_form-signature">
        <label>{Label}</label>
        <div className="fs_wr-signature">
          <button className={'fs_btn-clear'} onClick={this.onClickClear}>
            <i className="fas fa-times"></i>
          </button>
          <div id={htmlid + '_wrapper'} className={`w-100 fs_form-control ${validationClass}`}>
            <canvas id={htmlid}/>
          </div>
        </div>
        <div className={' invalid-feedback fs_show-feedback ' + validationClass}>Signature is a mandatory field</div>
      </div>
    )
  }

  postWillUnmount() {
    this.signPad.off();
  }

the used lib signature pad by szimek Used React and Bootstrap and some custome styles

the result would be

enter image description here

enter image description here