รูปภาพแบบ DPI สูงสำหรับความหนาแน่นของพิกเซลที่ไม่แน่นอน

คุณสมบัติอย่างหนึ่งของสภาพแวดล้อมที่ซับซ้อนของอุปกรณ์ในปัจจุบันคือมีความหนาแน่นของพิกเซลหน้าจอที่กว้างมาก อุปกรณ์บางรุ่นมีหน้าจอความละเอียดสูงมาก แต่บางชิ้นจะอยู่หลังหน้าจอ นักพัฒนาแอปพลิเคชันต้องรองรับความหนาแน่นของพิกเซลในระดับต่างๆ ซึ่งเรื่องนี้ค่อนข้างยาก ในเว็บบนอุปกรณ์เคลื่อนที่ ความท้าทายเหล่านี้ขึ้นอยู่กับหลายปัจจัย ดังนี้

  • อุปกรณ์หลากหลายที่มีรูปแบบของอุปกรณ์แตกต่างกัน
  • แบนด์วิดท์ของเครือข่ายและอายุการใช้งานแบตเตอรี่ที่จํากัด

ในแง่ของรูปภาพ เป้าหมายของนักพัฒนาเว็บแอปคือให้บริการรูปภาพที่มีคุณภาพดีที่สุดอย่างมีประสิทธิภาพมากที่สุด บทความนี้จะอธิบายเทคนิคที่มีประโยชน์สำหรับการดำเนินการนี้ในปัจจุบันและอนาคตอันใกล้

หากเป็นไปได้ ให้หลีกเลี่ยงรูปภาพ

ก่อนที่จะเปิดหนอนกระป๋องนี้ โปรดทราบว่าเว็บมีเทคโนโลยีที่มีประสิทธิภาพมากมายที่ส่วนใหญ่ต้องใช้ความละเอียดและ DPI ไม่ต้องอาศัย DPI กล่าวอย่างเจาะจงคือ ข้อความ, SVG และ CSS ส่วนใหญ่จะ "ทำงานได้" เนื่องจากฟีเจอร์การปรับขนาดพิกเซลอัตโนมัติของเว็บ (ผ่าน devicePixelRatio)

อย่างไรก็ตาม คุณไม่สามารถหลีกเลี่ยงภาพแรสเตอร์ได้ ตัวอย่างเช่น คุณอาจได้รับเนื้อหาที่ค่อนข้างยากต่อการเผยแพร่ใน SVG/CSS เพียงอย่างเดียว หรือคุณกำลังจัดการกับภาพถ่าย แม้ว่าคุณจะแปลงรูปภาพเป็น SVG ได้โดยอัตโนมัติ แต่การแปลงรูปภาพเวกเตอร์นั้นก็ดูสมเหตุสมผลสักหน่อย เพราะเวอร์ชันที่ปรับขนาดแล้วมักจะดูไม่ดี

ที่มา

ประวัติความหนาแน่นของการแสดงผลที่สั้นมาก

ในช่วงแรกๆ จอแสดงผลคอมพิวเตอร์มีความหนาแน่นของพิกเซลอยู่ที่ 72 หรือ 96dpi (จุดต่อนิ้ว)

จอแสดงผลจะค่อยๆ มีความหนาแน่นของพิกเซลมากขึ้น ซึ่งส่วนใหญ่เกิดจากกรณีการใช้งานบนอุปกรณ์เคลื่อนที่ ซึ่งโดยทั่วไปแล้วผู้ใช้จะถือโทรศัพท์ไว้ใกล้ใบหน้ามากขึ้น ทำให้มองเห็นพิกเซลได้ชัดเจนขึ้น ในปี 2008 โทรศัพท์ความละเอียด 150dpi เป็นบรรทัดฐานใหม่ แนวโน้มความหนาแน่นของการแสดงผลที่เพิ่มขึ้นยังคงเกิดขึ้นอย่างต่อเนื่อง และโทรศัพท์รุ่นใหม่ในปัจจุบันนี้ จอแสดงผลความละเอียด 300 dpi (แบรนด์ "Retina" ของ Apple)

แน่นอนว่าจอกศักดิ์สิทธิ์คือการแสดงภาพที่พิกเซลมองไม่เห็นเลย สำหรับรูปแบบของอุปกรณ์โทรศัพท์ จอแสดงผล Retina/HiDPI รุ่นปัจจุบันอาจใกล้เคียงกับในอุดมคติดังกล่าว แต่ฮาร์ดแวร์และอุปกรณ์ที่สวมใส่ได้คลาสใหม่ๆ อย่าง Project Glass มีแนวโน้มที่จะเพิ่มความหนาแน่นของพิกเซลต่อไปได้

