Radislav Gandapas hat ein ausgezeichnetes Buch Complete J. Es geht darum, wie Sie die Richtung Ihres Lebens einschätzen und einen Entwicklungsplan entwickeln können.
Ich wollte ein Tool erstellen, das sich in meinem Smartphone befindet und beim Zusammenstellen meines Radars hilft.

1. Vorbereitung
Der Quellcode des Tutorials und der Demo kann hier eingesehen werden .
Dieses Projekt ist klein, daher werden wir sofort in REPL , dem schlanken Online-Editor, schreiben. Wenn Sie die lokale Entwicklung mögen, können Sie Webpack- oder Rollup- Svelte-Vorlagen verwenden.
Ich kann das Codesandbox- Tool online als Alternative zur lokalen Entwicklung empfehlen.
Wenn Sie VScode verwenden, empfehle ich die Installation des Plugins svelte-vscode
Also öffnen wir REPL und beginnen
2. Rahmen
Jetzt haben wir die App.svelte- Datei, dies ist der Einstiegspunkt in die Anwendung. Svelte-Komponenten werden im Style- Tag wie in normalem HTML-Format gestaltet. Auf diese Weise erhalten Sie eine Stilisolation auf Komponentenebene. Wenn Sie globale Stile hinzufügen müssen, auf die "außerhalb" des Objekts zugegriffen werden kann, müssen Sie die Direktive verwenden : global () . Fügen Sie Stile hinzu und erstellen Sie einen Container für unsere Anwendung.
App.svelte<style> :global(body) { height: 100%; overscroll-behavior: none; user-select: none; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; background: rgb(35, 41, 37); } :global(html) { height: 100%; } .container { flex-grow: 1; display: flex; flex-direction: column; justify-content: center; height: 100%; } </style> <div class="container"> </div>
Erstellen Sie die Radar.svelte- Komponente. Dies wird das SVG-Element sein, in das wir unser Rad zeichnen werden.
Radar.svelte <svg viewBox="-115 -110 230 220"> </svg>
Javascript-Code in der Svelte-Komponente wird im Skript- Tag platziert. Wir importieren unsere Radar.svelte in App.svelte und zeichnen sie.
App.svelte <script> import Radar from './Radar.svelte' </script> <style> :global(body) { height: 100%; overscroll-behavior: none; user-select: none; margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; background: rgb(35, 41, 37); } :global(html) { height: 100%; } .container { flex-grow: 1; display: flex; flex-direction: column; justify-content: center; height: 100%; } </style> <div class="container"> <Radar/> </div>
Das Radar selbst wird aus Sektoren bestehen, die Lebensaspekten entsprechen. Jeder Sektor hat seinen eigenen Index.

Jeder Sektor besteht aus einem Raster, das wiederum ein kleinerer Sektor ist.

Um einen Sektor zu zeichnen, müssen wir die Koordinaten der drei Eckpunkte kennen.

