サムネイルの用意が面倒なので絵文字を使えるようにした

Category: Tech
2025-01-27
thumbnail

🥳 ← 一番好きな絵文字のPartyfaceくん

サムネめんどい

noteとかみてるとストック画像とかにタイトルを重ねたり凝ったサムネを用意している人が多いですが、ブログを始めて自分も挑戦してみたところ、まぁこれがめんどい。記事書くのも大変なのに、んなことやってられっか
ということで早々に諦め、Zennの機能をパクって絵文字をサムネイルに使えるようにしました。
絵文字はOS標準のものではなくTwitterがオープンソースで開発するTwemojiを使用します。

ここで残念なお知らせです

「Twitterがオープンソースで開発するTwemojiを使用」と書きましたが、ご存じの通り某実業家によってTwitter社は消滅、メンテナーが解雇されTwemojiの開発は停止しています。非公開になったわけではないですが、今後開発が再開される見込みはありません。
しかし、元従業員がTwemojiをフォークし開発を続けてくれています。なので今回はこちらのTwemojiフォークを使用します。
https://github.com/jdecked/twemoji

Twemojiのurlを取得

当初、1文字ずつUnicodeの絵文字コードポイント範囲に含まれるかをチェックしてTwemoji(のCDN)のURLを作ってたのですが、そんなことしなくてもtwemoji/parserなんていう便利なものが公開されているではありませんか。npm i @twemoji/parserでインストールしたらimportしてparse()にぶち込むだけです。まぁ簡単!
自分の実装では絵文字シーケンスが無視される問題がありましたが、その周りもこのライブラリが全部やってくれます。

import { parse } from '@twemoji/parser';

const text = "aaa❤️‍🔥aaa🥰";

const emoji = parse(text);

console.log(emoji);
console.log(emoji[0].url);  //一番最初の絵文字のurl
[
  {
    url: 'https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/svg/2764-fe0f-200d-1f525.svg',
    indices: [ 3, 8 ],
    text: '❤️‍🔥',
    type: 'emoji'
  },
  {
    url: 'https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/svg/1f970.svg',
    indices: [ 11, 13 ],
    text: '🥰',
    type: 'emoji'
  }
]

https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/svg/2764-fe0f-200d-1f525.svg

あまり関係ないですが、自分は文字コードとかその符号化方式の話が好きなので、いずれまとめて記事にしたいです。(長くなりそう..)

オプション

assetTypeにpngかsvgを指定することができます。(デフォルトはsvgです)

- const emoji = parse(text);
+ const emoji = parse(text,{assetType:'png'});

出力: https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/72x72/2764-fe0f-200d-1f525.png

ブログに実装

該当部分の抜粋です。→コード全体

パースではTwemojiが対応する絵文字がない場合の処理を追加しています。

// https://git.moris.day/moris/day.moris.blog/src/branch/main/src/lib/server/MetaParser.ts

import { parse } from '@twemoji/parser';

/* ~なんやかんやあって~ */

emoji = parse(emoji)[0]?.url ?? '';

Frontmatterにサムネイル画像が設定されていない場合に絵文字を表示。それもない場合はブログ一覧では代替画像、本文では空にしました。

// https://git.moris.day/moris/day.moris.blog/src/branch/main/src/routes/%28DefaultStyle%29/post/%5Bslug%5D/+page.svelte

{#if data.metadata.thumbnail}
    <img class="thumbnail" alt="thumbnail" src="{data.metadata.thumbnail}"/>
{:else if data.metadata.emoji}
    <div class='thumbnail emoji'><img class="emoji" alt="thumbnail" src="{data.metadata.emoji}"/></div>
{/if}

最後にCSSでいい感じにして完成です。🎉
ここに書いたこと以外ではOGPの設定などもしています。(og:imageはSVG不可なので注意)

/* https://git.moris.day/moris/day.moris.blog/src/branch/main/src/lib/components/Markdown.css */

article {
/* ~ いろいろあっての ~ */
    & .thumbnail {
        display: block;
        margin: 0 auto;
        max-height: 50vh;
    }
    & img.thumbnail {
        height: 35vh;
        max-width: 100%;
        object-fit: contain;
    }
    & .emoji.thumbnail {
        width: 30%;
        max-width: 30vh;
        aspect-ratio: 1;
        @media(width<480px) {
            width: 50%;
        }
        & img {
            margin: 15%;
        }
    }
}

ちなみに絵文字はCDNからダウンロードされますが、headerの中に<link rel="preconnect" href="https://cdn.jsdelivr.net">を追加しておくことで名前解決やハンドシェイクの処理を事前に行い、ちょっっっとだけ高速化できます。