ในทางปฏิบัติ รูปภาพที่มีความหนาแน่นต่ำควรมีลักษณะเหมือนกับรูปภาพบนหน้าจอใหม่ๆ เหมือนกับบนหน้าจอเดิม แต่เมื่อเทียบกับภาพที่คมชัดและความหนาแน่นสูงที่ผู้ใช้เคยเห็นแล้ว รูปภาพที่มีความหนาแน่นต่ำจะดูไม่ชัดจนทำให้ภาพแตก ต่อไปนี้เป็นการจำลองคร่าวๆ ว่ารูปภาพขนาด 1x จะมีลักษณะอย่างไรบนหน้าจอ 2 เท่า ในทางตรงกันข้าม รูปภาพขนาด 2 เท่าดูดีแล้ว

ลิงบาบูน 1 เท่า
ลิงบาบูน 2 เท่า
ลิงบาบูน! ที่ความหนาแน่นของพิกเซลต่างกัน

Pixels บนเว็บ

ในตอนที่ออกแบบเว็บ หน้าจอ 99% จะมีความละเอียด 96 dpi (หรือดูเหมือนเป็น) และมีบทบัญญัติบางประการสำหรับรูปแบบที่เปลี่ยนแปลงในแนวนี้ เนื่องจากขนาดหน้าจอและความละเอียดที่หลากหลายนั้นแตกต่างกัน เราจึงต้องการวิธีมาตรฐานเพื่อทำให้รูปภาพดูดีในความหนาแน่นและมิติข้อมูลของหน้าจอที่หลากหลาย

เมื่อเร็วๆ นี้ข้อกำหนด HTML ได้แก้ปัญหานี้โดยการกำหนดพิกเซลอ้างอิงที่ผู้ผลิตใช้ในการกำหนดขนาดของพิกเซล CSS

เมื่อใช้พิกเซลอ้างอิง ผู้ผลิตสามารถกำหนดขนาดพิกเซลจริงของอุปกรณ์เทียบกับพิกเซลมาตรฐานหรือพิกเซลที่เหมาะสม อัตราส่วนนี้เรียกว่าอัตราส่วนพิกเซลของอุปกรณ์

การคำนวณอัตราส่วนพิกเซลของอุปกรณ์

สมมติว่าสมาร์ทโฟนมีหน้าจอขนาดพิกเซล 180 พิกเซลต่อนิ้ว (ppi) การคำนวณอัตราส่วนพิกเซลของอุปกรณ์มี 3 ขั้นตอนดังนี้

  1. เปรียบเทียบระยะทางจริงที่ถืออุปกรณ์กับระยะทางของพิกเซลอ้างอิง

    จากข้อมูลจำเพาะ เรารู้ว่าที่ขนาด 28 นิ้ว ความละเอียดสูงสุดคือ 96 พิกเซลต่อนิ้ว แต่เนื่องจากเป็นสมาร์ทโฟน ผู้คนจะถืออุปกรณ์ไว้ใกล้กับใบหน้ามากกว่าการถือแล็ปท็อป ลองประมาณระยะทางที่ 18 นิ้ว

  2. คูณอัตราส่วนระยะทางกับความหนาแน่นมาตรฐาน (96ppi) เพื่อหาความหนาแน่นของพิกเซลที่เหมาะกับระยะทางที่ระบุ

    idePixelDensity = (28/18) * 96 = 150 พิกเซลต่อนิ้ว (โดยประมาณ)

  3. ลองปรับอัตราส่วนของความหนาแน่นของพิกเซลจริงกับความหนาแน่นของพิกเซลที่เหมาะสมเพื่อให้ได้อัตราส่วนพิกเซลของอุปกรณ์

    devicePixelRatio = 180/150 = 1.2

วิธีคำนวณ devicePixelRatio
แผนภาพแสดงพิกเซลเชิงมุมอ้างอิง 1 พิกเซลเพื่อช่วยอธิบายวิธีคำนวณ devicePixelRatio

ดังนั้น ทีนี้เมื่อเบราว์เซอร์ต้องการทราบวิธีปรับขนาดรูปภาพให้พอดีกับหน้าจอตามความละเอียดที่เหมาะสมหรือความละเอียดมาตรฐาน เบราว์เซอร์จึงหมายถึงอัตราส่วนพิกเซลของอุปกรณ์ที่ 1.2 ซึ่งระบุว่าสำหรับทุกพิกเซลที่ต้องการ อุปกรณ์นี้มีพิกเซลจริง 1.2 พิกเซล สูตรที่ใช้ระหว่างพิกเซลอุดมคติ (ตามที่กำหนดโดยข้อกำหนดเว็บ) กับพิกเซลจริง (จุดบนหน้าจออุปกรณ์) มีดังนี้

physicalPixels = window.devicePixelRatio * idealPixels

ที่ผ่านมา ผู้ขายอุปกรณ์มีแนวโน้มที่จะประมาณ devicePixelRatios (DPR) iPhone และ iPad ของ Apple รายงาน DPR อยู่ที่ 1 และ รายงานเทียบเท่า Retina 2 ข้อกำหนดของ CSS แนะนำให้ดำเนินการดังนี้

หน่วยพิกเซลหมายถึงจำนวนเต็มของพิกเซลของอุปกรณ์ ที่ตรงกับพิกเซลอ้างอิงมากที่สุด

