第2部:【Cocoon】アイキャッチ3要素:タイトル・投稿者・アバターを自由に制御する方法
【重要な免責事項】 この記事は第1部より高度な内容で、generate_dynamic_image関数の完全オーバーライドを含みます。実装前には必ずサイトの完全バックアップを取得してください。設定ミスによりサイトが表示されなくなるリスクがあります。サーバー環境やテーマのバージョンによっては正常に動作しない場合があります。本記事の情報を基に行った変更による問題について、筆者は責任を負いかねます。
この記事を読む前に
第1部のランダムカラー機能を使ってみて、満足している方は無理に第2部に進む必要はありません。

第2部は以下のような方向けです:
- 複数のライターがいるサイト運営者
- 記事の性質によって表現を変えたい方
- より詳細なカスタマイズを楽しみたい方
実際にやることは第1部と同じ「functions.phpにコピペ」です。作業自体は変わりません。
この記事で実現できること:
✅ 3つの要素を自由にオンオフ:タイトル・投稿者名・アバターを個別制御
✅ たった3行の設定変更:define()で簡単切り替え
✅ 5つの代表的パターン:用途に応じて自由に組み合わせ
✅ ランダムカラーとの完全統合:第1部の機能もそのまま活用
第1章:なぜ「完全制御」が必要になるのか
第1部の実装で気づいた限界
第1部のランダムカラー機能は確かに便利です。でも、使い続けていると「もっと細かく制御できたらな…」という欲が出てきます。
「アバターだけ消したい」
「投稿者名は表示したいけど、アバターはいらない」
「シンプルにタイトルだけの記事もあれば、フル表示の記事もある」
など。
アイキャッチの3つの要素を再確認
改めて、Cocoonの自動生成アイキャッチを分解してみましょう:
- タイトル:記事のタイトルテキスト
- 投稿者名:記事を書いた人の名前
- アバター:投稿者のプロフィール画像
デフォルトでは、この3つがすべて表示されるものでした。
実際の運用で見えてくる5つのパターン
ブログ運営を続けていると、「こんなパターンがあったらいいな」という組み合わせ例が見えてきます:
パターン名 | タイトル | 投稿者名 | アバター | 最適な用途 |
---|---|---|---|---|
シンプル | ✅ | ❌ | ❌ | 個人ブログ・日記 |
ライター重視 | ✅ | ✅ | ❌ | 複数執筆者サイト |
フル表示 | ✅ | ✅ | ✅ | パーソナルブランディング |
ビジュアル重視 | ✅ | ❌ | ✅ | 写真・デザインサイト |
著者フォーカス | ❌ | ✅ | ✅ | ゲストライター重視 |
このような使い分けができれば、記事の性質に応じた適切な表現が可能になります。
第2章:「オーバーライド」とは何か
なぜ「完全オーバーライド」が必要なのか
第1部では、3つのフィルターだけでランダムカラーを実現しました。しかし、オンオフ機能を実装するには、画像生成の根幹部分に手を入れる必要があります。
でも安心してください。Cocoonには公式の「安全なオーバーライド方法」が用意されています。
オーバーライドの正確な定義
オーバーライド(override)とは、プログラミングにおいて「既存の機能を新しい機能で置き換える」ことです。
身近な例で説明すると:
レストランで新しい料理レシピを導入することを想像してください:
- 元のレシピ:Cocoon標準の画像生成手順
- 新しいレシピ:あなたがカスタマイズした手順
- レシピの変更:オーバーライド
重要なポイント:
- キッチンの設備は変わらない(WordPressの基本機能は影響なし)
- 料理名は同じ(アイキャッチ機能は残る)
- 元のレシピも保管されている(コードを削除すれば復旧)
Cocoon公式の「安全な仕組み」
Cocoonでは、以下の仕組みでオーバーライドを安全に行えます:
// Cocoon公式の安全な方法
if (!function_exists('generate_dynamic_image')):
function generate_dynamic_image($post_id, $new_image_path, $width, $height) {
// 自分好みの処理
}
endif;
この仕組みの安全性:
- !function_exists()チェック:「もし同名の関数が存在しなかったら」という条件で重複エラーを防ぐ安全装置
- 子テーマでの実装:親テーマが更新されても設定が残り、テーマ更新に強い構造
- 復旧の簡単さ:子テーマのコードを削除すれば元に戻り、データベース変更なし
なぜこの方法が安全なのか?
Cocoonが意図的に用意している仕組みだからです。開発者のわいひら氏が「ユーザーが安全にカスタマイズできるように」と設計した公式の拡張方法です。
たった3行で制御する仕組み
複雑な条件分岐やデータベース設定は一切不要。たった3行で全てを制御できます:
// ここを変更するだけで自由自在
define('EYECATCH_SHOW_TITLE', true); // タイトル表示
define('EYECATCH_SHOW_AUTHOR_NAME', false); // 投稿者名表示
define('EYECATCH_SHOW_AVATAR', false); // アバター表示
なぜdefine()なのか?
設定変更の頻度は意外に低く、管理画面よりコードで管理する方が確実で、データベースアクセスが不要で高速です。結果的に、この「シンプル」が最適解でした。
第3章:完全実装コードと解説
重要な前置き
以下のコードは私のサイト専用です。そのままコピペではなく、あなたのサイトに合わせて調整してから使用してください。
特に色の部分は、第1部を参考に変更してください。
私が実際に使っている完全版コード
// アイキャッチ画像生成の設定(ここでオンオフを簡単に変更可能)
define('EYECATCH_SHOW_TITLE', true); // タイトルを表示する
define('EYECATCH_SHOW_AUTHOR_NAME', true); // 投稿者名を表示する
define('EYECATCH_SHOW_AVATAR', false); // アバターを表示する
// アイキャッチ画像のランダム色設定(第1部から継承)
function my_site_random_eyecatch_colors() {
// 背景色の配列(宇宙テーマ専用)
$bg_colors = [
'#060b25', '#0f1942', '#1a1035', '#0d1c26', '#131217', '#0A0F20',
'#151326', '#1B1F3B', '#16213E', '#0D1B2A', '#17223B', '#121629',
'#0F111A', '#0B132B', '#1A1B41'
];
// テキスト色の配列(宇宙テーマ専用)
$text_colors = [
'#FFE5A3', '#E5D0B1', '#F0C987', '#DCD0FF', '#A7D8FF', '#FFD700',
'#C4B1E6', '#EAAC8B', '#E2C275', '#F7D08A', '#BCDBDF', '#FFEFD3',
'#BDB2FF', '#FFD9CE', '#FFC857'
];
// ボーダー色の配列(宇宙テーマ専用)
$border_colors = [
'#2d3168', '#4A5086', '#6A4978', '#3D5A80', '#48416C', '#563D7C',
'#3A506B', '#6D597A', '#533747', '#415A77', '#5C6E91', '#4B3F72',
'#524B58', '#3A5E8C', '#4A4E69'
];
// ランダム色設定
add_filter('featured_image_background_color_code', function() use ($bg_colors) {
return $bg_colors[array_rand($bg_colors)];
});
add_filter('featured_image_text_color_code', function() use ($text_colors) {
return $text_colors[array_rand($text_colors)];
});
add_filter('featured_image_border_color_code', function() use ($border_colors) {
return $border_colors[array_rand($border_colors)];
});
}
my_site_random_eyecatch_colors();
// オンオフ機能付きアイキャッチ生成関数(完全カスタマイズ版)
if (!function_exists('generate_dynamic_image')):
function generate_dynamic_image($post_id, $new_image_path, $width, $height) {
$post_title = get_the_title($post_id);
if ($post_title === __('Auto Draft')) return false;
$post_title = sanitize_post_title($post_title);
if (!$post_title) return false;
// 投稿者情報の取得(オンオフ設定に応じて)
$author_id = null;
$author_name = '';
$avatar_url = '';
if (EYECATCH_SHOW_AUTHOR_NAME || EYECATCH_SHOW_AVATAR) {
$author_id = get_post_field('post_author', $post_id);
$author_name = get_the_author_meta('display_name', $author_id);
if (EYECATCH_SHOW_AVATAR) {
$avatar_url = get_the_author_upladed_avatar_url($author_id);
if (empty($avatar_url)) {
$avatar_url = get_avatar_url($author_id, ['size' => 100]);
}
}
}
// 画像とカラーの設定
$image = imagecreatetruecolor($width, $height);
$colors = get_image_colors($image, $width);
$border_width = get_dynamic_featured_image_size($width, 30);
// 背景とボーダーの描画
draw_background_and_border($image, $width, $height, $colors, $border_width);
// フォント設定
$font_path = get_cocoon_template_directory() . '/webfonts/googlefonts/NotoSansJP-Regular.ttf';
if (!file_exists($font_path)) {
error_log("Font file not found: " . $font_path);
return false;
}
$font_size = get_dynamic_featured_image_size($width, 48);
$author_name_font_size = get_dynamic_featured_image_size($width, 38);
$margin = $width * 0.1;
$max_width = $width - ($margin * 2);
// レイアウト計算(オンオフ設定に応じて動的に調整)
$avatar_size = EYECATCH_SHOW_AVATAR ? get_dynamic_featured_image_size($width, 100) : 0;
$available_height = calculate_available_height($height, $margin, $avatar_size, EYECATCH_SHOW_AVATAR, EYECATCH_SHOW_AUTHOR_NAME, $width);
// タイトル描画(オンの場合のみ)
if (EYECATCH_SHOW_TITLE) {
draw_title($image, $post_title, $font_path, $font_size, $margin, $max_width, $width, $height, $available_height, $colors['text']);
}
// アバター描画(オンの場合のみ)
if (EYECATCH_SHOW_AVATAR && !empty($avatar_url)) {
draw_avatar($image, $avatar_url, $margin, $height, $avatar_size, EYECATCH_SHOW_AUTHOR_NAME, $author_name_font_size);
}
// 投稿者名描画(オンの場合のみ)
if (EYECATCH_SHOW_AUTHOR_NAME && !empty($author_name)) {
draw_author_name($image, $author_name, $font_path, $author_name_font_size, $margin, $height, $max_width, $avatar_size, EYECATCH_SHOW_AVATAR, $width, $colors['text']);
}
// 画像保存
$result = imagepng($image, $new_image_path);
imagedestroy($image);
return $result;
}
// ヘルパー関数群(画像生成に必要な補助機能)
function get_image_colors($image, $width) {
$background_code = apply_filters('featured_image_background_color_code', '#060b25');
$text_code = apply_filters('featured_image_text_color_code', '#FFE5A3');
$border_code = apply_filters('featured_image_border_color_code', '#2d3168');
return [
'background' => imagecolorallocate($image, ...sscanf($background_code, "#%02x%02x%02x")),
'text' => imagecolorallocate($image, ...sscanf($text_code, "#%02x%02x%02x")),
'border' => imagecolorallocate($image, ...sscanf($border_code, "#%02x%02x%02x"))
];
}
function draw_background_and_border($image, $width, $height, $colors, $border_width) {
imagefilledrectangle($image, 0, 0, $width, $height, $colors['border']);
imagefilledrectangle($image, $border_width, $border_width, $width - $border_width, $height - $border_width, $colors['background']);
// 角を丸くする処理(簡略版)
$radius = $border_width;
$corners = [
[$radius, $radius, 0, 90, 180, 270],
[$width - $radius, $radius, 90, 180, 270, 360],
[$radius, $height - $radius, 270, 360, 90, 180],
[$width - $radius, $height - $radius, 180, 270, 0, 90]
];
foreach ($corners as $corner) {
imagefilledarc($image, $corner[0], $corner[1], $radius * 2, $radius * 2, $corner[2], $corner[3], $colors['border'], IMG_ARC_PIE);
imagefilledarc($image, $corner[0] + ($corner[0] < $width/2 ? $radius : -$radius),
$corner[1] + ($corner[1] < $height/2 ? $radius : -$radius),
$radius * 2, $radius * 2, $corner[4], $corner[5], $colors['background'], IMG_ARC_PIE);
}
}
function calculate_available_height($height, $margin, $avatar_size, $show_avatar, $show_author_name, $width) {
$available_height = $height - ($margin * 2);
if ($show_avatar) {
$available_height -= ($avatar_size + get_dynamic_featured_image_size($width, 20));
}
if ($show_author_name) {
$available_height -= get_dynamic_featured_image_size($width, 80);
}
return $available_height;
}
function draw_title($image, $post_title, $font_path, $font_size, $margin, $max_width, $width, $height, $available_height, $text_color) {
// 日本語対応の単語分割処理
$words = preg_split('/(?<=[\p{Hiragana}\p{Katakana}\p{Han}\s])|(?=[\p{Hiragana}\p{Katakana}\p{Han}\s])|(?<=\s)|(?=\s)|(?<=\p{P}(?<!-))|(?=\p{P}(?<!-))|(?<=-)(?=[^\p{L}])|(?<=[^\p{L}])(?=-)/u', $post_title, -1, PREG_SPLIT_NO_EMPTY);
$lines = [];
$current_line = '';
foreach ($words as $word) {
$box = imagettfbbox($font_size, 0, $font_path, $current_line . $word);
$text_width = $box[2] - $box[0];
if ($text_width > $max_width && !empty(trim($current_line))) {
// 句読点の処理
if (preg_match('/^[\p{Ps}]/u', $word)) {
$lines[] = trim($current_line);
$current_line = $word;
} elseif (preg_match('/^[\p{Pe}]/u', $word)) {
$lines[] = trim($current_line) . mb_substr($word, 0, 1);
$current_line = mb_substr($word, 1);
} elseif (preg_match('/^[\p{P}\p{S}]/u', $word)) {
$lines[] = trim($current_line) . mb_substr($word, 0, 1);
$current_line = mb_substr($word, 1);
} else {
$lines[] = trim($current_line);
$current_line = $word;
}
} else {
$current_line .= $word;
$current_line = preg_replace('/^\x{3000}/u', '', $current_line);
}
}
if (!empty(trim($current_line))) {
$lines[] = trim($current_line);
}
// 行数制限と省略記号処理
$line_space = get_dynamic_featured_image_size($width, 40);
$max_row = floor($available_height / ($font_size + $line_space));
if (count($lines) > $max_row) {
$lines = array_slice($lines, 0, $max_row);
$ellipsis = '...';
$last_line = $lines[$max_row - 1];
$last_line_width = imagettfbbox($font_size, 0, $font_path, $last_line)[2] - imagettfbbox($font_size, 0, $font_path, $last_line)[0];
$ellipsis_width = imagettfbbox($font_size, 0, $font_path, $ellipsis)[2] - imagettfbbox($font_size, 0, $font_path, $ellipsis)[0];
while ($last_line_width + $ellipsis_width > $max_width && mb_strlen($last_line) > 0) {
$last_line = mb_substr($last_line, 0, -1);
$last_line_width = imagettfbbox($font_size, 0, $font_path, $last_line)[2] - imagettfbbox($font_size, 0, $font_path, $last_line)[0];
}
$lines[$max_row - 1] = $last_line . $ellipsis;
}
// タイトルの描画位置計算
$total_text_height = count($lines) * ($font_size + $line_space) - $line_space;
$title_start_y = (EYECATCH_SHOW_AVATAR || EYECATCH_SHOW_AUTHOR_NAME)
? get_dynamic_featured_image_size($width, 150)
: ($height - $total_text_height) / 2 + $font_size;
$current_y = $title_start_y;
foreach ($lines as $line) {
imagettftext($image, $font_size, 0, $margin, $current_y, $text_color, $font_path, $line);
$current_y += $font_size + $line_space;
}
}
function draw_avatar($image, $avatar_url, $margin, $height, $avatar_size, $show_author_name, $author_name_font_size) {
$avatar_data = wp_remote_get($avatar_url);
if (is_wp_error($avatar_data)) return;
$avatar_content = wp_remote_retrieve_body($avatar_data);
$avatar_temp = imagecreatefromstring($avatar_content);
if ($avatar_temp === false) return;
$avatar_x = $margin;
$avatar_y = $height - $margin - $avatar_size;
if ($show_author_name) {
$avatar_y -= $author_name_font_size + 20;
}
$avatar_resized = imagecreatetruecolor($avatar_size, $avatar_size);
imagecopyresampled($avatar_resized, $avatar_temp, 0, 0, 0, 0, $avatar_size, $avatar_size, imagesx($avatar_temp), imagesy($avatar_temp));
// 丸いマスクの適用
for ($x = 0; $x < $avatar_size; $x++) {
for ($y = 0; $y < $avatar_size; $y++) {
$distance = sqrt(pow($x - $avatar_size/2, 2) + pow($y - $avatar_size/2, 2));
if ($distance <= $avatar_size/2) {
$color = imagecolorat($avatar_resized, $x, $y);
imagesetpixel($image, $avatar_x + $x, $avatar_y + $y, $color);
}
}
}
imagedestroy($avatar_temp);
imagedestroy($avatar_resized);
}
function draw_author_name($image, $author_name, $font_path, $font_size, $margin, $height, $max_width, $avatar_size, $show_avatar, $width, $text_color) {
$author_text_x = $margin;
$author_text_y = $height - $margin - ($font_size / 2);
if ($show_avatar) {
$author_text_x = $margin + $avatar_size + get_dynamic_featured_image_size($width, 20);
$author_text_y = $height - $margin - $avatar_size + ($avatar_size / 2) + ($font_size / 3);
}
$max_author_width = $max_width - ($show_avatar ? ($avatar_size + get_dynamic_featured_image_size($width, 20)) : 0);
$author_box = imagettfbbox($font_size, 0, $font_path, $author_name);
$author_width = $author_box[2] - $author_box[0];
if ($author_width > $max_author_width) {
while ($author_width > $max_author_width - 30 && mb_strlen($author_name) > 0) {
$author_name = mb_substr($author_name, 0, -1);
$author_box = imagettfbbox($font_size, 0, $font_path, $author_name . '...');
$author_width = $author_box[2] - $author_box[0];
}
$author_name .= '...';
}
imagettftext($image, $font_size, 0, $author_text_x, $author_text_y, $text_color, $font_path, $author_name);
}
endif;
コードの仕組み解説
array_rand()の仕組み
$bg_colors[array_rand($bg_colors)]
この1行が全ての仕組みです。配列からランダムに1つの色を選択してくれます。毎回違う色が選ばれるため、記事投稿のたびに新しい組み合わせを楽しめます。
use構文のテクニック
function() use ($bg_colors) {
return $bg_colors[array_rand($bg_colors)];
}
これは「クロージャ」という仕組みで、関数の外で定義した配列を関数の中で使えるようにする構文です。
実際の切り替え例
「今日はシンプルモードにしよう」
define('EYECATCH_SHOW_TITLE', true);
define('EYECATCH_SHOW_AUTHOR_NAME', false);
define('EYECATCH_SHOW_AVATAR', false);
「ゲスト投稿だから投稿者名も出そう」
define('EYECATCH_SHOW_TITLE', true);
define('EYECATCH_SHOW_AUTHOR_NAME', true); // ここだけ変更
define('EYECATCH_SHOW_AVATAR', false);
「重要な記事だからフル表示で」
define('EYECATCH_SHOW_TITLE', true);
define('EYECATCH_SHOW_AUTHOR_NAME', true);
define('EYECATCH_SHOW_AVATAR', true); // ここも変更
本当にこれだけです。
第4章:実装時の注意事項
必須の事前準備
以下は絶対に守ってください:
完全バックアップの取得
- WordPress全体のバックアップ
- データベースのバックアップ
子テーマの使用確認
- 親テーマの直接編集は絶対に避ける
- 子テーマのfunctions.phpに追加
段階的な実装
- まず3色程度で動作確認
- 問題なければ色数を増やす
万が一の復旧方法
もしサイトにアクセスできなくなった場合:
- FTPまたはサーバーのファイルマネージャーで子テーマフォルダにアクセス
- functions.phpを開く
- 追加したコード部分を削除またはコメントアウト
- ファイルを保存
これで元の状態に戻ります。データベースに変更を加えていないので、完全に復旧できます。
トラブルシューティング(実体験ベース)
Q: アイキャッチが生成されない
A: 以下を確認してください:
- Cocoonの自動生成機能が有効になっているか
- 子テーマのfunctions.phpに正しく追加されているか
Q: エラーが発生する
A: 焦らず、追加したコードをコメントアウトして原因を特定:
// my_site_random_eyecatch_colors(); // 一時的にコメントアウト
Q: 色の組み合わせが気に入らない
A: これは完全に好みの問題です。配列内の色を好みに合わせて変更してください。
実用的な補足:安全な実装方法
今後、アイキャッチ自動生成機能をカスタマイズする際は、以下のような防御的プログラミングを推奨いたします:
// 関数の存在確認を行う安全な実装
if (EYECATCH_SHOW_AVATAR) {
$avatar_url = '';
// Cocoon独自関数の存在確認
if (function_exists('get_the_author_upladed_avatar_url')) {
$avatar_url = get_the_author_upladed_avatar_url($author_id);
}
// 取得できない場合はWordPress標準関数を使用
if (empty($avatar_url)) {
$avatar_url = get_avatar_url($author_id, ['size' => 100]);
}
}
このような実装により、将来的な関数名変更にも対応可能なコードを作成できます。
まとめ
第2部の機能を実装すると、あなたのCocoonサイトはさらに他と差別化され、記事の性質に応じた適切な表現ができるようになります。
「アイキャッチをどうしよう」と悩む時間がゼロになるのは、想像以上に大きなメリットです。記事を書くことに集中できるようになります。
最後に重要な注意事項:
- 必ずバックアップを取ってから実装してください(これは絶対に!)
- 問題が発生した場合は、追加したコードを削除すれば元に戻ります
- サンプルコードをそのまま使わず、あなたのサイトに合わせて調整してください
- より詳細なサポートが必要な場合は、Cocoonコミュニティで相談してください
ここまでお疲れ様でした。
実装コード中の関数名で気になる部分に気づいた方もいるかもしれません。
次回は、この関数名について技術的背景を調査・考察した記事をお届けします。

あなたのサイトも、完全制御されたアイキャッチで読者を魅了してください!
関連記事:
