Smooth-Signature H5 verfügt über eine Handschrift-Signatur mit Stiftspitze und unterstützt die PC-/mobile Nutzung
Mini-Stores Mini-Programm-Multi-State-Management-Bibliothek, unterstützt die Verwendung von Multi-Plattform-Mini-Programmen
Betroffen von der Epidemie sind papierlose Prozesse und elektronische Verträge populär geworden, auch die Nachfrage nach elektronischen Signaturen ist weiter gestiegen und das Signaturerlebnis hat sich allmählich verbessert, vom anfänglichen Zeichnen von Linien auf einer einfachen Leinwand bis hin zum Verfolgen seidiger und runder Linien , und dann ist japanisches Papier erforderlich. Der Stiftkanteneffekt ist der gleiche wie beim Schreiben usw. Es gibt viele vorgefertigte Open-Source-Signaturbibliotheken im Internet. Darunter ist der Signature-Pad-Stricheffekt besser, aber Sie werden immer noch ein offensichtliches gezacktes Gefühl feststellen, wenn Sie ihn verwenden. Deshalb habe ich meine Freizeit genutzt, um eine andere Lösung zu implementieren Eigenes Verständnis. Gleichzeitig haben wir auch eine Version des Miniprogramms entwickelt und es mit bedürftigen Schülern geteilt.
npm install mini-smooth-signature
# 或
yarn add mini-smooth-signature
Unterstützt WeChat/Alipay/DingTalk/QQ-Miniprogramme. Im Folgenden finden Sie den Testcode für die DingTalk-Plattform. Die Beispielcodes der einzelnen Plattformen finden Sie unter Mini-Programme auf anderen Plattformen, um deren Verwendung zu überprüfen.
< view >
< canvas
id = " signature "
width = " {{width * scale}} "
height = " {{height * scale}} "
style = " width:{{width}}px;height:{{height}}px; "
disable-scroll = " {{true}} "
onTouchStart = " handleTouchStart "
onTouchMove = " handleTouchMove "
onTouchCancel = " handleTouchEnd "
onTouchEnd = " handleTouchEnd "
/>
</ view >
import Signature from 'mini-smooth-signature' ;
Page ( {
data : {
width : 320 ,
height : 200 ,
scale : 2 ,
} ,
onReady ( ) {
this . initSignature ( )
} ,
// 初始化
initSignature ( ) {
const ctx = dd . createCanvasContext ( 'signature' ) ;
this . signature = new Signature ( ctx , {
width : this . data . width ,
height : this . data . height ,
scale : this . data . scale ,
getImagePath : ( ) => {
return new Promise ( ( resolve ) => {
ctx . toTempFilePath ( {
success : res => resolve ( res . filePath ) ,
} )
} )
}
} ) ;
} ,
// 绑定touchstart事件
handleTouchStart ( e ) {
const pos = e . touches [ 0 ] ;
this . signature . onDrawStart ( pos . x , pos . y ) ;
} ,
// 绑定touchmove事件
handleTouchMove ( e ) {
const pos = e . touches [ 0 ] ;
this . signature . onDrawMove ( pos . x , pos . y ) ;
} ,
// 绑定touchend/touchcancel事件
handleTouchEnd ( ) {
this . signature . onDrawEnd ( ) ;
} ,
} ) ;
Alle Konfigurationselemente sind optional
const signature = new Signature ( ctx , {
width : 300 ,
height : 600 ,
scale : 2 ,
minWidth : 4 ,
maxWidth : 10 ,
color : '#1890ff' ,
bgColor : '#efefef' ,
} ) ;
Optionen.Breite
Die Breite der tatsächlich auf der Seite gerenderten Leinwand (px)
number
Optionen.Höhe
Die tatsächlich auf der Seite gerenderte Höhe der Leinwand (px)
number
Optionen.Skala
Leinwandzoom
number
Optionen.Farbe
Pinselfarbe
string
Optionen.bgColor
Hintergrundfarbe der Leinwand
string
Optionen.getImagePath
Promise-Funktion, die temporäre Bilder zum Speichern des Verlaufs generiert. Wenn dieses Element nicht konfiguriert ist, ist die Rückgängig-Funktion nicht verfügbar.
promise
Optionen.toDataURL
Base64-Bildfunktion generieren
function
Optionen.requestAnimationFrame
Wird beim nächsten Neuzeichnen ausgeführt. Wird verwendet, um die Lackierleistung zu verbessern und Verzögerungen und Unebenheiten zu reduzieren
function
Optionen.openSmooth
Ob der Stiftkanteneffekt aktiviert werden soll, standardmäßig aktiviert
boolean
Optionen.minWidth
Die minimale Breite des Pinsels (px), die minimale Breite des Pinsels, wenn die Stiftspitze eingeschaltet ist
number
Optionen.maxWidth
Die maximale Breite des Pinsels (px), die maximale Breite des Pinsels, wenn die Pinselkante aktiviert ist, oder die normale Breite des Pinsels, wenn die Pinselkante nicht aktiviert ist
number
Optionen.minSpeed
Die Mindestgeschwindigkeit (px/ms), die der Pinsel benötigt, um die Mindestbreite zu erreichen. Der Wertebereich liegt zwischen 1,0 und 10,0. Je kleiner der Wert, desto leichter wird der Pinsel dünner und der Pinselspitzeneffekt ist stärker Offensichtlich können Sie den Anzeigeeffekt selbst anpassen und einen Wert wählen, mit dem Sie zufrieden sind.
number
Optionen.maxWidthDiffRate
Die maximale prozentuale Vergrößerung (Verringerung) der Breite zweier benachbarter Linien beträgt 1–100. Um den Pinselstricheffekt zu erzielen, ändert sich die Pinselbreite mit der Pinselgeschwindigkeit Wenn er zu groß ist, ist der Übergangseffekt abrupt. Verwenden Sie maxWidthDiffRate, um den Breitenunterschied zu begrenzen und den Übergangseffekt natürlicher zu gestalten. Sie können den Anzeigeeffekt selbst anpassen und einen Wert wählen, mit dem Sie zufrieden sind.
number
Optionen.maxHistoryLength
Begrenzen Sie die Anzahl der Verlaufsdatensätze, d. h. die maximale Anzahl, die rückgängig gemacht werden kann. Durch die Übergabe von 0 wird die Verlaufsdatensatzfunktion deaktiviert.
number
// 画布上下文context
signature . ctx
// 清屏
signature . clear ( )
// 撤销,如果未配置getImagePath,则不可用
signature . undo ( )
// 获取base64图片,若未配置toDataURL,则不可用
signature . toDataURL ( )
// 是否为空
signature . isEmpty ( )
Normalerweise schreiben wir auf Papier. Wenn Sie genau hinsehen, werden Sie feststellen, dass die Strichstärke ungleichmäßig ist. Dies liegt an der unterschiedlichen Druckkraft und Bewegungsgeschwindigkeit des Stifts während des Schreibvorgangs. Auf Computern und mobilen Browsern können wir zwar nicht den Druck einer Berührung erzeugen, aber wir können die Geschwindigkeit der Pinselbewegung nutzen, um ungleichmäßige Stricheffekte zu erzielen, sodass die Schrift so scharf aussieht wie das Schreiben auf Papier. Im Folgenden wird der spezifische Implementierungsprozess vorgestellt (der unten gezeigte Code dient nur dem besseren Verständnis und ist nicht der endgültige Implementierungscode).
Erfassen Sie die Koordinaten der verschobenen Punkte, indem Sie das Canvas-Bewegungsereignis abhören, die aktuelle Zeit aufzeichnen und sie dann im Punktearray speichern.
function onTouchMove ( x , y ) {
const point = { x , y , t : Date . now ( ) }
points . push ( point ) ;
} ;
Berechnen Sie den Abstand zwischen den beiden Punkten anhand der Koordinaten der beiden Punkte und dividieren Sie ihn dann durch die Zeitdifferenz, um die Bewegungsgeschwindigkeit zu erhalten.
const distance = Math . sqrt ( Math . pow ( end . x - start . x , 2 ) + Math . pow ( end . y - start . y , 2 ) ) ;
const speed = distance / ( end . t - start . t ) ;
Ermitteln Sie die Bewegungsgeschwindigkeit zwischen zwei Punkten und berechnen Sie dann die Breite der Linie mithilfe eines einfachen Algorithmus, bei dem maxWidth, minWidth und minSpeed Konfigurationselemente sind
const addWidth = ( maxWidth - minWidth ) * speed / minSpeed ;
const lineWidth = Math . min ( Math . max ( maxWidth - addWidth , minWidth ) , maxWidth ) ;
Um zu verhindern, dass der Breitenunterschied zwischen zwei benachbarten Zeilen zu groß wird und einen abrupten Übergangseffekt verursacht, müssen außerdem Einschränkungen vorgenommen werden, wobei maxWidthDiffRate ein Konfigurationselement und preLineWidth die Breite der vorherigen Zeile ist.
const rate = ( lineWidth - preLineWidth ) / preLineWidth ;
const maxRate = maxWidthDiffRate / 100 ;
if ( Math . abs ( rate ) > maxRate ) {
const per = rate > 0 ? maxRate : - maxRate ;
lineWidth = preLineWidth * ( 1 + per ) ;
}
Da Sie nun die Breite der Linie zwischen jeweils zwei Punkten kennen, besteht der nächste Schritt darin, die Linie zu zeichnen. Damit die Linien runder wirken und der Übergang zwischen den Linienstärken natürlicher wird, habe ich die Linie zwischen zwei Punkten in drei Segmente gemittelt, wobei:
Beginnen Sie mit dem Zeichnen der Linie. Schauen Sie sich zunächst das erste Segment der Linie an, da das erste Segment der Linie die vorherige Linie schneidet. Um einen reibungslosen Übergang zwischen den beiden Linien sicherzustellen, wird eine quadratische Bezier-Kurve verwendet Punkt ist der Startpunkt des dritten Segments der vorherigen Linie (pre_x2, pre_y2).
ctx . lineWidth = lineWidth1
ctx . beginPath ( ) ;
ctx . moveTo ( pre_x2 , pre_y2 ) ;
ctx . quadraticCurveTo ( x0 , y0 , x1 , y1 ) ;
ctx . stroke ( ) ;
Das zweite Liniensegment ist die Übergangslinie, die das erste und das dritte Segment verbindet. Da die Linienbreiten des ersten und dritten Segments unterschiedlich sind, ist das zweite Liniensegment mit einem Trapez gefüllt, um den Übergangseffekt zu verstärken natürlich.
ctx . beginPath ( ) ;
ctx . moveTo ( point1 . x , point1 . y ) ;
ctx . lineTo ( point2 . x , point2 . y ) ;
ctx . lineTo ( point3 . x , point3 . y ) ;
ctx . lineTo ( point4 . x , point4 . y ) ;
ctx . fill ( ) ;
Wiederholen Sie den obigen Vorgang, wenn Sie die nächste Zeile im dritten Absatz zeichnen.