إعادة إنشاء زر ضغط Arduino باستخدام SVG و <lit-element>

نشرت: 2022-03-10
ملخص سريع ↬ يأتي HTML مع مجموعة من عناصر التحكم في الإدخال ، وهناك الكثير من مكتبات المكونات التي تتضمن العديد من عناصر التحكم القياسية مثل مربعات الاختيار وأزرار الاختيار. لكن ماذا يحدث عندما تحتاج إلى شيء غير عادي؟

في هذه المقالة ، ستتعلم كيفية إنشاء مكونات HTML مخصصة تحاكي الكائنات المادية ، مثل Arduino Pushbutton. سنقوم برسم المكون في Inkscape من البداية ، وتحسين كود SVG الذي تم إنشاؤه للويب ، ولفه كمكون ويب مستقل باستخدام مكتبة lit-element خفيفة الوزن ، مع إيلاء اهتمام إضافي لاعتبارات إمكانية الوصول وقابلية الاستخدام على الأجهزة المحمولة.

اليوم ، سوف آخذك خلال رحلة إنشاء مكون HTML يحاكي مكون زر الضغط اللحظي الذي يستخدم بشكل شائع مع Arduino وفي المشاريع الإلكترونية. سنستخدم تقنيات مثل SVG ومكونات الويب lit-element ، ونتعلم كيفية إتاحة الوصول إلى الزر من خلال بعض خدع JavaScript-CSS.

لنبدأ!

من Arduino إلى HTML: الحاجة إلى مكون زر ضغط

قبل الشروع في الرحلة ، دعنا نستكشف ما سننشئه ، والأهم من ذلك ، لماذا. أقوم بإنشاء محاكي Arduino مفتوح المصدر في JavaScript يسمى avr8js. هذا المحاكي قادر على تنفيذ كود Arduino وسأستخدمه في سلسلة من البرامج التعليمية والدورات التدريبية التي تعلم صانعي البرامج كيفية البرمجة لـ Arduino.

لا يهتم المحاكي نفسه إلا بتنفيذ البرنامج - فهو يدير تعليمات الكود عن طريق التعليمات ويحدّث حالته الداخلية ومخزن الذاكرة المؤقت وفقًا لمنطق البرنامج. للتفاعل مع برنامج Arduino ، تحتاج إلى إنشاء بعض المكونات الإلكترونية الافتراضية التي يمكنها إرسال مدخلات إلى جهاز المحاكاة أو الاستجابة لمخرجاته.

تشغيل المحاكي بمفرده يشبه إلى حد كبير تشغيل JavaScript بمعزل عن غيرها. لا يمكنك التفاعل مع المستخدم إلا إذا قمت أيضًا بإنشاء بعض عناصر HTML وربطها بشفرة JavaScript عبر DOM.

وبالتالي ، بالإضافة إلى محاكي المعالج ، فأنا أعمل أيضًا على مكتبة من مكونات HTML التي تحاكي الأجهزة المادية ، بدءًا من المكونين الأولين اللذين ستجدهما في أي مشروع إلكترونيات تقريبًا: مصباح LED وزر ضغط.

عناصر LED وزر الضغط قيد التشغيل
عناصر LED وزر الضغط قيد التشغيل

LED بسيط نسبيًا ، حيث أنه يحتوي على حالتين فقط من حالات الإخراج: تشغيل وإيقاف. وراء الكواليس ، يستخدم مرشح SVG لإنشاء تأثير الإضاءة.

زر الضغط أكثر إثارة للاهتمام. يحتوي أيضًا على حالتين ، لكن يجب أن يتفاعل مع مدخلات المستخدم وتحديث حالته وفقًا لذلك ، ومن هنا يأتي التحدي ، كما سنرى قريبًا. لكن أولاً ، دعنا نحدد المتطلبات من المكون الذي سننشئه.

المزيد بعد القفز! أكمل القراءة أدناه ↓

تحديد متطلبات الزر الانضغاطي

