SVG ve <lit-element> Kullanarak Arduino Butonunu Yeniden Oluşturma

Yayınlanan: 2022-03-10
Hızlı özet ↬ HTML, bir dizi girdi denetimiyle birlikte gelir ve onay kutuları ve radyo düğmeleri gibi birçok standart denetimi içeren tonlarca bileşen kitaplığı vardır. Ama sıra dışı bir şeye ihtiyacınız olduğunda ne olur?

Bu makalede, Arduino Buton gibi fiziksel nesneleri taklit eden özel HTML bileşenlerinin nasıl oluşturulacağını öğreneceksiniz. Bileşeni Inkscape'de sıfırdan çizeceğiz, oluşturulan SVG kodunu Web için optimize edeceğiz ve erişilebilirlik ve mobil kullanılabilirlik konularına ekstra dikkat ederek hafif lit-element kitaplığını kullanarak bağımsız bir web bileşeni olarak saracağız.

Bugün sizi Arduino ve elektronik projelerde yaygın olarak kullanılan anlık bir buton bileşenini taklit eden bir HTML bileşeni oluşturma yolculuğuna çıkaracağım. SVG, Web Bileşenleri ve lit-element gibi teknolojileri kullanacağız ve bazı JavaScript-CSS hileleriyle düğmeyi nasıl erişilebilir hale getireceğimizi öğreneceğiz.

Hadi başlayalım!

Arduino'dan HTML'ye: Bir Buton Bileşeni İhtiyacı

Yolculuğa çıkmadan önce, neyi yaratacağımızı ve daha da önemlisi nedenini keşfedelim. JavaScript'te avr8js adlı açık kaynaklı bir Arduino simülatörü oluşturuyorum. Bu simülatör Arduino kodunu çalıştırabilir ve ben bunu yapımcılara Arduino için nasıl programlama yapacaklarını öğreten bir dizi öğretici ve kursta kullanacağım.

Simülatörün kendisi yalnızca programın yürütülmesiyle ilgilenir - kod talimatını talimatla çalıştırır ve dahili durumunu ve bir bellek arabelleğini program mantığına göre günceller. Arduino programı ile etkileşime girebilmek için simülatöre girdi gönderebilen veya çıktılarına tepki verebilen bazı sanal elektronik bileşenler oluşturmanız gerekir.

Simülatörü tek başına çalıştırmak, JavaScript'i tek başına çalıştırmak gibidir. Ayrıca bazı HTML öğeleri oluşturmadığınız ve bunları DOM aracılığıyla JavaScript koduna bağlamadığınız sürece, kullanıcıyla gerçekten etkileşim kuramazsınız.

Bu nedenle, işlemci simülatörüne ek olarak, hemen hemen her elektronik projesinde bulacağınız ilk iki bileşenden başlayarak, fiziksel donanımı taklit eden bir HTML bileşenleri kitaplığı üzerinde çalışıyorum: bir LED ve bir buton.

LED ve buton elemanları iş başında
LED ve buton elemanları iş başında

LED, yalnızca iki çıkış durumuna sahip olduğu için nispeten basittir: açık ve kapalı. Perde arkasında, aydınlatma efekti oluşturmak için bir SVG filtresi kullanır.

Buton daha ilginç. Aynı zamanda iki durumu vardır, ancak kullanıcı girdisine tepki vermesi ve durumunu buna göre güncellemesi gerekir ve birazdan göreceğimiz gibi, zorluk da buradan geliyor. Ama önce, oluşturacağımız bileşenimizden gereksinimleri belirleyelim.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Buton İçin Gereksinimlerin Tanımlanması

Bileşenimiz 12 mm'lik bir düğmeye benzeyecek. Bu düğmeler elektronik başlangıç ​​kitlerinde çok yaygındır ve aşağıdaki fotoğrafta görebileceğiniz gibi birden çok renkte kapaklarla birlikte gelir:

Sarı, Kırmızı, Mavi ve Yeşil butonlu Simon Oyunu
Sarı, Kırmızı, Mavi ve Yeşil butonlu Simon Oyunu (Geniş önizleme)

