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

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

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

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

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

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

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

ข้อมูลเบื้องต้น

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

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

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

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

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

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

พิกเซลบนเว็บ

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

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

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

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

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

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

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

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

    idealPixelDensity = (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 และ 2x ที่มีคุณภาพ JPEG ที่ 90, 50 และ 20 ต่อไปนี้คือสคริปต์ Shell ที่ฉันใช้ (ใช้ ImageMagick) ในการสร้าง

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

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

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

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

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

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

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

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

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

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

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

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

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

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

วิธีแก้ปัญหาอย่างหนึ่งคือการใช้คำขอ 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 ได้ด้วย คําค้นหาสื่อที่เกี่ยวข้องคือ device-pixel-ratio และมีตัวแปรขั้นต่ำและสูงสุดที่เกี่ยวข้องตามที่คาดไว้ หากต้องการโหลดรูปภาพที่มี 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> และรูปภาพทั้งหมดต้องเป็นองค์ประกอบที่มีพื้นหลัง สุดท้ายนี้ หากคุณยึดอัตราส่วนพิกเซลของอุปกรณ์อย่างเคร่งครัด คุณอาจประสบกับสถานการณ์ที่สมาร์ทโฟนที่มี DPI สูงดาวน์โหลดเนื้อหารูปภาพขนาดมหึมาได้ถึง 2 เท่าขณะใช้การเชื่อมต่อ EDGE ซึ่งไม่ใช่ประสบการณ์การใช้งานที่ดีที่สุด

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

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

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

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

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

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

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

วิธีนี้ได้ผลดี ยกเว้นในเบราว์เซอร์ที่ไม่รองรับพร็อพเพอร์ตี้ 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 ส่วนใหญ่จะได้รับชิ้นงานขนาด 1x

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

ณ จุดนี้ คุณอาจสงสัยว่าทำไมไม่ใช้ polyfill (นั่นคือสร้าง shim ของ JavaScript สําหรับ) image-set() แล้วจบเลย แต่กลับพบว่าการใช้ 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 สำหรับสิ่งอื่นๆ

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

เนื่องจากเบราว์เซอร์ส่วนใหญ่ไม่ได้ใช้แอตทริบิวต์ 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>

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

ชุด</b>โพลีฟิล

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

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

ต่อไปนี้คือการสาธิตการใช้งาน polyfill

บทสรุป

ปัญหารูปภาพที่มี DPI สูงนั้นไม่มีวิธีแก้ปัญหาที่ได้ผลแน่นอน

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

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

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

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