سيبدو المكون الخاص بنا على شكل زر ضغط 12 مم. هذه الأزرار شائعة جدًا في مجموعات بدء تشغيل الإلكترونيات ، وتأتي بأغطية بألوان متعددة ، كما ترون في الصورة أدناه:

لعبة سيمون مع أزرار الضغط الأصفر والأحمر والأزرق والأخضر
لعبة Simon بأزرار ضغط باللونين الأصفر والأحمر والأزرق والأخضر (معاينة كبيرة)

من حيث السلوك ، يجب أن يكون للزر الضغط حالتين: الضغط والإفراج. تتشابه هذه الأحداث مع أحداث HTML / mouseup ، ولكن يجب أن نتأكد من إمكانية استخدام الأزرار الانضغاطية أيضًا من الأجهزة المحمولة ، ويمكن الوصول إليها للمستخدمين بدون ماوس.

نظرًا لأننا سنستخدم حالة الزر الانضغاطي كمدخل لـ Arduino ، فلا داعي لدعم أحداث "click" أو "النقر المزدوج". الأمر متروك لبرنامج Arduino الذي يعمل في المحاكاة ليقرر كيفية التصرف بناءً على حالة الزر ، والأزرار المادية لا تنشئ أحداث نقر.

إذا كنت ترغب في معرفة المزيد ، تحقق من حديث أجريته مع بنيامين جرونباوم في SmashingConf Freiburg في عام 2019: "Anatomy of a Click".

لتلخيص متطلباتنا ، يحتاج زر الضغط الخاص بنا إلى:

  1. تبدو مشابهة لزر الضغط المادي 12 مم ؛
  2. لها حالتان مميزتان: مضغوط ، وإفراج ، ويجب أن تكون مرئية بصريًا ؛
  3. دعم تفاعل الماوس والأجهزة المحمولة ويمكن الوصول إليها من قبل مستخدمي لوحة المفاتيح ؛
  4. دعم ألوان غطاء مختلفة (على الأقل الأحمر والأخضر والأزرق والأصفر والأبيض والأسود).

الآن بعد أن حددنا المتطلبات ، يمكننا البدء في العمل على التنفيذ.

SVG للفوز

يتم تنفيذ معظم مكونات الويب باستخدام مزيج من CSS و HTML. عندما نحتاج إلى رسومات أكثر تعقيدًا ، فإننا عادةً ما نستخدم الصور النقطية ، إما بتنسيق JPG أو PNG (أو GIF إذا كنت تشعر بالحنين إلى الماضي).

ومع ذلك ، في حالتنا ، سوف نستخدم طريقة أخرى: رسومات SVG. يفسح SVG نفسه للرسومات المعقدة بسهولة أكبر بكثير من CSS (نعم ، أعرف ، يمكنك إنشاء أشياء رائعة باستخدام CSS ، لكن هذا لا يعني أنه ينبغي ذلك). لكن لا تقلق ، فنحن لا نتخلى عن CSS تمامًا. سيساعدنا ذلك في تصميم الأزرار الانضغاطية ، وفي النهاية حتى في جعلها سهلة الوصول.

تتمتع SVG بميزة كبيرة أخرى ، مقارنةً بصور الرسومات النقطية: من السهل جدًا معالجتها من JavaScript ويمكن تصميمها من خلال CSS. هذا يعني أنه يمكننا توفير صورة واحدة للزر واستخدام JavaScript لتخصيص غطاء اللون وأنماط CSS للإشارة إلى حالة الزر. أنيق ، أليس كذلك؟

أخيرًا ، يعد SVG مجرد مستند XML ، يمكن تحريره باستخدام برامج تحرير النصوص ، ودمجها مباشرةً في HTML ، مما يجعلها حلاً مثاليًا لإنشاء مكونات HTML قابلة لإعادة الاستخدام. هل أنت مستعد لرسم زر الضغط الخاص بنا؟

رسم زر الضغط باستخدام إنكسكيب

