ข้ามไปยังเนื้อหา
รูปและสื่อ

ทำไมรูปใน WordPress ถึงเบลอหลังอัพโหลด (พร้อมวิธีแก้)

เก้าในสิบครั้ง รูปของคุณไม่ได้เบลอจริง ๆ หรอก — WordPress ย่อขนาดมันตอนอัพโหลดแล้วเสิร์ฟไฟล์ที่ถูกจำกัดขนาดออกมาแทน ตั้งแต่ WordPress 5.3 เป็นต้นมา รูปใด

เผยแพร่แล้ว

เก้าในสิบครั้ง รูปของคุณไม่ได้เบลอจริง ๆ หรอก — WordPress ย่อขนาดมันลงตอนอัพโหลดและตอนนี้กำลังเสิร์ฟไฟล์ก็อปปี้ที่ถูกจำกัดขนาดออกมาแทน ตั้งแต่ WordPress 5.3 เป็นต้นมา รูปที่มีด้านกว้างหรือด้านสูงเกิน 2560px จะถูกแทนที่แบบเงียบ ๆ ด้วยเวอร์ชัน -scaled ที่จำกัดด้านที่ยาวที่สุดไว้ที่ 2560px และไฟล์ที่เล็กลงนี้เองที่ธีมของคุณเอาไปยืดให้เต็ม hero, บล็อกแบบเต็มความกว้าง หรือจอ retina วิธีแก้คือพิสูจน์ให้ได้ก่อนว่าเกิดเรื่องนี้ขึ้นจริง แล้วค่อยเลือกว่าจะเพิ่มค่า threshold หรือเลิกอัพโหลดรูปที่ต้องใหญ่กว่ากล่องที่มันจะไปวางอยู่

ทำไมถึงเกิดขึ้น

เวลาคุณอัพโหลดรูป WordPress จะรัน wp_create_image_subsizes() (อยู่ใน wp-admin/includes/image.php) ถ้าด้านที่ยาวที่สุดเกินค่า big_image_size_threshold — ค่าเริ่มต้นคือ 2560px — มันจะเรียก _wp_image_meta_replace_original(), สร้างไฟล์ your-photo-scaled.jpg ขึ้นมา แล้วเขียนทับ metadata ของ attachment เพื่อให้ขนาด full ชี้ไปยังก็อปปี้ที่ถูกย่อแทน ไฟล์ต้นฉบับจริงของคุณยังถูกเก็บไว้บนดิสก์ในชื่อ your-photo.jpg และอ้างอิงผ่าน wp_get_original_image_path() แต่ไม่มีอะไรบนหน้าเว็บที่เสิร์ฟมันออกมาเลย ทุกขนาดกลาง (intermediate) และทุก candidate ใน srcset ล้วนถูกสร้างขึ้น จากไฟล์ scaled ขนาด 2560px ไม่ใช่จากไฟล์ที่คุณอัพโหลดเข้าไป

นี่แหละที่มาของความเบลอ คุณอัพโหลดรูปคมชัดขนาด 4000px เพื่อทำ header แบบเต็มขอบ WordPress จำกัดมันไว้ที่ 2560px บนจอทั่วไปในกล่องกว้าง 1600px ก็ไม่มีปัญหาอะไร แต่บนจอ retina 2x เบราว์เซอร์ต้องการพิกเซลจริงราว ๆ 3200 พิกเซลเพื่อเติมกล่องนั้นให้คมชัด แต่มีอยู่แค่ 2560 แล้วก็อัพสเกลส่วนที่ขาดขึ้นมาเอง ผลคือขอบนุ่มเบลอ ตัวหนังสือในรูปเลอะ ๆ ตรงกับอาการ “เบลอหลังอัพโหลด” เป๊ะ ๆ

อีกครึ่งหนึ่งของปัญหาเดียวกันนี้คือ srcset เลือกขนาดที่เล็กเกินไป ถ้าธีมของคุณรายงานค่า content width ที่ไม่ตรงกับกล่องที่ถูกเรนเดอร์จริง WordPress จะยื่นไฟล์กลางอย่าง -1024x683 หรือ -1536x1024 ให้เบราว์เซอร์ แล้วมันก็ถูกยืดขึ้นมา สาเหตุเดียวกัน — กล่องแสดงผลใหญ่กว่าพิกเซลที่มี — แต่จุดที่ทำให้เกิดต่างกัน

ตรวจดูว่า WordPress เสิร์ฟอะไรออกมาจริง ๆ

อย่าเดา ให้คลิกขวาที่รูปที่เบลอ เปิดในแท็บใหม่ แล้วดูชื่อไฟล์ใน URL ถ้ามันลงท้ายด้วย -scaled.jpg หรือมี suffix บอกขนาดอย่าง -1024x683.jpg ก็ยืนยันได้เลยว่า เบราว์เซอร์กำลังเรนเดอร์ไฟล์ที่ถูกย่อขนาดลงมา ไม่ใช่ไฟล์ที่คุณอัพโหลด คุณยัง inspect element แล้วอ่านค่า currentSrc เพื่อดูได้ด้วยว่าเบราว์เซอร์เลือก candidate ตัวไหนกันแน่

ถ้าอยากเห็นภาพรวมทั้งหมดก่อนจะอัพโหลดรูปใหม่ ให้ลากรูปเข้าไปใน เครื่องมือดูขนาดรูป WordPress — มันจะแสดงทุกไฟล์ที่ WordPress จะสร้างจากไฟล์นั้น ๆ รวมถึงว่าจะมีการสร้างก็อปปี้ -scaled ขึ้นมาไหมและที่ขนาดเท่าไหร่ คุณจะได้รู้ล่วงหน้าว่าไฟล์ต้นฉบับของคุณใหญ่พอกับที่ที่จะเอาไปวางหรือเปล่า

