Skip to content

Commit

Permalink
Issue/7219-Signature-Pad-is-not-Responsive-on-Mobile-Screens (#7284)
Browse files Browse the repository at this point in the history
* #7219 Signature Pad is not Responsive on Mobile Screens
Fixes #7219

* #7219 - add stretch option

* #7219 Signature Pad is not Responsive on Mobile Screens
Fixes #7219

* #7219 Signature Pad is not Responsive on Mobile Screens
Fixes #7219

* #7219 - fixing tests

* #7219 - fixed tests

* #7219 - fixed renderedWidth

* Update Signaturepad.vue

* #7219 Signature Pad is not Responsive on Mobile Screens - change styles and fix clear
Fixes #7219

* Add descriptions

* #7219 Signature Pad is not Responsive on Mobile Screens - fixed mobile
Fixes #7219

---------

Co-authored-by: RomanTsukanov <[email protected]>
  • Loading branch information
novikov82 and RomanTsukanov authored Nov 3, 2023
1 parent 05d3081 commit 4bcf7b2
Show file tree
Hide file tree
Showing 19 changed files with 287 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div
[class]="model.cssClasses.root"
[style.height]="model.signatureHeight + 'px'" [style.width]="model.signatureWidth + 'px'" #contentElement>
[style.width]="model.renderedCanvasWidth" #contentElement>
<div [class]="model.cssClasses.placeholder" [visible]="model.needShowPlaceholder()" [model]="$any(model).locPlaceholder" sv-ng-string></div>
<div>
<img *ngIf="!!model.backgroundImage" [src]="model.backgroundImage" [width]="model.signatureWidth" [height]="model.signatureHeight" [class]="model.cssClasses.backgroundImage">
<img *ngIf="!!model.backgroundImage" [src]="model.backgroundImage" [style.width]="model.renderedCanvasWidth" [class]="model.cssClasses.backgroundImage">
<canvas tabindex="0" [class]="model.cssClasses.canvas"></canvas>
</div>
<div [class]="model.cssClasses.controls" [visible]="model.canShowClearButton">
Expand Down
8 changes: 4 additions & 4 deletions packages/survey-vue3-ui/src/Signaturepad.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
:class="question.cssClasses.root"
ref="root"
v-bind:style="{
height: question.signatureHeight + 'px',
width: question.signatureWidth + 'px',
width: question.renderedCanvasWidth,
}"
>
<div
Expand All @@ -18,8 +17,9 @@
v-if="question.backgroundImage"
:class="question.cssClasses.backgroundImage"
:src="question.backgroundImage"
:width="question.signatureWidth"
:height="question.signatureHeight"
v-bind:style="{
width: question.renderedCanvasWidth,
}"
/>
<canvas tabindex="0" :class="question.cssClasses.canvas"></canvas>
</div>
Expand Down
1 change: 0 additions & 1 deletion src/defaultV2-theme/blocks/sd-signaturepad.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
}
}

