Skip to content

Instantly share code, notes, and snippets.

@mhamzas
Created October 4, 2019 18:57
Show Gist options
  • Save mhamzas/b89bf3f2de4b12cb41c4b873110db342 to your computer and use it in GitHub Desktop.
Save mhamzas/b89bf3f2de4b12cb41c4b873110db342 to your computer and use it in GitHub Desktop.
Capture Signature using HTML5 Canvas inside Salesforce Lightning Web Component [Salesforce Files]
<template>
<lightning-card
title="Signature Capturing using LWC"
icon-name="custom:custom30">
<div class="c-container">
<div style="text-align: center; ">
<h2 class="slds-text-heading_medium slds-m-bottom_medium">
Sign Here
</h2>
<p class="slds-m-bottom_small">
<canvas name="canvasItem" style="border:2px solid rgb(136, 135, 135);
background: transparent;"></canvas>
</p>
<p class="slds-m-bottom_small">
<lightning-button variant="brand" label="Save" title="Save"
onclick={handleSaveClick} class="slds-m-left_x-small"></lightning-button>
<lightning-button variant="brand" label="Clear" title="Clear"
onclick={handleClearClick} class="slds-m-left_x-small"></lightning-button>
</p>
</div>
</div>
</lightning-card>
</template>
/* eslint-disable no-unused-vars */
/* eslint-disable no-console */
import { LightningElement,api } from 'lwc';
import saveSign from '@salesforce/apex/SignatureHelper.saveSign';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
//declaration of variables for calculations
let isDownFlag,
isDotFlag = false,
prevX = 0,
currX = 0,
prevY = 0,
currY = 0;
let x = "#0000A0"; //blue color
let y = 1.5; //weight of line width and dot.
let canvasElement, ctx; //storing canvas context
let attachment; //holds attachment information after saving the sigture on canvas
let dataURL,convertedDataURI; //holds image data
export default class CapturequestedEventignature extends LightningElement {
@api recordId;
//event listeners added for drawing the signature within shadow boundary
constructor() {
super();
this.template.addEventListener('mousemove', this.handleMouseMove.bind(this));
this.template.addEventListener('mousedown', this.handleMouseDown.bind(this));
this.template.addEventListener('mouseup', this.handleMouseUp.bind(this));
this.template.addEventListener('mouseout', this.handleMouseOut.bind(this));
}
//retrieve canvase and context
renderedCallback(){
canvasElement = this.template.querySelector('canvas');
ctx = canvasElement.getContext("2d");
}
//handler for mouse move operation
handleMouseMove(event){
this.searchCoordinatesForEvent('move', event);
}
//handler for mouse down operation
handleMouseDown(event){
this.searchCoordinatesForEvent('down', event);
}
//handler for mouse up operation
handleMouseUp(event){
this.searchCoordinatesForEvent('up', event);
}
//handler for mouse out operation
handleMouseOut(event){
this.searchCoordinatesForEvent('out', event);
}
/*
handler to perform save operation.
save signature as attachment.
after saving shows success or failure message as toast
*/
handleSaveClick(){
//set to draw behind current content
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = "#FFF"; //white
ctx.fillRect(0,0,canvasElement.width, canvasElement.height);
//convert to png image as dataURL
dataURL = canvasElement.toDataURL("image/png");
//convert that as base64 encoding
convertedDataURI = dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
//call Apex method imperatively and use promise for handling sucess & failure
saveSign({strSignElement: convertedDataURI,recId : this.recordId})
.then(result => {
//this.ContentDocumentLink = result;
//console.log('ContentDocumentId=' + this.ContentDocumentLink.ContentDocumentId);
//show success message
this.dispatchEvent(
new ShowToastEvent({
title: 'Success',
message: 'Salesforce File created with Signature',
variant: 'success',
}),
);
})
.catch(error => {
//show error message
this.dispatchEvent(
new ShowToastEvent({
title: 'Error creating Salesforce File record',
message: error.body.message,
variant: 'error',
}),
);
});
}
//clear the signature from canvas
handleClearClick(){
ctx.clearRect(0, 0, canvasElement.width, canvasElement.height);
}
searchCoordinatesForEvent(requestedEvent, event){
event.preventDefault();
if (requestedEvent === 'down') {
this.setupCoordinate(event);
isDownFlag = true;
isDotFlag = true;
if (isDotFlag) {
this.drawDot();
isDotFlag = false;
}
}
if (requestedEvent === 'up' || requestedEvent === "out") {
isDownFlag = false;
}
if (requestedEvent === 'move') {
if (isDownFlag) {
this.setupCoordinate(event);
this.redraw();
}
}
}
//This method is primary called from mouse down & move to setup cordinates.
setupCoordinate(eventParam){
//get size of an element and its position relative to the viewport
//using getBoundingClientRect which returns left, top, right, bottom, x, y, width, height.
const clientRect = canvasElement.getBoundingClientRect();
prevX = currX;
prevY = currY;
currX = eventParam.clientX - clientRect.left;
currY = eventParam.clientY - clientRect.top;
}
//For every mouse move based on the coordinates line to redrawn
redraw() {
ctx.beginPath();
ctx.moveTo(prevX, prevY);
ctx.lineTo(currX, currY);
ctx.strokeStyle = x; //sets the color, gradient and pattern of stroke
ctx.lineWidth = y;
ctx.closePath(); //create a path from current point to starting point
ctx.stroke(); //draws the path
}
//this draws the dot
drawDot(){
ctx.beginPath();
ctx.fillStyle = x; //blue color
ctx.fillRect(currX, currY, y, y); //fill rectrangle with coordinates
ctx.closePath();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="captureSignature">
<apiVersion>46.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
public with sharing class SignatureHelper {
@AuraEnabled
public static void saveSign(String strSignElement,Id recId){
// Create Salesforce File
//Insert ContentVersion
ContentVersion cVersion = new ContentVersion();
cVersion.ContentLocation = 'S'; //S-Document is in Salesforce. E-Document is outside of Salesforce. L-Document is on a Social Netork.
cVersion.PathOnClient = 'Signature-'+System.now() +'.png';//File name with extention
cVersion.Origin = 'H';//C-Content Origin. H-Chatter Origin.
//cVersion.OwnerId = attach.OwnerId;//Owner of the file
cVersion.Title = 'Signature-'+System.now() +'.png';//Name of the file
cVersion.VersionData = EncodingUtil.base64Decode(strSignElement);//File content
Insert cVersion;
//After saved the Content Verison, get the ContentDocumentId
Id conDocument = [SELECT ContentDocumentId FROM ContentVersion WHERE Id =:cVersion.Id].ContentDocumentId;
//Insert ContentDocumentLink
ContentDocumentLink cDocLink = new ContentDocumentLink();
cDocLink.ContentDocumentId = conDocument;//Add ContentDocumentId
cDocLink.LinkedEntityId = recId;//Add attachment parentId
cDocLink.ShareType = 'I';//V - Viewer permission. C - Collaborator permission. I - Inferred permission.
cDocLink.Visibility = 'AllUsers';//AllUsers, InternalUsers, SharedUsers
Insert cDocLink;
// Create attachement
/*
Attachment objAttachment = new Attachment();
objAttachment.Name = 'Demo-Signature.png';
objAttachment.ParentId = recId;
objAttachment.ContentType = 'image/png';
objAttachment.Body = EncodingUtil.base64Decode(strSignElement);
insert objAttachment;
return objAttachment;
*/
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment