Давно интересовал вопрос адаптивности изображений с помощью атрибута srcset. Как это работает и как правильно настроить. Пришло время это выяснить.
Теория srcset
В атрибуте srcset выводятся все варианты миниатюры и с помощью атрибута sizes (который в свою очередь связан с определенной функцией WP) определяется какое изображение получить.
По умолчанию функция wp_calculate_image_sizes работает так: определяет ширину viewport и под эту ширину выдает нужную миниатюру. Но складывается ощущение, что обычно подбираются миниатюры с запасом в 2 раза по ширине.
Структура атрибута sizes:
sizes="(max-width: $width px) 100vw, $width px"
- max-width: $width px — максимальная ширина экрана
- 100vw — ширина области просмотра
- $width px — необходимый размер миниатюры
Запросы на формирование srcset:
<?php
$image_id = get_post_thumbnail_id();
$img_src = wp_get_attachment_image_url( $image_id, 'large' );
$img_srcset = wp_get_attachment_image_srcset( $image_id, 'full' );
$img_alt = get_post_meta( $image_id, '_wp_attachment_image_alt', true ); ?>
...
<img src="<?php echo esc_attr( $img_src ); ?>" srcset="<?php echo esc_attr( $img_srcset ); ?>" sizes="100vw" alt="<?php echo $img_alt; ?>">
Произвольная функция подбора изображений:
function aw_custom_responsive_image_sizes($sizes, $size) {
$width = $size[0];
// blog posts
if ( is_singular( 'post' ) ) {
// half width images - medium size
if ( $width === 600 ) {
return '(min-width: 768px) 322px, (min-width: 576px) 255px, calc( (100vw - 30px) / 2)';
}
// full width images - large size
if ( $width === 1024 ) {
return '(min-width: 768px) 642px, (min-width: 576px) 510px, calc(100vw - 30px)';
}
// default to return if condition is not met
return '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
}
// default to return if condition is not met
return '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
}
add_filter('wp_calculate_image_sizes', 'aw_custom_responsive_image_sizes', 10 , 2);
Назначить какому-либо размеру миниатюры неизменяемый от размера экрана вывод, как бы блокировка размера миниатюры. Нужен для того если мы точно знаем где будет выводиться это изображение:
function aw_custom_declare_custom_image_responsive_sizes($attr, $attachment, $size) {
// Full width header images
if ($size === 'xxxl') {
$attr['sizes'] = '100vw';
}
return $attr;
}
add_filter('wp_get_attachment_image_attributes', 'aw_custom_declare_custom_image_responsive_sizes', 10 , 3);
Проработал свою функцию изменения размера миниатюры, немного упростив код из найденного примера:
function aw_custom_responsive_image_sizes($sizes, $size) {
if ( is_singular( 'post' ) ) {
if ( $width <= 1024 ) { return 'calc( (100vw) / 2)'; } // выводим large
if ( $width <= 768 ) { return 'calc( (100vw) / 2)'; } // выводим medium_large
if ( $width <= 425 ) { return 'calc( (100vw) / 2)'; } // выводим medium
return '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
}
return '(max-width: ' . $width . 'px) 100vw, ' . $width . 'px';
}
add_filter('wp_calculate_image_sizes', 'aw_custom_responsive_image_sizes', 10 , 2);
Рассуждения на тему вывода вариантов изображений в srcset wordpress
Я создав пользовательский размер миниатюры (add_image_size( ‘mobile’, 420, 210, array( ‘center’, ‘center’ ) );) обратил внимание что в выводе изображений в цикле, не у всех изображений в srcset есть размер этой миниатюры. Думаю, что это связано с пропорциями, т.к. данный размер миниатюры выводился у тех изображений изначальные пропорции которого были близки к этому размеру (именно не точно пропорционально, а близки, погрешность в несколько px).
т.е. на атрибуте srcset мы не сможем настроить вывод в desctop прямоугольного изображения, а в mobile квадратного. Это нужно будет делать другими способами.
Непропорциональная адаптивность
Получить массив всех доступных вариантов размеров изображения. Функция:
function prepareImageData( $attachment_id ){
$uploads_baseurl = wp_upload_dir()['baseurl'];
$prepared = [];
$data = wp_get_attachment_metadata($attachment_id);
$prepared = [
'mime_type' => get_post_mime_type($attachment_id),
'url' => $uploads_baseurl.'/'.$data['file'],
'sizes' => [],
];
foreach( $data['sizes'] as $size => $sizeInfo ){
$prepared['sizes'][$size] = [
'url' => $uploads_baseurl.'/'.$sizeInfo['file'],
];
}
return $prepared;
}
Выводиться так:
$thumb_id = get_post_thumbnail_id($post->ID);
$image_data = prepareImageData( $thumb_id );
echo $image_data;
Приведенная выше функция особо толку не имеет (оставил, вдруг пригодиться), а вот у Миши Рудастых нашел то что нужно. Добавление в srcset миниатюру произвольного размера, даже если та отличается по пропорциям от оригинала:
function misha_sources( $sources, $size_array, $image_src, $image_meta, $attachment_id ){
$image_size_name = 'square500'; // add_image_size('square500', 500, 500, true);
$breakpoint = 500;
$upload_dir = wp_upload_dir();
$img_url = $upload_dir['baseurl'] . '/' . str_replace( basename( $image_meta['file'] ), $image_meta['sizes'][$image_size_name]['file'], $image_meta['file'] );
$sources[ $breakpoint ] = array(
'url' => $img_url,
'descriptor' => 'w',
'value' => $breakpoint,
);
return $sources;
}
add_filter('wp_calculate_image_srcset','misha_sources',10,5);
Чтобы устранить вывод произвольного размера миниатюр у изображений которых его нет, нужно обернуть часть функции в проверку:
if (!empty($image_meta['sizes'][$image_size_name]['file'])) {
$img_url = $upload_dir['baseurl'] . '/' . str_replace( basename( $image_meta['file'] ), $image_meta['sizes'][$image_size_name]['file'], $image_meta['file'] );
$sources[ $breakpoint ] = array(
'url' => $img_url,
'descriptor' => 'w',
'value' => $breakpoint
);
}
Собрал из двух предыдущих функций (все таки первая пригодилась!) функцию вывода всех вариантов миниатюр с указанием ширины (w) в конце.
function prepareImageData( $attachment_id ){
$uploads_baseurl = wp_upload_dir()['baseurl'];
$prepared = [];
$data = wp_get_attachment_metadata($attachment_id);
$size = '';
$prepared[$size] = [
'url' => $uploads_baseurl.'/'.$data['file'],
'value' => $data['width'].'w',
];
$prepared[$size] = implode(" ", $prepared[$size]);
foreach( $data['sizes'] as $size => $sizeInfo ){
$prepared[$size] = [
'url' => $uploads_baseurl.'/'.$sizeInfo['file'],
'value' => $sizeInfo['width'].'w',
];
$prepared[$size] = implode(" ", $prepared[$size]);
}
$thumb_srcset = implode(", ", $prepared);
return $thumb_srcset;
}
Выводиться при получении ID миниатюры:
$thumb_id = get_post_thumbnail_id($post->ID);
$image_data = prepareImageData( $thumb_id );
Решение основано на функции wp_get_attachment_metadata, которая получает от изображения: ширину, высоту, неполный путь к файлу (от wp-upload) и массив с вариантами миниатюр, а также массив с метаданными изображения. Каждый элемент (название миниатюры) дочернего массива с размерами включает: наименование файла, ширину. высоту и тип файла. Пример:
Array
(
[width] => 1280
[height] => 400
[file] => 2016/07/VS_Panties_Banner.jpg
[sizes] => Array
(
[mobile] => Array
(
[file] => VS_Panties_Banner-420x210.jpg
[width] => 420
[height] => 210
[mime-type] => image/jpeg
)
[thumbnail] => Array
(
[file] => VS_Panties_Banner-150x150.jpg
[width] => 150
[height] => 150
[mime-type] => image/jpeg
)
[medium] => Array
(
[file] => VS_Panties_Banner-400x125.jpg
[width] => 400
[height] => 125
[mime-type] => image/jpeg
)
[medium_large] => Array
(
[file] => VS_Panties_Banner-768x240.jpg
[width] => 768
[height] => 240
[mime-type] => image/jpeg
)
[large] => Array
(
[file] => VS_Panties_Banner-1024x320.jpg
[width] => 1024
[height] => 320
[mime-type] => image/jpeg
)
)
[image_meta] => Array
(
[aperture] => 0
[credit] =>
[camera] =>
=>
[created_timestamp] => 0
[copyright] =>
[focal_length] => 0
[iso] => 0
[shutter_speed] => 0
[title] =>
[orientation] => 0
[keywords] => Array
(
)
)
)
Доработал функцию, упростив, но при этом добавив возможность устанавливать произвольный набор миниатюр.
function prepareImageData( $attachment_id ){
$uploads_baseurl = wp_upload_dir()['baseurl'];
$prepared = [];
$data = wp_get_attachment_metadata($attachment_id);
$thumb_names = array('mobile', 'medium_large')
$prepared[$size] = $uploads_baseurl.'/'.$data['file'].' '.$data['width'].'w';
foreach( $data['sizes'] as $size => $sizeInfo ){
if (in_array($size, $thumb_names)) {
$prepared[$size] = [
'url' => $uploads_baseurl.'/'.$sizeInfo['file'],
'value' => $sizeInfo['width'].'w',
];
$prepared[$size] = implode(" ", $prepared[$size]);
}
}
$thumb_srcset = implode(", ", $prepared);
return $thumb_srcset;
}
Это решение можно применять для придания адаптивности не стандартных миниатюр статей (для этого достаточно и штатной функции), а для специфических элементов, например слайдер.
И еще одна доработка: пути миниатюр указывались в папке upload без указания года и месяца. Прописал функцию замены:
foreach( $data['sizes'] as $size => $sizeInfo ){
if (in_array($size, $thumb_names)) {
$img_name = str_replace( basename( $data['file'] ), $sizeInfo['file'], $data['file'] );
$img_url = $uploads_baseurl. '/' .$img_name;
$prepared[$size] = [
'url' => $img_url,
'value' => $sizeInfo['width'].'w',
];
$prepared[$size] = implode(" ", $prepared[$size]);
}
}
Теперь осталось правильно управлять атрибутом sizes в зависимости от ширины экрана.
[site-socialshare]