เหตุผลหนึ่งที่อัตราส่วนแบบกลมอาจดีกว่านี้ก็เพราะอาจทำให้มีอาร์ติแฟกต์พิกเซลย่อยน้อยลง

อย่างไรก็ตาม ภาพแนวนอนของอุปกรณ์นั้นมีความหลากหลายกว่ามาก และโทรศัพท์ Android มักมี DPR อยู่ที่ 1.5 แท็บเล็ต Nexus 7 มี DPR อยู่ที่ประมาณ 1.33 ซึ่งคำนวณโดยการคำนวณที่คล้ายกับการทดสอบข้างต้น เราคาดว่าจะเห็นอุปกรณ์ที่มี DPR แปรผันมากขึ้นในอนาคต ด้วยเหตุนี้คุณไม่ควรคิดเลยว่าลูกค้าของคุณจะมี DPR ที่เป็นจำนวนเต็ม

ภาพรวมของเทคนิครูปภาพ HiDPI

มีเทคนิคมากมายในการแก้ไขปัญหาในการแสดงรูปภาพที่มีคุณภาพดีที่สุดให้เร็วที่สุด โดยแบ่งเป็น 2 หมวดหมู่ ดังนี้

  1. การเพิ่มประสิทธิภาพภาพเดี่ยวและ
  2. กำลังเพิ่มประสิทธิภาพการเลือกระหว่างรูปภาพหลายรูป

แนวทางที่ใช้ภาพเดียว: ใช้รูปภาพเดียว แต่ใช้ภาพอย่างชาญฉลาด วิธีการเหล่านี้มีข้อเสียซึ่งคุณจะต้องลดทอนประสิทธิภาพอย่างหลีกเลี่ยงไม่ได้ เนื่องจากคุณจะต้องดาวน์โหลดรูปภาพ HiDPI แม้ในอุปกรณ์รุ่นเก่าจะมี DPI ต่ำกว่า ตัวอย่างวิธีการสำหรับกรณี รูปภาพเดี่ยวมีดังนี้

  • รูปภาพ HiDPI ที่บีบอัดแบบหนัก
  • รูปแบบรูปภาพที่ยอดเยี่ยมมาก
  • รูปแบบรูปภาพแบบโพรเกรสซีฟ

วิธีที่ใช้รูปภาพหลายรูปคือ ใช้รูปภาพหลายรูป แต่ควรเลือกรูปที่จะโหลดด้วย วิธีการเหล่านี้ช่วยให้นักพัฒนาซอฟต์แวร์มีค่าใช้จ่ายในการสร้างเนื้อหาเดียวกันหลายๆ เวอร์ชัน แล้วนำไปกำหนดกลยุทธ์การตัดสินใจ ตัวเลือกมีดังต่อไปนี้:

  • JavaScript
  • การนำส่งฝั่งเซิร์ฟเวอร์
  • คำค้นหาสื่อ CSS
  • ฟีเจอร์ในตัวของเบราว์เซอร์ (image-set(), <img srcset>)

รูปภาพ HiDPI ที่บีบอัดแบบหนัก

รูปภาพมีแบนด์วิดท์มากถึง 60% ที่ใช้ในการดาวน์โหลดเว็บไซต์โดยเฉลี่ยแล้ว การแสดงรูปภาพ HiDPI แก่ลูกค้าทั้งหมดทำให้เรา สามารถเพิ่มตัวเลขนี้ สินค้าจะใหญ่ขึ้นเท่าใด

