You are currently viewing HTMLとCSSでサムネイルジェネレータを作ってみました
  • 投稿カテゴリー:-- Web
  • 投稿の最終変更日:2022-03-13

どうも、繁忙期をなんとか乗り越え本ブログを更新できる時間が戻ってきました。しかし、またいつ時間が取れなくなるかはわかりません。そこで、せめてブログの更新時間が減るようにサムネイルを簡単に作れる仕組みを作ってみました。色々作り方はあると思いますが、今回はHTMLとCSSでサムネイルのフォーマットを作り、それを画像化するという方針です。

どんなのができる?

本ブログでは主に2種類のフォーマットでサムネイルを作っているのですが、その中でも一番使っているものを生成できるようにしてみました。

やっていることは、背景画像を半透明の黒で暗くして、そこに半透明の文字抜きの白をのせています。

できたもの

というわけで、このような物ができました。

動作画面イメージ

背景画像の項目で端末内の画像を選択すると背景画像として表示され、その下のタイトルの欄に文字列を入力するとタイトルとして表示されます。基本的に誰にも公開する気はなかったので見た目としては全く手を入れてません。

ちょっとだけコード詳細

大したことはしていませんが、少しだけどんなコードかを書いておこうと思います。

コード全体
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>サムネイルジェネレータ</title>
  <link rel="stylesheet" href="./resource/ress.min.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.9.0/html-to-image.min.js"></script>
</head>
<body>
  <div class="container">
    <h1>koneta.click サムネイルジェネレータ</h1>

    <div id="thumbnail-background" class="thumbnail">
      <div class="thumbnail__background-black__wrapper"></div>
      <!-- 半透明のテキストエリア テキストはエリアに高さを持たせるためのダミー -->
      <div class="thumbanail__title-dummy__area">
        <p id="p-title-dummy" class="thumbanail__title-dummy__text">タイトルサンプル</p>
      </div>
      <!-- タイトル文字に背景が透過しているように見せるための要素 -->
      <div id="thumbnail-background-dummy" class="thumbanail__title__area">
        <p id="p-title" class="thumbanail__title__text">タイトルサンプル</p>
      </div>
    </div>

    <div class="contoroler">
      <h2>操作</h2>
      <h3>背景画像</h3>
      <input type="file" id="form-thumbnail-background">
      <h3>タイトル(HTML記法でOK)</h3>
      <textarea id="form-text-title">タイトルサンプル</textarea>
      <h3>保存</h3>
      <button id="button-save">画像にする</button>
      <div class="controler__result-img__wrapper">
        <img id="result-image" />
      </div>
    </div>
  </div>

  <script>
    // 背景画像用処理
    const fileInput = document.getElementById('form-thumbnail-background')
    fileInput.addEventListener('change', () => {
      const reader = new FileReader()
      // ファイルが読み込まれたときに実行する
      reader.onload = function (e) {
        const imageUrl = e.target.result
        const thumbnailBackground = document.getElementById("thumbnail-background")
        thumbnailBackground.style.backgroundImage = `url(${imageUrl})`

        const thumbnailBackgroundDummy = document.getElementById("thumbnail-background-dummy")
        thumbnailBackgroundDummy.style.backgroundImage = `url(${imageUrl})`
      }
      reader.readAsDataURL(fileInput.files[0])
    })

    // タイトル用処理
    const textInput = document.getElementById('form-text-title')
    textInput.addEventListener('change', () => {
      // 高さ産出用と背景を見せる用の要素にテキストを設定する
      document.getElementById('p-title-dummy').innerHTML = textInput.value
      document.getElementById('p-title').innerHTML = textInput.value
    })

    // 画像化関係処理
    const saveButton = document.getElementById('button-save')
    saveButton.addEventListener('click', () => {
      const thumbnail = document.getElementById('thumbnail-background')
      htmlToImage.toJpeg(thumbnail)
        .then(function (dataUrl) {
          document.getElementById('result-image').src = dataUrl
        })
        .catch(function (error) {
          console.error('oops, something went wrong!', error);
        });
    })
  </script>
  <style>
    @font-face {
      font-family: 'NikumaruFont';
      src: url(./resource/nikumaru.otf);
    }
    .container {
      width: 100%;
      max-width: 1000px;
      margin: 0 auto;
    }
    .thumbnail {
      width: 100%;
      aspect-ratio: 16 / 9;
      position: relative;
      background-color: #000000;
      background-size: cover;
      background-position: center;
    }
    .thumbnail__background-black__wrapper {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 10;
      background-color: rgba(0, 0, 0, 0.3);
    }
    .thumbanail__title-dummy__area {
      width: 80%;
      padding: 3% 0;
      position: absolute;
      top: 50%;
      left: 50%;
      z-index: 20;
      transform: translate(-50%, -50%);
      background: rgba(255, 255, 255, 0.9);
      outline: 6px solid rgba(255, 255, 255, 0.9);
      outline-offset: 4px;
      text-align: center;
    }
    .thumbanail__title__area {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 30;
      background-size: cover;
      background-position: center;
      background-clip: text;
      -webkit-background-clip: text;
    }
    .thumbanail__title-dummy__text {
      font-size: 3rem;
      font-family: NikumaruFont;
      color: transparent;
    }
    .thumbanail__title__text {
      font-size: 3rem;
      font-family: NikumaruFont;
      color: rgba(0, 0, 0, 0.3);
    }
    .controler__result-img__wrapper {
      width: 100%;
    }
    .controler__result-img__wrapper img {
      width: 100%;
    }
  </style>