Davranış açısından, basmalı düğmenin iki durumu olmalıdır: basılı ve bırakılmış. Bunlar mousedown/mouseup HTML olaylarına benzer, ancak butonların mobil cihazlardan da kullanılabildiğinden ve faresiz kullanıcılar için erişilebilir olduğundan emin olmalıyız.

Arduino için giriş olarak buton durumunu kullanacağımız için, "tıklama" veya "çift tıklama" olaylarını desteklemeye gerek yoktur. Düğmenin durumuna göre nasıl hareket edileceğine karar vermek simülasyonda çalışan Arduino programına bağlıdır ve fiziksel düğmeler tıklama olayları oluşturmaz.

Daha fazlasını öğrenmek isterseniz, 2019'da SmashingConf Freiburg'da Benjamin Gruenbaum ile yaptığım bir konuşmaya göz atın: “Bir Tıklamanın Anatomisi”.

Gereksinimlerimizi özetlemek için butonumuzun şunları yapması gerekir:

  1. fiziksel 12 mm basmalı düğmeye benziyor;
  2. iki farklı durumu vardır: basılmış ve bırakılmış ve görsel olarak fark edilebilir olmalıdırlar;
  3. fare etkileşimini, mobil cihazları destekleyin ve klavye kullanıcıları tarafından erişilebilir olun;
  4. farklı kapak renklerini destekler (en azından kırmızı, yeşil, mavi, sarı, beyaz ve siyah).

Artık gereksinimleri tanımladığımıza göre, uygulama üzerinde çalışmaya başlayabiliriz.

Kazanmak İçin SVG

Çoğu web bileşeni, CSS ve HTML'nin bir kombinasyonu kullanılarak uygulanır. Daha karmaşık grafiklere ihtiyacımız olduğunda, genellikle JPG veya PNG formatında (veya nostaljik hissediyorsanız GIF) raster görüntüler kullanırız.

Ancak bizim durumumuzda başka bir yaklaşım kullanacağız: SVG grafikleri. SVG, karmaşık grafiklere CSS'den çok daha kolay uyum sağlar (evet, biliyorum, CSS ile büyüleyici şeyler yaratabilirsiniz, ancak bu olması gerektiği anlamına gelmez). Ama merak etmeyin, CSS'den tamamen vazgeçmiyoruz. Butonları şekillendirmemize ve sonunda onları erişilebilir hale getirmemize yardımcı olacak.

SVG'nin raster grafik görüntülerine kıyasla bir başka büyük avantajı daha vardır: JavaScript'ten işlemek çok kolaydır ve CSS aracılığıyla biçimlendirilebilir. Bu, düğme için tek bir görüntü sağlayabileceğimiz ve renk başlığını özelleştirmek için JavaScript'i ve düğmenin durumunu belirtmek için CSS stillerini kullanabileceğimiz anlamına gelir. Temiz, değil mi?

Son olarak, SVG, metin düzenleyicilerle düzenlenebilen ve doğrudan HTML'ye gömülebilen, yeniden kullanılabilir HTML bileşenleri oluşturmak için onu mükemmel bir çözüm haline getiren yalnızca bir XML belgesidir. Butonumuzu çizmeye hazır mısınız?

Inkscape ile Basmalı Düğmeyi Çizim

Inkscape, SVG vektör grafikleri oluşturmak için en sevdiğim araçtır. Ücretsizdir ve geniş bir yerleşik filtre ön ayarı koleksiyonu, bitmap izleme ve yol ikili işlemleri gibi güçlü özelliklerle doludur. Inkscape'i PCB sanatı oluşturmak için kullanmaya başladım, ancak son iki yılda grafik düzenleme görevlerimin çoğu için kullanmaya başladım.