ฉันทำการทดสอบซึ่งสร้างส่วนย่อยของรูปภาพ 1x และ 2 เท่าด้วยคุณภาพ JPEG ที่ 90, 50 และ 20 นี่คือสคริปต์ Shell ที่ฉันใช้ ((กำลังใช้ ImageMagick) ในการสร้างสคริปต์เหล่านั้น

ไทล์ตัวอย่างที่ 1 ไทล์ตัวอย่างที่ 2 ไทล์ตัวอย่างที่ 3
ตัวอย่างรูปภาพที่บีบอัดและความหนาแน่นของพิกเซลแบบต่างๆ

จากการสุ่มตัวอย่างขนาดเล็กที่ไม่เป็นวิทยาศาสตร์ ดูเหมือนว่าการบีบอัดรูปภาพขนาดใหญ่จะให้ผลตอบแทนที่ดีตามขนาด สำหรับตาของผม ภาพ 2 เท่า ที่บีบอัดอย่างหนักดูดีกว่ารูปภาพ 1 เท่า ที่ไม่มีการบีบอัด

แน่นอนว่าการแสดงภาพคุณภาพต่ำที่มีการบีบอัดสูง 2 เท่าไปยังอุปกรณ์ 2 เท่า นั้นแย่กว่าการแสดงผลภาพที่มีคุณภาพสูงขึ้น และวิธีการข้างต้น ทำให้ได้ภาพที่มีคุณภาพ หากเปรียบเทียบคุณภาพ: รูปภาพ 90 รูปกับคุณภาพ: 20 รูป คุณจะเห็นว่าความคมชัดและเม็ดสีลดลง อาร์ติแฟกต์เหล่านี้อาจไม่ได้รับอนุญาตในกรณีที่รูปภาพคุณภาพสูงเป็นกุญแจสำคัญ (เช่น แอปพลิเคชันโปรแกรมดูรูปภาพ) หรือสำหรับนักพัฒนาแอปที่ไม่หวังดี

การเปรียบเทียบข้างต้นทั้งหมดดำเนินการโดยใช้ JPEG ที่บีบอัดไว้ทั้งหมด แต่โปรดทราบว่ามีข้อดีและข้อเสียมากมายระหว่างรูปแบบรูปภาพที่นิยมใช้กันอย่างแพร่หลาย (JPEG, PNG, GIF) ซึ่งนำเราถึงสิ่งต่อไปนี้...

รูปแบบรูปภาพที่ยอดเยี่ยมมาก

WebP เป็นรูปแบบรูปภาพที่น่าสนใจซึ่งบีบอัดได้เป็นอย่างดีในขณะที่รักษาคุณภาพของรูปภาพไว้สูง แน่นอนว่ายังมีที่อื่น ยังไม่พร้อมใช้งานในทุกที่

วิธีหนึ่งคือการตรวจสอบการรองรับ WebP คือผ่าน JavaScript คุณโหลดรูปภาพ 1px ผ่าน data-uri รอให้เหตุการณ์ที่โหลดหรือข้อผิดพลาดเริ่มทำงาน จากนั้นยืนยันว่าขนาดถูกต้อง Modernizr จะมีสคริปต์การตรวจจับฟีเจอร์ดังกล่าวให้ใช้งาน ซึ่งมีให้ใช้งานผ่านทาง Modernizr.webp

อย่างไรก็ตาม วิธีที่ดีกว่าคือทำใน CSS โดยตรงโดยใช้ฟังก์ชันimage() ดังนั้นหากมีรูปภาพ WebP และไฟล์สำรอง JPEG คุณก็เขียนสิ่งต่อไปนี้ได้

#pic {
  background: image("foo.webp", "foo.jpg");
}

วิธีนี้มีปัญหา 2-3 ข้อ อย่างแรกคือ image() ไม่ได้มีการติดตั้งใช้งานกันอย่างแพร่หลายเลย อันดับที่สอง ถึงแม้การบีบอัด WebP จะกระจาย JPEG ในน้ำ แต่ก็ยังได้รับการปรับปรุงค่อนข้างเพิ่มขึ้น โดยมีขนาดเล็กกว่าประมาณ 30% โดยอิงตามแกลเลอรี WebP นี้ ดังนั้น WebP เพียงอย่างเดียวยังไม่เพียงพอที่จะจัดการกับปัญหา DPI ระดับสูง

รูปแบบรูปภาพแบบโพรเกรสซีฟ

รูปแบบรูปภาพแบบโพรเกรสซีฟ เช่น JPEG 2000, Progressive JPEG, Progressive PNG และ GIF มีประโยชน์ (เป็นที่ถกเถียงกัน) ว่ารูปภาพจะเข้าที่ ก่อนที่จะโหลดจนเสร็จ อาจมีค่าใช้จ่ายเกินขนาดอยู่บ้าง แม้ว่ามีหลักฐานที่ขัดแย้งกันเกี่ยวกับเรื่องนี้ Jeff Atwood อ้างว่าโหมดโพรเกรสซีฟ "เพิ่มขนาดรูปภาพ PNG ประมาณ 20% และมีขนาดของรูปภาพ JPEG และ GIF เพิ่มขึ้นประมาณ 10%" อย่างไรก็ตาม Stoyan Stefanov อ้างว่าโหมดโพรเกรสซีฟมีประสิทธิภาพดีกว่า (ในกรณีส่วนใหญ่) สำหรับไฟล์ขนาดใหญ่

เมื่อมองเผินๆ รูปภาพที่โปรเกรสซีฟจะดูน่าสนใจมากในบริบทของการแสดงรูปภาพที่มีคุณภาพดีที่สุดให้เร็วที่สุด แนวคิดคือเบราว์เซอร์สามารถหยุดดาวน์โหลดและถอดรหัสรูปภาพได้เมื่อทราบว่าข้อมูลเพิ่มเติมจะไม่เพิ่มคุณภาพของรูป (เช่น การปรับปรุงความแม่นยำทั้งหมดเป็นแบบพิกเซลย่อย)

แม้ว่าการเชื่อมต่อจะยุติได้ง่าย แต่การเริ่มต้นใหม่มักมีค่าใช้จ่ายสูง สำหรับเว็บไซต์ที่มีรูปภาพจำนวนมาก วิธีการที่มีประสิทธิภาพมากที่สุดคือทำให้การเชื่อมต่อ HTTP รายการเดียวยังคงใช้งานได้ โดยนำการเชื่อมต่อเดิมไปใช้ใหม่ให้นานที่สุดเท่าที่จะเป็นไปได้ หากการเชื่อมต่อสิ้นสุดลงก่อนกำหนดเนื่องจากมีการดาวน์โหลดรูปภาพเพียงพอ เบราว์เซอร์จะต้องสร้างการเชื่อมต่อใหม่ ซึ่งอาจช้ามากในสภาพแวดล้อมที่มีเวลาในการตอบสนองต่ำ

