Giter Site home page Giter Site logo

Comments (8)

sks-ttaylor avatar sks-ttaylor commented on June 21, 2024 1

Found a hack/fix that works for me, what do you think?

The issue is somewhere in the vendor/sigpad.js -- not touching szimek/signature_pad
So a workaround might be:

In SignaturePad.razor.cs:

protected override async Task OnAfterRenderAsync( bool firstRender )
{
    if ( firstRender )
    {
        DotNetObjectRef ??= DotNetObjectReference.Create( this );

        JSModule = new JSSignaturePadModule( JSRuntime, VersionProvider );

        await JSModule.Initialize( DotNetObjectRef, ElementRef, ElementId, new
        {
            //DataUrl = GetDataUrl( Value, ImageType ), ** Do not set DataUrl during initialize
            DotSize,
            MinLineWidth,
            MaxLineWidth,
            Throttle,
            MinDistance,
            BackgroundColor,
            PenColor,
            VelocityFilterWeight,
            ImageType = GetImageTypeString( ImageType ),
            ImageQuality,
            IncludeImageBackgroundColor,
            ReadOnly,
        } );

        //Added the following
        ExecuteAfterRender( async () => 
            await JSModule.UpdateOptions( ElementRef, ElementId, new
            {
                DataUrl = new
                {
                    Changed = true,
                    Value = GetDataUrl( Value, ImageType )
                }
            } )
        );
       //End of Add
    }
    await base.OnAfterRenderAsync( firstRender );
}

from blazorise.

stsrki avatar stsrki commented on June 21, 2024

I can confirm there is a bug when the Value is provided initially.

from blazorise.

sks-ttaylor avatar sks-ttaylor commented on June 21, 2024

found the real issue:

resizeCanvas uses sigpad.toData();

when sigpad.fromDataURL is used to load the canvas, sigpad.toData() returns zero length data.

I'm looking for a fix...

from blazorise.

sks-ttaylor avatar sks-ttaylor commented on June 21, 2024

my proposed fix for signaturepad.js
modifications:

  1. All sigpad.fromDataUrl calls
    • added parameter { ratio: 1}
  2. resizeCanvas
    • change from using fromData/toData to getImageData/putImageData
import { getRequiredElement } from "../Blazorise/utilities.js?v=1.4.2.0";
import "./vendors/sigpad.js?v=1.4.2.0";

const _instances = [];

export function initialize(dotNetAdapter, element, elementId, options) {
  element = getRequiredElement(element, elementId);

  if (!element)
    return;

  // we need to match the canvas size ans size in styles so that we can properly calculate scaling based on screen size
  if (element.width && element.height) {
    element.style.width = `${element.width}px`;
    element.style.height = `${element.height}px`;
  }

  const sigpad = new SignaturePad(element, {
    minWidth: options.minLineWidth || 0.5,
    maxWidth: options.maxLineWidth || 2.5,
    backgroundColor: options.backgroundColor || "rgba(0,0,0,0)",
    penColor: options.penColor || "black",
    velocityFilterWeight: options.velocityFilterWeight || 0.7,
    dotSize: options.dotSize || 0,
    throttle: options.throttle || 16,
    minPointDistance: options.minPointDistance || 0.5,
  });

  if (options.dataUrl) {
    sigpad.fromDataURL(options.dataUrl, { ratio: 1 });
  }

  if (options.readOnly === true) {
    sigpad.off();
  }

  const instance = {
    options: options,
    sigpad: sigpad,
    dotNetAdapter: dotNetAdapter,
    element: element
  };

  registerToEvents(dotNetAdapter, instance);

  resizeCanvas(sigpad, element);

  // Observe the element visibily. This is needed in cases when signaturepad is placed inside of tabs.
  const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      resizeCanvas(sigpad, entry.target);
    });
  }, options);
  observer.observe(element);

  _instances[elementId] = instance;
}

export function destroy(element, elementId) {
  const instances = _instances || {};
  const instance = instances[elementId];

  if (instance) {
    if (instance.sigpad) {
      instance.sigpad.off();
    }

    delete instances[elementId];
  }
}