Scheitelpunkt A liegt immer bei den Koordinaten [0, 0], da der Ursprung in der Mitte unseres Radars liegt. Um die Eckpunkte B und C zu finden, verwenden wir eine Funktion aus einem hervorragenden Tutorial zu hexagonalen Gittern. Bei der Eingabe empfängt die Funktion die Sektorgröße und -richtung und gibt eine Zeichenfolge mit den Koordinaten 'x, y' zurück.
Erstellen Sie eine Datei getHexCorner.js , in der wir unsere Funktion getHexCorner (Größe, Richtung) platzieren.
getHexCorner.js export default function getHexCorner(size, direction) { const angleDeg = 60 * direction - 30; const angleRad = (Math.PI / 180) * angleDeg; return `${size * Math.cos(angleRad)},${size * Math.sin(angleRad)}`; }
Erstellen Sie nun die Sektorkomponente Sector.svelte , die das Raster zeichnet. Wir brauchen einen 10-Stufen-Zyklus. Im Hauptteil der Komponente kann svelte die for-Schleife nicht implementieren, daher habe ich nur ein Grid-Array erstellt, über das ich in der Direktive #each iterieren werde . Wenn Sie Ideen haben, wie Sie es eleganter gestalten können, schreiben Sie in den Kommentaren darüber.
Sector.svelte <script> import getHexCorner from "./getHexCorner.js"; export let direction = 0; const grid = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; </script> <style> polygon { fill: #293038; stroke: #424a54; } </style> {#each grid as gridValue, i} <polygon points={`${getHexCorner(gridValue * 10, direction)}, ${getHexCorner(gridValue * 10, direction + 1)}, 0, 0`} strokeLinejoin="miter-clip" stroke-dasharray="4" stroke-width="0.5" /> {/each}
Importieren und zeichnen Sie einen Sektor in der Radar.svelte- Komponente.
Radar.svelte <script> import Sector from './Sector.svelte'; </script> <svg viewBox="-115 -110 230 220"> <Sector/> </svg>
Jetzt zeigt unsere Anwendung 1 Sektor an.

3. Datenspeicherung
Um das gesamte Radar zu zeichnen, müssen Sie die Liste der Sektoren kennen. Daher werden wir einen State Store erstellen. Wir werden ein benutzerdefiniertes Gate verwenden, in dem wir die Logik der Aktualisierung des Status implementieren. Im Allgemeinen ist dies das übliche Svelte-Repository , das in eine Funktion eingeschlossen ist. Dies schützt das Repository vor Änderungen, indem eine Reihe verfügbarer Aktionen bereitgestellt wird. Ich mag diesen Ansatz dahingehend, dass sich die Datenstruktur und die Logik der Arbeit mit ihnen an einem Ort befinden.
Erstellen Sie eine store.js- Datei
Wir brauchen zwei Lager:
- Radar zum Speichern aktueller Werte
- activeSector zum Speichern des aktiven Sektors, wenn Touchmove- und Mousemove-Ereignisse auftreten.
store.js import { writable } from "svelte/store"; const defaultStore = ["hobby", "friendship", "health", "job", "love", "rich"]; function Radar() { /* */ const { subscribe, update } = writable(defaultStore.map(item=>({name:item, value:0}))); /* */ return { subscribe, set: (id, value) => update(store => store.map(item => (item.name === id ? { ...item, value } : item)) ) }; } export const radar = Radar(); export const activeSector = writable(null);
Jetzt importieren wir den erstellten Speicher in die Komponente Radar.svelte und fügen die Logik zum Rendern des vollständigen Radars hinzu.
Radar.svelte <script> import { radar } from "./store.js"; import Sector from "./Sector.svelte"; </script> <svg viewBox="-115 -110 230 220"> {#each $radar as sector, direction (sector.name)} <Sector {...sector} {direction} /> {/each} </svg>
Einige Feinheiten der Direktive #each . Wir verwenden den Variablennamen $ radar . Die $ -Direktive macht dem Svelte-Compiler klar, dass unser Ausdruck ein Repository ist, und erstellt ein Abonnement für Änderungen. Die Richtungsvariable speichert den Index der aktuellen Iteration, entsprechend legen wir die Richtung unseres Sektors fest. Der Ausdruck (branch.name) zeigt svelte auf die ID des Objekts in der Iteration . Analogschlüssel in Reagieren.
Jetzt sieht unser Gitter so aus

Es bleibt, den Sektor auf die Arbeit mit Ereignissen des Klickens und Ziehens vorzubereiten.
Das Touchmove-Ereignis wird im Gegensatz zu Mousemove nur auf das Element ausgelöst, auf dem es gestartet wurde. Daher können wir den Moment nicht erfassen, in dem sich der Zeiger auf einen anderen Sektor bewegt hat. Um dieses Problem zu lösen, speichern wir im Markup des Elements den aktuellen Namen des Sektors und seinen Wert. Zum Zeitpunkt des Ereignisses bestimmen wir, welcher Sektor sich unter dem Cursor befindet, und ändern seinen Wert.
Beachten Sie, dass Svelte das Konstrukt {varName} in varName = {varName} erweitern kann . Dies macht es sehr einfach, Eigenschaften zu rollen.
Sector.svelte <script> import getHexCorner from "./getHexCorner.js"; export let direction = 0; export let name; export let value; const grid = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; </script> <style> polygon { fill: #293038; stroke: #424a54; } .rich { fill: #469573; } .hobby { fill: #7c3f7a; } .friendship { fill: #5c6bc0; } .health { fill: #e5b744; } .job { fill: #e16838; } .love { fill: #e23f45; } </style> {#each grid as gridValue, i} <polygon points={`${getHexCorner(gridValue * 10, direction)}, ${getHexCorner(gridValue * 10, direction + 1)}, 0, 0`} strokeLinejoin="miter-clip" stroke-dasharray="4" stroke-width="0.5" class={value >= gridValue ? name : ''} {name} value={gridValue} /> /> {/each}
Wenn wir in unserem Geschäft (store.js) einen anderen Wert als Null hinzufügen, sollte dies Folgendes ergeben:

4. Ereignisse
Es ist Zeit, unserem Radar Leben einzuhauchen, einen Handler zu erstellen, der einen Knoten an der Eingabe nimmt und darin Berührungs- und Mausereignisse verarbeitet.
handleRadar.js import { radar, activeSector } from "./store.js"; import { get } from "svelte/store"; export default function handleRadar(node) { const getRadarElementAtPoint = e => { const event = e.touches ? e.touches[0] : e; const element = document.elementFromPoint(event.pageX, event.pageY); const score = element.getAttribute("value"); const id = element.getAttribute("name"); return { id, score, type: event.type }; }; const start = e => { const { id } = getRadarElementAtPoint(e); activeSector.set(id); }; const end = () => { activeSector.set(null); }; const move = e => { window.requestAnimationFrame(() => { const { id, score, type } = getRadarElementAtPoint(e); if (!id || (id !== get(activeSector) && type !== "click") || !score) return; radar.set(id, score); }); }; node.addEventListener("mousedown", start); node.addEventListener("touchstart", start); node.addEventListener("mouseup", end); node.addEventListener("touchend", end); node.addEventListener("mousemove", move); node.addEventListener("touchmove", move); node.addEventListener("touch", move); node.addEventListener("click", move); return { destroy() { node.removeEventListener("mousedown", start); node.removeEventListener("touchstart", start); node.removeEventListener("mouseup", end); node.removeEventListener("touchend", end); node.removeEventListener("mousemove", move); node.removeEventListener("touchmove", move); node.removeEventListener("touch", move); node.removeEventListener("click", move); } }; }
Fügen Sie jetzt einfach unseren Handler über die Use- Direktive zum SVG-Radarelement hinzu :
Radar.svelte <script> import { radar } from "./store.js"; import Sector from "./Sector.svelte"; import handleRadar from "./handleRadar.js"; </script> <svg viewBox="-115 -110 230 220" use:handleRadar> {#each $radar as sector, direction (sector.name)} <Sector {...sector} {direction} /> {/each} </svg>
Radar reagiert jetzt auf Klicks und Drags.

6. Letzte Berührungen
Fügen Sie Beschriftungen für Sektoren und Beschreibungen hinzu
Sector.svelte <script> import getHexCorner from "./getHexCorner.js"; export let name; export let value; export let direction; const grid = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; const flip = direction === 2 || direction === 1; const radarTranslation = { hobby: "", friendship: "", health: "", job: "", love: "", rich: "" }; </script> <style> polygon { fill: #293038; stroke: #424a54; } text { font-size: 8px; fill: white; } .value { font-weight: bold; font-size: 12px; } .rich { fill: #469573; } .hobby { fill: #7c3f7a; } .friendship { fill: #5c6bc0; } .health { fill: #e5b744; } .job { fill: #e16838; } .love { fill: #e23f45; } </style> {#each grid as gridValue, i} <polygon points={`${getHexCorner(gridValue * 10, direction)}, ${getHexCorner(gridValue * 10, direction + 1)}, 0, 0`} strokeLinejoin="miter-clip" stroke-dasharray="4" stroke-width="0.5" class={value >= gridValue ? name : ''} {name} value={gridValue} /> {/each} <g transform={`translate(${getHexCorner(105, flip ? direction + 1 : direction)}) rotate(${direction * 60 + (flip ? -90 : 90)})`}> <text x="50" y={flip ? 5 : 0} text-anchor="middle"> {radarTranslation[name]} </text> <text x="50" y={flip ? 18 : -10} text-anchor="middle" class="value"> {value} </text> </g>
Das Radar sollte so aussehen.

5. Bonus
Ich habe die Radarfunktionalität ein wenig erweitert, Datenspeicherung in localStorage und einen Aktionsplan hinzugefügt. Sie können die Life-Checkup-Anwendung ausprobieren . Der Quellcode ist in gitlab verfügbar.