วิธีแก้ปัญหานี้คือการใช้คำขอ HTTP Range ซึ่งช่วยให้เบราว์เซอร์ระบุช่วงของไบต์ที่จะดึงข้อมูลได้ เบราว์เซอร์ที่ชาญฉลาดสามารถสร้างคำขอ HEAD เพื่อเข้าถึงส่วนหัว ประมวลผล ตัดสินใจว่าจะใช้รูปภาพมากน้อยเพียงใด แล้วค่อยดึงข้อมูล แต่น่าเสียดายที่เว็บเซิร์ฟเวอร์รองรับช่วง HTTP ต่ำ ทำให้วิธีนี้ใช้ไม่ได้

สุดท้าย ข้อจำกัดที่ชัดเจนของวิธีการนี้คือคุณจะเลือกไม่ได้ว่าต้องการโหลดรูปภาพใด แต่จะใช้ความละเอียดที่ต่างกันของรูปภาพเดียวกันเท่านั้น ด้วยเหตุนี้ จึงไม่ถือเป็นกรณีการใช้งาน "ทิศทางศิลปะ"

ใช้ JavaScript เพื่อตัดสินใจว่าจะโหลดรูปภาพใด

วิธีแรกที่ชัดเจนที่สุดในการตัดสินใจว่าจะโหลดรูปภาพใดคือการใช้ JavaScript ในไคลเอ็นต์ วิธีนี้ช่วยให้คุณเข้าใจทุกเรื่องเกี่ยวกับ User Agent และทำในสิ่งที่ถูกต้อง คุณกำหนดอัตราส่วนพิกเซลของอุปกรณ์ได้ผ่าน window.devicePixelRatio ดูความกว้างและความสูงของหน้าจอ รวมถึงอาจดักจับการเชื่อมต่อเครือข่ายผ่าน navigator.connection หรือออกคำขอปลอมอย่างที่ไลบรารี foresight.js ทำได้ เมื่อคุณรวบรวมข้อมูลทั้งหมดนี้แล้ว คุณก็ตัดสินใจว่าจะโหลดรูปภาพไหน

มีไลบรารี JavaScript ประมาณ 1 ล้านไลบรารีที่ทำงานในลักษณะเดียวกับด้านบน แต่ก็ไม่มีไลบรารีใดที่โดดเด่นเป็นพิเศษ

ข้อเสียสำคัญอย่างหนึ่งของวิธีนี้คือการใช้ JavaScript จะทำให้การโหลดรูปภาพล่าช้าจนกว่าโปรแกรมแยกวิเคราะห์ล่วงหน้าจะทำงานเสร็จ ซึ่งหมายความว่ารูปภาพจะไม่เริ่มดาวน์โหลดจนกว่าเหตุการณ์ pageload จะเริ่มทำงาน ดูข้อมูลเพิ่มเติมได้ในบทความของ Jason Grigsby

ตัดสินใจว่าจะโหลดรูปภาพใดบนเซิร์ฟเวอร์

คุณเลื่อนการตัดสินใจไปยังฝั่งเซิร์ฟเวอร์ได้โดยเขียนตัวควบคุมคำขอที่กำหนดเองสำหรับแต่ละรูปภาพที่คุณแสดง เครื่องจัดการดังกล่าวจะตรวจหาการสนับสนุนของ Retina ตาม User-Agent (ข้อมูลเดียวที่ส่งผ่านไปยังเซิร์ฟเวอร์) จากนั้นคุณโหลดเนื้อหาที่เหมาะสม (ตั้งชื่อตามแบบแผนที่รู้จัก) โดยขึ้นอยู่กับว่าตรรกะฝั่งเซิร์ฟเวอร์ต้องการแสดงเนื้อหา HiDPI หรือไม่

ทั้งนี้ User-Agent ไม่ได้ให้ข้อมูลเพียงพอที่จะตัดสินใจว่าอุปกรณ์ควรได้รับรูปภาพที่มีคุณภาพสูงหรือต่ำ นอกจากนี้ ไม่ต้องบอกก็รู้ว่าอะไรที่เกี่ยวข้องกับ User-Agent เป็นเรื่องแฮ็ก และควรหลีกเลี่ยงหากเป็นไปได้

ใช้คิวรี่สื่อ CSS

ด้วยการประกาศที่ชัดเจน คิวรี่สื่อ CSS ทำให้คุณสามารถระบุความตั้งใจของคุณ และอนุญาตให้เบราว์เซอร์ทำสิ่งที่ถูกต้องในนามของคุณ นอกเหนือจากการใช้คำค้นหาสื่อที่พบบ่อยที่สุด เช่น การจับคู่ขนาดอุปกรณ์แล้ว คุณยังจับคู่ devicePixelRatio ได้ด้วย คำค้นหาสื่อที่เกี่ยวข้องคืออัตราส่วนพิกเซลของอุปกรณ์ และมีตัวแปรต่ำสุดและสูงสุดที่เกี่ยวข้องตามที่คุณอาจคาดหวัง หากต้องการโหลดรูปภาพแบบ DPI สูงและอัตราส่วนพิกเซลของอุปกรณ์เกินเกณฑ์ คุณอาจดำเนินการต่อไปนี้

