Bỏ qua tới nội dung
Ảnh & media

Tại sao ảnh WordPress bị mờ sau khi up lên (và cách khắc phục)

Chín trên mười trường hợp thì ảnh của bạn không hề bị mờ — WordPress đã thu nhỏ ảnh ngay khi up lên và giờ đang trả về bản đã bị giới hạn. Kể từ WordPress 5.3, bất kỳ

Đã đăng

Chín trên mười trường hợp thì ảnh của bạn không hề bị mờ — WordPress đã thu nhỏ ảnh ngay khi up lên và giờ đang trả về bản đã bị giới hạn. Kể từ WordPress 5.3, bất kỳ ảnh nào rộng hoặc cao hơn 2560px đều bị âm thầm thay bằng một bản -scaled với cạnh dài nhất tối đa 2560px, và chính file nhỏ hơn đó là thứ mà theme của bạn kéo giãn ra để lấp đầy một hero, block full-width, hay màn hình retina. Cách khắc phục là chứng minh đúng là chuyện đó đã xảy ra, rồi hoặc là nâng ngưỡng lên, hoặc là ngừng up những ảnh cần lớn hơn cái khung mà chúng nằm trong.

Vì sao lại xảy ra chuyện này

Khi bạn up một tấm ảnh lên, WordPress chạy wp_create_image_subsizes() (trong wp-admin/includes/image.php). Nếu cạnh dài nhất vượt quá big_image_size_threshold — mặc định là 2560px — thì nó gọi _wp_image_meta_replace_original(), tạo ra your-photo-scaled.jpg, và ghi lại metadata của attachment để kích thước full giờ trỏ vào bản scaled. Ảnh gốc thật của bạn vẫn được giữ trên đĩa dưới tên your-photo.jpg và được tham chiếu bởi wp_get_original_image_path(), nhưng không có gì ở phía front end trả về nó cả. Mọi ảnh con trung gian và mọi ứng viên trong srcset đều được tạo ra từ file scaled 2560px, chứ không phải từ thứ bạn đã up lên.

Và đây là chỗ sinh ra độ mờ. Bạn up một ảnh nét 4000px cho một header tràn viền. WordPress giới hạn nó xuống 2560px. Trên màn hình thường với một khung rộng 1600px thì vẫn ổn. Nhưng trên màn hình retina 2x, trình duyệt cần khoảng 3200 pixel thật để lấp đầy cái khung đó cho sắc nét, mà nó chỉ có 2560, thế là nó phóng to phần còn thiếu lên. Cạnh bị mềm, chữ-trong-ảnh nhòe nhoẹt, đúng y cái triệu chứng “mờ sau khi up lên”.

Nửa còn lại của cùng vấn đề này là srcset chọn một kích thước quá nhỏ. Nếu theme của bạn báo một content width không khớp với cái khung thực tế được render, WordPress sẽ đưa cho trình duyệt một bản trung gian -1024x683 hoặc -1536x1024 và nó bị kéo giãn lên. Cùng nguyên nhân — khung hiển thị lớn hơn số pixel có sẵn — chỉ khác cái kích hoạt.

Kiểm tra xem WordPress thực sự đã trả về cái gì

Đừng đoán. Click chuột phải vào tấm ảnh bị mờ, mở nó trong một tab mới, và nhìn tên file trong URL. Nếu nó kết thúc bằng -scaled.jpg hoặc một hậu tố kích thước kiểu -1024x683.jpg, thì bạn đã xác nhận được rồi: trình duyệt đang render một bản dẫn xuất đã bị thu nhỏ, chứ không phải ảnh bạn up lên. Bạn cũng có thể inspect element và đọc currentSrc để thấy chính xác trình duyệt đã chọn ứng viên nào.

Để thấy toàn cảnh trước khi up lại bất cứ thứ gì, hãy thả tấm ảnh vào công cụ kích thước ảnh WordPress — nó cho bạn thấy mọi bản dẫn xuất mà WordPress sẽ tạo ra từ đúng file đó, bao gồm cả việc liệu một bản -scaled có được tạo ra hay không và ở kích thước bao nhiêu, để bạn biết trước ảnh nguồn của mình có đủ lớn cho vị trí bạn định đặt nó hay không.

