管理しているサイトで、数字を扱う記事のアイキャッチを見直していたときのことです。
いくつかの記事に、アイキャッチがありませんでした。
写真の素材を探すことも考えましたが、記事の中の数字をグラフにしたほうが、記事の内容が一目で伝わりました。
そこで、管理画面にログインしたブラウザーの開発ツール(コンソール)で、数字から横棒グラフの画像を作り、そのままアイキャッチに登録する手順にしました。
画像の編集のソフトも、プラグインも使いません。
この記事では、手順ごとのコードと、作業の中で気づいたこと、作業環境で確かめた結果をまとめます。
流れ
| 手順 | すること | 使うもの |
|---|---|---|
| 1 | canvas(1200×630)に、タイトル・小見出し・横棒グラフ・出どころの一行を描く | Canvas API |
| 2 | canvas を PNG のデータにする | canvas.toBlob() |
| 3 | PNG をメディアにアップロードする(alt とタイトルも付ける) | REST API の /wp/v2/media |
| 4 | 返ってきた画像の ID を、記事のアイキャッチにする | REST API の /wp/v2/posts の featured_media |
| 5 | 本文にも同じ画像を入れる(入れるときだけ) | 返ってきた source_url |
手順 1〜5 は、同じタブのコンソールに順に貼って流します。
前の手順で作った chart・canvas・blob・media などを、次の手順で使います。
開発ツールのコンソールでは、await をそのまま書けます。
管理画面のページであれば、どこで開いてもかまいません(例:ダッシュボード)。
手順 1:canvas にグラフを描く
上の chart に、記事の中の数字を書き入れます。
下のコードの数字は例です。
// グラフにする数字(例の数字。記事の中の数字に書きかえる)
const chart = {
title: '例:月ごとのお問い合わせの数',
subtitle: '2026 年 4 月〜8 月・フォームから届いた数',
source: '出どころ:(記事の中の表の名前や、数字を取った場所を書く)',
unit: '件',
items: [
{ label: '4 月', value: 32 },
{ label: '5 月', value: 41 },
{ label: '6 月', value: 38 },
{ label: '7 月', value: 55 },
{ label: '8 月', value: 61 },
],
};
// 位置と大きさ(px)
const L = {
width: 1200,
height: 630,
side: 100, // 左右の余白。テーマが左右を切り抜いても文字が切れない幅
titleY: 92, // タイトルの文字の下の線
subtitleY: 140, // 小見出しの文字の下の線
chartTop: 190, // グラフの上の端
labelWidth: 200, // 棒の左の名前の幅
valueWidth: 140, // 棒の右の値の幅
maxBarH: 56, // 棒の高さの上限
sourceSize: 22, // 出どころの文字の大きさ
sourceBottom: 36, // 出どころの文字の下の線から、画像の下の端まで
sourceGap: 24, // いちばん下の棒と、出どころの文字の上の端のあいだ
};
// 棒の数から、棒の高さと位置を決める
function layoutBars(count, L) {
const sourceY = L.height - L.sourceBottom;
const sourceTop = sourceY - L.sourceSize;
const chartBottom = sourceTop - L.sourceGap;
const slot = (chartBottom - L.chartTop) / count;
const barH = Math.min(L.maxBarH, Math.floor(slot * 0.7));
const fontSize = Math.min(24, Math.floor(slot * 0.75));
const bars = [];
for (let i = 0; i < count; i++) {
bars.push({ y: Math.round(L.chartTop + slot * i + (slot - barH) / 2), h: barH });
}
return { sourceY, sourceTop, chartBottom, slot, fontSize, bars };
}
const FONT = '"Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic", "Meiryo", "Noto Sans JP", sans-serif';
const canvas = document.createElement('canvas');
canvas.width = L.width;
canvas.height = L.height;
const ctx = canvas.getContext('2d');
// 背景
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, L.width, L.height);
// タイトルと小見出し
ctx.textAlign = 'left';
ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#1f2937';
ctx.font = `bold 44px ${FONT}`;
ctx.fillText(chart.title, L.side, L.titleY, L.width - L.side * 2);
ctx.fillStyle = '#4b5563';
ctx.font = `26px ${FONT}`;
ctx.fillText(chart.subtitle, L.side, L.subtitleY, L.width - L.side * 2);
// 横棒グラフ
const pos = layoutBars(chart.items.length, L);
const barX = L.side + L.labelWidth;
const barMaxW = L.width - L.side - L.valueWidth - barX;
const maxValue = Math.max(...chart.items.map((item) => item.value));
chart.items.forEach((item, i) => {
const { y, h } = pos.bars[i];
const w = Math.round(barMaxW * item.value / maxValue);
ctx.fillStyle = '#2563eb';
ctx.beginPath();
ctx.roundRect(barX, y, w, h, Math.min(8, h / 2));
ctx.fill();
ctx.textBaseline = 'middle';
ctx.fillStyle = '#1f2937';
ctx.textAlign = 'right';
ctx.font = `${pos.fontSize}px ${FONT}`;
ctx.fillText(item.label, barX - 16, y + h / 2, L.labelWidth - 16);
ctx.textAlign = 'left';
ctx.font = `bold ${pos.fontSize}px ${FONT}`;
ctx.fillText(item.value.toLocaleString('ja-JP') + chart.unit, barX + w + 12, y + h / 2, L.valueWidth - 12);
});
// 出どころ
ctx.textAlign = 'left';
ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#6b7280';
ctx.font = `${L.sourceSize}px ${FONT}`;
ctx.fillText(chart.source, L.side, pos.sourceY, L.width - L.side * 2);
// 画面の左上に出して、目で見る
canvas.style.cssText = 'position:fixed;top:40px;left:20px;z-index:99999;width:600px;border:1px solid #9ca3af;';
document.body.appendChild(canvas);
| 書き方 | 役目 |
|---|---|
const chart = { … } |
タイトル・小見出し・出どころ・単位と、棒にする名前と値。ここだけを記事ごとに書きかえる |
const L = { … } |
画像の大きさと、文字や棒の位置。1200×630 は、SNS で共有されたときの画像にもよく使われる比率 |
layoutBars(count, L) |
棒の数から、棒の高さと位置を決める(下の「棒の数から高さを決める」) |
FONT |
日本語のフォントを、Mac・Windows の順に並べる。どれもないときは sans-serif |
ctx.fillText(文字, x, y, 最大の幅) |
文字を描く。4 つ目に幅を渡すと、長い文字はその幅に縮めて描かれ、はみ出さない |
textBaseline = 'middle' |
名前と値の文字を、棒の高さのまん中にそろえる |
ctx.roundRect(x, y, 幅, 高さ, 角の丸み) |
角を丸めた四角の形を作り、fill() で塗る。丸みは棒の高さの半分より大きくしない |
barMaxW * item.value / maxValue |
いちばん大きい値の棒を、使える幅いっぱいにする。ほかの棒はその割合の長さ |
toLocaleString('ja-JP') |
値に 3 けたごとのカンマを付ける(1,200 など) |
canvas.style.cssText と appendChild |
描いた画像を、画面の左上に半分の大きさで出す。アップロードする前に目で見るため |
画面に出した画像は、ページを読み直すと消えます。
数字や文字を直したいときは、ページを読み直してから、手順 1 をもう一度流します。
棒の数から高さを決める
最初は、棒の高さと間を、決まった数にしていました。
// 最初の書き方:棒の高さと間を決まった数にしていた const y = L.chartTop + i * 90; // 棒 1 本ごとに 90px 下げる const h = 60; // 棒の高さはいつも 60px
この書き方では、棒の数が多い記事で、いちばん下の棒が、出どころの文字に重なりました。
棒の数が増えると、そのぶん下へ伸びていくためです。
そこで、先に出どころの文字の位置を決め、その上に残った高さを、棒の数で分ける計算にしました。
function layoutBars(count, L) {
const sourceY = L.height - L.sourceBottom;
const sourceTop = sourceY - L.sourceSize;
const chartBottom = sourceTop - L.sourceGap;
const slot = (chartBottom - L.chartTop) / count;
const barH = Math.min(L.maxBarH, Math.floor(slot * 0.7));
const fontSize = Math.min(24, Math.floor(slot * 0.75));
const bars = [];
for (let i = 0; i < count; i++) {
bars.push({ y: Math.round(L.chartTop + slot * i + (slot - barH) / 2), h: barH });
}
return { sourceY, sourceTop, chartBottom, slot, fontSize, bars };
}
| 書き方 | 役目 |
|---|---|
sourceY = L.height - L.sourceBottom |
出どころの文字の下の線。画像の下の端から 36px 上 |
sourceTop = sourceY - L.sourceSize |
出どころの文字の上の端。文字の大きさ(22px)だけ上 |
chartBottom = sourceTop - L.sourceGap |
棒を置いてよい下の端。出どころの文字の上から、さらに 24px 空ける |
slot = (chartBottom - L.chartTop) / count |
棒 1 本に使える高さ。グラフの上の端から下の端までを、棒の数で分ける |
Math.min(L.maxBarH, Math.floor(slot * 0.7)) |
棒の高さは、1 本ぶんの高さの 7 割。棒が少ないときに太くなりすぎないよう、56px を上限にする |
Math.min(24, Math.floor(slot * 0.75)) |
名前と値の文字の大きさ。棒が多くて 1 本ぶんが低いときは、文字も小さくする |
slot * i + (slot - barH) / 2 |
1 本ぶんの高さの中で、棒をまん中に置く |
この計算では、棒がいくつあっても、いちばん下の棒の下の端は chartBottom(ここでは 548)より上になります。
棒の数ごとの結果は、下の「確かめた結果」にまとめました。
日本語のフォント
canvas の文字は、そのブラウザーで使えるフォントで描かれます。
同じコードでも、Mac と Windows では、できる画像の文字の形が少し変わります。
Web フォントを使いたいときは、ページでそのフォントが読み込まれてから描きます(await document.fonts.load('bold 44px "フォントの名前"') など)。
読み込まれる前に描くと、ほかのフォントで描かれます。
手順 2:PNG のデータにする
// canvas を PNG のデータ(Blob)にする
const blob = await new Promise((resolve, reject) => {
canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('PNG にできませんでした'))), 'image/png');
});
console.log(blob.type, blob.size);
| 書き方 | 役目 |
|---|---|
canvas.toBlob(関数, 'image/png') |
canvas の中身を PNG のデータ(Blob)にして、関数に渡す。でき上がりは後から来るので、Promise で待つ |
b ? resolve(b) : reject(…) |
PNG にできなかったときは null が渡されるので、エラーにして止める |
console.log(blob.type, blob.size) |
image/png と、データの大きさ(バイト)が出れば次へ進む |
toDataURL() でも PNG にできますが、長い文字列になります。
アップロードには、そのまま送れる toBlob() のほうが向いています。
手順 3:メディアにアップロードする
// REST API の nonce をもらう(ログインしているブラウザのクッキーで動く)
const nonce = await fetch('/wp-admin/admin-ajax.php?action=rest-nonce', { credentials: 'same-origin' }).then((r) => r.text());
// alt は、グラフの数字が読める文にする
const alt = chart.title + '。' + chart.items.map((item) => `${item.label} ${item.value.toLocaleString('ja-JP')}${chart.unit}`).join('、');
const form = new FormData();
form.append('file', blob, 'chart-inquiries-2026.png');
form.append('title', chart.title);
form.append('alt_text', alt);
const res = await fetch('/wp-json/wp/v2/media', {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce },
body: form,
});
const media = await res.json();
if (!res.ok) {
throw new Error(media.message);
}
console.log(media.id, media.source_url, media.media_details.width, media.media_details.height);
| 書き方 | 役目 |
|---|---|
admin-ajax.php?action=rest-nonce |
ログインしているユーザーの、REST API 用の nonce をもらう。ブラウザーのクッキーでログインを見るときは、nonce がないと「ログインしていない」扱いになる |
credentials: 'same-origin' |
同じサイトへのリクエストに、ログインのクッキーを付ける |
alt |
タイトルと、棒の名前と値を 1 つの文にする。画像が見えない人や、画像が読み込めないときにも、数字が伝わる |
form.append('file', blob, 'ファイル名') |
PNG をファイルとして付ける。3 つ目がメディアに残るファイル名になる |
form.append('title', …)・form.append('alt_text', …) |
メディアの「タイトル」と「代替テキスト」を、アップロードと同時に入れる |
headers: { 'X-WP-Nonce': nonce } |
もらった nonce を送る。Content-Type は書かない。FormData のときは、ブラウザーが区切りの文字を付けて自分で入れる |
res.ok |
うまくいくと 201 が返る。それ以外は message にわけが入っているので、エラーにして止める |
media.id・media.source_url |
できた画像の ID と URL。次の手順で使う |
パーマリンクの設定が「基本」のサイトでは、/wp-json/ の URL が使えません。
そのときは、/?rest_route=/wp/v2/media のように書きます。
WordPress をサブフォルダに入れているサイトでは、URL の前にそのフォルダを付けます。
メディアをアップロードできるのは、「メディアのアップロード」の権限(upload_files)を持つユーザーです。
手順 4:アイキャッチに登録する
const postId = 123; // 記事の ID(編集の画面の URL の post= のあとの数)
const res2 = await fetch(`/wp-json/wp/v2/posts/${postId}`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' },
body: JSON.stringify({ featured_media: media.id }),
});
const post = await res2.json();
if (!res2.ok) {
throw new Error(post.message);
}
console.log(post.id, post.featured_media);
| 書き方 | 役目 |
|---|---|
postId |
アイキャッチを付ける記事の ID。記事の編集の画面の URL(post.php?post=123&action=edit)の数 |
POST /wp-json/wp/v2/posts/{ID} |
記事を更新する。送った項目だけが変わる |
{ featured_media: media.id } |
アイキャッチの画像の ID。手順 3 で返ってきた ID を入れる |
'Content-Type': 'application/json' |
送るものが JSON だと伝える |
post.featured_media |
保存したあとの値。手順 3 の ID と同じ数が出ればよい |
カスタム投稿タイプの記事は、URL の posts のところが、その投稿タイプの名前になります(例:/wp-json/wp/v2/my_article/123)。
カスタム投稿タイプは、REST API に出す設定(show_in_rest)と、アイキャッチを使う設定(supports の thumbnail)が要ります。
記事の編集の画面は、手順 4・5 が終わってから開き直します。
開いたままの編集の画面で「更新」を押すと、その画面が開いたときの内容で保存され、手順 5 で足した本文が戻ってしまうおそれがあるためです。
手順 5:本文にも同じ画像を入れる
本文の中にもグラフを出したいときだけ流します。
// 属性に入れる文字を HTML 用にする
const escAttr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
// いまの本文を、編集用の形(raw)で読む
const cur = await fetch(`/wp-json/wp/v2/posts/${postId}?context=edit`, {
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce },
}).then((r) => r.json());
const img = `<p><img src="${escAttr(media.source_url)}" width="${media.media_details.width}" height="${media.media_details.height}" alt="${escAttr(alt)}" class="wp-image-${media.id}"></p>`;
const res3 = await fetch(`/wp-json/wp/v2/posts/${postId}`, {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce, 'Content-Type': 'application/json' },
body: JSON.stringify({ content: img + '\n\n' + cur.content.raw }),
});
const saved = await res3.json();
if (!res3.ok) {
throw new Error(saved.message);
}
console.log(saved.id, saved.modified);
| 書き方 | 役目 |
|---|---|
escAttr() |
URL や alt の中の &・"・<・> を、HTML の属性に入れられる形にする |
?context=edit |
本文を、表示用に変える前の形(content.raw)で受け取る。ここに足して保存しないと、表示用の HTML で本文が置きかわる |
width・height |
画像の大きさ。書いておくと、画像が読み込まれる前から場所が取られ、ページの表示がずれにくい |
class="wp-image-{ID}" |
どのメディアの画像かを WordPress に伝える。表示のときに loading などの属性が足される |
img + '\n\n' + cur.content.raw |
本文のいちばん上にグラフを入れる。数字の表の近くなど、置きたい場所があれば、保存したあとに編集の画面で動かす |
今回検証したサイトは、クラシックエディターを使っていて、上の img のタグがそのまま本文に入りました。
ブロックエディターの記事に、ブロックの区切りのない HTML を足したときの見え方は、試していません。
気をつけること
テーマがアイキャッチを切り抜く比率
多くのテーマは、アイキャッチを決まった比率に切り抜いて表示します。
1200×630 の画像でも、表示する場所によっては、左右が切れます。
今回検証したサイトのテーマで、元の画像のどこが残るかを、WordPress の image_resize_dimensions() で計算しました。
| テーマの大きさ | 使われている場所 | 元の画像(横 0〜1200)で残る範囲 |
|---|---|---|
| 830×510 | 記事のページ・一覧 | 87〜1112 |
| 786×556 | トップの 3 つのブロック | 154〜1045 |
| 361×221 | 特集の一覧 | 85〜1114 |
| 400×400 | サイドの記事の一覧 | 285〜915 |
記事のページと一覧では、左右が 87px ずつ切れます。
そのため、手順 1 の左右の余白(L.side)を 100px にして、文字が切れないようにしました。
正方形の 400×400 では、まん中の 630px しか残らないので、タイトルの左右も切れます。
正方形で出る場所がある場合は、その場所で読めなくてもよいかを決めておきます。
テーマの大きさは、functions.php の add_image_size() で見られます。
alt を必ず付ける
グラフの画像は、数字そのものが内容です。
alt がないと、画像が見えない人には、何の数字かが伝わりません。
手順 3 では、タイトルと、棒の名前と値を、そのまま alt にしています。
今回の例では、次の alt になりました。
例:月ごとのお問い合わせの数。4 月 32件、5 月 41件、6 月 38件、7 月 55件、8 月 61件
棒が多くて alt が長くなるときは、いちばん大きい値と小さい値だけを書き、くわしくは本文の表で示す形にします。
作り直すと、前の画像がメディアに残る
数字を直してもう一度アップロードすると、新しい画像が別のメディアとして増えます。
同じファイル名にしても、上書きはされず、chart-inquiries-2026-1.png のように、後ろに数の付いた名前になります。
アイキャッチは新しい画像に変わりますが、前の画像はメディアに残ったままです。
前の画像を消すときは、次のコードを流します。
const oldId = 456; // 前に作ったグラフの画像の ID
const res4 = await fetch(`/wp-json/wp/v2/media/${oldId}?force=true`, {
method: 'DELETE',
credentials: 'same-origin',
headers: { 'X-WP-Nonce': nonce },
});
const del = await res4.json();
if (!res4.ok) {
throw new Error(del.message);
}
console.log(del.deleted, del.previous.id);
| 書き方 | 役目 |
|---|---|
oldId |
前のグラフの画像の ID。メディアの画面で、画像を開いたときの URL の item= の数 |
method: 'DELETE' |
メディアを消す |
?force=true |
ゴミ箱に入れずに消す。メディアはゴミ箱を使わない設定が標準なので、これがないと 501 のエラーで断られる |
del.deleted |
消えると true が返る。元の画像と、テーマ用に作られた大きさ違いの画像のファイルも消える |
消す前に、その画像をほかの記事で使っていないかを見ます。
手順 5 で本文にも入れていたときは、本文に前の画像の img が残ります。
画像を消すと、その img は表示されなくなるので、本文からも消してください。
確かめた結果
作業環境(本番から複製した WordPress 7.1.3 のサイト・クラシックエディター)で確かめました。
作業環境のブラウザーは使っていません。
描く計算は Node で、REST API の手順はこの記事のコードを Node の fetch で流して確かめました。
棒の位置の計算(Node)
手順 1 の L と layoutBars() をそのまま取り出し、棒を 1〜10 本にして計算しました。
出どころの文字の上の端は、どの本数でも 572 です。
| 棒の数 | 棒の高さ | いちばん下の棒の下の端 | 出どころの文字までのあいだ | 最初の書き方での下の端 |
|---|---|---|---|---|
| 3 本 | 56px | 516 | 56px | 430 |
| 4 本 | 56px | 531 | 41px | 520 |
| 5 本 | 50px | 537 | 35px | 610(重なる) |
| 6 本 | 41px | 539 | 33px | 700(重なる) |
| 8 本 | 31px | 541 | 31px | 880(重なる) |
| 10 本 | 25px | 543 | 29px | 1060(重なる) |
1〜10 本のどれも、いちばん下の棒は 548 より上で、出どころの文字との間が 24px 以上ありました。
隣の棒どうしも重なりませんでした。
最初の書き方では、5 本で下の端が 610 になり、出どころの文字(572〜594)に重なりました。
6 本からは、画像の下の端(630)もこえました。
アップロードとアイキャッチ(作業環境の WordPress)
作業環境の WordPress に、試しの下書きの記事を 1 つ作りました。
パスワードを使わずに管理者のログインのクッキーを作り、手順 3〜5 と、前の画像を消すコードを、Node の fetch・FormData・Blob でそのまま流しました。
ブラウザーとの違いは、URL の前に作業環境のアドレスを付けたことと、クッキーを手で付けたことです。
PNG は、canvas の代わりに PHP の GD で、同じ 1200×630・同じ棒の位置の試しの画像を作って使いました。
手順 3〜5 を 2 回流し(作り直し)、そのあと前の画像を消しました。
| リクエスト | 結果 |
|---|---|
GET admin-ajax.php?action=rest-nonce |
200・10 文字の nonce |
POST /wp/v2/media(1 回目) |
201・1200×630・alt とタイトルが入った |
POST /wp/v2/posts/{ID}(featured_media) |
200・featured_media が 1 回目の画像の ID になった・下書きのまま |
GET /wp/v2/posts/{ID}?context=edit と POST(本文) |
どちらも 200・本文のいちばん上に img が入った |
POST /wp/v2/media(2 回目・同じファイル名) |
201・別のメディアになり、ファイル名は -1 の付いた名前 |
POST /wp/v2/posts/{ID}(2 回目) |
200・featured_media が 2 回目の画像の ID に変わった |
DELETE /wp/v2/media/{前の ID}(force なし) |
501・rest_trash_not_supported |
DELETE /wp/v2/media/{前の ID}?force=true |
200・deleted: true |
流したあと、WordPress の関数で状態を見ました。
| 見たこと | 結果 |
|---|---|
get_post_thumbnail_id() |
2 回目の画像の ID |
代替テキスト(_wp_attachment_image_alt) |
手順 3 で送った alt と同じ |
| テーマ用に作られた大きさ | 11 種類(830×510・400×400 など) |
| 前の画像 | メディアからも、ファイルも消えた |
| 本文の img | 2 つ(2 回流したため)。前の画像の img も残っていた |
| 表示のときの img | loading="lazy" と decoding="async" が足された |
画像の親(post_parent) |
0。記事に「添付」された形にはならない |
今回検証したサイトでは、テーマのカスタマイズで srcset を止めているため、img に srcset は付きませんでした。
今回検証したサイトでは、WebP に変えるプラグインが、アップロードした画像の WebP のファイルを別のフォルダに作っていました。
前の画像を消したあと、そのフォルダの WebP のファイルは残っていました。
画像を消したときは、プラグインが作ったファイルも残っていないかを見てください。
確かめたあと、試しの記事・画像・WebP のファイル・作ったログインのセッションを消し、メディアの数とファイルが元と同じになったことを見ました。
試していないこと
- ブラウザーの canvas で描くところ(手順 1)と、
canvas.toBlob()(手順 2)は、試していません。作業環境の Node には canvas がないためです - そのため、日本語のフォントの見え方・角の丸い棒・文字の位置は、目で見ていません
- ブラウザーのコンソールから、ログインのクッキーで流すところも、試していません(Node でクッキーを付けて同じリクエストを送りました)
- ブロックエディターの記事での見え方は、試していません
まとめ
- 数字を扱う記事のアイキャッチは、記事の中の数字のグラフにすると、内容が伝わりやすい
- 管理画面のコンソールで、canvas に描く →
toBlob()で PNG にする →/wp/v2/mediaにアップロード →featured_mediaに ID を入れる、の順で、ソフトやプラグインを使わずに登録できる - 棒の高さは、先に出どころの文字の位置を決め、残った高さを棒の数で分けて決める。決まった高さで並べると、棒が多いときに文字に重なる
- テーマが左右を切り抜くので、文字は切り抜かれない範囲に置く
- alt には、グラフの数字が読める文を入れる
- 作り直すと前の画像がメディアに残る。消すときは
force=trueを付け、本文の img やプラグインが作ったファイルも見る
参考
- Canvas API(MDN)
- CanvasRenderingContext2D.roundRect()(MDN)
- CanvasRenderingContext2D.fillText()(MDN)
- HTMLCanvasElement.toBlob()(MDN)
- FormData(MDN)
- Media(REST API Handbook・developer.wordpress.org)
- Posts(REST API Handbook・developer.wordpress.org)
- Authentication(REST API Handbook・developer.wordpress.org)
- add_image_size()(developer.wordpress.org)