#my-image { background: (low.png); }

@media only screen and (min-device-pixel-ratio: 1.5) {
  #my-image { background: (high.png); }
}

จะมีขั้นตอนซับซ้อนขึ้นเล็กน้อยเมื่อมีคำนำหน้าผู้ให้บริการทั้งหมดผสมกัน โดยเฉพาะอย่างยิ่งเนื่องจากมีความแตกต่างในตำแหน่งของคำนำหน้า "min" และ "max" มากเกินไป

@media only screen and (min--moz-device-pixel-ratio: 1.5),
    (-o-min-device-pixel-ratio: 3/2),
    (-webkit-min-device-pixel-ratio: 1.5),
    (min-device-pixel-ratio: 1.5) {

  #my-image {
    background:url(high.png);
  }
}

เมื่อใช้วิธีนี้ คุณจะได้รับประโยชน์จากการแยกวิเคราะห์แบบมองล่วงหน้าซึ่งหมดไปกับโซลูชัน JS นอกจากนี้ คุณยังเลือกเบรกพอยท์ที่ปรับเปลี่ยนตามอุปกรณ์ได้อย่างยืดหยุ่น (เช่น คุณอาจมีรูปภาพ DPI ต่ำ กลาง และสูง) ซึ่งหายไปเมื่อใช้วิธีฝั่งเซิร์ฟเวอร์

ยังคงเป็นเรื่องยุ่งยากเล็กน้อยและนำไปสู่ CSS ที่ดูแปลกๆ (หรือต้องประมวลผลล่วงหน้า) นอกจากนี้ วิธีนี้ยังจำกัดไว้สำหรับพร็อพเพอร์ตี้ CSS เท่านั้น จึงไม่มีวิธีตั้งค่า <img src> และรูปภาพทั้งหมดต้องเป็นองค์ประกอบที่มีพื้นหลัง สุดท้าย การใช้อัตราส่วนพิกเซลของอุปกรณ์อย่างเคร่งครัดอาจช่วยคุณได้ในสถานการณ์ที่สมาร์ทโฟนแบบ High-DPI ดาวน์โหลดชิ้นงานรูปภาพขนาด 2 เท่าขณะที่ใช้การเชื่อมต่อ EDGE นี่ไม่ใช่ประสบการณ์ของผู้ใช้ที่ดีที่สุด

ใช้ฟีเจอร์ใหม่ของเบราว์เซอร์

เมื่อเร็วๆ นี้มีการพูดคุยเกี่ยวกับการรองรับแพลตฟอร์มเว็บสำหรับปัญหาภาพ DPI ระดับสูง เมื่อไม่นานมานี้ Apple ได้นำฟังก์ชัน CSS image-set() ไปใช้กับ WebKit ซึ่งเป็นผลให้ทั้ง Safari และ Chrome สนับสนุนเครื่องมือนี้ เนื่องจากเป็นฟังก์ชัน CSS image-set() จึงไม่ช่วยแก้ปัญหาสำหรับแท็ก <img> ป้อน @srcset ซึ่งช่วยแก้ปัญหานี้ แต่ (ในขณะที่เขียนบทความนี้) ยังไม่มีการติดตั้งใช้งานข้อมูลอ้างอิง (ในขณะนี้) ส่วนถัดไปจะเจาะลึกเกี่ยวกับ image-set และ srcset

ฟีเจอร์ของเบราว์เซอร์สำหรับการรองรับ DPI ระดับสูง

ท้ายที่สุดแล้ว การตัดสินใจว่าคุณจะใช้แนวทางใดนั้นขึ้นอยู่กับข้อกำหนดเฉพาะของคุณ แต่อย่าลืมว่าแนวทางที่กล่าวมาทั้งหมดมีข้อเสีย อย่างไรก็ตาม ในอนาคตเมื่อ image-set และ srcset ได้รับการรองรับในวงกว้างแล้ว ก็จะเป็นทางออกที่เหมาะสมสำหรับปัญหานี้ คราวนี้เรามาพูดถึงแนวทางปฏิบัติแนะนำ ที่จะทำให้เราเข้าใกล้อนาคตอันสมบูรณ์แบบนั้นมากที่สุดได้ไหม

ข้อแรก สองโซลูชันนี้แตกต่างกันอย่างไร image-set() เป็นฟังก์ชัน CSS ที่เหมาะจะใช้เป็นค่าของพร็อพเพอร์ตี้ CSS เบื้องหลัง srcset เป็นแอตทริบิวต์เฉพาะสำหรับองค์ประกอบ <img> ซึ่งมีไวยากรณ์คล้ายกัน แท็กทั้ง 2 อย่างนี้ให้คุณระบุการประกาศรูปภาพได้ แต่แอตทริบิวต์ srcset ช่วยให้คุณกำหนดค่ารูปภาพที่จะโหลดโดยอิงตามขนาดวิวพอร์ตได้ด้วย

