คุณรู้จัก box-shadow ดีแค่ไหน?
(dgerrells.com)- CSS box-shadow โดยปกติเป็นพร็อพเพอร์ตีที่ใช้เพิ่มมิติความลึกให้ UI แต่ถ้านำมาซ้อนกันหลายร้อยถึงหลายพันรายการบน
divเดียว มันจะทำงานคล้าย API สำหรับวาดภาพ ชนิดหนึ่ง - แม้จะมีข้อจำกัดว่าเป็นกล่อง แต่ด้วยมุมโค้งมน การต่อเงาหลายรายการเป็นเชน และการผสมสี·เบลอ·ความโปร่งใส ก็สามารถสร้างจุดวงกลมและเอฟเฟกต์แบบเลเยอร์ได้
- ในการทดลอง blur และความโปร่งใส ทำให้จำนวนรายการที่แอนิเมตได้ลดลงอย่างมาก และเมื่อเอาสองอย่างนี้ออก โน้ตบุ๊ก M1 ก็สามารถวาด box-shadow ได้หลายพันรายการ
- การจำลองลูกบอล พิกเซลของภาพ จุดบนผิวคิวบ์·ทรงกลม และ visualization ที่ตอบสนองต่อเพลง ล้วนถูกทำด้วยวิธีอัปเดต สตริง box-shadow ในทุกเฟรม
- ตอนท้ายแสดงให้เห็นว่า ray tracer แบบใช้ CPU ซึ่งทำงานขนานด้วย Web Workers สามารถเรนเดอร์ฉากได้ด้วย
divเดียวและ box-shadow เท่านั้น
ใช้ box-shadow เหมือนเครื่องมือวาดภาพ
- CSS
box-shadowเป็นรูปแบบหนึ่งของ drop shadow ซึ่งเดิมใช้วาดเงาด้านหลังรูปภาพหรือองค์ประกอบ UI เพื่อเพิ่มมิติความลึก - drop shadow ทั่วไปจะเลื่อน raster ของภาพไปตามแกน x/y แล้ววาดด้วยสีเดียวไว้ด้านหลังต้นฉบับเพื่อสร้างมิติความลึก
- CSS
filter: drop-shadow()รับค่าออฟเซ็ต x/y และสี และรองรับค่า blur เพิ่มเติมได้ด้วย - การซ้อนฟิลเตอร์ drop shadow หลายชั้นช่วยเพิ่มมิติความลึกให้กับองค์ประกอบบนหน้าจอได้
คุณลักษณะและข้อจำกัดของ box-shadow
- “Box” ใน
box-shadowหมายความว่ารูปร่างของเงาถูกจำกัดอยู่กับ bounding box ของคอนเทนเนอร์ - ข้อจำกัดนี้อาจดูจำกัดมาก แต่ UI ส่วนใหญ่ประกอบด้วยกล่อง จึงมีประโยชน์ใน UI จริง
- การทำงานของ CSS box-shadow รองรับการปรับแต่งเชิงคณิตศาสตร์เพื่อวาดกล่องมุมโค้งมนได้อย่างประหยัด และกล่องโค้งมนอาจดูเหมือนวงกลมได้
- นักออกแบบสามารถใช้ box-shadow เพื่อหลีกเลี่ยงการเพิ่มขนาดดาวน์โหลด โดยไม่ต้องใช้ภาพที่เรนเดอร์ไว้ล่วงหน้า
divเดียวสามารถต่อ box-shadow หลายรายการเป็นเชนได้ และตัวอย่างก็ใช้วิธีนี้เพื่อประกอบสีและเงา- สามารถทดลองปรับ border-radius ได้ที่ CSS border-radius generator
วิธีใช้ box-shadow ที่ “ผิด”
- ในงานออกแบบ UI ทั่วไป เงาถูกใช้เป็นองค์ประกอบเสริมร่วมกับระยะว่าง padding typography และ accessibility เพื่อแยกสถานะและการโต้ตอบ
- แต่ box-shadow สามารถถูกมองเหมือน painting API ที่ระบายสี่เหลี่ยมจำนวนเท่าใดก็ได้บนหน้าจอ และเลือกใส่ blur ได้
- การทดลองแรกเริ่มจาก มินิมัลอาร์ตในบทความก่อนหน้า และสร้างผลลัพธ์ทางภาพได้ด้วยการตั้งค่าบล็อกสีแบบง่าย ๆ
- หลังจากนั้น จึงสร้างเครื่องมือ visualization เพื่อดูว่า box-shadow จำนวนมากขึ้น blur และความโปร่งใสส่งผลต่อประสิทธิภาพอย่างไร
- สร้างสตริง box-shadow ขนาดใหญ่แล้วตั้งค่าให้
divเดียว - แอนิเมชันเริ่มจากการเปลี่ยนสตริง box-shadow ทุก 300ms แล้วปล่อยให้
transition: allจัดการ - วิธีนี้มี jank และช้ากว่าการตั้งค่า box-shadow ในทุกเฟรม
- สร้างสตริง box-shadow ขนาดใหญ่แล้วตั้งค่าให้
- ตัวอย่าง box-shadow 100 รายการให้แตะเพื่อรีมิกซ์พาเลตสี และแสดงประวัติพาเลต 10 รายการล่าสุดทางซ้าย
- เมื่อใช้ blur จำนวนรายการที่แอนิเมตได้ลดลง และสีโปร่งใสก็ทำให้จำนวนที่วาดได้ลดลงอย่างมากเช่นกัน
- ขนาดของ
divก็ส่งผลต่อประสิทธิภาพ และระหว่างแอนิเมชันดูเหมือนว่าจะมี software rasterizer เข้ามาเกี่ยวข้อง - หากไม่ใช้ความโปร่งใสและ blur โน้ตบุ๊ก M1 สามารถวาด box-shadow ได้หลายพันรายการ
สร้างการจำลองลูกบอลด้วย box-shadow
- box-shadow หมุนไม่ได้ แต่ใช้ border-radius ทำให้ดูเหมือนวงกลมได้
- เงาทรงกลมหลายรายการถูก扱เหมือนลูกบอล และสเกลขนาดตามค่า z เพื่อสร้างมิติ 3D ปลอม
- การทำงานคืออัปเดตสถานะเกมใน
requestAnimationFrameและตั้งค่า สตริง box-shadow ขนาดใหญ่ให้divในทุกเฟรม - กระบวนการเรนเดอร์เป็นดังนี้
- เรียงลูกบอลตามค่า z
- คำนวณขนาดตามค่า z
- แปลงตำแหน่ง x/y, spread และสีของลูกบอลแต่ละลูกให้เป็นรายการ box-shadow
- เชื่อมรายการด้วยคอมมาให้เป็นสตริงเดียว
- ตัวอย่างลูกบอล 50 ลูกให้ลากเพื่อขยับลูกบอล และทำให้เด้งอยู่ในกล่อง
- การสเกลแบบ 3D ปลอมช่วยให้เกิดมิติความลึกได้ระดับหนึ่ง แต่เมื่อลูกบอลเข้าใกล้ “กล้อง” วงกลมอาจดูแตกได้
- เพราะ
divที่ใช้เป็นฐานสร้าง box-shadow เล็กเกินไปเมื่อเทียบกับวิธีสเกล - แก้ได้ด้วยการเพิ่มขนาดคอนเทนเนอร์ แต่ยิ่งคอนเทนเนอร์ใหญ่ ประสิทธิภาพก็ยิ่งช้าลง
- เพราะ
- เวอร์ชันที่ทำให้ลูกบอลชนกันใช้การตรวจชนแบบ
n^2และสะท้อนเฉพาะความเร็วเมื่อชนกัน- ไม่ได้จำลองปฏิสัมพันธ์ทางฟิสิกส์จริง
- เพื่อให้ดูง่าย จึงตรึงตำแหน่ง z ให้เป็น 2D
- ยังสร้างตัวอย่างที่ลูกบอลพยายามกลับไปยังตำแหน่งเริ่มต้นแบบสุ่ม และเมื่อใช้แรงสัมผัสดึงลูกบอลเข้ามา จะเกิดเอฟเฟกต์เหมือนดึงชิ้นส่วนออกจากฟองน้ำ
ภาพและ point cloud 3D
- การทดลองถัดมาคือการแมปพิกเซลของภาพเป็นจุดบนระนาบ 2D แล้ววาดแต่ละจุดด้วย box-shadow
- อ่านตำแหน่งและสีของพิกเซลภาพแล้วเก็บไว้ใน
state.particlesโดยใช้แต่ละพิกเซลเป็นอนุภาคหนึ่งตัว - ซอร์สอยู่ใน ตัวอย่าง CodeSandbox
- ตัวอย่างนี้เรนเดอร์ box-shadow หลายพันรายการในพื้นที่ 3D และเมื่อลากก็โต้ตอบได้เหมือนทำให้ภาพแตกกระจาย
- เวอร์ชัน live แบบหมุนอัตโนมัติให้ใช้งานผ่าน
/box-shadow/v3?width=80&size=5&autoRotate=1พร้อมคำเตือนเรื่องการใช้แบตเตอรี่เมื่อรัน - ตัวอย่างที่มีจำนวนสูงกว่านั้นอยู่ที่ประมาณ 12,000 box-shadow และในระดับนี้เริ่มเห็นอาการกระตุก
- บน M1 รองรับ box-shadow ได้จำนวนมากมาก แต่เดสก์ท็อป, iPhone และ Android รุ่นเก่าไม่สามารถรับระดับเดียวกันได้
วางจุดบนผิวคิวบ์และทรงกลม
- มีการทดลองต่อด้วยการฉายจุดให้กระจายสม่ำเสมอบนผิว mesh
- คิวบ์ทำโดยวางจุดตามแต่ละหน้า
- สมมติว่าทุกด้านมีความยาวเท่ากัน
- วนผ่านแต่ละหน้าแล้วเติมจุดตามขนาดที่กำหนด
- เพิ่มจำนวนจุดเพื่อเพิ่มความละเอียดของคิวบ์ได้
- ตัวอย่างคิวบ์โต้ตอบได้ด้วยการแตะ และเพิ่มแสงเล็ก ๆ ที่ตามตำแหน่งเมาส์เพื่อเพิ่มมิติความลึก
- การคำนวณแสงไม่ถูกต้องแม่นยำ และใช้ “magic constants” หลายค่า
- ฟังก์ชันแมปอนุภาคของคิวบ์และตัวช่วยคณิตศาสตร์หลายตัวได้มาด้วย gypity
- ผลลัพธ์แรกเป็นการกระจายแบบสุ่ม
- ความพยายามครั้งที่สองจึงได้การวางจุดสม่ำเสมอบนผิวคิวบ์
- การกระจายสม่ำเสมอบนผิวทรงกลมซับซ้อนกว่า จึงใช้วิธี spiral discretization
- แนวคิดคือวางจุดให้สม่ำเสมอบนเส้นที่พันรอบทรงกลมจากล่างขึ้นบน
- เปรียบได้กับเชือกที่พันรอบลูกบอล
- ใช้การแมปเชิงเส้นนี้เชื่อมกับอาร์เรย์ค่าความถี่ของเพลงเพื่อสร้างแอนิเมชันด้วย
- จุดต่าง ๆ ใช้แอนิเมชันพาเลตแบบไล่สีที่ interpolate ตามเวลา และส่วนนี้ยากกว่างานก่อนหน้า
- ตัวอย่างทรงกลมที่มีจำนวนรอบน้อยแสดงให้เห็นว่าเกลียวประมาณการกระจายสม่ำเสมอได้ แต่ถ้าจำนวนรอบไม่พอ ภาพจะดูแตก
ray tracer ด้วย box-shadow
- ความคิดที่ว่าถ้าวาดสามเหลี่ยมด้วย box-shadow ได้ ก็แทบจะสร้าง CGI ได้ทุกอย่าง นำไปสู่การทดลอง ray tracing
- เป้าหมายคือการวาดฉากด้วย
divเดียวและ box-shadow เท่านั้น - ตัวอย่างหลังจากนั้นใช้ทั้งการเรนเดอร์ live ความละเอียดต่ำและการเรนเดอร์ภาพความละเอียดสูง โดยมีบางตัวอย่างที่ไม่จำกัดความละเอียด จึงต้องระวังเมื่อรัน
- ray tracer และ ray marcher เป็นวิธีสร้างภาพที่แม่นยำแต่ช้า และใช้กันทั่ววงการ CGI
- ที่นี่ใช้ tracer แบบใช้ CPU แทน GPU
- เพราะถ้าใช้ GPU จุดประสงค์ของการทดลองจะอ่อนลง และการทำงานก็ยากขึ้นด้วย
- tracer แรกถูกทำให้เรนเดอร์เฉพาะทรงกลม และ data model ของฉากประกอบด้วยกล้อง ทรงกลมหลายลูก และข้อมูลวัสดุ
- โค้ดเริ่มต้นที่ได้จาก gypity ทำงานแบบเรียลไทม์ได้ แต่มีปัญหา
- ในการปรับแต่ง sampling ขาดโค้ดที่บวก bias coefficient ที่จำเป็น
- มีคอมเมนต์ที่เกี่ยวข้องอยู่ แต่ไม่มีบรรทัดจริง และแก้ไขหลังจากไปค้นหาเทคนิค sampling
progressive rendering และ Three.js
- หลังจากนั้นโค้ดเปลี่ยนเป็นโครงสร้าง progressive rendering
- progressive rendering แบ่งการคำนวณ ray จำนวนมากออกไปทำหลายเฟรม เพื่อแสดงกระบวนการที่ภาพเรนเดอร์ค่อย ๆ เข้าใกล้ “ground truth”
- โครงสร้างนี้เข้ากันได้ดีกับฟังก์ชันขยับกล้องแบบโต้ตอบ
- กล้องและ orbit controls ใช้ไลบรารี Three.js
- เพื่อไม่ต้องทำคณิตศาสตร์เมทริกซ์และการรองรับมือถือเอง
- เวอร์ชันนี้เรนเดอร์ได้เฉพาะทรงกลม และทุกองค์ประกอบในฉากเป็นทรงกลมที่ถูกสเกลในระดับหนึ่ง
- ตัวอย่าง โดยพื้นฐานรันที่ประมาณ มากกว่า 6% เล็กน้อย ของความละเอียดเต็ม
- ยิ่งอยู่ห่างจากหน้าจอ ฉากความละเอียดต่ำยิ่งดูคมขึ้น และเมื่อมองใกล้ ความละเอียดที่ไม่พอก็ชัดเจนขึ้น
- สามารถปรับ resolution scale,
pixelSize, จำนวน ray bounce และจำนวน sample สูงสุดได้ด้วย query parameter - เพิ่มความละเอียดและ sample แล้วผลลัพธ์ดีขึ้น แต่จะช้ามาก
การทำงานของ tracer และคอขวดด้านประสิทธิภาพ
- การเรนเดอร์ใช้วิธีมาตรฐานคือยิง ray จากมุมมองกล้องสำหรับแต่ละพิกเซล คำนวณสี แล้วเฉลี่ยข้ามเฟรม
- เนื่องจาก tracer ใช้ Three.js จึงมีการสร้างอ็อบเจ็กต์ใหม่จำนวนมากและกลายเป็น garbage อย่างรวดเร็ว
- มีการลองนำอ็อบเจ็กต์กลับมาใช้ซ้ำบางส่วน แต่ถ้าจะรีดประสิทธิภาพให้ถึงที่สุด การไม่ใช้ Three.js น่าจะดีกว่า
- ตาม profiler แล้ว garbage collection ไม่ได้สร้างความแตกต่างมากนัก จึงยังคงใช้ Three.js ต่อไป
- แนวคิดพื้นฐานของ tracer คือ ray กระทบวัตถุในฉากแล้วเด้งไปจนกว่าจะถึงแสง และคืนค่าสีตามคุณสมบัติของวัตถุและแสง
- เหตุผลที่ต้องยิง ray จำนวนมากคือ บาง ray ไม่ถึงแหล่งกำเนิดแสง และบาง ray ถึง
- tracer นี้ใช้โมเดลแสงแบบง่าย
- ไม่มี BRDF ที่ถูกต้องตามฟิสิกส์
- ไม่มี texture
- ไม่มี subsurface scattering
- ใช้แสง diffuse แบบง่ายและ specular reflection
- แหล่งเรียนรู้ ray tracing ดูได้ที่ Ray Tracing in One Weekend
- ต่อมาสามารถเรนเดอร์ plane ได้ และหลังจากแก้ข้อผิดพลาดที่ bit manipulation กับค่า floating point ก็เรนเดอร์สี่เหลี่ยมแสงได้ด้วย
ทำงานขนานด้วย Web Workers
- การปรับปรุงประสิทธิภาพเลือกใช้มัลติเธรดด้วย Web Workers แทนการแก้ปัญหา GC ของ Three.js
- ray tracing เหมาะกับการทำงานขนาน เพราะแต่ละการคำนวณคืนผลลัพธ์เดียวและไม่มี side effect
- worker manager สร้าง pool ของ worker และมีเมธอด
render,updateSceneเพื่อให้เปลี่ยนฉากระหว่างรันไทม์ได้ - โค้ด worker เป็นรูปแบบที่คัดลอก tracer เดิมมา และต้องมี data marshaling เพื่อเปลี่ยนฉาก
- เพื่อลด overhead ของ
postMessageworker จะเรนเดอร์ทั้งเฟรม ไม่ใช่พิกเซลเดียว - ตัวอย่างเต็มหน้าจอ ทำงานเร็วกว่าเดิมมาก
- ข้อเสียคือเมื่อกล้องหรือฉากเปลี่ยน ผลลัพธ์ ray ที่คำนวณไว้เดิมทั้งหมดจะใช้ไม่ได้ และหน้าจออาจดูดำ
- วิธีแก้คือเมื่อเกิด input event ให้ตั้งแฟล็ก
isDirtyแล้วให้ update loop ตัดสินใจว่าจะล้างเฟรมหรือไม่ - มีกรณีที่ worker อาจส่งเฟรมที่คำนวณจากข้อมูลฉากเก่ามาทันทีหลังอัปเดตฉาก
- ใส่ timestamp หรือ scene id ใน
postMessageเพื่อทิ้งได้ - ในการทดลองปล่อยไว้ เพราะข้อมูล ray ที่ผิดของหนึ่งเฟรมจะถูกเฉลี่ยไปในไม่ช้า
- ใส่ timestamp หรือ scene id ใน
- สุดท้าย ray tracing ด้วย box-shadow ทำงานได้เพียงพอ และซอร์สอยู่ที่ CodeSandbox
- สถิติประสิทธิภาพแสดงค่าประมาณจำนวน ray รวม จำนวน sample และความละเอียดการเรนเดอร์
- sample เริ่มต้นหยุดที่ 1200 แต่ปรับตั้งได้
- มีโมเดลแสงทางเลือกที่เร็วกว่าแต่แม่นยำน้อยกว่าให้ toggle ได้
- ข้อมูลฉากเป็น JSON จึงแก้ไขเองได้ง่าย
สรุป: ทำได้ แต่ไม่แนะนำให้ใช้จริง
- ด้วย box-shadow ของ
divเดียว สามารถวาดได้ตั้งแต่การจำลองลูกบอล point cloud อนุภาคจากภาพ ผิวคิวบ์·ทรงกลม ไปจนถึงฉาก ray tracing - อย่างไรก็ตาม การทดลองทั้งหมดใกล้เคียงกับวิธี “ที่ไม่ควรทำ” ซึ่งไม่มีกรณีใช้งานจริง
- ยังมีพื้นที่ให้ปรับปรุงได้ เช่น เพิ่มการโหลด triangle mesh, acceleration structure และโมเดลแสงที่แม่นยำขึ้น
- gypity เคยตอบในตอนแรกว่า box-shadow ray tracer เป็นไปไม่ได้ แต่ก็มีตัวอย่างจริงถูกสร้างขึ้นแล้ว
- CSS ไม่ได้ตรงไปตรงมาเสมอไป แต่บางครั้งก็มีความเป็นไปได้แปลก ๆ ที่เห็นได้ชัดว่าทำงานได้
1 ความคิดเห็น
ความคิดเห็นใน Hacker News
ถ้าใช้สีโปร่งใสจะช้าลง เพราะทำให้ การประมวลผลแบบแบตช์ของการวาดด้วย GPU ถูกจำกัด
การวาดแบบทึบแสงสามารถใช้ depth buffer เพื่อสลับลำดับได้ตามใจ และเพิ่มการประมวลผลแบบแบตช์ให้มากที่สุดได้ แต่กรณีโปร่งใสต้องวาดตาม ลำดับแบบ painter เพื่อให้การ blend ถูกต้อง
โดยเฉพาะ Chromium จะพยายามลดจำนวน layer ทั้งหมดให้เหลือน้อยที่สุด แล้ว render แต่ละ layer เป็น pixel map จากนั้นจึง composite layer ที่มองเห็นในแต่ละเฟรมให้เป็นภาพสุดท้าย ในความเป็นจริงมีหลาย layer ที่พิกเซลไม่ได้เปลี่ยน แค่ตำแหน่งขยับ ดังนั้นแต่ละเฟรมแค่ composite ก็พอ ไม่ต้อง rasterize ใหม่
ถ้ามี box-shadow แบบไม่มีความโปร่งใสจำนวนมาก Chromium อาจ rasterize ทั้งหมดเป็น layer เดียวได้ในครั้งเดียว แต่ถ้ามี box-shadow แบบโปร่งใสจำนวนมาก ก็อาจทำแต่ละอันเป็น layer แยก ในกรณีนี้อาจไม่มีประสิทธิภาพ แต่ถ้า box-shadow กึ่งโปร่งใสเหล่านั้นต้องเคลื่อนไหวแยกกันบนหน้าเว็บ ก็เป็นวิธีที่จำเป็นเช่นกัน
เริ่มจาก buffer ที่โปร่งใสหมด (α=0.0) แล้ววนผ่านแต่ละพื้นผิวจากหน้าไปหลัง สำหรับแต่ละพิกเซลก็ blend พิกเซลใหม่เข้ากับ buffer เดิมตามสัดส่วน
1.0-buffer.αก็พอ ถ้าbuffer.α == 1.0ก็ข้ามได้ทั้งหมดเหมือน depth bufferอย่างไรก็ตาม คณิตศาสตร์ในกรณีที่มีวัตถุโปร่งใสอีกชิ้นอยู่หลังวัตถุโปร่งใสต้องตรวจสอบอีกครั้ง และกรณีที่พื้นผิวซ้อนกันเป็นวงวนหรือทะลุผ่านกันก็ค่อนข้างยุ่งยาก
นี่ก็เป็นเหตุผลที่โดยทั่วไปการวาดสามเหลี่ยมให้มากขึ้นมักดีกว่าการใช้ texture โปร่งใส
การ render แบบทึบแสงสามารถวาดจากหน้าไปหลังได้ จึง render เฉพาะสิ่งที่มองเห็นจริงใน framebuffer สุดท้าย และจำนวนพิกเซลหลัง depth pass จะแปรตามขนาด framebuffer
การ render แบบโปร่งใสต้องวาดจากหลังมาหน้า และต้อง render ฉากจำนวนมากที่ภายหลังจะถูก polygon อื่นบังบางส่วน ดังนั้นจำนวนพิกเซลที่ผ่าน shader pipeline อาจเพิ่มขึ้นจนแปรตามขนาดของ mesh
ถ้าเป็นองค์ประกอบที่ไม่ซ้อนทับกัน ความโปร่งใสน่าจะทำให้ช้าลงแทบไม่มีนัยสำคัญ เพราะอย่างไรก็ต้องแตะแต่ละพิกเซลหนึ่งครั้งอยู่แล้ว สิ่งที่เปลี่ยนไปมีแค่สูตรของ shader
ถ้าการวาดแบบทึบแสงอยู่ทับการวาดอื่น ในกรณีดีที่สุดสามารถตัดงานวาดที่ซ้อนกันทั้งหมดออกได้ และแม้ในกรณีแย่ที่สุดก็ใช้ bandwidth เท่ากับการวาดแต่ละครั้งเท่านั้น
ในกรณีความโปร่งใส ถ้าไม่สามารถรวมการคำนวณเข้าด้วยกันได้ ก็ต้องอ่านพื้นที่ที่ซ้อนกันทั้งหมดอีกครั้ง ดังนั้นทุกครั้งที่วาดแบบโปร่งใส bitmap ที่มีขนาดอย่างน้อยสองเท่าของ framebuffer สุดท้ายจะต้องวิ่งผ่าน memory bus
ถ้าคิดว่าอุปกรณ์มือถือจำนวนมากเคยมี memory bandwidth ไม่พอแม้กระทั่งสำหรับ blit ทั้งหน้าจอสองครั้งภายในเวลาที่ต้องรักษา 60fps นี่ก็เป็นปัญหาใหญ่ทีเดียว
เป็นการสำรวจที่สนุกจริง ๆ
คำว่า layering เป็นคำสำคัญจริง ๆ โปรเจกต์เงาข้อความที่ผมทำเมื่อ 14 ปีก่อน ซึ่งให้เอฟเฟกต์ที่ไร้สาระแต่บางทีก็ดูเท่ ก็มี layering เป็นแกนหลักเช่นกัน: https://paulirish.github.io/mothereffingtextshadow/
เพิ่งมารู้สึกอายตอนย่อหน้าสุดท้ายว่าคำว่า
gypityหมายถึง ChatGPTgypแฮ็กแบบเก่า ๆ ที่ ไม่ค่อยมีประโยชน์จริง นี่ยินดีต้อนรับเต็มที่ แต่ก็ควรจำไว้ว่ามี canvas อยู่แล้ว และงานแบบนี้ canvas ทำได้ง่ายกว่า เร็วกว่า และดีกว่า
visualization เพลงนี่เจ๋งจริง ๆ คิดถึงยุค Winamp เก่า ๆ มาก ตอนที่เปิดเพลงแล้วเปิด visualization เต็มหน้าจอ
อยากให้เครื่องเล่นเพลงสตรีมมิงสมัยนี้มีฟีเจอร์แบบนี้บ้าง
แต่ก็จริงที่เครื่องเล่นเพลงสตรีมมิงเป็นซอฟต์แวร์ที่มีแต่ฟีเจอร์พื้นฐานจริง ๆ
สุดท้าย Firefox กับ Chrome ก็ยัง render 1px box-shadow ต่างกันที่การซูมเบราว์เซอร์ 150%
คงต้องฝากความหวังไว้กับ Baseline 2025
อยากรู้ว่ามีแหล่งอ้างอิงให้อ่านเพิ่มเกี่ยวกับ “แฮ็กทางคณิตศาสตร์สำหรับวาดกล่องมนที่คนทำ UI ชอบให้ถูกมาก ๆ” ไหม
https://www.folklore.org/Round_Rects_Are_Everywhere.html
http://wg20.criticalcodestudies.com/index.php?p=/discussion/...
สำหรับแนวทาง 3D accelerated สมัยใหม่ มีบทความที่ใช้ SDF อย่างที่อีกคนคาดไว้
https://mortoray.com/quickly-drawing-a-rounded-rectangle-wit...
เป็นแฮ็กประเภทที่ผมชอบเลย
แทบจะตรงข้ามกับบทความของ Josh Comeau ที่เคยอ่านในหัวข้อนี้: https://www.google.com/search?q=josh+comeau+shadows
บทความยอดเยี่ยมที่อาจเป็นบทความดีที่สุดที่ผมอ่านปีนี้ กลับจบด้วย your welcome แทนที่จะเป็น
you'reต้องรีบแก้แล้วล่ะ หรือไม่ก็ผมอาจไม่เข้าใจมุก
ตลอด 30 ปีที่ผ่านมา ผมเขียนโปรแกรมได้ดีขึ้น แต่แทบไม่ได้ทำ graphics เพราะไม่ชอบเกม
ตอนนี้มองว่านั่นเป็นความผิดพลาดมหาศาล และพยายามตามให้ทันมานานกว่าหนึ่งปีแล้ว แต่มันยากจริง ๆ