บนเซิร์ฟเวอร์ คุณสามารถ list ทุกไฟล์ที่ถูกจำกัดขนาดได้ด้วยคำสั่งนี้

find wp-content/uploads -name '*-scaled.*'

วิธีแก้ ทำตามลำดับ

1. ถ้ารูปของคุณจำเป็นต้องใหญ่กว่า 2560px จริง ๆ (hero ขนาดใหญ่, retina, แกลเลอรีคุณภาพระดับพิมพ์) ให้เพิ่มค่า threshold

// functions.php or a small mu-plugin
add_filter( 'big_image_size_threshold', function () {
    return 3840; // longest edge, in pixels
} );

หรือปิดการจำกัดขนาดไปเลยทั้งหมด

add_filter( 'big_image_size_threshold', '__return_false' );

2. อัพโหลดรูปที่มีปัญหาใหม่อีกครั้ง นี่คือขั้นตอนที่คนมักจะข้าม การเปลี่ยนค่า threshold มีผลกับการอัพโหลด ใหม่ เท่านั้น รูปที่อยู่ในไลบรารีอยู่แล้วยังคงเก็บ metadata เดิมไว้ และการ regenerate thumbnails ก็แค่สร้างขนาดกลางขึ้นใหม่จากไฟล์ -scaled ที่ถูกจำกัดขนาดไปแล้ว — มันไม่ได้เพิ่มความละเอียดที่ถูกทิ้งไปกลับคืนมา ทางที่ได้ผลจริงคือ เปลี่ยน filter, ลบ attachment, แล้วอัพโหลดใหม่

3. ถ้าไฟล์ต้นฉบับของคุณเล็กกว่าหรือเท่ากับ 2560px อยู่แล้วแต่ยังเบลอ ปัญหาไม่ได้อยู่ที่ threshold — แต่กล่องแสดงผลใหญ่กว่ารูป ให้อัพโหลดไฟล์ต้นฉบับที่ใหญ่ขึ้น หรือย่อกล่องให้เล็กลง สำหรับความคมชัดระดับ retina ไฟล์ต้นฉบับของคุณต้องมีความกว้างราว ๆ สองเท่าของความกว้างในหน่วย CSS pixel ของกล่องที่มันไปเติม

4. ค่อย regenerate thumbnails หลังทำข้างบนเสร็จแล้วเท่านั้น เพื่อให้ขนาดกลางสอดคล้องกัน — แต่ต้องเข้าใจว่านี่คือการเก็บกวาด ไม่ใช่ยารักษา

สิ่งที่ไม่ควรทำ

อย่าดัน JPEG quality ขึ้นไปที่ 100 WordPress บีบอัดที่ค่า quality เริ่มต้น 82 ผ่าน wp_editor_set_quality และคำแนะนำครึ่งหนึ่งบนเน็ตก็บอกให้คุณ override มัน

add_filter( 'wp_editor_set_quality', function () { return 90; } );

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

อย่าปิด -scaled แบบสะท้อนกลับอัตโนมัติ การปิดการจำกัดขนาด (มีเครื่องมือสำหรับเรื่องนี้) เป็นทางเลือกที่ถูกต้อง ถ้า คุณเสิร์ฟรูปที่ใหญ่กว่า 2560px — แต่ถ้าปัญหาจริงของคุณคือไฟล์กลางที่เล็กเกินไปถูกยืด การปิดมันก็ไม่เปลี่ยนอะไรเลยและคุณยังเสียการประหยัด bandwidth ไปเปล่า ๆ วินิจฉัยให้ชัดก่อน

อย่าโทษ cache หรือ regenerate thumbnails เป็นอย่างแรก การ regenerate อ่านจากไฟล์ต้นทางที่ถูกจำกัดขนาดตัวเดียวกัน มันจึงคืนรายละเอียดกลับมาไม่ได้ การล้าง CDN หรือ page cache ก็ไม่ช่วยอะไรกับไฟล์ที่มันเล็กบนดิสก์มาตั้งแต่แรก

อย่าไปพึ่ง CSS ของธีมหรือ object-fit ไม่มีการจัดสไตล์แบบไหนที่เพิ่มพิกเซลได้ ถ้าไฟล์ที่เสิร์ฟออกมาลงท้ายด้วย -scaled หรือมี suffix บอกขนาด ปัญหาอยู่ที่ไฟล์ ไม่ใช่ layout

ยังแก้ไม่ได้อีก?

ถ้าคุณยืนยันแล้วว่าไฟล์ที่เสิร์ฟออกมาคือไฟล์ที่คุณอัพโหลดแบบเต็มความละเอียด — ไม่มี -scaled, ไม่มี suffix บอกขนาด — แต่มันก็ยังนุ่มเบลออยู่ ให้ลองเช็คว่ามีปลั๊กอิน wordpress (พวก optimizer, CDN image-resizer หรือไลบรารี lazy-load) กำลังเขียนทับค่า src ของคุณให้ชี้ไปยัง proxy ที่ถูกบีบอัดหรือเปล่า ลองรันรูปผ่าน เครื่องมือดูขนาดรูป เพื่อยืนยันว่า WordPress ควรจะ สร้างอะไรออกมา แล้วเทียบกับสิ่งที่เบราว์เซอร์โหลดมาจริง เมื่อสองอย่างนี้ไม่ตรงกัน แสดงว่ามีบางอย่างระหว่าง WordPress กับเบราว์เซอร์ที่เป็นตัวการ