web-dev-qa-db-fra.com

Générer une taille d'image en fonction de l'orientation de l'image

Je travaille sur une refonte assez complexe d'un projet wordpress. Actuellement, je travaille sur l'implémentation de l'image. Le site est très chargé en images et je souhaite fournir une image optimale pour chaque application. J'ai pensé à quelque chose comme ça:

1. orientation

L'image peut être au format paysage, portrait ou carré. Chacun a une largeur maximale différente. La solution optimale consisterait à fournir une version différente pour chaque orientation.

Landscape
Portrait
Square

2. IPP élevé

J'aimerais également fournir une version @ 2x de chaque image:

Landscape
Landscape@2x

Portrait
Portrait@2x

Square
Square@2x

3. Points d'arrêt

Le site final comporte au moins quatre points d'arrêt. J'aimerais également optimiser l'image pour chaque point d'arrêt:

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    

Le problème

Au final, cela me donne 24 images générées par image, ce qui représente une énorme quantité de données générées. Les deux tiers de ces données ne seront jamais utilisées (les deux autres orientations) et j'aimerais les éliminer. add_image_size() ne prend pas en compte l'orientation de l'image.

Est-il possible de ne générer que les images nécessaires à l'orientation de l'image?


Mettre à jour

TL; DR: J'ai peut-être trouvé une solution, le code est au bas de la page, des tests plus poussés demain.

Ok, j’ai plongé dans le Wordpress Trac aujourd’hui, et je pense l’avoir.

Il est difficile de modifier la génération de la fonction add_image_size(), alors j’ai pris le hook wp_generate_attachment_metadata,

Les images sont générées dans la fonction wp_generate_attachment_metadata(). Il est difficile de modifier la fonction, car le hook wp_generate_attachment_metadata correspondant a lieu après la génération des images.

Pourtant, c'était le meilleur crochet que j'ai trouvé à cette fin. J'ai décidé de créer mes propres copies redimensionnées de l'image d'origine (WP_Image_Editor a rendu cela assez facile) et d'inclure les images nouvellement générées dans les métadonnées de l'image d'origine.

Jusqu’à présent, cela semble plutôt bien, sauf que la barre de progression reste allumée à 100% jusqu’à ce que les images aient été traitées. Cela peut être un problème lorsque les utilisateurs quittent la page, avant que toutes les images aient été traitées. Le deuxième problème était que les images générées personnalisées ne sont pas supprimées si je supprime la pièce jointe. Cela est dû à la fonction get_intermediate_image_sizes(), qui recherche les tailles d'image ajoutées par add_image_size(). J'ai trouvé le crochet intermediate_image_sizes et je veux essayer d'y ajouter toutes les tailles d'image possibles demain. Entre-temps, j'ai une solution rapide qui utilise le crochet delete_attachment.

Je ferai d'autres tests demain et mettrai à jour ce post.

Voici mon code jusqu'à présent:

/**
 * 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');
2
Afterlame

Voici ma solution de travail. Le code est documenté, il devrait donc être clair ce que chaque fonction fait.

J'utilise beaucoup le filtre wp_generate_attachment_metadata pour créer les images nécessaires une fois l'image téléchargée.

Les images générées sont également répertoriées dans les métadonnées, comme toute autre taille d'image intermédiaire. De cette façon, vous pouvez travailler avec eux comme avec n'importe quelle autre taille d'image.

Une autre partie importante consiste à utiliser le filtre delete_attachment pour supprimer les images générées.

// ==========================
// 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');
1
Afterlame