Inkscape'de buton çizmek oldukça basittir. Düğmenin ve onu diğer parçalara bağlayan dört metal ucunun üstten görünüşlü bir resmini aşağıdaki gibi çizeceğiz:

  1. Plastik kasa için 12×12mm koyu gri dikdörtgen, daha yumuşak olması için köşeleri hafif yuvarlatılmış.
  2. Metal kapak için daha küçük, 10,5×10,5 açık gri dikdörtgen.
  3. Düğmeyi bir arada tutan pimler için her köşede birer tane olmak üzere dört koyu daire.
  4. Ortada büyük bir daire, yani düğme kapağının konturu.
  5. Düğme kapağının üst kısmı için ortada daha küçük bir daire.
  6. Düğmenin metal uçları için "T" şeklinde dört açık gri dikdörtgen.

Ve sonuç, biraz büyütülmüş:

Elle Çizilmiş Buton Krokimiz
Elle çizilmiş Buton Eskizimiz (Büyük önizleme)

Son bir dokunuş olarak, 3D hissi vermek için düğmenin çevresine biraz SVG gradyan büyüsü ekleyeceğiz:

3D hissi oluşturmak için degrade dolgu ekleme
3D hissi oluşturmak için degrade dolgu ekleme (Büyük önizleme)

Oraya gidiyoruz! Görseller elimizde, şimdi onu internete taşımamız gerekiyor.

Inkscape'den Web SVG'ye

Yukarıda bahsettiğim gibi, SVG'leri HTML'ye yerleştirmek oldukça basittir - SVG dosyasının içeriğini HTML belgenize yapıştırabilir, bir tarayıcıda açabilir ve ekranınızda görüntülenecektir. Aşağıdaki CodePen örneğinde bunu çalışırken görebilirsiniz:

@Uri Shaked tarafından yazılan HTML'deki Pen SVG Butonuna bakın

@Uri Shaked tarafından yazılan HTML'deki Pen SVG Butonuna bakın

Ancak, Inkscape'den kaydedilen SVG dosyaları, Inkscape sürümü ve dosyayı en son kaydettiğiniz pencere konumu gibi birçok gereksiz bagaj içerir. Çoğu durumda, boş öğeler, kullanılmayan gradyanlar ve filtreler de vardır ve bunların tümü dosya boyutunu şişirir ve HTML içinde onunla çalışmayı zorlaştırır.

Neyse ki, Inkscape bizim için pisliğin çoğunu temizleyebilir. Bunu nasıl yapacağınız aşağıda açıklanmıştır:

  1. Dosya menüsüne gidin ve Belgeyi temizle seçeneğine tıklayın. (Bu, kullanılmayan tanımları belgenizden kaldıracaktır.)
  2. Tekrar Dosya'ya gidin ve Farklı kaydet… 'e tıklayın. Kaydederken, Kayıt türü açılır menüsünden Optimize Edilmiş SVG'yi ( *.svg ) seçin.
  3. Üç sekmeli bir "Optimize Edilmiş SVG Çıktısı" iletişim kutusu göreceksiniz. “Düzenleyici verilerini sakla”, “Referanssız tanımları koru” ve “Manuel olarak oluşturulmuş kimlikleri koru…” dışındaki tüm seçenekleri işaretleyin.
(Büyük önizleme)

Tüm bunları kaldırmak, üzerinde çalışması daha kolay olan daha küçük bir SVG dosyası oluşturacaktır. Benim durumumda, dosya 4593 bayttan sadece 2080 bayta düştü, boyutun yarısından daha az. Daha karmaşık SVG dosyaları için bu, bant genişliğinden büyük bir tasarruf sağlayabilir ve web sayfanızın yüklenme süresinde kayda değer bir fark yaratabilir.

Optimize edilmiş SVG'nin okunması ve anlaşılması da çok daha kolaydır. Aşağıdaki alıntıda, düğmenin gövdesini oluşturan iki dikdörtgeni kolayca görebilmeniz gerekir:

 <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"/>

Örneğin, ilk dikdörtgenin kontur genişliğini 1.0003 sadece 1 değiştirerek kodu daha da kısaltabilirsiniz. Dosya boyutunda önemli bir fark yaratmaz, ancak kodun okunmasını kolaylaştırır.