إنكسكيب هي أداتي المفضلة لإنشاء رسومات متجهة SVG. إنه مجاني ومليء بالميزات القوية ، مثل مجموعة كبيرة من إعدادات المرشحات المضمنة مسبقًا ، وتتبع الصور النقطية ، والعمليات الثنائية للمسار. لقد بدأت في استخدام Inkscape لإنشاء فن PCB ، لكن في العامين الماضيين ، بدأت في استخدامه لمعظم مهام تحرير الرسومات الخاصة بي.

يعد رسم زر الضغط في Inkscape أمرًا بسيطًا جدًا. سنقوم برسم رسم توضيحي للرؤية العلوية للزر وأسلاكه المعدنية الأربعة التي تربطه بأجزاء أخرى ، على النحو التالي:

  1. مستطيل رمادي غامق مقاس 12 × 12 مم للعلبة البلاستيكية ، مع زوايا مستديرة قليلاً لجعلها أكثر نعومة.
  2. أصغر ، 10.5 × 10.5 مستطيل رمادي فاتح للغطاء المعدني.
  3. أربع دوائر داكنة ، واحدة في كل زاوية للدبابيس التي تثبت الزر معًا.
  4. دائرة كبيرة في المنتصف ، هذا هو محيط غطاء الزر.
  5. دائرة أصغر في المنتصف أعلى غطاء الزر.
  6. أربعة مستطيلات رمادية فاتحة على شكل "T" للأسلاك المعدنية للزر.

والنتيجة مكبرة قليلاً:

رسم زر الضغط المرسوم باليد
رسم زر الضغط المرسوم يدويًا (معاينة كبيرة)

كلمسة أخيرة ، سنضيف سحر تدرج لوني SVG إلى محيط الزر ، لمنحه إحساسًا ثلاثي الأبعاد:

إضافة تعبئة متدرجة لإنشاء إحساس ثلاثي الأبعاد
إضافة تعبئة متدرجة لإنشاء إحساس ثلاثي الأبعاد (معاينة كبيرة)

هناك نذهب! لدينا العناصر المرئية ، والآن نحتاج إلى نقلها إلى الويب.

من إنكسكيب إلى ويب SVG

كما ذكرت أعلاه ، من السهل جدًا تضمين SVGs في HTML - يمكنك فقط لصق محتوى ملف SVG في مستند HTML الخاص بك ، وفتحه في متصفح ، وسيتم عرضه على شاشتك. يمكنك رؤيته أثناء العمل في مثال CodePen التالي:

شاهد زر Pen SVG Pushbutton بتنسيق HTML بواسطةUri Shaked

شاهد زر Pen SVG Pushbutton بتنسيق HTML بواسطةUri Shaked

ومع ذلك ، تحتوي ملفات SVG المحفوظة من Inkscape على الكثير من الأمتعة غير الضرورية مثل إصدار Inkscape وموضع النافذة عندما حفظت الملف آخر مرة. في كثير من الحالات ، هناك أيضًا عناصر فارغة ، وتدرجات لونية وفلاتر غير مستخدمة ، وكلها تضخم حجم الملف ، وتجعل من الصعب التعامل معها داخل HTML.

لحسن الحظ ، يمكن لـ Inkscape تنظيف معظم الفوضى بالنسبة لنا. هنا كيف تفعلها:

  1. اذهب إلى قائمة File وانقر على Clean up document . (سيؤدي هذا إلى إزالة التعريفات غير المستخدمة من المستند الخاص بك.)
  2. اذهب مرة أخرى إلى ملف وانقر على حفظ باسم… . عند الحفظ ، حدد Optimized SVG ( * .svg ) في القائمة المنسدلة حفظ كنوع .
  3. سترى مربع حوار "Optimized SVG Output" بثلاث علامات تبويب. حدد جميع الخيارات ، باستثناء "الاحتفاظ ببيانات المحرر" و "الاحتفاظ بالتعريفات غير المشار إليها" و "الاحتفاظ بالمعرفات التي تم إنشاؤها يدويًا ...".
(معاينة كبيرة)