</body>
</html>

今回のコードではサムネイルで使っているフォントやcssのリセットを読み込んでいますが、それらはライセンスの関係もありここでは載せていません。フォントはこちらのにくまるフォント、リセットにはress.cssを使っています。もし、そのまま当サイトと同じフォーマットのサムネイルを作りたい場合は上記の2つをresourseディレクトリに設置してからアクセスしてみてください。

ちなみにフォントやサムネイルの画像素材など、本サイトで使わせてもらっている素材たちはこちらのページにまとめていますので、気になった方はこちらをご覧ください。

要所としては入力された画像やタイトルをサムネイルに反映する部分だと思います。値が入力されたときそれを画面に反映しようと思ったとき、最近ではVueなどのフレームワークがうかぶかもしれませんが、今回のツールくらいであれば直書きで十分です。

まずは画像が選択されたときの処理です。

const fileInput = document.getElementById('form-thumbnail-background')
fileInput.addEventListener('change', () => {
  const reader = new FileReader()
  // ファイルが読み込まれたときに実行する
  reader.onload = function (e) {
    const imageUrl = e.target.result
    // 選択された画像を要素の背景に設定する
    const thumbnailBackground = document.getElementById("thumbnail-background")
    thumbnailBackground.style.backgroundImage = `url(${imageUrl})`
  }
  reader.readAsDataURL(fileInput.files[0])
})

これで画像読み込みはオッケーです。次にテキスト部分は以下のようになります。

const textInput = document.getElementById('form-text-title')
textInput.addEventListener('change', () => {
  document.getElementById('p-title').innerHTML = textInput.value
})

テキストに関してはこれだけです。これだけの記述量ですみ、環境構築も不要な点でもフレームワークは不要だと思います。これでサムネイル画像完成です。

もしこのコードから改造を行う場合は、サムネイルのフォーマットを作っているcssと上記のjsで変更する対象を調整すればいいと思います。

終わりに

もともとサムネイルを作る際はGIMPというフリーソフトを使っており、作業時間は30分ほどかかっていました。それがこのジェネレータを使うことで5分程度でサムネイルを作れるようになりました。また、webページとして作成したため、作業できる端末がGIMPをインストールしているデスクトップやMacBookに限られていたところを、主にブログ更新に使っているChromebookでもできるようになったのが大きいところです。

実はボタンを押すだけで画像を生成するところまで実装しようと思っていたのですが、どうやらいま出ているライブラリでは対応していないcssを使ってしまっているためうまく生成してくれませんでした。そのため今はフォーマット完成後スクリーンショットを撮るという微妙な形になっています。そのうち時間ができたらここらもどうにかしていきたいと思います。

あとは他のサムネイルもサクッと作れるように更新していこうと思います。