แนวทางปฏิบัติแนะนำสำหรับการกำหนดรูปภาพ

ฟังก์ชัน CSS image-set() จะมีคำนำหน้าเป็น -webkit-image-set() ไวยากรณ์ค่อนข้างง่าย โดยใช้การประกาศรูปภาพที่คั่นด้วยคอมมาอย่างน้อย 1 รายการ ซึ่งประกอบด้วยสตริง URL หรือฟังก์ชัน url() ตามด้วยความละเอียดที่เกี่ยวข้อง เช่น

background-image:  -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

สิ่งที่จะบอกเบราว์เซอร์คือมีภาพ 2 ภาพให้เลือก โดยเวอร์ชันหนึ่งได้รับการเพิ่มประสิทธิภาพสำหรับหน้าจอ 1 จอ และอีกหน้าจอหนึ่งเพิ่มประสิทธิภาพสำหรับจอแสดงผล 2 จอ จากนั้นเบราว์เซอร์จะเลือกได้ว่าจะโหลดโปรแกรมใดโดยพิจารณาจากปัจจัยหลายประการ ซึ่งอาจรวมถึงความเร็วของเครือข่าย หากเบราว์เซอร์นั้นฉลาดเพียงพอ (ยังไม่มีการใช้งานอย่างที่ผมทราบ)

นอกจากการโหลดรูปภาพที่ถูกต้องแล้ว เบราว์เซอร์จะปรับขนาดให้สอดคล้องกันด้วย กล่าวอีกนัยหนึ่งคือ เบราว์เซอร์จะถือว่ารูปภาพ 2 ภาพมีขนาดใหญ่เป็น 2 เท่าของรูปภาพ 1 เท่า และจะลดขนาดรูปภาพ 2 เท่าลงด้วยตัวประกอบเป็น 2 เพื่อให้รูปภาพดังกล่าวมีขนาดเท่ากันในหน้าเว็บ

แทนที่จะระบุ 1x, 1.5x หรือ Nx คุณยังสามารถระบุความหนาแน่นพิกเซลของอุปกรณ์ที่ต้องการเป็น dpi ได้ด้วย

วิธีนี้ได้ผลดี ยกเว้นในเบราว์เซอร์ที่ไม่รองรับพร็อพเพอร์ตี้ image-set ซึ่งจะไม่แสดงรูปภาพเลย ข้อนี้ถือว่าเลวร้ายอย่างชัดเจน คุณจึงต้องใช้รายการสำรอง (หรือชุดวิดีโอสำรอง) เพื่อจัดการกับปัญหาดังกล่าว ดังนี้

background-image: url(icon1x.jpg);
background-image: -webkit-image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);
/* This will be useful if image-set gets into the platform, unprefixed.
    Also include other prefixed versions of this */
background-image: image-set(
  url(icon1x.jpg) 1x,
  url(icon2x.jpg) 2x
);

เนื้อหาด้านบนจะโหลดเนื้อหาที่เหมาะสมในเบราว์เซอร์ที่รองรับชุดรูปภาพ และจะกลับไปใช้เนื้อหา 1x หากไม่เป็นเช่นนั้น คำเตือนที่ชัดเจนคือแม้ว่าการรองรับเบราว์เซอร์ของ image-set() จะต่ำ แต่ User Agent ส่วนใหญ่จะได้รับเนื้อหา 1 เท่า

การสาธิตนี้ใช้ image-set() เพื่อโหลดรูปภาพที่ถูกต้อง โดยกลับไปใช้เนื้อหา 1 เท่าหากระบบไม่รองรับฟังก์ชัน CSS นี้

ณ จุดนี้ คุณอาจสงสัยว่าทำไมไม่เพียงแค่ polyfill (หรือ สร้าง JavaScript shim สำหรับ) image-set() แล้วเรียกแค่ 1 วัน ผลที่ได้ การใช้ Polyfill ที่มีประสิทธิภาพสำหรับฟังก์ชัน CSS นั้นค่อนข้างยาก (สำหรับคำอธิบายโดยละเอียด โปรดดูการสนทนาแบบ www นี้)

srcset ของรูปภาพ

ต่อไปนี้เป็นตัวอย่างของ srcset:

<img alt="my awesome image"
  src="banner.jpeg"
  srcset="banner-HD.jpeg 2x, banner-phone.jpeg 640w, banner-phone-HD.jpeg 640w 2x">