.sd-signaturepad__canvas,
.sd-signaturepad__background-image {
position: absolute;
top: 0;
Expand Down
6 changes: 3 additions & 3 deletions src/knockout/templates/question-signaturepad.html
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<script type="text/html" id="survey-question-signaturepad">
<div data-bind="css: question.cssClasses.root, style: { height: signatureHeight, width: signatureWidth }">
<div data-bind="css: question.cssClasses.root, style: { width: question.renderedCanvasWidth }">
<div data-bind="css: question.cssClasses.placeholder, visible: $data.needShowPlaceholder()">
<!-- ko template: { name: 'survey-string', data: question.locPlaceholder } -->
<!-- /ko -->
</div>
<div>
<!-- ko if: question.backgroundImage -->
<img data-bind="attr: { src: question.backgroundImage, width: question.signatureWidth, height: question.signatureHeight }, css: question.cssClasses.backgroundImage">
<img data-bind="attr: { src: question.backgroundImage}, style: { width: question.renderedCanvasWidth }, css: question.cssClasses.backgroundImage">
<!-- /ko -->
<canvas tabindex='0' data-bind="css: question.cssClasses.canvas"></canvas>
<canvas tabindex='0' data-bind="css: question.cssClasses.canvas" ></canvas>
</div>
<!-- ko if: question.canShowClearButton -->
<div data-bind="css: question.cssClasses.controls">
Expand Down
135 changes: 91 additions & 44 deletions src/question_signaturepad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,11 @@ import { SurveyModel } from "./survey";
import { ISurveyImpl } from "./base-interfaces";
import { ConsoleWarnings } from "./console-warnings";
import { ITheme } from "./themes";
import { classesToSelector } from "./utils/utils";

var defaultWidth = 300;
var defaultHeight = 200;

export function getCanvasRatio(canvas: HTMLCanvasElement): number {
var context: any = canvas.getContext("2d");
var devicePixelRatio = window.devicePixelRatio || 1;
var backingStoreRatio =
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio ||
1;

return devicePixelRatio / backingStoreRatio;
}

function resizeCanvas(canvas: HTMLCanvasElement) {
var context: any = canvas.getContext("2d");
var ratio = getCanvasRatio(canvas);

var oldWidth = canvas.width;
var oldHeight = canvas.height;

canvas.width = oldWidth * ratio;
canvas.height = oldHeight * ratio;

canvas.style.width = oldWidth + "px";
canvas.style.height = oldHeight + "px";

context.scale(ratio, ratio);
}

/**
* A class that describes the Signature question type.
*
Expand Down Expand Up @@ -77,7 +48,9 @@ export class QuestionSignaturePadModel extends Question {
const format = this.dataFormat === "jpeg" ? "image/jpeg" :
(this.dataFormat === "svg" ? "image/svg+xml" : "");
var data = this.signaturePad.toDataURL(format);
this.valueIsUpdatingInternally = true;
this.value = data;
this.valueIsUpdatingInternally = false;
}
}

Expand All @@ -103,10 +76,48 @@ export class QuestionSignaturePadModel extends Question {
this.updateColors(this.signaturePad);
}
}
private canvas: any;
private scale: number;
private valueIsUpdatingInternally: boolean = false;

private scaleCanvas(refresh: boolean = true) {
const canvas = this.canvas;
if (canvas.width != this.containerWidth) canvas.width = this.containerWidth;
if (canvas.height != this.containerHeight) canvas.height = this.containerHeight;
const scale = canvas.offsetWidth / canvas.width;
if (this.scale != scale) {
this.scale = scale;
canvas.style.width = this.renderedCanvasWidth;
this.signaturePad.minWidth = this.penMinWidth * scale;
this.signaturePad.maxWidth = this.penMaxWidth * scale;
canvas.getContext("2d").scale(1 / scale, 1 / scale);

if (refresh) this.refreshCanvas();
}
}
private refreshCanvas() {
var data = this.value;
const canvas = this.canvas;
if (!data) {
canvas.getContext("2d").clearRect(0, 0, canvas.width * this.scale, canvas.height * this.scale);
this.signaturePad.clear();
} else {
this.signaturePad.fromDataURL(data, { width: canvas.width * this.scale, height: canvas.height * this.scale });
}
}

private updateValueHandler = () => {
this.scaleCanvas(false);
this.refreshCanvas();
};

initSignaturePad(el: HTMLElement) {
var canvas: any = el.getElementsByTagName("canvas")[0];
this.canvas = canvas;
canvas.width = this.containerWidth;
canvas.height = this.containerHeight;
var signaturePad = new SignaturePad(canvas, { backgroundColor: "#ffffff" });
this.signaturePad = signaturePad;
if (this.isInputReadOnly) {
signaturePad.off();
}
Expand All @@ -122,6 +133,7 @@ export class QuestionSignaturePadModel extends Question {
this.updateColors(signaturePad);

(signaturePad as any).addEventListener("beginStroke", () => {
this.scaleCanvas();
this.isDrawingValue = true;
canvas.focus();
}, { once: false });
Expand All @@ -131,23 +143,11 @@ export class QuestionSignaturePadModel extends Question {
this.updateValue();
}, { once: false });

var updateValueHandler = () => {
var data = this.value;
canvas.width = this.signatureWidth || defaultWidth;
canvas.height = this.signatureHeight || defaultHeight;
resizeCanvas(canvas);
if (!data) {
signaturePad.clear();
} else {
signaturePad.fromDataURL(data);
}
};
updateValueHandler();
this.updateValueHandler();
this.readOnlyChangedCallback();
this.signaturePad = signaturePad;
var propertyChangedHandler = (sender: any, options: any) => {
if (options.name === "signatureWidth" || options.name === "signatureHeight" || options.name === "value") {
updateValueHandler();
if (!this.valueIsUpdatingInternally) this.updateValueHandler();
}
};
this.onPropertyChanged.add(propertyChangedHandler);
Expand Down Expand Up @@ -195,6 +195,38 @@ export class QuestionSignaturePadModel extends Question {
public set signatureHeight(val: number) {
this.setPropertyValue("signatureHeight", val);
}
/**
* Specifies whether the signature area should be scaled to fit into the question width.
*
* Default value: `false`
*
* > The signature area is scaled only for display. The resulting image will have dimensions specified by the [`signatureHeight`](#signatureHeight) and [`signatureWidth`](#signatureWidth) properties.
*/
@property({ defaultValue: false }) signatureAutoScaleEnabled: boolean;
/**
* Speicifies the minimum width of pen strokes, measured in pixels.
*
* Default value: 0.5
*/
@property({ defaultValue: 0.5 }) penMinWidth: number;
/**
* Speicifies the maximum width of pen strokes, measured in pixels.
*
* Default value: 2.5
*/
@property({ defaultValue: 2.5 }) penMaxWidth: number;

private get containerHeight(): any {
return this.signatureHeight || defaultHeight;
}

private get containerWidth(): any {
return this.signatureWidth || defaultWidth;
}

public get renderedCanvasWidth(): string {
return this.signatureAutoScaleEnabled ? "100%" : this.containerWidth + "px";
}

//todo: need to remove this property
public get height(): number {
Expand Down Expand Up @@ -320,6 +352,21 @@ Serializer.addClass(
category: "general",
default: 200,
},
{
name: "signatureAutoScaleEnabled:boolean",
category: "general",
default: false,
},
{
name: "penMinWidth:number",
category: "general",
default: 0.5,
},
{
name: "penMaxWidth:number",
category: "general",
default: 2.5,
},
//need to remove this property
{
name: "height:number",
Expand Down
4 changes: 2 additions & 2 deletions src/react/signaturepad.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class SurveyQuestionSignaturePad extends SurveyQuestionElementBase {
var cssClasses = this.question.cssClasses;
var clearButton = this.renderCleanButton();
return (
<div className={cssClasses.root} ref={(root) => (this.setControl(root))} style={{ height: this.question.signatureHeight, width: this.question.signatureWidth }}>
<div className={cssClasses.root} ref={(root) => (this.setControl(root))} style={{ width: this.question.renderedCanvasWidth }}>
<div
className={cssClasses.placeholder}
style={{ display: this.question.needShowPlaceholder() ? "" : "none" }}
Expand All @@ -35,7 +35,7 @@ export class SurveyQuestionSignaturePad extends SurveyQuestionElementBase {
renderBackgroundImage(): JSX.Element | null {
if(!this.question.backgroundImage) return null;

return <img className={this.question.cssClasses.backgroundImage} src={this.question.backgroundImage} width={this.question.signatureWidth} height= {this.question.signatureHeight}></img>;
return <img className={this.question.cssClasses.backgroundImage} src={this.question.backgroundImage} style={{ width: this.question.renderedCanvasWidth }}></img>;
}

renderCleanButton(): JSX.Element | null {
Expand Down
10 changes: 8 additions & 2 deletions src/signaturepad.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.sjs_sp_container {
position: relative;
max-width: 100%;
}
.sjs_sp_controls {
position: absolute;
Expand All @@ -25,11 +26,16 @@
width: 100%;
height: 100%;
}

.sjs_sp_canvas,
.sjs_sp_canvas {
position: relative;
max-width: 100%;
}
.sjs_sp__background-image {
position: absolute;
top: 0;
left: 0;
object-fit: cover;
max-width: 100%;
width: 100%;
height: 100%;
}
7 changes: 4 additions & 3 deletions src/vue/signaturepad.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@
<div
:class="question.cssClasses.root"
v-bind:style="{
height: question.signatureHeight + 'px',
width: question.signatureWidth + 'px',
width: question.renderedCanvasWidth,
}"
>
<div :class="question.cssClasses.placeholder" v-show="question.needShowPlaceholder()">
<survey-string :locString="question.locPlaceholder"></survey-string>
</div>
<div>
<img v-if="question.backgroundImage" :class="question.cssClasses.backgroundImage" :src="question.backgroundImage"
:width="question.signatureWidth" :height="question.signatureHeight">
v-bind:style="{
width: question.renderedCanvasWidth,
}">
<canvas tabindex="0" :class="question.cssClasses.canvas"></canvas>
</div>
<div :class="question.cssClasses.controls" v-if="question.canShowClearButton">
Expand Down
17 changes: 16 additions & 1 deletion tests/markup/etalon_signaturepad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ registerMarkupTests(
]
},
snapshot: "signaturepad-with-backgroundImage"
}
},
{
name: "Test Scaled Signaturepad question markup",
json: {
questions: [
{
type: "signaturepad",
name: "q1",
titleLocation: "hidden",
"backgroundImage": "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg",
signatureAutoScaleEnabled: true
}
]
},
snapshot: "signaturepad-scaled"
},
]
);
15 changes: 15 additions & 0 deletions tests/markup/snapshots/signaturepad-scaled.snap.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="sjs_sp_container sv_q_signaturepad" style="width:100%;">
<div class="sjs_sp_placeholder">
<span class="sv-string-viewer">Sign here</span>
</div>
<div>
<img class="sjs_sp__background-image" src="https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" style="width:100%;">
<canvas class="sjs_sp_canvas" height="200" style="user-select:none; width:100%;" tabindex="0" width="300">
</canvas>
</div>
<div class="sjs_sp_controls">
<button class="sjs_sp_clear" title="Clear" type="button">
<span></span>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<div class="sjs_sp_container sv_q_signaturepad" style="height:200px; width:300px;">
<div class="sjs_sp_placeholder"><span class="sv-string-viewer">Sign here</span></div>
<div class="sjs_sp_container sv_q_signaturepad" style="width:300px;">
<div class="sjs_sp_placeholder">
<span class="sv-string-viewer">Sign here</span>
</div>
<div>
<img class="sjs_sp__background-image" height="200" src="https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" width="300">
<canvas class="sjs_sp_canvas" height="200" style="height:200px; user-select:none; width:300px;" tabindex="0" width="300">
<img class="sjs_sp__background-image" src="https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" style="width:300px;">
<canvas class="sjs_sp_canvas" height="200" style="user-select:none; width:300px;" tabindex="0" width="300">
</canvas>
</div>
<div class="sjs_sp_controls">
Expand Down
8 changes: 5 additions & 3 deletions tests/markup/snapshots/signaturepad.snap.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<div class="sjs_sp_container sv_q_signaturepad" style="height:200px; width:300px;">
<div class="sjs_sp_placeholder"><span class="sv-string-viewer">Sign here</span></div>
<div class="sjs_sp_container sv_q_signaturepad" style="width:300px;">
<div class="sjs_sp_placeholder">
<span class="sv-string-viewer">Sign here</span>
</div>
<div>
<canvas class="sjs_sp_canvas" height="200" style="height:200px; user-select:none; width:300px;" tabindex="0" width="300">
<canvas class="sjs_sp_canvas" height="200" style="user-select:none; width:300px;" tabindex="0" width="300">
</canvas>
</div>
<div class="sjs_sp_controls">
Expand Down
Loading

0 comments on commit 4bcf7b2

Please sign in to comment.