Radislav Gandapas tiene un excelente libro Complete J. Habla sobre cómo evaluar la dirección de su vida y cómo desarrollar un plan de desarrollo.
Quería crear una herramienta que esté en mi teléfono inteligente y que ayude a componer mi radar.

1. Preparación
El código fuente del tutorial y la demostración se puede ver aquí .
Este proyecto es pequeño, por lo que escribiremos de inmediato en REPL , el editor esbelto en línea. Si te gusta el desarrollo local, puedes usar plantillas webpack o roll up svelte.
Puedo recomendar la herramienta codesandbox en línea como alternativa al desarrollo local.
Si usa VScode, le recomiendo instalar el complemento svelte-vscode
Entonces, abrimos REPL y comenzamos
2. marco
Ahora tenemos el archivo App.svelte , este es el punto de entrada a la aplicación. Los componentes esbeltos tienen un estilo en la etiqueta de estilo , como en html normal. Al hacerlo, obtienes aislamiento de estilo a nivel de componente. Si necesita agregar estilos globales que estarán disponibles "fuera" del objeto, entonces debe usar la directiva : global () . Agregue estilos y cree un contenedor para nuestra aplicación.
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>
Cree el componente Radar.svelte . Este será el elemento SVG en el que dibujaremos nuestra rueda.
Radar.svelte <svg viewBox="-115 -110 230 220"> </svg>
El código Javascript en el componente Svelte se coloca en la etiqueta del script . Importamos nuestro Radar.svelte en App.svelte y lo dibujamos.
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>
El radar mismo consistirá en sectores correspondientes a aspectos de la vida. Cada sector tiene su propio índice.

Cada sector consta de una cuadrícula, que, a su vez, es un sector con un tamaño más pequeño.

Para dibujar un sector, necesitamos conocer las coordenadas de los tres vértices.

El vértice A siempre está con las coordenadas [0, 0], ya que el origen estará en el centro de nuestro radar. Para encontrar los vértices B y C, utilizamos una función de un excelente tutorial sobre cuadrículas hexagonales. En la entrada, la función recibe el tamaño y la dirección del sector, y devuelve una cadena con las coordenadas 'x, y'.
Cree un archivo getHexCorner.js , donde ponemos nuestra función getHexCorner (tamaño, dirección)
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)}`; }
Ahora cree el componente de sector Sector.svelte , que dibuja la cuadrícula. Necesitamos un ciclo de 10 pasos. En el cuerpo del componente, svelte no puede implementar el bucle for, por lo que acabo de hacer una matriz de cuadrícula, que repetiré en la directiva #each . Si tiene alguna idea de cómo hacerlo más elegante, escríbalo en los comentarios.
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}
Importe y dibuje un sector en el componente Radar.svelte .
Radar.svelte <script> import Sector from './Sector.svelte'; </script> <svg viewBox="-115 -110 230 220"> <Sector/> </svg>
Ahora nuestra aplicación muestra 1 sector.

3. almacenamiento de datos
Para dibujar todo el radar, debe conocer la lista de sectores. Por lo tanto, crearemos una tienda estatal. Utilizaremos una puerta personalizada en la que implementaremos la lógica de actualizar el estado. En general, este es el repositorio Svelte habitual, que está envuelto en una función. Esto protege el repositorio de los cambios al proporcionar un conjunto de acciones disponibles. Me gusta este enfoque porque la estructura de datos y la lógica de trabajar con ellos están en un solo lugar.
Crear un archivo store.js
Necesitaremos dos almacenamientos:
- radar para almacenar valores actuales
- activeSector para almacenar el sector activo si se producen eventos de movimiento táctil y movimiento del mouse.
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);
Ahora importamos el archivo creado en el componente Radar.svelte y agregamos la lógica para representar el radar completo.
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>
Algunas sutilezas de la directiva #each . Usamos el nombre de la variable $ radar . La directiva $ deja claro al compilador Svelte que nuestra expresión es un repositorio, y crea una suscripción para los cambios. La variable de dirección almacena el índice de la iteración actual, de acuerdo con ella estableceremos la dirección de nuestro sector. La expresión (sector.name) apunta esbelta a la identificación del objeto en la iteración . Clave analógica en React.
Ahora nuestra cuadrícula se ve así

Queda por preparar al sector para trabajar con eventos de hacer clic y arrastrar.
El evento touchmove, a diferencia del mousemove, se dispara solo en el elemento en el que comenzó. Por lo tanto, no podemos captar el momento en que el puntero se movió a otro sector. Para resolver este problema, en el marcado del elemento, almacenaremos el nombre actual del sector y su valor. En el momento del evento, determinaremos qué sector está debajo del cursor y cambiaremos su valor.
Tenga en cuenta que Svelte puede expandir la construcción {varName} en varName = {varName} . Esto simplifica enormemente las propiedades de prokidyvanie.
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}
Si agregamos un valor distinto de cero en nuestra tienda (store.js), esto debería resultar:

4. Eventos
Es hora de dar vida a nuestro radar, crear un controlador que tome un nodo en la entrada y procese los eventos táctiles y del mouse.
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); } }; }
Ahora solo agregue nuestro controlador al elemento de radar svg a través de la directiva de uso:
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 ahora responde a clics y arrastres.

6. Toques finales
Agregar subtítulos para sectores y descripción
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>
El radar debería verse así.

5. Bonificación
Expandí un poco la funcionalidad del radar, agregué almacenamiento de datos en localStorage y un plan de acción. Puede probar la aplicación de verificación de vida , el código fuente está disponible en gitlab .