Trên server bạn có thể liệt kê mọi file đã bị giới hạn:

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

Cách khắc phục, theo thứ tự

1. Nếu ảnh của bạn thực sự cần lớn hơn 2560px (hero lớn, retina, thư viện chất lượng in ấn), hãy nâng ngưỡng lên:

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

Hoặc tắt hẳn giới hạn đi:

add_filter( 'big_image_size_threshold', '__return_false' );

2. Up lại những ảnh bị ảnh hưởng. Đây là bước mà mọi người hay bỏ qua. Thay đổi ngưỡng chỉ ảnh hưởng đến các ảnh up mới. Những ảnh đã có sẵn trong thư viện vẫn giữ metadata cũ, và regenerate thumbnails sẽ dựng lại các kích thước trung gian từ file -scaled đã bị giới hạn — nó không hề thêm lại độ phân giải đã bị vứt bỏ. Cách chắc ăn là: đổi filter, xóa attachment, rồi up lại.

3. Nếu ảnh nguồn của bạn đã ≤2560px mà vẫn mờ, thì ngưỡng không phải là vấn đề — cái khung hiển thị lớn hơn ảnh. Hãy up một ảnh nguồn lớn hơn, hoặc thu nhỏ cái khung lại. Để nét trên retina, ảnh nguồn của bạn cần rộng khoảng gấp đôi chiều rộng pixel CSS của cái khung mà nó lấp đầy.

4. Chỉ sau khi làm xong những bước trên mới regenerate thumbnails để các kích thước trung gian được nhất quán — nhưng phải hiểu rằng đây là dọn dẹp, chứ không phải phương thuốc chữa.

Những gì không nên làm

Đừng đẩy chất lượng JPEG lên 100. WordPress nén ảnh về mức chất lượng mặc định là 82 thông qua wp_editor_set_quality, và một nửa lời khuyên trên mạng bảo bạn ghi đè nó:

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

Việc đó thì ổn để giảm các artifact do nén, nhưng một tấm ảnh bị kéo giãn, bị phóng to là vấn đề độ phân giải, chứ không phải vấn đề nén. Đi từ 82 lên 100 chỉ làm phình file của bạn ra mà gần như chẳng cải thiện được gì cho độ mờ bạn đang thấy.

Đừng tắt -scaled như một phản xạ. Tắt giới hạn đi (có một công cụ cho việc đó) là lựa chọn đúng nếu bạn phục vụ ảnh trên 2560px — nhưng nếu vấn đề thật của bạn là một bản trung gian quá nhỏ bị kéo giãn, thì tắt nó đi chẳng thay đổi gì cả mà bạn còn mất luôn phần tiết kiệm băng thông một cách vô cớ. Chẩn đoán trước đã.

Đừng đổ lỗi cho cache hay regenerate thumbnails trước. Regenerate đọc từ chính cái nguồn đã bị giới hạn, nên nó không thể khôi phục lại chi tiết được. Xóa cache của CDN hay của trang cũng chẳng động chạm được gì đến một file vốn dĩ đã nhỏ trên đĩa ngay từ đầu.

Đừng vội tìm đến CSS của theme hay object-fit. Không có kiểu styling nào thêm được pixel cả. Nếu file được trả về kết thúc bằng -scaled hoặc một hậu tố kích thước, thì file mới là vấn đề, chứ không phải layout.

Vẫn bí?

Nếu bạn đã xác nhận file được trả về đúng là ảnh up lên ở độ phân giải đầy đủ — không -scaled, không hậu tố kích thước — mà nó vẫn mềm, thì hãy kiểm tra xem có plugin nào (một trình tối ưu ảnh, một CDN resize ảnh, hay một thư viện lazy-load) đang ghi đè src của bạn thành một proxy đã bị nén hay không. Chạy tấm ảnh qua công cụ kích thước ảnh để xác nhận WordPress đáng lẽ phải tạo ra cái gì, rồi so sánh với thứ mà trình duyệt thực sự tải về. Khi hai cái đó không khớp nhau, thì thủ phạm là một thứ gì đó nằm giữa WordPress và trình duyệt.