Genel olarak, oluşturulan SVG dosyasının üzerinden manuel olarak geçmek her zaman yararlıdır. Çoğu durumda, boş grupları kaldırabilir veya matris dönüşümleri uygulayabilir ve degrade koordinatlarını "kullanılan kullanıcı alanından" (küresel koordinatlar) "nesne sınırlama kutusuna" (nesneye göre) eşleyerek basitleştirebilirsiniz. Bu optimizasyonlar isteğe bağlıdır, ancak anlaşılması ve bakımı daha kolay olan bir kod alırsınız.

Bu noktadan sonra Inkscape'i kaldıracağız ve SVG görüntüsünün metin temsiliyle çalışacağız.

Yeniden Kullanılabilir Bir Web Bileşeni Oluşturma

Şimdiye kadar, simülatörümüze eklenmeye hazır butonumuz için grafikleri aldık. Daha küçük dairenin fill niteliğini ve daha büyük dairenin degradesinin başlangıç ​​rengini değiştirerek düğmenin rengini kolayca özelleştirebiliriz.

Bir sonraki hedefimiz, butonumuzu bir color özniteliği ileterek özelleştirilebilen ve kullanıcı etkileşimine (basma/bırakma olayları) tepki veren yeniden kullanılabilir bir Web Bileşenine dönüştürmektir. Web Bileşenlerinin oluşturulmasını basitleştiren küçük bir kitaplık olan lit-element kullanacağız.

lit-element , küçük, bağımsız bileşen kitaplıkları oluşturmada mükemmeldir. Kullanılan çerçeveden bağımsız olarak bu bileşenlerin herhangi bir web uygulaması tarafından tüketilmesine izin veren Web Bileşenleri standardının üzerine inşa edilmiştir: Angular, React, Vue veya Vanilla JS, bileşenimizi kullanabilir.

lit-element bileşenlerin oluşturulması, öğenin HTML kodunu döndüren bir render() yöntemiyle sınıf tabanlı bir sözdizimi kullanılarak yapılır. Eğer aşina iseniz, React'e biraz benzer. Ancak, tepkiden farklı olarak, lit-element , bileşenin içeriğini tanımlamak için standart Javascript etiketli şablon değişmezlerini kullanır.

Basit bir hello-world bileşenini şu şekilde oluşturabilirsiniz:

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

Bu bileşen daha sonra sadece <hello-world></hello-world> yazarak HTML kodunuzun herhangi bir yerinde kullanılabilir.

