I am working on a fairly complex redesign of a wordpress project. Currently I am working on the image implementation. The site is very image heavy and I want to provide the optimal image for each application. I thought about something like this:
1. Orientation
The image can be in landscape, portait or square format. Each has a different max-width. The optimal solution would be to provide a different version for each orientation.
Landscape
Portrait
Square
2. High PPI
I would also like to provide a @2x version of each image:
Landscape
Landscape@2x
Portrait
Portrait@2x
Square
Square@2x
3. Breakpoints
The final site comes with at least four breakpoints. I would also like to optimize the image for each breakpoint:
Breakpoint 1 | Breakpoint 2 | Breakpoint 3 | Breakpoint 4
-------------|--------------|--------------|-------------
Landscape | Landscape | Landscape | Landscape
Landscape@2x | Landscape@2x | Landscape@2x | Landscape@2x
| | |
Portrait | Portrait | Portrait | Portrait
Portrait@2x | Portrait@2x | Portrait@2x | Portrait@2x
| | |
Square | Square | Square | Square
Square@2x | Square@2x | Square@2x | Square@2x
The Problem
In the end, this gives me 24 generated images per image, which is a huge amount of generated data. Two thirds of that data will never be used (the two other orientations) and I would love to strip those out. add_image_size()
does not take the image orientation into account.
Is there any way to only generate the images needed for the orientation of the image?
Update
TL;DR: I may have found a solution, code is at the bottom, further testing tomorrow.
Ok, I took a deep dive into the Wordpress Trac today, and I think I have got it.
It is hard to alter the generation of the add_image_size()
function, so I took the wp_generate_attachment_metadata
hook,
The images get generated in the wp_generate_attachment_metadata()
function. It is hard to alter the function, because the matching wp_generate_attachment_metadata
hook takes place, after the images have been generated.
Still, this was the best hook I have found for this purpose.
I decided to create my own resized copies of the original image (WP_Image_Editor
made that fairly easy) and include the newly generated images in the metadata of the original image.
So far this looks pretty good, except, that the progressbar stays on 100% until the images have been processed. This could be a problem with users leaving the page, before all images have been processed. The second problem was, that the custom generated images do not get deleted, if I delete the attachment. This is because of the get_intermediate_image_sizes()
function, which looks for image sizes, added by add_image_size()
.
I have found the intermediate_image_sizes
hook and want to try to add all possible image sizes to it tomorrow, in the meantime I have a quick solution, which makes use of the delete_attachment
hook.
I will do further testing tomorrow and update this post.
Here is my code so far:
/**
* Generates custom image sizes, depending on the image orientation. Use the wp_generate_attachment_metadata hook!
*/
function r21_create_custom_image_sizes($meta) {
// Initialize variables
global $r21_image_sizes;
$image_sizes = \'\';
$new_meta = array();
// Generate the full file path for the image
$image[\'path\'] = path_join(wp_upload_dir()[\'basedir\'], $meta[\'file\']);
// Get the dimensions of the original image
list($image[\'width\'], $image[\'height\'], $image[\'type\']) = getimagesize($image[\'path\']);
// Check the image orientation
if ($image[\'width\'] > $image[\'height\']) {
// Landscape
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'landscape\', true);
} else if ($image[\'width\'] < $image[\'height\']) {
// Portrait
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'portrait\', true);
} else {
// Square
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'square\', true);
}
// Iterate through the sizes to be generated
foreach ($image_sizes as $size) {
// TODO: Check if an image in the requested dimensions allready exists.
// Create an instance of WP_Image_Editor for the original image
$new_image = wp_get_image_editor($image[\'path\']);
// Resize the image
$new_image->resize($size[\'width\'], $size[\'height\'], $size[\'crop\']);
// Save new image and store new meta data in variable
$new_image_meta = $new_image->save();
// Reflect back new metadata
$meta[\'sizes\'][$size[\'name\']][\'file\'] = $new_image_meta[\'file\'];
$meta[\'sizes\'][$size[\'name\']][\'width\'] = $new_image_meta[\'width\'];
$meta[\'sizes\'][$size[\'name\']][\'height\'] = $new_image_meta[\'height\'];
$meta[\'sizes\'][$size[\'name\']][\'mime-type\'] = $new_image_meta[\'mime-type\'];
}
return $meta;
}
add_filter(\'wp_generate_attachment_metadata\', \'r21_create_custom_image_sizes\');
/**
* Deletes images, generated by r21_create_custom_image_sizes(). Use the delete_attachment hook!
*/
function r21_delete_custom_image_size_files($post_id) {
$sizes_meta = wp_get_attachment_metadata($post_id)[\'sizes\'];
foreach ($sizes_meta as $size) {
// TODO: Add support for wp_delete_file hook here
@ unlink(path_join(wp_upload_dir()[\'path\'], $size[\'file\']));
}
}
add_action(\'delete_attachment\', \'r21_delete_custom_image_size_files\');
最合适的回答,由SO网友:Afterlame 整理而成
这是我的工作解决方案。代码是有文档记录的,因此应该清楚每个函数的作用。
我几乎使用wp_generate_attachment_metadata
上载图像后,筛选以创建所需的图像。
与任何其他中间图像大小一样,生成的图像也会列在元数据中。这样,您就可以像处理任何其他图像大小一样处理它们。
另一个重要部分是使用delete_attachment
筛选以删除生成的图像。
// ==========================
// Custom Image Size Handling
// ==========================
/**
* Removes default and plugin generated image sizes.
* This is optional!
*/
function r21_remove_image_sizes($sizes) {
unset($sizes[\'thumbnail\']);
unset($sizes[\'medium\']);
unset($sizes[\'large\']);
return $sizes;
}
add_filter(\'intermediate_image_sizes\', \'r21_remove_image_sizes\');
add_filter(\'intermediate_image_sizes_advanced\', \'r21_remove_image_sizes\');
/**
* Generate a handle for thumbnail regeneration tools.
* The custom images will always be regenerated after one of
* the site wide image sizes have been regenerated.
* The problem here is, that if there are no site wide image sizes
* defined, you can not regenerate any custom size images.
* To avoid this we create a 1x1 px image that works as handle, if there
* are no other imge sizes.
*/
add_image_size( \'cstm-img-regeneration-handle\' , 1, 1, array( \'left\', \'top\' ));
/**
* Delete unneeded generated images and their metadata.
* Also deletes images, generated in the filter, this is why
* this function has to be used before the image generation function.
*
* @param array $attachment_meta
* @return array
*/
function r21_remove_old_image_sizes($attachment_meta) {
foreach ($attachment_meta[\'sizes\'] as $size_name => $size_data) {
// Ceck if image size is currently an active intermediate image size
if (array_key_exists($size_name, get_intermediate_image_sizes())) { continue; }
// Delete file
@ unlink(path_join(r21_get_attachment_path_by(\'meta\', $attachment_meta), $attachment_meta[\'sizes\'][$size_name][\'file\']));
// Delete metadata
unset($attachment_meta[\'sizes\'][$size_name]);
}
return $attachment_meta;
}
add_filter(\'wp_generate_attachment_metadata\', \'r21_remove_old_image_sizes\', 10, 1);
/**
* Removes the the custom image regneration handle image, if existing.
*
* @return array Returns the metadata, without the handle image entry.
*/
function r21_remove_regeneration_hook_image($attachment_meta) {
$name = \'cstm-img-regeneration-handle\';
// Check if image exists
if (array_key_exists($name, $attachment_meta[\'sizes\'])) {
// Delete Image File
@ unlink(path_join(r21_get_attachment_path_by(\'meta\', $attachment_meta), $attachment_meta[\'sizes\'][$name][\'file\']));
// Delete Image Metadata
unset($attachment_meta[\'sizes\'][$name]);
}
return $attachment_meta;
}
add_filter(\'wp_generate_attachment_metadata\', \'r21_remove_regeneration_hook_image\', 10, 1);
/**
* Generates custom image sizes, depending on the image orientation. Use the wp_generate_attachment_metadata hook!
*/
function r21_create_custom_image_sizes($meta) {
// Initialize variables
global $r21_image_sizes;
$image_sizes = \'\';
// Generate the full file path for the image
$image[\'path\'] = path_join(wp_upload_dir()[\'basedir\'], $meta[\'file\']);
// Get the dimensions of the original image
list($image[\'width\'], $image[\'height\'], $image[\'type\']) = getimagesize($image[\'path\']);
// Check the image orientation
if ($image[\'width\'] > $image[\'height\']) {
// Landscape
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'landscape\', true);
} else if ($image[\'width\'] < $image[\'height\']) {
// Portrait
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'portrait\', true);
} else {
// Square
$image_sizes = r21_filter_custom_image_sizes($r21_image_sizes, \'square\', true);
}
// Iterate through the sizes to be generated
foreach ($image_sizes as $size_name => $size) {
// TODO: Check if an image in the requested dimensions allready exists.
// Create an instance of WP_Image_Editor for the original image
$new_image = wp_get_image_editor($image[\'path\']);
// Check if there is an error
if (is_wp_error($new_image)) { continue; }
// Resize the image
$new_image->resize($size[\'width\'], $size[\'height\'], $size[\'crop\']);
// Save new image and store new meta data in variable
$new_image_meta = $new_image->save();
// Reflect back new metadata
$meta[\'sizes\'][$size_name][\'file\'] = $new_image_meta[\'file\'];
$meta[\'sizes\'][$size_name][\'width\'] = $new_image_meta[\'width\'];
$meta[\'sizes\'][$size_name][\'height\'] = $new_image_meta[\'height\'];
$meta[\'sizes\'][$size_name][\'mime-type\'] = $new_image_meta[\'mime-type\'];
}
return $meta;
}
add_filter(\'wp_generate_attachment_metadata\', \'r21_create_custom_image_sizes\', 10, 1);
/**
* Deletes images, generated by r21_create_custom_image_sizes(). Use the delete_attachment hook!
*/
function r21_delete_custom_image_size_files($post_id) {
$meta = wp_get_attachment_metadata($post_id);
foreach ($meta[\'sizes\'] as $size) {
// TODO: Add support for wp_delete_file hook here
@ unlink(path_join(r21_get_attachment_path_by(\'meta\', $meta), $size[\'file\']));
}
}
add_action(\'delete_attachment\', \'r21_delete_custom_image_size_files\');