ستؤدي إزالة كل هذه الأشياء إلى إنشاء ملف SVG أصغر يسهل التعامل معه. في حالتي ، انتقل الملف من 4593 بايت إلى 2080 بايت فقط ، أي أقل من نصف الحجم. بالنسبة إلى ملفات SVG الأكثر تعقيدًا ، يمكن أن يكون هذا توفيرًا كبيرًا للنطاق الترددي ويمكن أن يحدث فرقًا ملحوظًا في وقت تحميل صفحة الويب الخاصة بك.

يعد SVG المحسن أسهل في القراءة والفهم. في المقتطف التالي ، يجب أن تكون قادرًا على تحديد المستطيلين اللذين يصنعان جسم الزر الانضغاطي بسهولة:

 <rect width="12" height="12" rx=".44" ry=".44" fill="#464646" stroke-width="1.0003"/> <rect x=".75" y=".75" width="10.5" height="10.5" rx=".211" ry=".211" fill="#eaeaea"/> <g fill="#1b1b1b"> <circle cx="1.767" cy="1.7916" r=".37"/> <circle cx="10.161" cy="1.7916" r=".37"/> <circle cx="10.161" cy="10.197" r=".37"/> <circle cx="1.767" cy="10.197" r=".37"/> </g> <circle cx="6" cy="6" r="3.822" fill="url(#a)"/> <circle cx="6" cy="6" r="2.9" fill="#ff2a2a" stroke="#2f2f2f" stroke-opacity=".47" stroke-width=".08"/>

يمكنك أيضًا تقصير الرمز ، على سبيل المثال ، عن طريق تغيير عرض حد المستطيل الأول من 1.0003 إلى 1 فقط. لا يحدث فرقًا كبيرًا في حجم الملف ، لكنه يجعل الكود أسهل في القراءة.

بشكل عام ، يكون التمرير اليدوي لملف SVG الذي تم إنشاؤه مفيدًا دائمًا. في كثير من الحالات ، يمكنك إزالة المجموعات الفارغة أو تطبيق تحويلات المصفوفة ، وكذلك تبسيط إحداثيات التدرج من خلال تعيينها من "مساحة المستخدم عند الاستخدام" (الإحداثيات العامة) إلى "مربع إحاطة الكائن" (بالنسبة للكائن). هذه التحسينات اختيارية ، لكنك تحصل على رمز يسهل فهمه وصيانته.

من هذه النقطة فصاعدًا ، سنضع Inkscape بعيدًا ونعمل مع التمثيل النصي لصورة SVG.

إنشاء مكون ويب قابل لإعادة الاستخدام

حتى الآن ، حصلنا على رسومات زر الضغط الخاص بنا ، وهي جاهزة لإدراجها في جهاز المحاكاة الخاص بنا. يمكننا بسهولة تخصيص لون الزر عن طريق تغيير fill للدائرة الأصغر ولون البداية للتدرج اللوني للدائرة الأكبر.

هدفنا التالي هو تحويل زر الضغط الخاص بنا إلى مكون ويب قابل لإعادة الاستخدام يمكن تخصيصه عن طريق تمرير سمة color ويتفاعل مع تفاعل المستخدم (أحداث الصحافة / الإصدار). سنستخدم lit-element ، وهو مكتبة صغيرة تبسط عملية إنشاء مكونات الويب.

يتفوق lit-element Lit في إنشاء مكتبات مكونات صغيرة قائمة بذاتها. إنه مبني على أعلى معيار Web Components ، والذي يسمح باستهلاك هذه المكونات بواسطة أي تطبيق ويب ، بغض النظر عن الإطار المستخدم: Angular أو React أو Vue أو Vanilla JS سيكون بمقدورهم جميعًا استخدام المكون الخاص بنا.

يتم إنشاء مكونات في العنصر lit-element باستخدام بناء جملة قائم على الفئة ، مع طريقة render() تُرجع كود HTML للعنصر. تشبه إلى حد ما React ، إذا كنت معتادًا عليها. ومع ذلك ، على عكس رد الفعل ، lit-element يستخدم قوالب جافا سكريبت ذات العلامات الحرفية القياسية لتحديد محتوى المكون.

