StartseiteBlog

Auf einer React Webapp zeichnen

By Andreas Riepl
Published in Softwareentwicklung
January 17, 2023
2 min read
Auf einer React Webapp zeichnen

In einem unserer Kundenprojekte hatten wir die Anforderung, handschriftliche Notizen zu den Texten in der Web-App hinterlegen zu können. Dieser Post erklärt die technischen Hintergründe, das Vorgehen, sowie die Umsetzung dieser Anforderung in unserer React-Anwendung.

screenshot sketch

Technische Möglichkeiten zur Umsetzung

Aus technischer Sicht gibt es zwei Arten, mit denen sich das Zeichnen auf einer Web-Anwendung realisieren lässt:

  • HTML5 Canvas: Mit dem HTML5-Element <canvas> können Entwickler eine Zeichenfläche in einer Webseite erstellen und mit JavaScript Formen, Linien und Bilder auf der Leinwand zeichnen. Dies ist eine beliebte Option zum Hinzufügen einfacher Skizzenfunktionen zu einer Webanwendung.
  • SVG: Scalable Vector Graphics (SVG) ist ein Vektorgrafikformat, mit dem sich interaktive, skalierbare Zeichnungen im Web erstellen lassen. SVG kann in Verbindung mit HTML und CSS verwendet werden, um benutzerdefinierte Zeichenwerkzeuge zu erstellen oder komplexe Grafiken anzuzeigen.

Wie häufig im Software-Umfeld wurde ein solches Feature bereits von anderen Entwicklern in unterschiedlichen Frameworks umgesetzt, die häufig auch als Open Source Software veröffentlicht werden. So finden sich nach kurzer Recherche auch Implementierungen der oben aufgeführten Umsetzungsmöglichkeiten in React. Speziell hierfür habe ich mir react-canvas-draw und react-sketch-canvas genauer angeschaut: Während react-canvas-draw die HTML-Canvas-Tags implementiert, stützt sich der Ansatz von react-sketch-canvas auf SVGs. Beide Bibliotheken bringen Funktionalitäten zum Speichern, zum Laden, zum Rückgängig machen der letzten Änderung und zum Bereinigen der Zeichenfläche mit. Zwar hat react-canvas-draw mit knapp Zehntausend wöchentlichen Downloads die etwa 4-fache Reichweite, wirft man allerdings einen Blick auf das Code-Repository, stellt man fest, dass es seit über einem Jahr nicht mehr aktualisiert wurde und sogar archiviert ist. React-sketch-canvas hingegen ist nicht nur neuer, sondern wird auch noch weiterentwickelt. Zusätzlich implementiert die Bibliothek eine Radier-Funktion, welche sich unser Kunde wünscht.

Dies waren letztendlich die Gründe, anhand derer ich mich für eine Umsetzung mit react-sketch-canvas entschieden habe.

Implementierung

Die Anforderung, auf dem Text schreiben zu können, wurde über das CSS-Grid-Layout gelöst. Damit ist es möglich, die transparente Zeichenfläche auf die gerenderten Texte zu legen, ohne die Texte bei einer Interaktion zu verändern. Die Implementierung ist auszugsweise im Folgenden abgebildet:

const useStyles = makeStyles((theme: Theme) => ({
    drawableTextContainer: {
        display: "grid",
        gridArea: "1 / 1 / 1 / 1",
        overflowY: "scroll",
        borderRadius: "4px",
        border: "1px solid #CCC",
        backgroundColor: "white",
        width: "100%"
    },
    drawableEditorTextElement: {
        gridArea: "1 / 1 / 1 / 1",
    },
    canvas: {
        border: "none !important",
        overflow: "hidden"
    },
    questionTemplate: {
        flexGrow: 1,
        flexShrink: 1,
        padding: "2.5mm 10mm",
        color: "black",
        transition: theme.transitions.create("width")
    },
}));
<div className={classes.drawableTextContainer}>
    <div
        ref={textRef}
        className={clsx(
            classes.questionTemplate,
            classes.drawableEditorTextElement
        )}>
        <RenderedText id={selectedText.id} />
    </div>
    <ReactSketchCanvas
        ref={canvasRef}
        className={clsx(
            classes.canvas
            classes.drawableEditorTextElement,
        )}
        canvasColor="transparent"
        strokeWidth={strokeWidth}
        eraserWidth={eraserWidth}
        strokeColor={pencilColor} />
</div>

Wie zu sehen ist, wird die Größe des Canvas anhand der Größe des Textes gesetzt und die Eigenschaften, wie die Schriftfarbe oder die Dicke des Stiftes werden durch State-Variablen festgelegt. Letztere kann der Benutzer über separate Eingabekomponenten konfigurieren.

Der Zugriff auf das Speichern und Laden von Zeichnungen ist über die canvasRef geregelt. Hierüber lässt sich die Änderung vom Zeichen- auf den Radier-Modus steuern. Ich stelle dem Benutzer einen Toggle-Button bereit, der ihm das schnelle Umschalten zwischen den Modi ermöglicht. Erfolgt eine Änderung, wird durch einen useEffect() die Einstellung über den Aufruf canvasRef.current?.eraseMode(pencilType === “eraser”) propagiert.

Erweiterung der Bibliothek

Unser Kunde bedient die Seite der Webanwendung verzugsweise mit einem Surface und dem Surface-Pen. Dieser Touch-Stift hat neben der vorderen Spitze zum Schreiben hinten einen Radierer, mit dem in den Office-Programmen die Anmerkungen wieder entfernt werden können. Das PointerEvent, auf das man in React über den EventListener vom Typ “pointerdown” zugreifen kann, unterscheidet bereits die PointerTypen “mouse”, “pen” und “touch”. Wenn bei einem “pen”-Type automatisch das fünfte Bit gesetzt ist, handelt es sich um den Radierer des Stiftes, was wiederum über den Bit-Compare (& 32) ausgelesen werden kann (vgl. hier).

Mein erster Ansatz war es, über einen EventListener in der Komponente zu überprüfen, ob es sich bei einer Interaktion um den Radierer oder die Spitze des Stiftes handelt und daraufhin den eraseMode() der canvasRef zu setzen. Allerdings musste ich dabei festellen, dass die Einstellung zu spät übernommen wurde und der Modus erst bei der zweiten Berührung vom Zeichnen zum Radieren bzw. andersherum wechselte. Gelöst werden konnte das Problem nur innerhalb der Bibliothek.

Also forkte ich das Repository, baute die Überprüfung ein und stellte einen Pull-Request.

Habt ihr eine ähnliche Aufgabenstellung bei euch im Projekt und konnte ich euch durch meinen Post weiterhelfen? Falls ihr Fragen oder Anmerkungen habt, könnt ihr euch gerne bei mir über LinkedIn oder per E-Mail melden.


Tags

#react#software-development#canvas#draw#sketch
Previous Article
Was ist ein Monorepo?
Andreas Riepl

Andreas Riepl

Fullstack Entwickler

Inhalte

1
Technische Möglichkeiten zur Umsetzung
2
Implementierung
3
Erweiterung der Bibliothek

Ähnliche Beiträge

i18n im Backend - Lösung für Internationalisierung auf mehreren Clients
August 16, 2023
3 min
© 2024, All Rights Reserved.

Links

StartseitePortfolioBPM TrainingÜber UnsKarriereBlog

Social Media