Not : Aslında, butonumuz biraz daha kod gerektiriyor: @property( @property() dekoratörünü (ve varsayılan değeri kırmızı olan) kullanarak renk için bir input özelliği bildirmemiz ve SVG kodunu render() yöntemi, düğme başlığının renginin color özelliğinin değeriyle değiştirilmesi (örneğe bakın). Önemli bitler, color özelliğini tanımladığımız 5. satırdadır: @property() color = 'red'; Ayrıca, ${color} olarak yazılan JavaScript şablonu değişmez sözdizimini kullanarak 35. satırda (düğmenin başlığını oluşturan dairenin dolgu rengini tanımlamak için bu özelliği kullanırız) :

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

Etkileşimli Hale Getirmek

Bulmacanın son parçası, düğmeyi etkileşimli hale getirmek olacaktır. Göz önünde bulundurmamız gereken iki husus var: etkileşime verilen görsel tepki ve etkileşime programatik tepki .

Görsel kısım için, düğmeye basıldığı yanılsamasını yaratacak olan düğme konturunun gradyan dolgusunu basitçe tersine çevirebiliriz:

Düğmenin kontur gradyanını ters çevirme
Düğmenin kontur gradyanını tersine çevirme (Büyük önizleme)

Düğme konturu için gradyan, aşağıda açıklandığı gibi ${color} öğesinin düğmenin rengiyle lit-element ile değiştirildiği aşağıdaki SVG koduyla tanımlanır:

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

Basılan düğme görünümü için bir yaklaşım, ikinci bir degrade tanımlamak, renklerin sırasını tersine çevirmek ve düğmeye her basıldığında bunu dairenin dolgusu olarak kullanmak olacaktır. Ancak, aynı gradyanı yeniden kullanmamıza izin veren güzel bir numara var: SVG dönüşümü kullanarak svg öğesini 180 derece döndürebiliriz:

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

transform özniteliği, SVG'ye daireyi 180 derece döndürmek istediğimizi ve dönüşün dairenin merkezi olan ( cx ve cy tarafından tanımlanan) (6, 6) noktası etrafında gerçekleşmesi gerektiğini söyler. SVG dönüşümleri ayrıca şeklin dolgusunu da etkiler ve bu nedenle gradyanımız da döndürülür.

Sadece düğmeye basıldığında gradyanı tersine çevirmek istiyoruz, bu nedenle, yukarıda yaptığımız gibi transform niteliğini doğrudan <circle> öğesine eklemek yerine, aslında bu öğe için bir CSS sınıfı ayarlayacağız ve sonra avantaj sağlayacağız. SVG niteliklerinin, biraz farklı bir sözdizimi kullanılarak da olsa CSS aracılığıyla ayarlanabilmesi gerçeğinden:

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

Bu iki CSS kuralı, yukarıda yaptığımız transform tamamen aynı şeyi yapar - daireyi (6, 6) noktasında merkezi etrafında 180 derece döndürün. Bu kuralların sadece butona basıldığında uygulanmasını istiyoruz, bu yüzden çemberimize bir CSS sınıf ismi ekleyeceğiz:

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

Ve şimdi, SVG öğesi tıklandığında button-contour bir dönüşüm uygulamak için :active CSS sözde sınıfını kullanabiliriz:

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

lit-element , etiketli bir şablon değişmezi kullanarak onu bileşen sınıfımız içinde statik bir alıcıda bildirerek bileşenimize bir stil sayfası eklememize olanak tanır:

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

Tıpkı HTML şablonu gibi, bu sözdizimi, burada ihtiyacımız olmasa bile, CSS kodumuza özel değerler eklememize izin verir. lit-element ayrıca bileşenimiz için Gölge DOM oluşturmaya özen gösterir, böylece CSS yalnızca bileşenimizdeki öğeleri etkiler ve uygulamanın diğer bölümlerine taşmaz.

Şimdi, basıldığında düğmenin programatik davranışı ne olacak? Bileşenimizin kullanıcılarının düğmenin durumu değiştiğinde anlayabilmeleri için bir olayı tetiklemek istiyoruz. Bunu yapmanın bir yolu, SVG öğesinde mousedown ve mouseup olaylarını dinlemek ve buna uygun olarak “button-press”/“button-release” olaylarını başlatmaktır. lit-element sözdizimi ile şöyle görünür:

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

Ancak, birazdan göreceğimiz gibi, bu en iyi çözüm değil. Ama önce, şu ana kadar elde ettiğimiz koda hızlıca bir göz atın:

 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> `; } }
  • Demoyu görün →

Düğmelerin her birine tıklayabilir ve nasıl tepki verdiklerini görebilirsiniz. Kırmızı olanın bazı olay dinleyicileri bile var ( index.html 'de tanımlanmıştır), bu yüzden üzerine tıkladığınızda konsola yazılmış bazı mesajları görmelisiniz. Ama bekleyin, bunun yerine klavyeyi kullanmak isterseniz ne olur?

Bileşeni Erişilebilir ve Mobil Dostu Hale Getirme

Yaşasın! SVG ve lit-element ile yeniden kullanılabilir bir buton bileşeni oluşturduk!

İşimizi imzalamadan önce, bakmamız gereken birkaç konu var. İlk olarak, tuşa klavyeyi kullanan kişiler erişemez. Ayrıca, mobildeki davranış tutarsız - parmağınızı üzerlerinde tuttuğunuzda düğmeler basılı görünüyor, ancak parmağınızı bir saniyeden fazla tutarsanız JavaScript olayları tetiklenmiyor.

Klavye sorununu ele alarak başlayalım. svg öğesine tabindex özniteliği ekleyerek odaklanabilir hale getirerek düğmeyi klavyeyle erişilebilir hale getirebiliriz. Bence daha iyi bir alternatif, düğmeyi standart bir <button> öğesiyle sarmak. Standart öğeyi kullanarak, ekran okuyucular ve diğer yardımcı teknolojilerle de sorunsuz çalışmasını sağlıyoruz.

Aşağıda görebileceğiniz gibi, bu yaklaşımın bir dezavantajı vardır:

Bir düğme elemanının içine yerleştirilmiş güzel bileşenimiz
Güzel bileşenimiz bir <button> öğesinin içine yerleştirilmiş. (Büyük önizleme)

<button> öğesi bazı yerleşik stiller ile birlikte gelir. Bu, bu stilleri kaldırmak için bazı CSS uygulanarak kolayca düzeltilebilir:

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

Ayrıca, svg:active yerine button:active kullanarak düğmelerin dış çizgisini tersine çeviren seçiciyi de değiştirdiğimizi unutmayın. Bu, kullanılan giriş aygıtından bağımsız olarak, gerçek <button> öğesine her basıldığında düğmeye basma stilinin uygulanmasını sağlar.

Düğmenin rengini içeren bir aria-label özniteliği ekleyerek bileşenimizi daha ekran okuyucu dostu hale getirebiliriz:

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

Hâlâ üstesinden gelinmesi gereken bir şey daha var: “düğmeye basma” ve “düğmeye basma” olayları. İdeal olarak, onları, tıpkı yukarıdaki CSS'de yaptığımız gibi, düğmenin CSS :active sözde sınıfına dayalı olarak tetiklemek istiyoruz. Başka bir deyişle, buton :active olduğunda "button-press" olayını ve :not(:active) olduğunda "button-release" olayını tetiklemek istiyoruz.

Ancak Javascript'ten bir CSS sözde sınıfını nasıl dinlersiniz?

Anlaşıldı, o kadar basit değil. Bu soruyu JavaScript İsrail topluluğuna sordum ve sonunda sonsuz iş parçacığından işe yarayan bir fikir buldum: süper kısa bir CSS animasyonunu tetiklemek için :active seçiciyi kullanın ve ardından animationstart kullanarak JavaScript'ten dinleyebilirim Etkinlik.

Hızlı bir CodePen deneyi, bunun gerçekten güvenilir bir şekilde çalıştığını kanıtladı. Bu fikrin karmaşıklığını ne kadar sevsem de, farklı ve daha basit bir çözüm bulmaya karar verdim. animationstart başlatma olayı Edge ve iOS Safari'de mevcut değildir ve yalnızca düğme durumundaki değişikliği algılamak için bir CSS animasyonunu tetiklemek, işleri yapmanın doğru yolu gibi görünmüyor.

Bunun yerine, <button> öğesine üç çift olay dinleyicisi ekleyeceğiz: fare için mousedown/mouseup, mobil cihazlar için touchstart/touchend ve klavye için keyup/keydown. Bence en zarif çözüm değil, ancak hile yapıyor ve tüm tarayıcılarda çalışıyor.

 <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'ye eşit bir sabit olduğunda ve up / down , button-press ve button-release olaylarını gönderen iki sınıf yöntemidir:

 @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')); } }
  • Tam kaynak kodunu burada bulabilirsiniz.

Yaptık!

Gereksinimlerin ana hatlarını çizerek ve Inkscape'deki düğme için çizimi çizerek başlayan, SVG dosyamızı lit-element kullanarak yeniden kullanılabilir bir web bileşenine dönüştürerek geçen ve erişilebilir ve mobil uyumlu olduğundan emin olduktan sonra oldukça uzun bir yolculuktu. keyifli bir sanal buton bileşeninin yaklaşık 100 satırlık koduyla sonuçlandı.

Bu düğme, oluşturmakta olduğum sanal elektronik bileşenlerin açık kaynaklı kitaplığındaki yalnızca tek bir bileşendir. Kaynak koduna göz atmaya veya mevcut tüm bileşenleri görebileceğiniz ve bunlarla etkileşime geçebileceğiniz çevrimiçi Storybook'a göz atmaya davetlisiniz.

Ve son olarak, eğer Arduino ile ilgileniyorsanız, şu anda inşa etmekte olduğum Arduino için Simon programlama kursuna bir göz atın, burada düğmeyi de çalışırken görebilirsiniz.

Bir dahaki sefere kadar!