إليك كيفية إنشاء مكون hello-world بسيط:

 import { customElement, html, LitElement } from 'lit-element'; @customElement('hello-world') export class HelloWorldElement extends LitElement { render() { return html` <h1> Hello, World! </h1> `; } }

يمكن بعد ذلك استخدام هذا المكون في أي مكان في كود HTML الخاص بك ببساطة عن طريق كتابة <hello-world></hello-world> .

ملاحظة : في الواقع ، يتطلب زر الضغط لدينا رمزًا أكثر قليلاً: نحتاج إلى الإعلان عن خاصية إدخال للون ، باستخدام @property() decoractor (وبقيمة افتراضية حمراء) ، ولصق كود SVG في العرض الخاص بنا render() طريقة استبدال لون غطاء الزر بقيمة خاصية اللون (انظر المثال). البتات المهمة موجودة في السطر 5 ، حيث نحدد خاصية اللون: @property() color = 'red'; أيضًا ، في السطر 35 (حيث نستخدم هذه الخاصية لتحديد لون التعبئة للدائرة التي تصنع غطاء الزر) ، باستخدام الصيغة الحرفية لقالب جافا سكريبت ، المكتوبة كـ ${color} :

 <circle cx="6" cy="6" r="2.9" fill="${color}" stroke="#2f2f2f" stroke-opacity=".47" stroke-width=".08" />

جعلها تفاعلية

ستكون آخر قطعة من اللغز هي جعل الزر تفاعليًا. هناك جانبان نحتاج إلى مراعاتهما: الاستجابة المرئية للتفاعل وكذلك الاستجابة البرمجية للتفاعل.

بالنسبة للجزء المرئي ، يمكننا ببساطة عكس التعبئة المتدرجة لمحيط الزر ، مما سيخلق الوهم بأنه تم الضغط على الزر:

عكس التدرج اللوني للزر
عكس التدرج اللوني للزر (معاينة كبيرة)

يتم تحديد التدرج اللوني لمحيط الزر بواسطة رمز SVG التالي ، حيث يتم استبدال ${color} بلون الزر بواسطة lit-element ، كما هو موضح أعلاه:

 <linearGradient x1="0" x2="1" y1="0" y2="1"> <stop stop-color="#ffffff" offset="0" /> <stop stop-color="${color}" offset="0.3" /> <stop stop-color="${color}" offset="0.5" /> <stop offset="1" /> </linearGradient>

تتمثل إحدى طرق مظهر الزر المضغوط في تحديد التدرج اللوني الثاني ، وعكس ترتيب الألوان ، واستخدامه كملء للدائرة عند الضغط على الزر. ومع ذلك ، هناك خدعة لطيفة تسمح لنا بإعادة استخدام نفس التدرج اللوني: يمكننا تدوير عنصر svg بمقدار 180 درجة باستخدام تحويل SVG:

 <circle cx="6" cy="6" r="3.822" fill="url(#a)" transform="rotate(180 6 6)" />

تخبر سمة transform SVG أننا نريد تدوير الدائرة بمقدار 180 درجة ، وأن الدوران يجب أن يحدث حول النقطة (6 ، 6) التي تمثل مركز الدائرة (المحددة بواسطة cx و cy ). تؤثر تحويلات SVG أيضًا على تعبئة الشكل ، وبالتالي سيتم تدوير التدرج اللوني أيضًا.

نريد فقط عكس التدرج اللوني عند الضغط على الزر ، لذا فبدلاً من إضافة سمة transform مباشرة على عنصر <circle> ، كما فعلنا أعلاه ، سنقوم بالفعل بتعيين فئة CSS لهذا العنصر ، ثم نستفيد من ذلك من حقيقة أنه يمكن تعيين سمات SVG من خلال CSS ، وإن كان ذلك باستخدام صيغة مختلفة قليلاً:

 transform: rotate(180deg); transform-origin: 6px 6px;

تعمل قاعدتا CSS هذه تمامًا مثل transform الذي أجريناه أعلاه - قم بتدوير الدائرة 180 درجة حول مركزها عند (6 ، 6). نريد تطبيق هذه القواعد فقط عند الضغط على الزر ، لذلك سنضيف اسم فئة CSS إلى دائرتنا:

 <circle class="button-contour" cx="6" cy="6" r="3.822" fill="url(#a)" />

