Comments (8)
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.
I can confirm there is a bug when the Value is provided initially.
from blazorise.
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.
my proposed fix for signaturepad.js
modifications:
- All sigpad.fromDataUrl calls
- added parameter { ratio: 1}
- 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.
@sks-ttaylor
Would you be so kind to submit a PR? It's easier to review.
from blazorise.
will do
from blazorise.
You can read our contribution guidelines at https://github.com/Megabit/Blazorise/blob/master/CONTRIBUTING.md#branch-organization
from blazorise.
Created PR
from blazorise.
Related Issues (20)
- Datepicker - Clear HOT 2
- [Bug]: RichtTextEdit - Cursor jumping to starting point of editor HOT 2
- [Bug]: DataGrid batch edit SOMETIMES saves if using tab in In cell mode HOT 4
- Show arrow for autocomplete HOT 6
- Autocomplete : Checkbox Menu closes when an item is unselected
- [Bug]: Width and Height .Is66/.Is33 properties do not work for Bootstrap 5
- DataGrid grouping example confusing HOT 5
- When I update record in DataGrid getting error of type cast HOT 3
- Autocomplete : Allow repeated texts
- TreeView: Context Menu API
- [Bug]: Autocomplete list has transparent slider background
- DataGrid Filter on press Enter
- [Bug]: The IntervalBeforeClose of NotificationService works incorrectly HOT 7
- [Bug]: mouse pointer on disabled button in bar is wrong
- [Bug]: TableRowHeader.razor using <th> not <tr> HOT 2
- Howto make a Button / Dropdown / Colorpicker combination HOT 1
- [Bug]: Slider inside a Tab causes it to break HOT 1
- [Bug]: Uncaught TypeError: Cannot read properties of null (reading 'getData') textEdit HOT 3
- [Bug]: flatpickrWrapper is not defined HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from blazorise.