LoginSignup
20
16

More than 5 years have passed since last update.

Wordpress用のアイキャッチ自動生成プラグインを作ってみた

Last updated at Posted at 2018-08-01

はじめに

メディアを運用していくにあたり、アイキャッチ作成って意外とコスト取ってますよね
実際にメディアを運用していてアイキャッチ作成には時間を取っていた(私もphotoshopで作ってました)

じゃあ仕組み化したらいいんじゃね?ということで表題の通り仕組み化(プラグインに)しました。

まあこんなアイキャッチができます
eyecatch-210.png

技術要件

  • canvas
  • php
  • Jquery
  • Wordpress

ロジック

Wordpresssの投稿画面で動作する。挙動としてはテキストフィールドに入力された文字と、Wordpressに登録されサイトのロゴを自動でcanvasへ適用する。
また、canvasは描画コンテンツを画像にできるのでクライアントサイドだけでアイキャッチを作ることができる。

開発フロー

以下の順序で開発した
1. add_meta_boxでサイドバーにアイキャッチジェネレータなるものを追加
2. meta_box内にinputやbutton,canvas等HTMLタグを出力
3. canvasにユーザが入力した文字を描画

1.add_meta_boxでサイドバーにアイキャッチジェネレータなるものを追加

add_meta_boxのロジックは ここが参考になります

function add_side_meta_box_free_generator() {
    if (has_post_thumbnail()) {
        add_meta_box( 'free_eyecatch_gene', '無料版アイキャッチジェネレーター', 'add_side_free_gene_meta_box_callback', 'post', 'side', 'default' );
    }
}
add_action( 'add_meta_boxes', 'add_side_meta_box_free_generator' );

add_side_free_gene_meta_box_callbackがmeta_boxに入れるコンテンツを描画するfunctionになります。次で書きます。

2. meta_box内にinputやbutton,canvas等HTMLタグを出力

小さすぎるとアイキャッチとして適さない画質になるので結構大きめの画像を生成するようにしてます。画像の比率は4:3です。

function add_side_free_gene_meta_box_callback() { ?>
    <canvas width="1000" height="750" id="generator-view-free"></canvas>
    <input type="text" class="generator-title-free" placeholder="一つ目のタイトルを入れてください" maxlength="15" style="width:100%">
    <p style="color: red; font-size: 12px; margin-top: 0;">※一つ目のタイトルは(全角)15文字までです</p>
    <input type="text" class="generator-title-free2" placeholder="二つ目のタイトルを入れてください" maxlength="30" style="width:100%">
    <p style="color: red; font-size: 12px; margin-top: 0;">※二つ目のタイトルは(全角)30文字までです(15文字を越えると自動で改行してくれます)</p>
    <button type="button" class="generator-btn-free">作成</button>
    <img src="" alt="" class="generated-img">
<?php }

3. canvasにユーザが入力した文字を描画


