พิมพ์โน้ตเพลงด้วย CSS Grid
(cruncher.ch)- สำหรับนักดนตรีที่ต้องซูมไฟล์ PDF ขนาด A4 เพื่อเล่นบนหน้าจอมือถือขนาดเล็ก จำเป็นต้องมีการเรนเดอร์โน้ตเพลงบนเว็บที่ลื่นไหลและตอบสนองได้
ต้นแบบ Scribe
- ก่อนหน้านี้ได้สร้างต้นแบบตัวเรนเดอร์เพลงชื่อ Scribe ที่สร้าง SVG จาก JSON
- เดิมทีเป้าหมายคือการสร้างตัวเรนเดอร์เพลงแบบ responsive แต่ความคืบหน้าเป็นไปได้ยากเพราะต้องเขียนเอนจินเลย์เอาต์แบบหลายพาสที่ซับซ้อน
- ต่อมาเมื่อได้นำ CSS Grid มาใช้ในโปรเจกต์ ก็รู้สึกว่าน่าจะเป็นคำตอบของปัญหาเลย์เอาต์ที่ Scribe เจออยู่
คลาส .stave
- บรรทัดห้าเส้นมีลักษณะคล้ายกริด แกนตั้งคือระดับเสียง และแกนนอนคือเวลา
- ในคลาส
.staveจะกำหนดแถวกริดของแกนตั้ง - สร้างแถวกริดขนาดคงที่ด้วยชื่อระดับเสียงมาตรฐาน และใช้ภาพพื้นหลังสำหรับวาดบรรทัดห้าเส้น
- ตัวอย่างแมปแถวสำหรับบรรทัดห้าเส้นของกุญแจซอล:
.stave { display: grid; row-gap: 0; grid-template-rows: [A5] 0.25em [G5] 0.25em [F5] 0.25em [E5] 0.25em [D5] 0.25em [C5] 0.25em [B4] 0.25em [A4] 0.25em [G4] 0.25em [F4] 0.25em [E4] 0.25em [D4] 0.25em [C4] 0.25em ; background-image: url('/path/to/stave.svg'); background-repeat: no-repeat; background-size: 100% 2.25em; background-position: 0 50%; } - จะได้เส้นกริดที่มีชื่อระดับเสียงสำหรับทุกเส้นและทุกช่องว่างของบรรทัดห้าเส้น
วางระดับเสียงบนบรรทัดห้าเส้น
- แต่ละแถวของบรรทัดห้าเส้นสามารถมีระดับเสียงได้หลายค่า
- เพื่อให้วางองค์ประกอบ DOM ไว้ในแถวที่ถูกต้อง จึงใส่ชื่อระดับเสียงในแอตทริบิวต์
data-pitchและใช้ CSS แมปค่าdata-pitchเข้ากับแถวของบรรทัดห้าเส้น.stave > [data-pitch^="G"][data-pitch$="4"] { grid-row-start: G4; } - กฎนี้จะจับระดับเสียงที่ขึ้นต้นด้วย
Gและลงท้ายด้วย4แล้วกำหนดG♭4,G4,G♯4เป็นต้น ไปยังแถว G4 - ต้องทำแบบนี้สำหรับทุกแถวของบรรทัดห้าเส้น
- ตอนนี้ก็สามารถวางสัญลักษณ์บางอย่างลงบนบรรทัดห้าเส้นได้แล้ว
คลาส .bar และจังหวะ
- การจัดการกับจังหวะนั้นซับซ้อนขึ้นอีกหน่อย
- ไม่มีหน่วยย่อยจังหวะขั้นต่ำที่ชัดเจนซึ่งรองรับจังหวะได้ทุกแบบ
- แนวทาง 24 คอลัมน์ต่อ 1 จังหวะ เป็นจุดเริ่มต้นที่ดี เพราะสามารถจัดวางโน้ตเขบ็ตหนึ่งชั้น สองชั้น สามชั้น และโน้ตสามพยางค์ได้อย่างสม่ำเสมอ
- กำหนด 4 จังหวะเป็น 4 × 24 = 96 คอลัมน์กริด และเพิ่มคอลัมน์ที่ต้นและท้าย:
.bar { column-gap: 0.03125em; grid-template-columns: [bar-begin] max-content repeat(96, minmax(max-content, auto)) max-content [bar-end]; } - เพิ่มเส้นห้องด้วย
::beforeและ::afterและวางกุญแจไว้กึ่งกลางด้วยdata-pitch="B4"
วางสัญลักษณ์ตามจังหวะ
- คราวนี้ใช้แอตทริบิวต์
data-beatเพื่อกำหนดจังหวะให้กับองค์ประกอบ และใช้กฎ CSS แมปจังหวะไปยังคอลัมน์กริด - แมป CSS นี้ประกอบด้วยกฎหนึ่งข้อสำหรับทุก ๆ 1/24 จังหวะ
- การใช้ตัวเลือกแอตทริบิวต์แบบขึ้นต้น
^=ทำให้กฎมีความยืดหยุ่นต่อความคลาดเคลื่อน - เมื่อใช้ร่วมกับคลาส
.staveก็สามารถตั้งdata-beatเป็นค่าจังหวะระหว่าง 1 ถึง 5 และตั้งdata-pitchเป็นชื่อโน้ต เพื่อวางสัญลักษณ์ตามทั้งจังหวะและระดับเสียงได้
โน้ตเพลงแบบลื่นไหลและตอบสนองได้
- เมื่อนำหลาย ๆ ห้องมาใส่ในคอนเทนเนอร์ flexbox ก็จะได้โน้ตเพลงแบบ responsive
- ยังมีอีกหลายอย่างที่ขาดอยู่ แต่ก็เป็นฐานเริ่มต้นที่ดี
- การตัดบรรทัดทำได้อย่างสวยงามกว่าตัวเรนเดอร์เพลงออนไลน์ที่มีอยู่แล้วมาก
ระยะห่างระหว่างตัวโน้ต
- หัวโน้ตที่เกิดขึ้นใกล้กันมากในเวลา จะถูกเรนเดอร์ให้อยู่ใกล้กันขึ้นเล็กน้อย
- นี่เป็นผลลัพธ์ที่ละเอียดอ่อนและตั้งใจจาก
column-gapขนาดเล็ก ซึ่งทำหน้าที่เหมือน “อีเธอร์” ของเวลาให้สัญลักษณ์ต่าง ๆ เข้าไปอยู่ในช่อง - ตัวคอลัมน์เองจะมีความกว้างเป็น 0 ถ้าไม่มีหัวโน้ต แต่ระหว่างเหตุการณ์ที่อยู่ห่างกันมากกว่าในเชิงจังหวะ จะมีช่องว่างระหว่างคอลัมน์มากขึ้นด้วย (24 ช่องต่อจังหวะ) จึงเกิดระยะห่างมากขึ้น
- สามารถควบคุมระยะห่างให้สม่ำเสมอได้ด้วยการปรับ margin ของสัญลักษณ์
กุญแจและเครื่องหมายกำหนดจังหวะ
- เหตุผลที่ใช้คลาสแยกสำหรับระยะห่างแนวตั้งและแนวนอน ก็เพื่อให้สามารถสลับอย่างใดอย่างหนึ่งได้โดยไม่ไปกระทบอีกอย่าง
- หากต้องการแสดงทำนองเดียวกันในกุญแจฟา ก็เพียงแทนคลาส
.staveด้วยคลาสbass-staveที่แมปแอตทริบิวต์data-pitchเดิมไปยังแถวของบรรทัดห้าเส้นแบบกุญแจฟา - หากแมป
data-duration="5"ด้วย CSS ไปยัง 120 คอลัมน์กริดของ.barก็จะกำหนดเครื่องหมายกำหนดจังหวะ 5/4 ให้กับบรรทัดห้าเส้นเดียวกันได้
คอร์ดและเนื้อร้อง
- การใช้ CSS Grid ทำให้สามารถจัดแนวสัญลักษณ์อื่น ๆ ภายในกริดของโน้ตเพลงได้ด้วย
- คอร์ด เนื้อร้อง ไดนามิก ฯลฯ สามารถจัดแนวและขยายตามเหตุการณ์ที่มีการกำหนดเวลาได้
ก้านโน้ต
- ก้านโน้ต คอร์ด และเครื่องหมายพักบางตัวที่ยาว สามารถทำให้กินหลายคอลัมน์ได้โดยแมปแอตทริบิวต์
data-durationไปยังค่าgrid-column-endแบบช่วง
การปรับขนาด
- ทั้งระบบกำหนดขนาดด้วยหน่วย em ดังนั้นจึงปรับขนาดได้เพียงแค่เปลี่ยน
font-size
ข้อจำกัดของ Flex และ Grid
- มันไม่ใช่ระบบที่สมบูรณ์แบบ ข้อจำกัดคือ:
- CSS ไม่สามารถวางกุญแจหรือเครื่องหมายกำหนดคีย์ใหม่โดยอัตโนมัติเมื่อมีการขึ้นบรรทัดใหม่
- ไม่สามารถเชื่อม beam ไปยังตัวโน้ตใหม่ในบรรทัดถัดไปได้
- ก้านโน้ตแบบเอียงจะรู้ตำแหน่งที่แม่นยำได้ก็ต่อเมื่อ Grid จัดวางเสร็จแล้ว จึงจัดแนวได้ยาก
- เพื่อให้เสร็จสมบูรณ์จริง ๆ ยังต้องใช้ JavaScript สำหรับเก็บรายละเอียดเล็กน้อย แต่เพราะ CSS จัดการงานเลย์เอาต์ไปเกือบทั้งหมดแล้ว งานเลย์เอาต์ที่ JavaScript ต้องทำจึงลดลงมาก
องค์ประกอบแบบกำหนดเอง
- ได้เขียนอินเทอร์พรีเตอร์ขึ้นมาบนระบบ CSS ใหม่นี้ และห่อไว้ในองค์ประกอบ
- แม้จะยังไม่พร้อมใช้งานระดับ production แต่สามารถเรนเดอร์ลีดชีตแบบ responsive และเขียนโน้ตกลองได้ จึงทั้งน่าสนใจและมีประโยชน์
- เรนเดอร์โน้ตเพลงจากข้อมูลของคอนเทนต์ จากไฟล์ที่ดึงผ่านแอตทริบิวต์
srcหรือจากอ็อบเจ็กต์ JS ที่ตั้งไว้ในพร็อพเพอร์ตี.dataขององค์ประกอบ - ขณะนี้สามารถนำ development build ไปใส่ในหน้าเว็บแล้วทดลองใช้งานได้
แผนต่อไป
- นอกจากการปรับปรุงใน Scribe 0.3 แล้ว ฟีเจอร์ที่อยากศึกษาในระยะยาวมีดังนี้:
- รองรับฟอนต์ SMuFL - เปลี่ยนฟอนต์ที่ใช้กับสัญลักษณ์โน้ตเพลง
- รองรับลำดับแบบซ้อน - เปิดใช้งานเพลงหลายพาร์ต
- เรนเดอร์บรรทัดห้าเส้นแบบแบ่ง - วางหลายพาร์ตในบรรทัดห้าเส้นเดียว
- เรนเดอร์หลายบรรทัดห้าเส้น - วางหลายพาร์ตบนหลายบรรทัดห้าเส้นที่จัดแนวตรงกัน
ความเห็นของ GN⁺
- การเรนเดอร์โน้ตเพลงบนเว็บให้ลื่นไหลและตอบสนองได้ น่าจะมีประโยชน์มากทั้งกับนักดนตรีและคนรักดนตรี เพราะช่วยลดความลำบากจากการต้องซูมเข้าออกไฟล์ PDF ของโน้ตเพลงบนหน้าจอเล็ก
- แนวทางที่ใช้เลย์เอาต์ Grid และ Flex ของ CSS นั้นน่าสนใจ เป็นตัวอย่างที่ดีว่าการใช้ CSS อย่างเดียวก็แก้ปัญหาได้มากพอสมควรโดยไม่ต้องมีเอนจินเลย์เอาต์ที่ซับซ้อน
- อย่างไรก็ตาม ด้วยธรรมชาติของโน้ตเพลง ก็ยังมีข้อจำกัดที่ CSS เพียงอย่างเดียวทำไม่ได้ เช่น การวางกุญแจหรือเครื่องหมายกำหนดคีย์อัตโนมัติเมื่อขึ้นบรรทัดใหม่ หรือการเชื่อม beam แบบอัตโนมัติ ซึ่งเป็นส่วนที่ต้องอาศัย JavaScript มาช่วยเข้าใจบริบททางดนตรี
- มีการทำไปได้ไกลพอสมควรแล้ว ทั้งการเรนเดอร์ลีดชีตและการรองรับโน้ตกลอง จึงดูมีโอกาสที่จะพัฒนาไปจนใช้งานได้ดีในเวลาไม่นาน หากเปิดซอร์สและพัฒนาต่อเนื่อง ก็อาจเป็นทางเลือกที่ดีแทนโปรแกรมเขียนโน้ตเพลงเดิมอย่าง MuseScore ได้
- หากฟีเจอร์ที่วางแผนไว้ เช่น การรองรับฟอนต์ SMuFL การรองรับหลายพาร์ต และการเรนเดอร์หลายบรรทัดห้าเส้น ถูกทำสำเร็จ ความสมบูรณ์ของการแสดงผลโน้ตเพลงก็น่าจะเพิ่มขึ้นอย่างมาก เป็นโปรเจกต์ที่น่าจับตา
2 ความคิดเห็น
ก็คงต้องมีเหตุผลที่ทำแบบนี้อยู่แล้วไม่ใช่เหรอ
ความคิดเห็นบน Hacker News
[...]) เช่น.stave > [data-pitch^="A"][data-pitch$="5"] { grid-row-start: A5; }<scribe-music>ด้วย