斯维尔特(Svelte)一生

拉迪斯拉夫·甘达帕斯(Radislav Gandapas)拥有一本出色的著作《 完全J 它讨论了如何评估您的生活方向,以及如何制定发展计划。


我想创建一个可以在我的智能手机中使用的工具,并帮助组成雷达。


图片


1.准备


教程和演示的源代码可以在这里查看。


这个项目很小,因此我们将立即使用在线Svelte编辑器REPL进行编写。 如果您喜欢本地开发,则可以使用webpack汇总 Svelte模板。


我可以在线推荐codesandbox工具,以代替本地开发。


如果您使用VScode,则建议安装svelte-vscode插件


因此,我们打开REPL ,我们开始


2.框架


现在我们有了App.svelte文件,这是应用程序的入口点。 Svelte组件在样式标签中进行样式设置 ,就像在常规html中一样。 这样,您就可以在组件级别实现样式隔离。 如果需要添加可在对象“外部”访问的全局样式,则需要使用指令:global() 。 添加样式并为我们的应用程序创建一个容器。


瘦身
<style> :global(body) { height: 100%; overscroll-behavior: none; /*  pull to refresh*/ 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> 

创建Radar.svelte组件。 这将是我们将在其中绘制轮子的SVG元素。


雷达
 <svg viewBox="-115 -110 230 220"> </svg> 

Svelte组件中的Javascript代码位于script标记中。 我们将Radar.svelte导入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/> <!--   Radar --> </div> 

雷达本身将包括与生活方面相对应的扇区。 每个部门都有自己的索引。


图片


每个扇区由一个网格组成,而网格又是一个较小的扇区。


图片


要绘制一个扇形,我们需要知道三个顶点的坐标。


图片


顶点A始终具有坐标[0,0],因为原点位于雷达的中心。 为了找到顶点B和C,我们使用了关于六角形网格的优秀教程中的函数。 在输入处,该函数接收扇区大小和方向,并返回坐标为'x,y'的字符串。
创建一个文件getHexCorner.js ,在其中放置我们的函数getHexCorner(大小,方向)


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)}`; } 

现在创建扇区组件Sector.svelte ,以绘制网格。 我们需要10个步骤的周期。 在组件的主体中,svelte无法实现for循环,因此我只是制作了一个网格数组,将在#each指令中进行迭代 。 如果您有任何想法使它变得更优雅,请在评论中写下。


扇形
 <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} 

Radar.svelte组件中导入并绘制一个扇区。


雷达
 <script> import Sector from './Sector.svelte'; </script> <svg viewBox="-115 -110 230 220"> <Sector/> </svg> 

现在,我们的应用程序显示1个扇区。


图片


3.数据存储


要绘制整个雷达,您需要知道扇区列表。 因此,我们将创建一个状态存储。 我们将使用自定义门,在其中实现更新状态的逻辑。 通常,这是通常的Svelte存储库 ,包装在一个函数中。 通过提供一组可用的操作,可以保护存储库免于更改。 我喜欢这种方法,因为数据结构和使用它们的逻辑都在一个地方。


创建一个store.js文件


我们将需要两个存储:


  • 雷达存储当前值
  • activeSector,用于在发生touchmove和mousemove事件时存储活动扇区。

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); 

现在,我们将创建的stor导入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> 

#each指令的一些细节。 我们使用变量名称$ radar$指令使Svelte编译器清楚我们的表达式是一个存储库,并且它创建更改的预订。 方向变量存储当前迭代的索引,根据它我们将设置扇区的方向。 表达式(sector.name)指向svelte指向迭代中对象ID 。 React中的模拟键。


现在我们的网格看起来像这样


图片


仍需要为该部门做好准备以处理单击和拖动事件。
与mousemove不同,touchmove事件仅在其启动的元素上触发。 因此,我们无法捕捉到指针移到另一个扇区的那一刻。 为了解决这个问题,在元素的标记中,我们将存储扇区的当前名称及其值。 在事件发生时,我们将确定光标所在的扇区并更改其值。

请注意,Svelte可以将{varName}构造扩展为varName = {varName} 。 这使得滚动特性非常容易。


扇形
 <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} 

如果我们在商店(store.js)中添加非零值,则结果应为:


4.活动


现在是时候充实我们的雷达,创建一个在输入端带有节点的处理程序,并在其内部处理触摸和鼠标事件的时候了。


handleRadar.js
 import { radar, activeSector } from "./store.js"; /*  get            */ 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); /*       html  */ 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 => { /*   requestAnimationFrame       */ 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); /*     destroy,          DOM */ 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); } }; } 

现在,只需通过use指令将处理程序添加到svg雷达元素中


雷达
 <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> 

雷达现在响应单击和拖动。


6.最后的润色


添加扇区标题和描述


扇形
 <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> 

雷达应该看起来像这样。


图片


5.奖金


我稍微扩展了雷达功能,在localStorage中添加了数据存储并制定了行动计划。 您可以尝试寿命检查应用程序 ,源代码可在gitlab获得


Source: https://habr.com/ru/post/zh-CN458974/


All Articles