画像として合成して表示する方法
以下ではライブラリ「html2canvas」を使用して作成しました。
しかし、html2canvasではobject-fitやfilterが未サポートのため、代わりにライブラリ「dom-to-image」を使用します。
完成イメージ

実装手順
以下の手順で実装します。
- my-html-image-blur
- html-image-blur
- assets
- css
- admin.css
- js
- admin.js
- css
- ラベルPHPを追加
以下のコードをhtml-image-blur.phpに追加します。
<?php /** * Plugin Name: My HTML Image Blue * Description: 縦長画像に背景のぼかしを画像として合成します。 * Version: 1.0.0 * Author: CHU-YA */ if (!defined('ABSPATH')) { exit; } class AFI_Featured_Image_Blur_Generator { public function __construct() { add_action('add_meta_boxes', [$this, 'add_meta_box']); add_action('admin_enqueue_scripts', [$this, 'enqueue_assets']); add_action('wp_ajax_afi_save_image', [$this, 'ajax_save_image']); } /** * メタボックスを追加 * * @return void */ public function add_meta_box() { add_meta_box( 'vertical_featured_blur', 'アイキャッチ画像自動生成', [$this, 'vertical_featured_blur'], 'post', 'side' ); } /** * メタボックスの入力フォームを出力 * * @param WP_Post $post 現在編集中の投稿オブジェクト * @return void */ public function vertical_featured_blur($post) { ?> <div id="vertical-featured"> <p> <button type="button" class="button" id="afi_select_image">画像を選択</button> <input type="hidden" id="afi_selected_image_url" value=""> </p> <p> <button type="button" class="button" id="afi_generate">画像を作成</button> </p> <p> <img id="afi_preview" src="" style="max-width:100%; display:none;"> </p> <p> <button type="button" class="button button-primary" id="afi_save">画像を保存</button> </p> </div> <?php } /** * エディター用JS・CSを追加 * * @param string $hook 現在の管理画面フック名 * @return void */ public function enqueue_assets($hook) { // 投稿編集画面以外では読み込まない if (!in_array($hook, ['post.php', 'post-new.php'])) { return; } global $post; // メディアアップローダー読み込み wp_enqueue_media(); // 外部ライブラリdom-to-image-moreの読み込み wp_enqueue_script('dom-to-image-more', 'https://cdnjs.cloudflare.com/ajax/libs/dom-to-image-more/3.5.0/dom-to-image-more.min.js', [], null, true); wp_enqueue_script('afi_js', plugin_dir_url(__FILE__) . 'assets/js/admin.js', ['jquery', 'dom-to-image-more'], null, true); wp_enqueue_style('afi_css', plugin_dir_url(__FILE__) . 'assets/css/admin.css'); // Ajaxで使うパラメータなどをJS側に渡す wp_localize_script('afi_js', 'afiData', [ 'ajaxUrl' => admin_url('admin-ajax.php'), 'postId' => $post->ID, 'nonce' => wp_create_nonce('vertical_featured_blur_action'), 'cardRatio' => get_thumbnail_aspect_ratio(), ]); } /** * Ajaxアクションを登録 * * @return void */ public function ajax_save_image() { // nonceチェック check_ajax_referer( 'vertical_featured_blur_action', 'vertical_featured_blur_nonce' ); $post_id = intval($_POST['postId']); $data = $_POST['imageData'] ?? ''; // 画像データ形式チェック(data URL) if (!preg_match('/^data:image\/png;base64,/', $data)) { wp_send_json_error(['message' => '無効な画像データ']); } // base64デコード $binary = base64_decode( str_replace('data:image/png;base64,', '', $data) ); // ファイルをアップロードディレクトリに保存 $upload = wp_upload_bits("afi-{$post_id}.png", null, $binary); if (!empty($upload['error'])) { wp_send_json_error(['message' => $upload['error']]); } // 添付ファイル情報を作成 $attachment = [ 'post_mime_type' => 'image/png', 'post_title' => "AFI Image {$post_id}", 'post_status' => 'inherit', ]; // 添付ファイルとして登録 $attach_id = wp_insert_attachment( $attachment, $upload['file'], $post_id ); if (is_wp_error($attach_id)) { wp_send_json_error([ 'message' => '画像の保存に失敗しました', 'error_details' => $attach_id->get_error_message(), ]); } // 画像メタデータ生成と更新 require_once ABSPATH . 'wp-admin/includes/image.php'; $meta = wp_generate_attachment_metadata($attach_id, $upload['file']); wp_update_attachment_metadata($attach_id, $meta); // 投稿のアイキャッチ画像に設定 set_post_thumbnail($post_id, $attach_id); // 更新情報をJSON形式で返却 wp_send_json_success([ 'url' => $upload['url'], 'attach_id' => $attach_id, ]); } } new AFI_Featured_Image_Blur_Generator(); - ラベルJavaScriptを追加
以下のコードをadmin.jsに追加します。
jQuery(function ($) { const width = 800; const height = width * afiData.cardRatio; let currentImageData = ''; let selectedImageUrl = ''; /** * 画像生成用のHTMLテンプレートを作成 * * @param {string} imageUrl 画像URL * @returns {string} HTML文字列 */ function generateTemplate(imageUrl) { const html = ` <div id="afi_preview_container" style="width: ${width}px; height: ${height}px;"> <div class="vertical-featured"> <img class="vertical-featured__blur" src="${imageUrl}" /> <img class="vertical-featured__main" src="${imageUrl}" /> </div> </div> `; console.log(html); return html; } /** * HTMLをPNGに変換してプレビュー表示 * * @param {string} htmlContent HTML文字列 * @returns {void} */ function htmlToPng(htmlContent) { const $container = $('<div>') .html(htmlContent) .appendTo('body'); const element = $container.find('#afi_preview_container')[0]; if (!element) { console.error('プレビュー要素が見つかりません'); $container.remove(); return; } domtoimage.toPng(element, { cacheBust: true }) .then(function (dataUrl) { currentImageData = dataUrl; $('#afi_preview') .attr('src', dataUrl) .show(); }) .catch(function (error) { console.error('dom-to-image Error:', error); alert('画像の生成に失敗しました。'); }) .finally(function () { $container.remove(); }); } // 画像選択ボタンのクリックイベント $('#afi_select_image').on('click', function () { const frame = wp.media({ title: '画像を選択', button: { text: 'この画像を使用' }, multiple: false }); frame.on('select', function () { const attachment = frame .state() .get('selection') .first() .toJSON(); selectedImageUrl = attachment.url; $('#afi_selected_image_url').val(selectedImageUrl); $('#afi_preview') .attr('src', selectedImageUrl) .show(); }); frame.open(); }); // 画像作成ボタンのクリックイベント $('#afi_generate').on('click', function () { if (!selectedImageUrl) { alert('画像を選択してください'); return; } const htmlContent = generateTemplate(selectedImageUrl); htmlToPng(htmlContent); }); // 画像保存ボタンのクリックイベント $('#afi_save').on('click', function () { if (!currentImageData) { alert('画像を作成してください。'); return; } $.post(afiData.ajaxUrl, { action: 'afi_save_image', vertical_featured_blur_nonce: afiData.nonce, postId: afiData.postId, imageData: currentImageData }, function (res) { if (res.success) { if ( typeof wp !== 'undefined' && wp.data && wp.data.dispatch ) { wp.data.dispatch('core/editor').editPost({ featured_media: res.data.attach_id }); } else { $('#_thumbnail_id').val(res.data.attach_id); } } else { alert( '保存に失敗しました: ' + (res.data?.message || 'エラー') ); } }); }); }); - ラベルCSSを追加
以下のコードをadmin.cssに追加します。
.vertical-featured { height: 100%; overflow: hidden; position: relative; width: 100%; } .vertical-featured__blur { filter: blur(5px); height: 100%; left: 0; object-fit: cover; position: absolute; scale: 1.05; top: 0; width: 100%; } .vertical-featured__main { display: block; height: 100%; margin: 0 auto; position: relative; z-index: 1; }
さいごに
今回紹介した方法を使えば、縦長のアイキャッチ画像によって発生する左右の余白を、自然な背景ぼかしで美しく補うことができます。
CSSでの視覚的表現に加え、ぼかし背景を含めた画像として合成し、実際のアイキャッチとして設定することも可能です。