والآن يمكننا استخدام فئة CSS الزائفة النشطة لتطبيق تحويل على button-contour كلما تم النقر فوق عنصر SVG:

 svg:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; }

يسمح lit-element بإرفاق ورقة أنماط بمكوِّننا عن طريق إعلانها في دفتر ثابت داخل فئة المكوِّن لدينا ، باستخدام قالب حرفي مميز:

 static get styles() { return css` svg:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; } `; }

تمامًا مثل قالب HTML ، تسمح لنا البنية هذه بإدخال قيم مخصصة في كود CSS الخاص بنا ، على الرغم من أننا لسنا بحاجة إليها هنا. يهتم lit-element أيضًا بإنشاء Shadow DOM لمكوننا ، بحيث يؤثر CSS فقط على العناصر الموجودة داخل المكون الخاص بنا ولا ينتقل إلى أجزاء أخرى من التطبيق.

الآن ، ماذا عن السلوك البرمجي للزر عند الضغط عليه؟ نريد إطلاق حدث حتى يتمكن مستخدمو المكون الخاص بنا من معرفة متى تتغير حالة الزر. تتمثل إحدى طرق القيام بذلك في الاستماع إلى أحداث الماوس وفأرة الماوس على عنصر SVG ، وإطلاق أحداث "الضغط على الزر" / "تحرير الزر" في المقابل. هذا ما يبدو عليه مع بناء جملة lit-element :

 render() { const { color } = this; return html` <svg @mousedown=${() => this.dispatchEvent(new Event('button-press'))} @mouseup=${() => this.dispatchEvent(new Event('button-release'))} ... </svg> `; }

ومع ذلك ، هذا ليس هو الحل الأفضل ، كما سنرى بعد قليل. لكن أولاً ، ألق نظرة سريعة على الكود الذي حصلنا عليه حتى الآن:

 import { customElement, css, html, LitElement, property } from 'lit-element'; @customElement('wokwi-pushbutton') export class PushbuttonElement extends LitElement { @property() color = 'red'; static get styles() { return css` svg:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; } `; } render() { const { color } = this; return html` <svg @mousedown=${() => this.dispatchEvent(new Event('button-press'))} @mouseup=${() => this.dispatchEvent(new Event('button-release'))} width="18mm" height="12mm" version="1.1" viewBox="-3 0 18 12" xmlns="https://www.w3.org/2000/svg" > <defs> <linearGradient x1="0" x2="1" y1="0" y2="1"> <stop stop-color="#ffffff" offset="0" /> <stop stop-color="${color}" offset="0.3" /> <stop stop-color="${color}" offset="0.5" /> <stop offset="1" /> </linearGradient> </defs> <rect x="0" y="0" width="12" height="12" rx=".44" ry=".44" fill="#464646" /> <rect x=".75" y=".75" width="10.5" height="10.5" rx=".211" ry=".211" fill="#eaeaea" /> <g fill="#1b1b1"> <circle cx="1.767" cy="1.7916" r=".37" /> <circle cx="10.161" cy="1.7916" r=".37" /> <circle cx="10.161" cy="10.197" r=".37" /> <circle cx="1.767" cy="10.197" r=".37" /> </g> <g fill="#eaeaea"> <path d="m-0.3538 1.4672c-0.058299 0-0.10523 0.0469-0.10523 0.10522v0.38698h-2.1504c-0.1166 0-0.21045 0.0938-0.21045 0.21045v0.50721c0 0.1166 0.093855 0.21045 0.21045 0.21045h2.1504v0.40101c0 0.0583 0.046928 0.10528 0.10523 0.10528h0.35723v-1.9266z" /> <path d="m-0.35376 8.6067c-0.058299 0-0.10523 0.0469-0.10523 0.10523v0.38697h-2.1504c-0.1166 0-0.21045 0.0939-0.21045 0.21045v0.50721c0 0.1166 0.093855 0.21046 0.21045 0.21046h2.1504v0.401c0 0.0583 0.046928 0.10528 0.10523 0.10528h0.35723v-1.9266z" /> <path d="m12.354 1.4672c0.0583 0 0.10522 0.0469 0.10523 0.10522v0.38698h2.1504c0.1166 0 0.21045 0.0938 0.21045 0.21045v0.50721c0 0.1166-0.09385 0.21045-0.21045 0.21045h-2.1504v0.40101c0 0.0583-0.04693 0.10528-0.10523 0.10528h-0.35723v-1.9266z" /> <path d="m12.354 8.6067c0.0583 0 0.10523 0.0469 0.10523 0.10522v0.38698h2.1504c0.1166 0 0.21045 0.0938 0.21045 0.21045v0.50721c0 0.1166-0.09386 0.21045-0.21045 0.21045h-2.1504v0.40101c0 0.0583-0.04693 0.10528-0.10523 0.10528h-0.35723v-1.9266z" /> </g> <g> <circle class="button-contour" cx="6" cy="6" r="3.822" fill="url(#a)" /> <circle cx="6" cy="6" r="2.9" fill="${color}" stroke="#2f2f2f" stroke-opacity=".47" stroke-width=".08" /> </g> </svg> `; } }
  • شاهد العرض التوضيحي →

