การสร้างปุ่มกด Arduino ใหม่โดยใช้ SVG และ <lit-element>
เผยแพร่แล้ว: 2022-03-10ในบทความนี้ คุณจะได้เรียนรู้วิธีสร้างส่วนประกอบ HTML แบบกำหนดเองที่เลียนแบบวัตถุทางกายภาพ เช่น ปุ่มกด Arduino เราจะวาดส่วนประกอบใน Inkscape ตั้งแต่เริ่มต้น เพิ่มประสิทธิภาพโค้ด SVG ที่สร้างขึ้นสำหรับเว็บ และรวมเป็นส่วนประกอบเว็บแบบสแตนด์อโลนโดยใช้ไลบรารี
lit-element
ที่มีน้ำหนักเบา โดยให้ความสนใจเป็นพิเศษกับการพิจารณาความสามารถในการเข้าถึงและการใช้งานบนมือถือ วันนี้ ฉันจะพาคุณผ่านการเดินทางของการสร้างองค์ประกอบ HTML ที่เลียนแบบส่วนประกอบปุ่มกดชั่วขณะซึ่งมักใช้กับ Arduino และในโครงการอิเล็กทรอนิกส์ เราจะใช้เทคโนโลยี เช่น SVG, Web Components และ lit-element
และเรียนรู้วิธีทำให้ปุ่มสามารถเข้าถึงได้ผ่านกลอุบาย JavaScript-CSS
เริ่มกันเลย!
จาก Arduino เป็น HTML: ความต้องการส่วนประกอบปุ่มกด
ก่อนที่เราจะเริ่มต้นการเดินทาง เรามาสำรวจสิ่งที่เรากำลังจะสร้าง และที่สำคัญกว่านั้น เพราะอะไร ฉันกำลังสร้างโปรแกรมจำลอง Arduino โอเพ่นซอร์สใน JavaScript ชื่อ avr8js โปรแกรมจำลองนี้สามารถรันโค้ด Arduino ได้ และฉันจะใช้มันในชุดของบทช่วยสอนและหลักสูตรที่สอนผู้ผลิตถึงวิธีตั้งโปรแกรมสำหรับ Arduino
ตัวจำลองเองดูแลเฉพาะการทำงานของโปรแกรม — มันรันคำสั่งรหัสตามคำสั่งและอัพเดตสถานะภายในและบัฟเฟอร์หน่วยความจำตามตรรกะของโปรแกรม ในการโต้ตอบกับโปรแกรม Arduino คุณต้องสร้างส่วนประกอบอิเล็กทรอนิกส์เสมือนที่สามารถส่งอินพุตไปยังเครื่องจำลองหรือตอบสนองต่อเอาต์พุตได้
การรันโปรแกรมจำลองเพียงอย่างเดียวก็เหมือนกับการเรียกใช้ JavaScript แบบแยกส่วน คุณไม่สามารถโต้ตอบกับผู้ใช้ได้จริงๆ เว้นแต่คุณจะสร้างองค์ประกอบ HTML บางอย่างและเชื่อมโยงเข้ากับโค้ด JavaScript ผ่าน DOM
ดังนั้น นอกจากตัวจำลองของโปรเซสเซอร์ ฉันยังทำงานกับไลบรารีของส่วนประกอบ HTML ที่เลียนแบบฮาร์ดแวร์จริง โดยเริ่มจากสององค์ประกอบแรกที่คุณจะพบในเกือบทุกโครงการอิเล็กทรอนิกส์: LED และปุ่มกด
LED ค่อนข้างเรียบง่าย เนื่องจากมีสถานะเอาต์พุตเพียงสองสถานะ: เปิดและปิด เบื้องหลังจะใช้ฟิลเตอร์ SVG เพื่อสร้างเอฟเฟกต์แสง
ปุ่มกดมีความน่าสนใจมากขึ้น นอกจากนี้ยังมีสองสถานะ แต่ต้องตอบสนองต่อการป้อนข้อมูลของผู้ใช้และอัปเดตสถานะตามนั้น และนี่คือที่มาของความท้าทาย ดังที่เราจะได้เห็นในเร็วๆ นี้ แต่ก่อนอื่น มาดูข้อกำหนดจากองค์ประกอบที่เราจะสร้างกันก่อน
การกำหนดข้อกำหนดสำหรับปุ่มกด
ส่วนประกอบของเราจะคล้ายกับปุ่มกดขนาด 12 มม. ปุ่มเหล่านี้มีอยู่ทั่วไปในชุดสตาร์ทอุปกรณ์อิเล็กทรอนิกส์ และมาพร้อมกับฝาครอบหลายสี ดังที่คุณเห็นในภาพด้านล่าง:
ในแง่ของพฤติกรรม ปุ่มกดควรมีสองสถานะ: กดและปล่อย สิ่งเหล่านี้คล้ายกับเหตุการณ์ HTML ของ mousedown/mouseup แต่เราต้องตรวจสอบให้แน่ใจว่าสามารถใช้ปุ่มกดจากอุปกรณ์มือถือและสามารถเข้าถึงได้สำหรับผู้ใช้โดยไม่ต้องใช้เมาส์
เนื่องจากเราจะใช้สถานะของปุ่มกดเป็นอินพุตสำหรับ Arduino จึงไม่มีความจำเป็นต้องรองรับเหตุการณ์ "คลิก" หรือ "ดับเบิลคลิก" ขึ้นอยู่กับโปรแกรม Arduino ที่ทำงานอยู่ในการจำลองเพื่อตัดสินใจว่าจะดำเนินการอย่างไรกับสถานะของปุ่ม และปุ่มจริงจะไม่สร้างเหตุการณ์การคลิก
หากคุณต้องการเรียนรู้เพิ่มเติม โปรดดูการพูดคุยที่ฉันจัดขึ้นกับ Benjamin Gruenbaum ที่ SmashingConf Freiburg ในปี 2019: “Anatomy of a Click”
เพื่อสรุปข้อกำหนดของเรา ปุ่มกดของเราจำเป็นต้อง:
- ดูคล้ายกับปุ่มกดขนาด 12 มม.
- มีสองสถานะที่แตกต่างกัน: ถูกกดและปล่อย และควรมองเห็นได้ชัดเจน
- รองรับการโต้ตอบของเมาส์ อุปกรณ์พกพา และผู้ใช้คีย์บอร์ดสามารถเข้าถึงได้
- รองรับสีหมวกที่แตกต่างกัน (อย่างน้อยสีแดง, สีเขียว, สีฟ้า, สีเหลือง, สีขาวและสีดำ)
ตอนนี้เราได้กำหนดข้อกำหนดแล้ว เราสามารถเริ่มทำงานในการดำเนินการได้
SVG เพื่อชัยชนะ
คอมโพเนนต์ของเว็บส่วนใหญ่ใช้งานโดยใช้ CSS และ HTML ผสมกัน เมื่อเราต้องการกราฟิกที่ซับซ้อนมากขึ้น เรามักจะใช้ภาพแรสเตอร์ ในรูปแบบ JPG หรือ PNG (หรือ GIF หากคุณรู้สึกย้อนอดีต)
อย่างไรก็ตาม ในกรณีของเรา เราจะใช้วิธีอื่น: กราฟิก SVG SVG รองรับกราฟิกที่ซับซ้อนได้ง่ายกว่า CSS มาก (ใช่ ฉันรู้ คุณสามารถสร้างสิ่งที่น่าสนใจด้วย CSS ได้ แต่ไม่ได้หมายความว่ามันควรจะเป็น) แต่อย่ากังวล เราไม่ทิ้ง CSS ทั้งหมด มันจะช่วยเราในการกำหนดสไตล์ของปุ่มกด และในที่สุดก็ทำให้เข้าถึงได้
SVG มีข้อได้เปรียบที่สำคัญอีกประการหนึ่ง เมื่อเปรียบเทียบกับภาพกราฟิกแรสเตอร์: ง่ายต่อการจัดการจาก JavaScript และสามารถจัดรูปแบบผ่าน CSS ได้ ซึ่งหมายความว่าเราสามารถจัดเตรียมรูปภาพเดียวสำหรับปุ่ม และใช้ JavaScript เพื่อปรับแต่งตัวพิมพ์สี และรูปแบบ CSS เพื่อระบุสถานะของปุ่ม เรียบร้อยใช่มั้ย?
สุดท้าย SVG เป็นเพียงเอกสาร XML ซึ่งสามารถแก้ไขได้ด้วยโปรแกรมแก้ไขข้อความ และฝังลงใน HTML โดยตรง ทำให้เป็นโซลูชันที่สมบูรณ์แบบสำหรับการสร้างส่วนประกอบ HTML ที่นำกลับมาใช้ใหม่ได้ คุณพร้อมที่จะวาดปุ่มกดของเราแล้วหรือยัง?
การวาดปุ่มกดด้วย Inkscape
Inkscape เป็นเครื่องมือที่ฉันโปรดปรานในการสร้างกราฟิกแบบเวกเตอร์ SVG ฟรีและอัดแน่นไปด้วยคุณสมบัติอันทรงพลัง เช่น ชุดตัวกรองที่ตั้งไว้ล่วงหน้าจำนวนมาก การติดตามบิตแมป และการดำเนินการไบนารีของพาธ ฉันเริ่มใช้ Inkscape เพื่อสร้างงานศิลปะ PCB แต่ในช่วงสองปีที่ผ่านมา ฉันเริ่มใช้มันสำหรับงานตัดต่อกราฟิกส่วนใหญ่ของฉัน
การวาดปุ่มกดใน Inkscape นั้นค่อนข้างตรงไปตรงมา เราจะวาดภาพมุมมองด้านบนของปุ่มและสายโลหะทั้งสี่ที่เชื่อมต่อกับส่วนอื่น ๆ ดังนี้:
- สี่เหลี่ยมสีเทาเข้มขนาด 12×12 มม. สำหรับกล่องพลาสติก โดยมีมุมโค้งมนเล็กน้อยเพื่อให้ดูนุ่มขึ้น
- สี่เหลี่ยมสีเทาอ่อนขนาดเล็กกว่า 10.5×10.5 สำหรับฝาครอบโลหะ
- วงกลมสีเข้มสี่วง แต่ละมุมสำหรับหมุดที่ยึดปุ่มไว้ด้วยกัน
- วงกลมขนาดใหญ่ตรงกลาง นั่นคือรูปร่างของฝาครอบปุ่ม
- วงกลมขนาดเล็กที่อยู่ตรงกลางสำหรับด้านบนของฝาครอบปุ่ม
- สี่เหลี่ยมสีเทาอ่อนสี่รูปในรูปร่าง "T" สำหรับสายโลหะของปุ่ม
และผลลัพธ์ก็ขยายใหญ่ขึ้นเล็กน้อย:
ในขั้นสุดท้าย เราจะเพิ่มเวทย์มนตร์การไล่ระดับสี SVG ให้กับเส้นขอบของปุ่ม เพื่อให้เกิดความรู้สึก 3 มิติ:
เราจะไปที่นั่น! เรามีภาพจริงแล้ว ตอนนี้เราต้องนำมันเข้าสู่เว็บ
จาก Inkscape สู่ Web SVG
ดังที่ได้กล่าวไว้ข้างต้น SVG นั้นค่อนข้างตรงไปตรงมาในการฝังลงใน HTML — คุณสามารถวางเนื้อหาของไฟล์ SVG ลงในเอกสาร HTML ของคุณ เปิดในเบราว์เซอร์ แล้วเนื้อหาก็จะแสดงบนหน้าจอของคุณ คุณสามารถเห็นการทำงานจริงในตัวอย่าง CodePen ต่อไปนี้:
อย่างไรก็ตาม ไฟล์ SVG ที่บันทึกจาก Inkscape มีสัมภาระที่ไม่จำเป็นจำนวนมาก เช่น เวอร์ชัน Inkscape และตำแหน่งหน้าต่างเมื่อคุณบันทึกไฟล์ครั้งล่าสุด ในหลายกรณี ยังมีองค์ประกอบที่ว่างเปล่า การไล่ระดับสีและตัวกรองที่ไม่ได้ใช้ และพวกเขาทั้งหมดขยายขนาดไฟล์ และทำให้การทำงานกับมันใน HTML ยากขึ้น
โชคดีที่ Inkscape สามารถล้างสิ่งสกปรกส่วนใหญ่ให้เราได้ นี่คือวิธีที่คุณทำ:
- ไปที่เมนู ไฟล์ และคลิกที่ ล้างเอกสาร (การดำเนินการนี้จะลบคำจำกัดความที่ไม่ได้ใช้ออกจากเอกสารของคุณ)
- ไปที่ File อีกครั้งแล้วคลิก Save as… เมื่อบันทึก ให้เลือก Optimized SVG ( *.svg ) ในรายการแบบเลื่อนลง บันทึกเป็นประเภท
- คุณจะเห็นกล่องโต้ตอบ "เอาต์พุต SVG ที่ปรับให้เหมาะสม" พร้อมแท็บสามแท็บ ตรวจสอบตัวเลือกทั้งหมด ยกเว้น "เก็บข้อมูลตัวแก้ไข", "เก็บคำจำกัดความที่ไม่อ้างอิง" และ "รักษารหัสที่สร้างด้วยตนเอง..."
การนำสิ่งเหล่านี้ออกจะสร้างไฟล์ 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
ของวงกลมที่เล็กกว่า และสีเริ่มต้นของการไล่ระดับสีของวงกลมที่ใหญ่กว่า
เป้าหมายต่อไปของเราคือการเปลี่ยนปุ่มกดของเราให้เป็น Web Component ที่นำกลับมาใช้ใหม่ได้ ซึ่งสามารถปรับแต่งได้โดยส่งผ่านแอตทริบิวต์ color
และตอบสนองต่อการโต้ตอบของผู้ใช้ (เหตุการณ์กด/เผยแพร่) เราจะใช้ lit-element
ซึ่งเป็นไลบรารีขนาดเล็กที่ช่วยให้การสร้าง Web Components ง่ายขึ้น
lit-element
เลิศในการสร้างไลบรารีส่วนประกอบแบบสแตนด์อโลนขนาดเล็ก มันถูกสร้างขึ้นบนมาตรฐาน Web Components ซึ่งช่วยให้ส่วนประกอบเหล่านี้ถูกใช้โดยเว็บแอปพลิเคชันใด ๆ โดยไม่คำนึงถึงเฟรมเวิร์กที่ใช้: Angular, React, Vue หรือ Vanilla JS ทั้งหมดจะสามารถใช้ส่วนประกอบของเราได้
การสร้างส่วนประกอบในองค์ประกอบ lit-element
ทำได้โดยใช้ไวยากรณ์ตามคลาส ด้วยวิธีการ render()
ที่ส่งคืนโค้ด HTML สำหรับองค์ประกอบ คล้ายกับ React เล็กน้อยหากคุณคุ้นเคย อย่างไรก็ตาม ไม่เหมือนกับ react ที่ lit-element
ใช้ตัวอักษรเทมเพลตที่ติดแท็ก Javascript มาตรฐานเพื่อกำหนดเนื้อหาของส่วนประกอบ
นี่คือวิธีสร้างองค์ประกอบ 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()
(และด้วยค่าเริ่มต้นเป็นสีแดง) และวางโค้ด SVG ลงในการ render()
วิธีการ แทนที่สีของฝาครอบปุ่มด้วยค่าของคุณสมบัติสี (ดูตัวอย่าง) บิตที่สำคัญอยู่ในบรรทัดที่ 5 ซึ่งเรากำหนดคุณสมบัติสี: @property() color = 'red';
นอกจากนี้ ในบรรทัดที่ 35 (ที่เราใช้คุณสมบัตินี้เพื่อกำหนดสีเติมสำหรับวงกลมที่ทำให้เป็นฝาของปุ่ม) โดยใช้ไวยากรณ์ตามตัวอักษรของเทมเพลต JavaScript เขียนเป็น ${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)" />
และตอนนี้ เราสามารถใช้ :active CSS pseudo-class เพื่อใช้การแปลงกับ button-contour
ทุกครั้งที่องค์ประกอบ SVG ถูกคลิก:
svg:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; }
lit-element
ช่วยให้เราแนบสไตล์ชีตเข้ากับองค์ประกอบของเราโดยการประกาศใน getter แบบคงที่ภายในคลาสส่วนประกอบของเรา โดยใช้เทมเพลตที่ติดแท็กตามตัวอักษร:
static get styles() { return css` svg:active .button-contour { transform: rotate(180deg); transform-origin: 6px 6px; } `; }
เช่นเดียวกับเทมเพลต HTML ไวยากรณ์นี้ช่วยให้เราสามารถใส่ค่าที่กำหนดเองลงในโค้ด CSS ของเราได้ แม้ว่าเราจะไม่ต้องการมันที่นี่ก็ตาม lit-element
ยังดูแลการสร้าง Shadow DOM สำหรับส่วนประกอบของเรา ดังนั้น CSS จะมีผลกับองค์ประกอบภายในส่วนประกอบของเราเท่านั้น และไม่ตกไปยังส่วนอื่นๆ ของแอปพลิเคชัน
แล้วพฤติกรรมทางโปรแกรมของปุ่มเมื่อกดเป็นอย่างไร เราต้องการเริ่มเหตุการณ์เพื่อให้ผู้ใช้องค์ประกอบของเราทราบเมื่อใดก็ตามที่สถานะของปุ่มเปลี่ยนแปลง วิธีหนึ่งในการทำเช่นนี้คือการฟังเหตุการณ์ mousedown และ mouseup ในองค์ประกอบ SVG และเริ่มต้นเหตุการณ์ "button-press"/"button-release" ตามลำดับ นี่คือสิ่งที่ดูเหมือนกับไวยากรณ์ 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>
องค์ประกอบมาพร้อมกับสไตล์ในตัว ซึ่งสามารถแก้ไขได้ง่ายโดยใช้ 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
แทน svg:active
เพื่อให้แน่ใจว่ารูปแบบการกดปุ่มจะถูกนำไปใช้ทุกครั้งที่มีการกดองค์ประกอบ <button>
จริง โดยไม่คำนึงถึงอุปกรณ์อินพุตที่ใช้
เราสามารถทำให้คอมโพเนนต์ของเราอ่านหน้าจอได้ง่ายขึ้นด้วยการเพิ่มแอตทริบิวต์ aria-label
ที่มีสีของปุ่ม:
<button aria-label="${color} pushbutton">
ยังมีอีกสิ่งหนึ่งที่ต้องจัดการ: เหตุการณ์ "การกดปุ่ม" และ "การกดปุ่ม" ตามหลักการแล้ว เราต้องการเริ่มทำงานโดยอิงจาก CSS :active pseudo-class ของปุ่ม เช่นเดียวกับที่เราทำใน CSS ด้านบน กล่าวอีกนัยหนึ่ง เราต้องการเริ่มเหตุการณ์ "button-press" เมื่อใดก็ตามที่ปุ่มกลายเป็น :active
และเหตุการณ์ "button-release" จะเริ่มทำงานเมื่อใดก็ตามที่เป็น :not(:active)
แต่คุณจะฟัง CSS pseudo-class จาก Javascript ได้อย่างไร
ปรากฎว่ามันไม่ง่ายนัก ฉันถามคำถามนี้กับชุมชน JavaScript Israel และในที่สุดก็ค้นพบแนวคิดหนึ่งที่ได้ผลจากเธรดที่ไม่มีที่สิ้นสุด: ใช้ :active
ตัวเลือกเพื่อทริกเกอร์แอนิเมชั่น CSS แบบสั้นสุด ๆ จากนั้นฉันก็สามารถฟังจาก JavaScript โดยใช้ animationstart
เหตุการณ์.
การทดลอง CodePen อย่างรวดเร็วได้พิสูจน์แล้วว่าการทำงานนี้เชื่อถือได้จริง เท่าที่ฉันชอบความซับซ้อนของแนวคิดนี้ ฉันตัดสินใจใช้วิธีแก้ปัญหาที่ต่างออกไปและง่ายกว่า เหตุการณ์เริ่มต้นของ animationstart
ไม่พร้อมใช้งานบน Edge และ iOS Safari และการทริกเกอร์แอนิเมชั่น CSS เพียงเพื่อตรวจจับการเปลี่ยนแปลงของสถานะของปุ่มนั้นฟังดูไม่เหมือนวิธีที่ถูกต้องในการทำสิ่งต่างๆ
แต่เราจะเพิ่ม Listener เหตุการณ์สามคู่ให้กับองค์ประกอบ <button>
: mousedown/mouseup สำหรับเมาส์, touchstart/touchend สำหรับอุปกรณ์มือถือ และ keyup/keydown สำหรับแป้นพิมพ์ ในความคิดของฉันไม่ใช่วิธีแก้ปัญหาที่หรูหราที่สุด แต่มันเป็นการหลอกลวงและใช้ได้กับทุกเบราว์เซอร์
<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 สำหรับหลักสูตร Arduino ที่ฉันกำลังสร้าง ซึ่งคุณยังสามารถดูการทำงานของปุ่มกดได้อีกด้วย
ครั้งต่อไปแล้ว!