อย่างที่คุณเห็น นอกจากการประกาศ x ที่ image-set มีให้แล้ว องค์ประกอบ srcset ยังรับค่า w และ h ที่สอดคล้องกับขนาดของวิวพอร์ตโดยพยายามแสดงเวอร์ชันที่เกี่ยวข้องมากที่สุด ด้านบนจะแสดงแบนเนอร์-phone.jpeg ไปยังอุปกรณ์ที่มีความกว้างวิวพอร์ตต่ำกว่า 640px, แบนเนอร์-phone-HD.jpeg ไปจนถึงอุปกรณ์แบบ DPI สูงสำหรับหน้าจอขนาดเล็ก, banner-HD.jpeg ถึงอุปกรณ์ DPI สูงที่มีหน้าจอใหญ่กว่า 640px และ banner.jpeg เป็นรุ่นอื่นๆ

การใช้ชุดรูปภาพสำหรับองค์ประกอบรูปภาพ

เนื่องจากเบราว์เซอร์ส่วนใหญ่ไม่มีแอตทริบิวต์ srcset ในองค์ประกอบ img คุณจึงอยากแทนที่องค์ประกอบ img ด้วย <div> ด้วยพื้นหลังและใช้วิธีการตั้งค่ารูปภาพ วิธีนี้ใช้ได้ผล แต่ต้องมีคำเตือน ข้อเสียของวิธีนี้คือแท็ก <img> มีค่าความหมายที่ยาว ในทางปฏิบัติ เรื่องนี้มีความสำคัญส่วนมากสำหรับโปรแกรมรวบรวมข้อมูลเว็บและเหตุผลด้านการเข้าถึง

หากคุณใช้ -webkit-image-set ก็อาจจะอยากใช้พร็อพเพอร์ตี้ CSS เบื้องหลัง ข้อด้อยของวิธีนี้คือคุณต้องระบุขนาดรูปภาพ ซึ่งไม่ทราบหากคุณใช้รูปภาพที่ไม่ใช่ 1x แทนที่จะดำเนินการข้างต้น คุณสามารถใช้พร็อพเพอร์ตี้ CSS เนื้อหาได้ดังนี้

<div id="my-content-image"
  style="content: -webkit-image-set(
    url(icon1x.jpg) 1x,
    url(icon2x.jpg) 2x);">
</div>

วิธีนี้จะปรับขนาดรูปภาพตามอุปกรณ์ PixelRatio โดยอัตโนมัติ ดูการใช้งานจริงของเทคนิคข้างต้นจากตัวอย่างนี้ โดยมีขั้นตอนสำรองเพิ่มเติมสำหรับ url() สำหรับเบราว์เซอร์ที่ไม่รองรับ image-set

ชุดวัสดุโพลีฟิลล์

ฟีเจอร์ที่มีประโยชน์อย่างหนึ่งของ srcset คือมาพร้อมกับวิดีโอสำรองที่เป็นธรรมชาติ ในกรณีที่ไม่ได้ใช้แอตทริบิวต์ srcset เบราว์เซอร์ทั้งหมดจะต้องประมวลผลแอตทริบิวต์ src และเนื่องจากนี่เป็นเพียงแอตทริบิวต์ HTML คุณจึงสร้าง polyfills ด้วย JavaScript ได้

Polyfill นี้มาพร้อมกับการทดสอบ 1 หน่วยเพื่อให้มั่นใจว่าใกล้เคียงกับข้อกำหนดมากที่สุด นอกจากนี้ ยังมีการตรวจสอบที่ป้องกันไม่ให้ polyfill ดำเนินการกับโค้ดใดๆ หากตั้งค่า srcset ไว้ตั้งแต่ต้น

โปรดดูการสาธิตการใช้ Polyfill

บทสรุป

ไม่มีวิธีแก้โจทย์ปัญหาภาพ DPI ระดับสูง

วิธีแก้ไขที่ง่ายที่สุดคือการหลีกเลี่ยงรูปภาพทั้งหมด ให้เลือกใช้ SVG และ CSS แทน อย่างไรก็ตาม ก็ไม่สมจริงเสมอไป โดยเฉพาะอย่างยิ่งหากคุณมีรูปภาพคุณภาพสูงบนเว็บไซต์

แนวทางใน JS, CSS และการใช้ฝั่งเซิร์ฟเวอร์ต่างก็มีจุดแข็งและจุดอ่อน อย่างไรก็ตาม วิธีที่น่าสนใจที่สุดคือการใช้ประโยชน์จากฟีเจอร์ใหม่ๆ ของเบราว์เซอร์ แม้ว่าการรองรับ image-set และ srcset ของเบราว์เซอร์ยังไม่สมบูรณ์ แต่ก็มีตัวเลือกสำรองที่สมเหตุสมผลสำหรับวันนี้

โดยสรุปแล้ว คำแนะนำของเรามีดังต่อไปนี้:

  • สำหรับภาพพื้นหลัง ให้ใช้ image-set กับสำรองที่เหมาะสมสำหรับเบราว์เซอร์ที่ไม่รองรับ
  • สำหรับรูปภาพเนื้อหา ให้ใช้ srcset polyfill หรือ เว็บสำรองเพื่อใช้ image-set (ดูด้านบน)
  • สำหรับกรณีต่างๆ ที่คุณยอมลดคุณภาพของรูป ให้ลองใช้รูปภาพขนาด 2 เท่าที่บีบอัดอย่างหนัก