يمكنك النقر فوق كل زر من الأزرار ومعرفة كيفية تفاعلها. يحتوي اللون الأحمر على بعض أدوات الاستماع للأحداث (محددة في index.html ) ، لذلك عند النقر فوقه ، سترى بعض الرسائل مكتوبة على وحدة التحكم. لكن انتظر ، ماذا لو كنت تريد استخدام لوحة المفاتيح بدلاً من ذلك؟

جعل المكون سهل الوصول إليه وملائم للجوّال

الصيحة! أنشأنا مكوِّنًا قابلًا لإعادة الاستخدام باستخدام زر ضغط SVG lit-element !

قبل أن نوقع على عملنا ، هناك بعض القضايا التي يجب أن ننظر فيها. أولاً ، لا يمكن الوصول إلى الزر للأشخاص الذين يستخدمون لوحة المفاتيح. بالإضافة إلى ذلك ، فإن السلوك على الهاتف المحمول غير متسق - تظهر الأزرار مضغوطة عند الضغط عليها بإصبعك ، ولكن لا يتم تشغيل أحداث JavaScript إذا أمسكت بإصبعك لأكثر من ثانية واحدة.

لنبدأ بمعالجة مشكلة لوحة المفاتيح. يمكننا جعل الزر يمكن الوصول إليه من خلال إضافة سمة tabindex إلى عنصر svg ، مما يجعله قابلاً للتركيز. البديل الأفضل ، في رأيي ، هو فقط لف الزر بعنصر <button> قياسي. باستخدام العنصر القياسي ، نجعله يلعب بشكل جيد مع قارئات الشاشة والتقنيات المساعدة الأخرى.

هذا النهج له عيب واحد ، كما ترى أدناه:

مكوننا الجميل محاط بعنصر زر
مكوننا الجميل محاط بداخل عنصر <button> . (معاينة كبيرة)

يأتي العنصر <button> مع بعض التصميم المدمج. يمكن إصلاح ذلك بسهولة عن طريق تطبيق بعض CSS لإزالة هذه الأنماط:

 button { border: none; background: none; padding: 0; margin: 0; text-decoration: none; -webkit-appearance: none; -moz-appearance: none; } button:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; }

لاحظ أننا أيضًا استبدلنا المحدد الذي يقلب شبكة محيط الأزرار ، باستخدام button:active in place of svg:active . يضمن ذلك تطبيق نمط الضغط على الزر عند الضغط على عنصر <button> الفعلي ، بغض النظر عن جهاز الإدخال المستخدم.

يمكننا حتى أن نجعل المكون الخاص بنا أكثر ملاءمة لقارئ الشاشة عن طريق إضافة سمة aria-label التي تتضمن لون الزر:

 <button aria-label="${color} pushbutton">