export function updateOptions(element, elementId, options) {
  const instance = _instances[elementId];
  if (instance && instance.sigpad && options) {
    if (options.dataUrl.changed) {
      if (options.dataUrl.value) {
        instance.sigpad.fromDataURL(options.dataUrl.value, { ratio: 1 });
      } else {
        instance.sigpad.clear();
      }
    }

    if (options.penColor.changed) {
      instance.sigpad.penColor = options.penColor.value || "black";
    }

    if (options.minLineWidth.changed) {
      instance.sigpad.minWidth = options.minLineWidth.value;
    }

    if (options.maxLineWidth.changed) {
      instance.sigpad.maxWidth = options.maxLineWidth.value;
    }

    if (options.backgroundColor.changed) {
      instance.sigpad.backgroundColor = options.backgroundColor.value || "rgba(0,0,0,0)";
      const data = instance.sigpad.toData();
      instance.sigpad.fromData(data);
    }

    if (options.velocityFilterWeight.changed) {
      instance.sigpad.velocityFilterWeight = options.velocityFilterWeight.value;
    }

    if (options.dotSize.changed) {
      instance.sigpad.dotSize = options.dotSize.value;
    }

    if (options.readOnly.changed) {
      if (options.readOnly.value === true) {
        instance.sigpad.off();
      }
      else {
        instance.sigpad.on();
      }
    }

    if (options.imageType.changed) {
      instance.options.imageType = options.imageType.value;
    }

    if (options.imageQuality.changed) {
      instance.options.imageQuality = options.imageQuality.value;
    }

    if (options.includeImageBackgroundColor.changed) {
      instance.options.includeImageBackgroundColor = options.includeImageBackgroundColor.value;
    }
  }
}

export function setData(element, elementId, data) {
  const instance = _instances[elementId];

  if (instance && instance.sigpad) {
    instance.sigpad.fromData(data);
  }
}

export function clear(element, elementId) {
  const instance = _instances[elementId];

  if (instance && instance.sigpad) {
    instance.sigpad.clear();
  }
}

export function undo(element, elementId) {
  const instance = _instances[elementId];

  if (instance && instance.sigpad) {
    const data = instance.sigpad.toData();

    if (data && data.length > 0) {
      data.pop();
      instance.sigpad.fromData(data);

      return getImageDataURL(instance.sigpad, instance.options);
    }
  }

  return null;
}

function registerToEvents(dotNetAdapter, instance) {
  if (instance && instance.sigpad) {
    instance.sigpad.addEventListener("beginStroke", (e) => {
      if (e && e.detail) {
        dotNetAdapter.invokeMethodAsync("NotifyBeginStroke", e.detail.offsetX, e.detail.offsetY)
      }
    });

    instance.sigpad.addEventListener("endStroke", (e) => {
      if (e && e.detail) {
        const dataURL = getImageDataURL(instance.sigpad, instance.options);

        dotNetAdapter.invokeMethodAsync("NotifyEndStroke", dataURL, e.detail.offsetX, e.detail.offsetY);
      }
    });
  }
}

function getImageDataURL(sigpad, options) {
  if (!sigpad || !options) {
    return null;
  }

  if (options.imageType === "jpeg") {
    return sigpad.toDataURL("image/jpeg", options.imageQuality || 1);
  }
  else if (options.imageType === "svg") {
    return sigpad.toDataURL("image/svg+xml", { includeBackgroundColor: options.includeImageBackgroundColor || false });
  }

  return sigpad.toDataURL("image/png", options.imageQuality || 1);
}

window.onresize = resizeAllCanvas;

function resizeAllCanvas() {
  if (_instances) {
    for (let i = 0; i < _instances.length; i++) {
      const instance = _instances[i];

      if (instance) {
        resizeCanvas(instance.sigpad, instance.element);
      }
    }
  }
}

function resizeCanvas(sigpad, canvas) {
  if (!sigpad || !canvas) {
    return null;
  }

  // When zoomed out to less than 100%, for some very strange reason,
  // some browsers report devicePixelRatio as less than 1
  // and only part of the canvas is cleared then.
  const ratio = Math.max(window.devicePixelRatio || 1, 1);

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

  const imageData = context.getImageData(0, 0, canvas.width, canvas.height, { colorSpace : 'srgb' });

  // This part causes the canvas to be cleared
  canvas.width = canvas.offsetWidth * ratio;
  canvas.height = canvas.offsetHeight * ratio;
  context.scale(ratio, ratio);

  context.putImageData(imageData, 0, 0);

}

from blazorise.

David-Moreira avatar David-Moreira commented on June 21, 2024

@sks-ttaylor
Would you be so kind to submit a PR? It's easier to review.

from blazorise.

sks-ttaylor avatar sks-ttaylor commented on June 21, 2024

will do

from blazorise.

stsrki avatar stsrki commented on June 21, 2024

You can read our contribution guidelines at https://github.com/Megabit/Blazorise/blob/master/CONTRIBUTING.md#branch-organization

from blazorise.

sks-ttaylor avatar sks-ttaylor commented on June 21, 2024

Created PR

from blazorise.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.