add_action( 'edit_form_advanced', 'disp_eyecatch_free_generator' );
function disp_eyecatch_free_generator($post){ ?>
    <?php
    $image_id = get_post_thumbnail_id();
    $image_url = wp_get_attachment_image_src($image_id, true);
    ?>
    <script type="text/javascript">
    var isStore = false;
    var canvasFree = document.querySelector("#generator-view-free");
    var contextFree = canvasFree.getContext("2d");
    var imgFree = new Image();
    var imgFree2 = new Image();
    function draw() {
        imgFree.src = "<?php echo $image_url[0]; ?>";
        imgFree.onload = function() {
            // clip case
            // height > width
            if (this.height > this.width) {
                contextFree.drawImage(this, 0, 0, 1000, (this.height / this.width) * 1000);
            } else {
                contextFree.drawImage(this, 0, 0, (this.width / this.height) * 750, 750);
            }
            contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
            contextFree.fillRect(0, 0, 1000, 750);
            contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
            contextFree.fillRect(0, 550, 1000, 200);
            imgFree2.src = "<?php echo( get_header_image() ); ?>";
            imgFree2.onload = function() {
                const aspect = this.height / this.width;
                const baseImgWidth = 1000 / 3;
                // basicaly, img width is one per three by canvasFree width;
                contextFree.drawImage(this, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
            }
        }
    };
    function strIns(str, idx, val){
        var res = str.slice(0, idx) + val + str.slice(idx);
        return res;
    };

    function fillTextLine(ctx, text, x, y) {
        let textList = text.split('\n');
        let lineHeight = ctx.measureText("").width;
        textList.forEach(function(text, i) {
            if (i === 0) {
                ctx.fillText(text, x, y+lineHeight*i);
            } else {
                ctx.fillText(text, (canvasFree.width / 2 - (text.length / 2 * 60)), y+lineHeight*i + 75);
            }
        });
    };
    $(function(){
      draw();
      $('.generator-btn-free').click(function() {
          let textFree = $('.generator-title-free').val();
          let textFree2 = $('.generator-title-free2').val();
          if (textFree2.length > 15) {
              textFree2 = strIns(textFree2, 15, '\n');
          }
          if (imgFree.height > imgFree.width) {
              contextFree.drawImage(imgFree, 0, 0, 1000, (imgFree.height / imgFree.width) * 1000);
          } else {
              contextFree.drawImage(imgFree, 0, 0, (imgFree.width / imgFree.height) * 750, 750);
          }
          contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
          contextFree.fillRect(0, 0, 1000, 750);
          contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
          contextFree.fillRect(0, 550, 1000, 200);
          const aspect = imgFree2.height / imgFree2.width;
          const baseImgWidth = 1000 / 3;
          // basicaly, img width is one per three by canvasFree width;
          contextFree.drawImage(imgFree2, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
          contextFree.font = "normal 60px 'Arial'";
          contextFree.fillStyle = "white";
          contextFree.fillText(
            textFree,
            (canvasFree.width / 2 - (textFree.length / 2 * 60)),
            165
          );
          const x = textFree2.length > 15 ? (canvasFree.width / 2 - 450) : (canvasFree.width / 2 - (textFree2.length / 2 * 12));
          fillTextLine(contextFree, textFree2, x, 300);
      });
    });
    </script>
    <style>
    canvas#generator-view-free {
        width: 100%;
    }
    </style>
<?php }

上から順番に説明していきます
まずはこれ

    $image_id = get_post_thumbnail_id();
    $image_url = wp_get_attachment_image_src($image_id, true);

これは投稿に設定されている現状のアイキャッチ画像を取得して画像のURLを後々生成するアイキャッチの背景に使います。


    function draw() {
        imgFree.src = "<?php echo $image_url[0]; ?>";
        imgFree.onload = function() {
            // clip case
            // height > width
            if (this.height > this.width) {
                contextFree.drawImage(this, 0, 0, 1000, (this.height / this.width) * 1000);
            } else {
                contextFree.drawImage(this, 0, 0, (this.width / this.height) * 750, 750);
            }
            contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
            contextFree.fillRect(0, 0, 1000, 750);
            contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
            contextFree.fillRect(0, 550, 1000, 200);
            imgFree2.src = "<?php echo( get_header_image() ); ?>";
            imgFree2.onload = function() {
                const aspect = this.height / this.width;
                const baseImgWidth = 1000 / 3;
                // basicaly, img width is one per three by canvasFree width;
                contextFree.drawImage(this, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
            }
        }
    };

先ほど取得した画像を背景として設定するためにimgFree.srcに入れonloadでcanvasに描画してます。
途中にclip caseとコメントアウトされてるとこの下になにやら計算しているのがありますがこれは画像のサイズを自動で調整して背景画像をフルサイズ表示するための計算です。

次に、画像を描画後rgba(0,0,0,0.4)で透過済みのレクタングルを描画
そして、wordpressに設定されてるサイトロゴ(get_header_image())をimgFree2.srcに入れ同様にonloadで描画してます。

    function strIns(str, idx, val){
        var res = str.slice(0, idx) + val + str.slice(idx);
        return res;
    };

    function fillTextLine(ctx, text, x, y) {
        let textList = text.split('\n');
        let lineHeight = ctx.measureText("").width;
        textList.forEach(function(text, i) {
            if (i === 0) {
                ctx.fillText(text, x, y+lineHeight*i);
            } else {
                ctx.fillText(text, (canvasFree.width / 2 - (text.length / 2 * 60)), y+lineHeight*i + 75);
            }
        });
    };
    $(function(){
      draw();
      $('.generator-btn-free').click(function() {
          let textFree = $('.generator-title-free').val();
          let textFree2 = $('.generator-title-free2').val();
          if (textFree2.length > 15) {
              textFree2 = strIns(textFree2, 15, '\n');
          }
          if (imgFree.height > imgFree.width) {
              contextFree.drawImage(imgFree, 0, 0, 1000, (imgFree.height / imgFree.width) * 1000);
          } else {
              contextFree.drawImage(imgFree, 0, 0, (imgFree.width / imgFree.height) * 750, 750);
          }
          contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
          contextFree.fillRect(0, 0, 1000, 750);
          contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
          contextFree.fillRect(0, 550, 1000, 200);
          const aspect = imgFree2.height / imgFree2.width;
          const baseImgWidth = 1000 / 3;
          // basicaly, img width is one per three by canvasFree width;
          contextFree.drawImage(imgFree2, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
          contextFree.font = "normal 60px 'Arial'";
          contextFree.fillStyle = "white";
          contextFree.fillText(
            textFree,
            (canvasFree.width / 2 - (textFree.length / 2 * 60)),
            165
          );
          const x = textFree2.length > 15 ? (canvasFree.width / 2 - 450) : (canvasFree.width / 2 - (textFree2.length / 2 * 12));
          fillTextLine(contextFree, textFree2, x, 300);
      });
  • strIns functionは指定した文字数を越えると\nを挿入してくれるメソッド
  • fillTextLine functionは先ほどのstrInsで挿入した\nを元にsplitし配列するメソッド

あとは以下のclickイベントを検出後テキストフィールドの文字や画像をcanvasへ描画します。

 $('.generator-btn-free').click

最後に

以上のサンプルコードを元にアイキャッチを生成する処理を体験してみてください。

最終コード

以下をfunction.phpに貼り付けるだけでも動作します。

function add_side_meta_box_free_generator() {
    if (has_post_thumbnail()) {
        add_meta_box( 'free_eyecatch_gene', '無料版アイキャッチジェネレーター', 'add_side_free_gene_meta_box_callback', 'post', 'side', 'default' );
    }
}

/**
 * 枠の内容を出力します。
 */
function add_side_free_gene_meta_box_callback() { ?>
    <canvas width="1000" height="750" id="generator-view-free"></canvas>
    <input type="text" class="generator-title-free" placeholder="一つ目のタイトルを入れてください" maxlength="15" style="width:100%">
    <p style="color: red; font-size: 12px; margin-top: 0;">※一つ目のタイトルは(全角)15文字までです</p>
    <input type="text" class="generator-title-free2" placeholder="二つ目のタイトルを入れてください" maxlength="30" style="width:100%">
    <p style="color: red; font-size: 12px; margin-top: 0;">※二つ目のタイトルは(全角)30文字までです(15文字を越えると自動で改行してくれます)</p>
    <button type="button" class="generator-btn-free">作成</button>
    <img src="" alt="" class="generated-img">
<?php }
add_action( 'add_meta_boxes', 'add_side_meta_box_free_generator' );

add_action( 'edit_form_advanced', 'disp_eyecatch_free_generator' );
function disp_eyecatch_free_generator($post){ ?>
    <?php
    $image_id = get_post_thumbnail_id();
    $image_url = wp_get_attachment_image_src($image_id, true);
    ?>
    <script type="text/javascript">
    var isStore = false;
    var canvasFree = document.querySelector("#generator-view-free");
    console.log(canvasFree);
    var contextFree = canvasFree.getContext("2d");
    var imgFree = new Image();
    var imgFree2 = new Image();
    function draw() {
        console.log(canvasFree);
        imgFree.src = "<?php echo $image_url[0]; ?>";
        console.log(imgFree);
        imgFree.onload = function() {
            console.log(this.width, this.height);
            // clip case
            // height > width
            if (this.height > this.width) {
                contextFree.drawImage(this, 0, 0, 1000, (this.height / this.width) * 1000);
            } else {
                contextFree.drawImage(this, 0, 0, (this.width / this.height) * 750, 750);
            }
            contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
            contextFree.fillRect(0, 0, 1000, 750);
            contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
            contextFree.fillRect(0, 550, 1000, 200);
            imgFree2.src = "<?php echo( get_header_image() ); ?>";
            console.log("<?php echo( get_header_image() ); ?>");
            imgFree2.onload = function() {
                const aspect = this.height / this.width;
                const baseImgWidth = 1000 / 3;
                console.log(baseImgWidth * aspect);
                // basicaly, img width is one per three by canvasFree width;
                contextFree.drawImage(this, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
            }
        }
    };
    function strIns(str, idx, val){
        var res = str.slice(0, idx) + val + str.slice(idx);
        return res;
    };

    function fillTextLine(ctx, text, x, y) {
        let textList = text.split('\n');
        let lineHeight = ctx.measureText("").width;
        textList.forEach(function(text, i) {
            if (i === 0) {
                ctx.fillText(text, x, y+lineHeight*i);
            } else {
                ctx.fillText(text, (canvasFree.width / 2 - (text.length / 2 * 60)), y+lineHeight*i + 75);
            }
        });
    };
    $(function(){
      draw();
      $('.generator-btn-free').click(function() {
          let textFree = $('.generator-title-free').val();
          let textFree2 = $('.generator-title-free2').val();
          if (textFree2.length > 15) {
              console.log('into state');
              textFree2 = strIns(textFree2, 15, '\n');
          }
          console.log('click eve', textFree, textFree2);
          if (imgFree.height > imgFree.width) {
              contextFree.drawImage(imgFree, 0, 0, 1000, (imgFree.height / imgFree.width) * 1000);
          } else {
              contextFree.drawImage(imgFree, 0, 0, (imgFree.width / imgFree.height) * 750, 750);
          }
          contextFree.fillStyle = 'rgba(0, 0, 0, 0.4)';
          contextFree.fillRect(0, 0, 1000, 750);
          contextFree.fillStyle = 'rgba(255, 255, 255, 0.8)';
          contextFree.fillRect(0, 550, 1000, 200);
          const aspect = imgFree2.height / imgFree2.width;
          const baseImgWidth = 1000 / 3;
          // basicaly, img width is one per three by canvasFree width;
          contextFree.drawImage(imgFree2, 500 - baseImgWidth/2, 650 - (baseImgWidth * aspect) / 2, baseImgWidth, baseImgWidth * aspect);
          contextFree.font = "normal 60px 'Arial'";
          contextFree.fillStyle = "white";
          contextFree.fillText(
            textFree,
            (canvasFree.width / 2 - (textFree.length / 2 * 60)),
            165
          );
          const x = textFree2.length > 15 ? (canvasFree.width / 2 - 450) : (canvasFree.width / 2 - (textFree2.length / 2 * 12));
          fillTextLine(contextFree, textFree2, x, 300);
      });
    });
    </script>
    <style>
    canvas#generator-view-free {
        width: 100%;
    }
    </style>
<?php }

とりあえず使いたい方へ

こちらにソースあげてます
使い方
1.clone or downladをクリック
2.download zipをクリック
3.ダウンロードしたzipをwordpress追加:pluginの追加方法はこちら

補足

他社のマーケターにこちらの機能を使ってもらったところ好評でした。
自分がやってたようにアイキャッチを作成するにはバナーを作るサービスやphotoshopを元に作成しますがやはり、視認性の良いアイキャッチを作成するには属人的なところが大きいのでパターンを考える必要がありました。そのためあえてツール側でパターンを用意することで作成コストが短縮できるのとライター自身にアイキャッチの作成を任せられるところが大き方のかと思います。

また、要望を元にさらに機能を拡張したpluginを作成しました。後々掲載いたします。
機能拡張により以下の機能を追加してます

  • ダウンロードボタン
  • 画像素材検索
  • 画像をアップロードできる
  • カラーピッカーで背景色変更
  • リアルタイムでテキスト変更
  • アイキャッチのパターンが10種以上
  • 文字サイズ変更可
20
16
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
16