لا يزال هناك شيء آخر يجب معالجته: أحداث "الضغط على الزر" و "تحرير الزر". من الناحية المثالية ، نريد إطلاقها استنادًا إلى CSS: فئة الزر الزائفة النشطة ، تمامًا كما فعلنا في CSS أعلاه. بعبارة أخرى ، نود إطلاق حدث "الضغط على الزر" عندما يصبح الزر :active ، ويتم تنشيط حدث "تحرير الزر" عندما يكون :not(:active) .

ولكن كيف تستمع إلى فئة CSS الزائفة من Javascript؟

اتضح أن الأمر ليس بهذه البساطة. لقد طرحت هذا السؤال على مجتمع JavaScript Israel ، وفي النهاية توصلت إلى فكرة واحدة عملت من سلسلة لا نهاية لها: استخدم :active لتشغيل رسوم متحركة قصيرة للغاية لـ CSS ، وبعد ذلك يمكنني الاستماع إليها من JavaScript باستخدام animationstart حدث.

أثبتت تجربة CodePen السريعة أن هذا يعمل بالفعل بشكل موثوق. بقدر ما أحببت تطور هذه الفكرة ، قررت أن أذهب بحل مختلف أبسط. لا يتوفر حدث animationstart في Edge و iOS Safari ، ولا يبدو أن تشغيل الرسوم المتحركة CSS لاكتشاف تغيير حالة الزر هو الطريقة الصحيحة للقيام بالأشياء.

بدلاً من ذلك ، سنقوم بإضافة ثلاثة أزواج من مستمعي الأحداث إلى عنصر <button> : الماوس / الماوس للماوس ، بدء اللمس / اللمس للأجهزة المحمولة ، ومفتاح التشغيل / مفتاح التشغيل للوحة المفاتيح. ليس الحل الأكثر أناقة في رأيي ، لكنه يفعل الحيلة ويعمل على جميع المتصفحات.

 <button aria-label="${color} pushbutton" @mousedown=${this.down} @mouseup=${this.up} @touchstart=${this.down} @touchend=${this.up} @keydown=${(e: KeyboardEvent) => e.keyCode === SPACE_KEY && this.down()} @keyup=${(e: KeyboardEvent) => e.keyCode === SPACE_KEY && this.up()} >

حيث يكون SPACE_KEY ثابتًا يساوي 32 ، up / down هما طريقتان من فئات الفئات التي ترسل أحداث button-press button-release :

 @property() pressed = false; private down() { if (!this.pressed) { this.pressed = true; this.dispatchEvent(new Event('button-press')); } } private up() { if (this.pressed) { this.pressed = false; this.dispatchEvent(new Event('button-release')); } }
  • يمكنك العثور على شفرة المصدر الكاملة هنا.

لقد فعلناها!

لقد كانت رحلة طويلة جدًا بدأت بتحديد المتطلبات ورسم الرسم التوضيحي للزر في Inkscape ، ثم مررنا بتحويل ملف SVG الخاص بنا إلى مكون ويب قابل لإعادة الاستخدام باستخدام عنصر lit-element ، وبعد التأكد من أنه سهل الوصول إليه وسهل الاستخدام للجوال ، انتهى الأمر بما يقرب من 100 سطر من التعليمات البرمجية لمكون زر ضغط افتراضي رائع.

هذا الزر هو مجرد مكون واحد في مكتبة مفتوحة المصدر للمكونات الإلكترونية الافتراضية التي أقوم ببنائها. أنت مدعو لإلقاء نظرة خاطفة على الكود المصدري ، أو الاطلاع على Storybook عبر الإنترنت حيث يمكنك رؤية جميع المكونات المتاحة والتفاعل معها.

وأخيرًا ، إذا كنت مهتمًا بـ Arduino ، فقم بإلقاء نظرة على دورة البرمجة Simon for Arduino التي أقوم ببنائها حاليًا ، حيث يمكنك أيضًا رؤية زر الضغط أثناء العمل.

حتى المرة القادمة ، إذن!