حياة كاملة على Svelte

Radislav Gandapas لديه كتاب ممتاز Complete J. يتحدث عن كيفية تقييم اتجاه حياتك ، وكيفية تطوير خطة التنمية.


كنت أرغب في إنشاء أداة ستكون في هاتفي الذكي وتساعد في تكوين راداري.


صورة


1. التحضير


يمكن الاطلاع على الكود المصدري للبرنامج التعليمي والعرض التوضيحي هنا .


هذا المشروع صغير ، لذا سنكتب على الفور في REPL ، محرر svelte عبر الإنترنت. إذا كنت ترغب في التطوير المحلي ، يمكنك استخدام قوالب webpack أو قوالب مجموعة التحديثات .


يمكنني أن أوصي بأداة codeandbox عبر الإنترنت كبديل للتنمية المحلية.


إذا كنت تستخدم VScode ، فنوصيك بتثبيت المكون الإضافي svelte-vscode


لذلك ، نحن نفتح REPL ونبدأ


2. الإطار


الآن لدينا ملف App.svelte ، هذه هي نقطة الدخول إلى التطبيق. يتم تصميم مكونات Svelte في علامة النمط ، كما هو الحال في HTML العادي. في القيام بذلك ، يمكنك الحصول على عزل النمط على مستوى المكون. إذا كنت بحاجة إلى إضافة أنماط عمومية يمكن الوصول إليها "خارج" الكائن ، فأنت بحاجة إلى استخدام التوجيه : global () . إضافة أنماط وإنشاء حاوية لتطبيقنا.


App.svelte
<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 الذي سنرسم به عجلة القيادة.


Radar.svelte
 <svg viewBox="-115 -110 230 220"> </svg> 

يتم وضع رمز Javascript في مكون Svelte في علامة البرنامج النصي . نحن استيراد Radar.svelte لدينا في App.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> 

سوف يتكون الرادار نفسه من القطاعات المقابلة لجوانب الحياة. كل قطاع له مؤشره الخاص.


صورة


يتكون كل قطاع من شبكة ، والتي بدورها قطاع بحجم أصغر.


صورة


لرسم القطاع ، نحن بحاجة إلى معرفة إحداثيات القمم الثلاثة.


صورة


يكون Vertex 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 . إذا كان لديك أي أفكار حول كيفية جعلها أكثر أناقة ، فاكتب عنها في التعليقات.


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} 

استيراد وارسم قطاع في مكون Radar.svelte .


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

الآن يعرض تطبيقنا قطاع واحد.


صورة


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

الآن نحن استيراد مخزن إنشاؤها في مكون Radar.svelte وإضافة المنطق لتقديم الرادار الكامل.


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 . نستخدم اسم المتغير $ الرادار . يوضح التوجيه $ لمترجم Svelte أن تعبيرنا هو مستودع ، وأنه ينشئ اشتراكًا للتغييرات. يقوم متغير الاتجاه بتخزين مؤشر التكرار الحالي ، وفقًا لذلك سنقوم بتعيين اتجاه قطاعنا. يشير التعبير (sector.name) إلى svelte بمعرف الكائن في التكرار . مفتاح التناظرية في React.


الآن شبكتنا تبدو هكذا


صورة


يبقى إعداد القطاع للعمل مع أحداث النقر والسحب.
الحدث touchmove ، على عكس mousemove ، يطلق فقط على العنصر الذي بدأ عليه. لذلك ، لا يمكننا اللحاق بالوقت الذي ينتقل فيه المؤشر إلى قطاع آخر. لحل هذه المشكلة ، في ترميز العنصر ، سنقوم بتخزين الاسم الحالي للقطاع وقيمته. في وقت الحدث ، سنحدد القطاع الذي يقع تحت المؤشر ونغير قيمته.

لاحظ أن Svelte يمكنه توسيع بنية {varName} إلى varName = {varName} . هذا يجعل من السهل جدا لخصائص لفة.


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} 

إذا أضفنا قيمة بخلاف الصفر في متجرنا (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); } }; } 

الآن فقط أضف معالجنا إلى عنصر الرادار svg من خلال توجيه الاستخدام:


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> 

يستجيب الرادار الآن للنقرات والسحب.


6. اللمسات النهائية


إضافة تعليق على القطاعات والوصف


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> 

يجب أن يبدو الرادار هكذا.


صورة


5. مكافأة


قمت بتوسيع وظيفة الرادار قليلاً ، وأضاف تخزين البيانات في localStorage وخطة عمل. يمكنك تجربة تطبيق فحص الحياة ، الكود المصدري متاح في gitlab .


Source: https://habr.com/ru/post/ar458974/


All Articles