html Javascript WordPress プログラミング

ブログのアイキャッチ画像をサクッと作成するWebアプリを作った

アイキャッチ画像づくりは意外とめんどくさい

このブログには記事の一番上にアイキャッチ画像が入るデザインになっています。

物理的な工作の記事の時は作った物の写真を使いますし、画面のあるプログラムの場合ならスクリーンショットが使えます。

しかし、画面もないようなプログラム関係の備忘録とかだといつも画像に困ります。

今まではCanvaとかを使ってなんかそれっぽいデザインの画像を作っていましたが、それも面倒になってきました。

そこで立派なものでなくてもよいので、シンプルなアイキャッチ画像を素早く作れるWebアプリを作りました。

アプリは以下にあります。

Blog Eye Catch Pict Maker(GitHub Pages)

ソースコードはこちらです。

https://github.com/ryjkmr/blog_eye_catch_pic_maker

専用ツールならではの快適さ

このアプリはこのブログの仕様に合わせてあります。

解像度は1280×720に固定、色の付いた抽象的な背景の上に英文でタイトルを乗せます。

起動すると、すでにその仕様の画面になっているので文字を入力してdownloadボタンを押すだけです。

いつもまったく同じではつまらないので、目的に合った範囲でバリエーションを付けられるようにしました。

書体は大きさ、太さ、幅を選択できるようにしました。

文字位置はマウスドラッグで自由に動かせます。

背景の色や模様は起動時や設定変更時にランダムに変わります。ただし色は文字の邪魔をしない程度の濃さに抑えています。

必要なら画像を読み込んで背景にすることもできます。画像は自動的に1280×720にリサイズして切り取られます。

背景の模様は四角、丸、三角のランダムな組み合わせです。

塗りの有無や大きさ、図形の数など、オプションがたくさんあるように見えますが、基本3段階の選択です。微調整はあえて出来ないようにしています。

すでに何度か使いましたが、アイキャッチ画像を作るのがとても簡単になって記事を公開する時のハードルが下がりました。

ニッチな用途ほど専用アプリの効果が高いことを実感しました。

ランダムな色と模様の背景を作る

背景はcanvasで描いています。

canvasへの描画の基本はここに書きました。

まず、canvasを単色で塗りつぶします。

色はランダムに取得しています。カラーモデルをHSLにして、色相だけを変えることで常に薄い色になるようにしています。

単色で塗りつぶした背景の上に、ランダムな位置、大きさ、角度で四角形、三角形、円を描きます。

図形を半透明にしているので重なりやグラデーション的な効果が得られます。

    //draw background
    function drawBackground() {
        context.fillStyle = makeRandomColor();
        context.globalAlpha = 1;
        context.fillRect(0, 0, canvas.width, canvas.height);
    }


    function draw() {
        const freq = $('input[name=freq]:checked').val();
        const size = $('input[name=size]:checked').val();
        const isWhite = $('input[name=white]:checked').val();
        drawBackground();
        context.globalAlpha = 0.3;
        if ($("#triangle").prop('checked')) drawTriangles(freq, isWhite, size);
        if ($("#rectangle").prop('checked')) drawRectangles(freq, isWhite, size);
        if ($("#circle").prop('checked')) drawCircles(freq, isWhite, size);
    }



    //draw Triangles
    function drawTriangles(f, w, s) {
        for (let i = 0; i < f; i++) {
            const position = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            position.x = Math.random() * canvas.width - 100;
            position.y = Math.random() * canvas.height - 100;
            const radius = Math.random() * canvas.width / 4 * s;
            const rotation = Math.random() * 360;
            const x1 = radius * Math.cos(2 * Math.PI * (rotation + 30) / 360) + position.x;
            const y1 = radius * Math.sin(2 * Math.PI * (rotation + 30) / 360) + position.y;
            const x2 = radius * Math.cos(2 * Math.PI * (rotation + 150) / 360) + position.x;
            const y2 = radius * Math.sin(2 * Math.PI * (rotation + 150) / 360) + position.y;
            const x3 = radius * Math.cos(2 * Math.PI * (rotation + 270) / 360) + position.x;
            const y3 = radius * Math.sin(2 * Math.PI * (rotation + 270) / 360) + position.y;
            context.beginPath();
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.moveTo(x1, y1);
            context.lineTo(x2, y2);
            context.lineTo(x3, y3);
            context.closePath();

            if ($("#fill").prop('checked')) {
                context.fillStyle = color;
                context.fill();
            } else {
                context.stroke();
            }

        }
    }

    function makeRandomColor() {
        const hue = Math.random() * 360;
        const lum = $('input[name=background]:checked').val();
        return `hsl( ${hue}, 80%, ${lum} )`;
    }

    //draw Rectangles
    function drawRectangles(f, w, s) {
        for (let i = 0; i < f; i++) {
            const position = {};
            const rect = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            const plusOrMinus = Math.round(Math.random()) ? 1 : -1;
            position.x = Math.random() * canvas.width - 200;
            position.y = Math.random() * canvas.height - 200;
            rect.width = Math.random() * canvas.width / 4 * plusOrMinus * s;
            rect.height = Math.random() * canvas.width / 4 * plusOrMinus * s;
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.beginPath();
            context.fillStyle = color;

            if ($("#fill").prop('checked')) {
                context.fillRect(position.x, position.y, position.x + rect.width, position.y + rect.height);
                context.fill();
            } else {
                context.strokeRect(position.x, position.y, position.x + rect.width, position.y + rect.height);


            }

        }
    }

    //draw circle
    function drawCircles(f, w, s) {
        for (let i = 0; i < f; i++) {
            // break;
            const position = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            position.x = Math.random() * canvas.width - 100;
            position.y = Math.random() * canvas.height - 100;
            const radius = Math.random() * canvas.width / 4 * s;
            context.beginPath();
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.arc(position.x, position.y, radius, 0, 2 * Math.PI);
            context.fillStyle = color;
            if ($("#fill").prop('checked')) {
                context.fill();
            } else {
                context.stroke();
            }

        }
    }

Webフォントを使用

アイキャッチ画像ではテキストのクオリティが重要なのでWebフォントを使いました。

Webフォントの説明はこちら。

公開版はGoogleフォントを使っていますが、個人用ではAdobeフォントを使っています。

やはりAdobeのフォントの方がクオリティが高いです。

AdobeフォントはGitHubとかでコードごと公開して良いのかどうか、ライセンスがよく分からないので公開版はGoogleフォントに差し替えました。

CSS変数を書き換えて文字サイズを変更

フォントサイズの変更にはCSS変数を使いました。

#textboxのCSSでtitle-font-sizeという変数を定義。

フォントサイズのボタンをクリックするとこの変数を書き換えます。

CSS変数の解説ではrootに設定することが多いようですが、ここでは特定の要素内で使ってみました。

変数をjavascriptから参照するのにちょっと手こずりました。

#textbox {
  --title-font-size: 144px;
  position: absolute;
  top: 50px;
  left: 50px;
  text-align: left;
  font-size: var(--title-font-size);
  line-height: 100%;
}
    $('.fontSize').on('click', function (e) {
        const size = $(this).prop("name");
        const element = document.querySelector('#textbox');
        element.style.setProperty("--title-font-size", size);

    });

文字位置をドラッグで自由に動かせるようにした

文字位置は自由にドラッグして動かせるようにしました。

HTML要素をドラッグ可能にするコードは、以下のブログにあったコードを元に作成しました。感謝。

https://q-az.net/elements-drag-and-drop/

変更点としては、クリックしたときに要素がマウスに張り付く現象の防止、グローバル的な変数の廃止、移動制限の追加などです。

また、#textboxのcontentseditable属性をtrueにして文字を直接編集できるようにしています。

<div id="textbox" class="gfont0 drag-and-drop" contenteditable="true">
//テキストボックスのドラッグ移動処理
    //以下のサイトにあったプログラムを元に作成
    // https://q-az.net/elements-drag-and-drop/

    const textbox = document.getElementById("textbox");

    //textboxにイベントを設定
    textbox.addEventListener("mousedown", mdown, false);
    textbox.addEventListener("touchstart", mdown, false);


    //マウスが押された時
    function mdown(e) {

        this.classList.add("drag");

        //タッチデイベントとマウスのイベントの差異を吸収
        const event = (e.type === "mousedown") ? e : e.changedTouches[0];

        //要素内の相対座標を取得。textboxにx,yプロパティを追加
        textbox.x = event.pageX - this.offsetLeft;
        textbox.y = event.pageY - this.offsetTop;

        //mousemoveイベントに設定
        document.body.addEventListener("mousemove", mmove, false);
        document.body.addEventListener("touchmove", mmove, false);
    }

    //mousedown中にカーソルが動いた時
    function mmove(e) {

        //マウスとタッチの差異を吸収
        const event = (e.type === "mousemove") ? e : e.changedTouches[0];

        //フリックしたときに画面を動かさないようにデフォルト動作を抑制
        e.preventDefault();

        //マウス位置にオブジェクトを移動。範囲制限付き
        textbox.style.top = clipNumber(event.pageY - textbox.y, 20, 600) + "px";
        textbox.style.left = clipNumber(event.pageX - textbox.x, 10, 1100) + "px";

        //マウスボタンが離されたとき、またはカーソルが外れたときmupを設定
        textbox.addEventListener("mouseup", mup, false);
        document.body.addEventListener("mouseleave", mup, false);
        document.body.addEventListener("click", mup, false);//クリック時の誤作動防止を追加
        textbox.addEventListener("touchend", mup, false);
        document.body.addEventListener("touchleave", mup, false);

    }

    //マウスボタンが上がった時の終了処理
    function mup() {

        document.body.removeEventListener("mousemove", mmove, false);
        document.body.removeEventListener("touchmove", mmove, false);
        document.body.removeEventListener("click", mup, false);
        textbox.removeEventListener("mouseup", mup, false);
        textbox.removeEventListener("touchend", mup, false);
        textbox.classList.remove("drag");
    }

    //数字を一定の範囲に収める関数
    function clipNumber(n, min, max) {
        if (isNaN(n)) return false;
        result = n < min ? min :
            n > max ? max : n;
        return result;
    }

画像の読み込みとフィット

画像の読み込みはinput type="file"のchangeイベントとして設定しています。


    $("#loadImage").change(function (e) {
        const file = e.target.files[0];
        const reader = new FileReader();

        reader.addEventListener("load", function () {
            $("#image").attr("src", reader.result);
            $("#image").removeClass("hide");
        }, false);

        if (file) {
            reader.readAsDataURL(file);
        }
    });

CSSでwidthを固定、"object-fit: cover;"を設定して画像が枠内に隙間無く収まるようにしています。

#image {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 1280px;
  object-fit: cover;
}

ダウンロード可能にする

ダウンロードは以前も使ったhtml2canvasを利用しています。

文字と画像を囲む親要素のdivをレンダリングしてダウンロードしています。

    $('#download').on('click', function (e) {
        html2canvas(document.querySelector("#wrapper"), { scale: 1 }).then(c => {
            let downloadEle = document.createElement("a");
            downloadEle.href = c.toDataURL("image/png");
            const filename = "catch" + ".png";
            downloadEle.download = filename;
            downloadEle.click();
        });
    });

UIはjQueryで設定

ボタン類がたくさんあるのでUIのイベント設定にはjQueryを使いました。

document.getElementByIDという長い文を何度も書かなくて良いのがありがたいです。

ラジオボタンやselect要素のどれが選ばれているかを取得するには、以下の方法を使いました。

全てのコード

GitHubにありますが、一応全コードを載せておきます。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Blog Eye Catch Picture Maker</title>

  <!-- Google Font setting -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link
    href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@300;500;700;900&family=Barlow:wght@300;500;700;900&display=swap"
    rel="stylesheet">

  <link rel="stylesheet" href="style-gfont.css">
</head>

<body>
  <p>ver.0808</p>
  <div id="wrapper">
    <canvas id="myCanvas"></canvas>
    <img src="" alt="" id="image" class="hide">
    <div id="textbox" class="gfont0 drag-and-drop" contenteditable="true">
      this is blog title
    </div>

  </div>
  <br><br>

  <div class="controls">
    <button class="fontSample gfont0" name="gfont0">font</button>
    <button class="fontSample gfont1" name="gfont1">font</button>
    <button class="fontSample gfont2" name="gfont2">font</button>
    <button class="fontSample gfont3" name="gfont3">font</button>
    <button class="fontSample gfont4" name="gfont4">font</button>
    <button class="fontSample gfont5" name="gfont5">font</button>
    <button class="fontSample gfont6" name="gfont6">font</button>
    <button class="fontSample gfont7" name="gfont7">font</button>
  </div>
  <div class="controls">
    <button class="fontSize" name="120px">small</button>
    <button class="fontSize" name="144px">medium</button>
    <button class="fontSize" name="170px">large</button>
    <span>----</span>
    <button class="textAlign" name="left">left</button>
    <button class="textAlign" name="center">center</button>
    <button class="textAlign" name="right">right</button>
    <span>----</span>

    <input class="checkbox " type="checkbox" id="shadow" name="shadow">
    <label for="shadow">glow</label>


  </div>

  <br>
  <div class="controls">
    <label for="loadImage" style="border:solid 1px black; padding:5px">
      <input id="loadImage" type="file" style="display:none">
      upload background image</label>
    <button id="deleteImage">delete image</button>
  </div>
  <br>
  <div class="controls">
    <span class="optionName">Background: </span>
    <label for="dark"><input type="radio" name="background" id="dark" value="70%" class="size control-items">dark</label>
    <label for="bright"><input type="radio" name="background" id="bright" value="90%" class="size control-items"
        checked>bright</label>
  </div>

  <div class="controls">
    <span class="optionName">Shape: </span>
    <input class="checkbox control-items" type="checkbox" id="triangle" name="triangle" checked>
    <label for="triangle">triangles</label>
    <input class="checkbox control-items" type="checkbox" id="rectangle" name="rectangle" checked>
    <label for="rectangle">rectangles</label>
    <input class="checkbox control-items" type="checkbox" id="circle" name="circle" checked>
    <label for="circle">circles</label>
  </div>
  <div class="controls">
    <span class="optionName">Paint: </span>
    <input class="checkbox control-items" type="checkbox" id="fill" name="fill" checked>
    <label for="fill">fill</label>
    <input class="checkbox control-items" type="checkbox" id="white" name="white" checked>
    <label for="white">white</label>

  </div>

  <div class="controls">
    <span class="optionName">Shape frequency: </span>
    <label for="less"><input type="radio" name="freq" id="less" value="5" class="freq control-items">less</label>
    <label for="normal"><input type="radio" name="freq" id="normal" value="10" class="freq control-items"
        checked>normal</label>
    <label for="more"><input type="radio" name="freq" id="more" value="20" class="freq control-items">more</label>
  </div>
  <div class="controls">
    <span class="optionName">Shape size: </span>
    <label for="small"><input type="radio" name="size" id="small" value="0.2" class="size control-items">small</label>
    <label for="medium"><input type="radio" name="size" id="medium" value="0.5" class="size control-items"
        checked>medium</label>
    <label for="large"><input type="radio" name="size" id="large" value="1" class="size control-items">large</label>
  </div>


  <div class="controls">
    <button id="redraw">redraw</button>
    <button id="download">download</button>
    <button id="reset">reset text</button>
  </div>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
    integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
    crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
  <script>

  </script>

  <script src="script.js"></script>
</body>

</html>
(function () {

    $('.control-items').on('change', function (e) {
        draw();
    });

    $('#redraw').on('click', function (e) {
        draw();
    });

    $('#download').on('click', function (e) {
        html2canvas(document.querySelector("#wrapper"), { scale: 1 }).then(c => {
            let downloadEle = document.createElement("a");
            downloadEle.href = c.toDataURL("image/png");
            const filename = "catch" + ".png";
            downloadEle.download = filename;
            downloadEle.click();
        });
    });


    const canvas = document.getElementById("myCanvas");
    const canvasWidth = 1280;
    const canvasHeight = 720;
    const context = canvas.getContext("2d");
    canvas.setAttribute("width", canvasWidth);
    canvas.setAttribute("height", canvasHeight);

    draw();

    //draw background
    function drawBackground() {
        context.fillStyle = makeRandomColor();
        context.globalAlpha = 1;
        context.fillRect(0, 0, canvas.width, canvas.height);
    }


    function draw() {
        const freq = $('input[name=freq]:checked').val();
        const size = $('input[name=size]:checked').val();
        const isWhite = $('input[name=white]:checked').val();
        drawBackground();
        context.globalAlpha = 0.3;
        if ($("#triangle").prop('checked')) drawTriangles(freq, isWhite, size);
        if ($("#rectangle").prop('checked')) drawRectangles(freq, isWhite, size);
        if ($("#circle").prop('checked')) drawCircles(freq, isWhite, size);
    }



    //draw Triangles
    function drawTriangles(f, w, s) {
        for (let i = 0; i < f; i++) {
            const position = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            position.x = Math.random() * canvas.width - 100;
            position.y = Math.random() * canvas.height - 100;
            const radius = Math.random() * canvas.width / 4 * s;
            const rotation = Math.random() * 360;
            const x1 = radius * Math.cos(2 * Math.PI * (rotation + 30) / 360) + position.x;
            const y1 = radius * Math.sin(2 * Math.PI * (rotation + 30) / 360) + position.y;
            const x2 = radius * Math.cos(2 * Math.PI * (rotation + 150) / 360) + position.x;
            const y2 = radius * Math.sin(2 * Math.PI * (rotation + 150) / 360) + position.y;
            const x3 = radius * Math.cos(2 * Math.PI * (rotation + 270) / 360) + position.x;
            const y3 = radius * Math.sin(2 * Math.PI * (rotation + 270) / 360) + position.y;
            context.beginPath();
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.moveTo(x1, y1);
            context.lineTo(x2, y2);
            context.lineTo(x3, y3);
            context.closePath();

            if ($("#fill").prop('checked')) {
                context.fillStyle = color;
                context.fill();
            } else {
                context.stroke();
            }

        }
    }

    function makeRandomColor() {
        const hue = Math.random() * 360;
        const lum = $('input[name=background]:checked').val();
        return `hsl( ${hue}, 80%, ${lum} )`;
    }

    //draw Rectangles
    function drawRectangles(f, w, s) {
        for (let i = 0; i < f; i++) {
            const position = {};
            const rect = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            const plusOrMinus = Math.round(Math.random()) ? 1 : -1;
            position.x = Math.random() * canvas.width - 200;
            position.y = Math.random() * canvas.height - 200;
            rect.width = Math.random() * canvas.width / 4 * plusOrMinus * s;
            rect.height = Math.random() * canvas.width / 4 * plusOrMinus * s;
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.beginPath();
            context.fillStyle = color;

            if ($("#fill").prop('checked')) {
                context.fillRect(position.x, position.y, position.x + rect.width, position.y + rect.height);
                context.fill();
            } else {
                context.strokeRect(position.x, position.y, position.x + rect.width, position.y + rect.height);


            }

        }
    }

    //draw circle
    function drawCircles(f, w, s) {
        for (let i = 0; i < f; i++) {
            // break;
            const position = {};
            const hue = Math.random() * 360;
            const color = w ? "rgb(255,255,255)" : `hsl( ${hue}, 30%, 70% )`;
            position.x = Math.random() * canvas.width - 100;
            position.y = Math.random() * canvas.height - 100;
            const radius = Math.random() * canvas.width / 4 * s;
            context.beginPath();
            context.strokeStyle = color;
            context.lineWidth = 10;
            context.arc(position.x, position.y, radius, 0, 2 * Math.PI);
            context.fillStyle = color;
            if ($("#fill").prop('checked')) {
                context.fill();
            } else {
                context.stroke();
            }

        }
    }

    $("#deleteImage").on("click", function (e) {
        $("#image").attr('src', "");
        $("#image").addClass("hide");
    });

    $("#shadow").on("change", function (e) {
        if ($(this).prop('checked')) {
            $("#textbox").addClass("shadow");
        } else {
            $("#textbox").removeClass("shadow");
        };
    });

    $('.fontSample').on('click', function (e) {
        const name = $(this).prop("name");
        const num = document.querySelectorAll(".fontSample").length;
        for (let i = 0; i < num; i++) {
            $("#textbox").removeClass(name.slice(0, -1) + i);
        }
        $("#textbox").addClass(name);

    });

    $('.fontSize').on('click', function (e) {
        const size = $(this).prop("name");
        const element = document.querySelector('#textbox');
        element.style.setProperty("--title-font-size", size);

    });

    $('.textAlign').on('click', function (e) {
        const align = $(this).prop("name");
        $("#textbox").css({
            textAlign: align
        });

    });


    $("#reset").on("click", function (e) {
        textbox.style.top = "50px";
        textbox.style.left = "50px";
        if (textbox.innerText === "") textbox.innerText = "text";
    });


    $("#loadImage").change(function (e) {
        const file = e.target.files[0];
        const reader = new FileReader();

        reader.addEventListener("load", function () {
            $("#image").attr("src", reader.result);
            $("#image").removeClass("hide");
        }, false);

        if (file) {
            reader.readAsDataURL(file);
        }
    });




    //テキストボックスのドラッグ移動処理
    //以下のサイトにあったプログラムを元に作成
    // https://q-az.net/elements-drag-and-drop/

    const textbox = document.getElementById("textbox");

    //textboxにイベントを設定
    textbox.addEventListener("mousedown", mdown, false);
    textbox.addEventListener("touchstart", mdown, false);


    //マウスが押された時
    function mdown(e) {

        this.classList.add("drag");

        //タッチデイベントとマウスのイベントの差異を吸収
        const event = (e.type === "mousedown") ? e : e.changedTouches[0];

        //要素内の相対座標を取得。textboxにx,yプロパティを追加
        textbox.x = event.pageX - this.offsetLeft;
        textbox.y = event.pageY - this.offsetTop;

        //mousemoveイベントに設定
        document.body.addEventListener("mousemove", mmove, false);
        document.body.addEventListener("touchmove", mmove, false);
    }

    //mousedown中にカーソルが動いた時
    function mmove(e) {

        //マウスとタッチの差異を吸収
        const event = (e.type === "mousemove") ? e : e.changedTouches[0];

        //フリックしたときに画面を動かさないようにデフォルト動作を抑制
        e.preventDefault();

        //マウス位置にオブジェクトを移動。範囲制限付き
        textbox.style.top = clipNumber(event.pageY - textbox.y, 20, 600) + "px";
        textbox.style.left = clipNumber(event.pageX - textbox.x, 10, 1100) + "px";

        //マウスボタンが離されたとき、またはカーソルが外れたときmupを設定
        textbox.addEventListener("mouseup", mup, false);
        document.body.addEventListener("mouseleave", mup, false);
        document.body.addEventListener("click", mup, false);//クリック時の誤作動防止を追加
        textbox.addEventListener("touchend", mup, false);
        document.body.addEventListener("touchleave", mup, false);

    }

    //マウスボタンが上がった時の終了処理
    function mup() {

        document.body.removeEventListener("mousemove", mmove, false);
        document.body.removeEventListener("touchmove", mmove, false);
        document.body.removeEventListener("click", mup, false);
        textbox.removeEventListener("mouseup", mup, false);
        textbox.removeEventListener("touchend", mup, false);
        textbox.classList.remove("drag");
    }

    //数字を一定の範囲に収める関数
    function clipNumber(n, min, max) {
        if (isNaN(n)) return false;
        result = n < min ? min :
            n > max ? max : n;
        return result;
    }


})()

#wrapper {
  position: relative;
  display: inline-block;
  overflow: hidden;
  width: 1280px;
  height: 720px;
}

#image {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 1280px;
  object-fit: cover;
}

.shadow {
  text-shadow: 2px 2px 15px white,
    -2px 2px 15px white,
    2px -2px 15px white,
    -2px -2px 15px white;
}

#textbox {
  --title-font-size: 144px;
  position: absolute;
  top: 50px;
  left: 50px;
  text-align: left;
  font-size: var(--title-font-size);
  line-height: 100%;
}

.optionName{
  font-weight: bolder;
  /* background-color:lightgray; */
  /* padding:3px; */
}

.controls {
  font-size: 26px;
  margin-left: 50px;
  user-select: none
}

.fontSample {
  margin: 10px;
  padding: 10px;
  background-color: lightgray;
  font-size: 32px;
}


button {
  font-size: 24px;
  margin: 10px;
}

.drag-and-drop {
  cursor: move;
  position: absolute;
  z-index: 1000;
}

.drag {
  z-index: 1001;
}

.hide {
  display: none;
}

.barlow {
  font-family: "Barlow", sans-serif;
}

.condenced {
  font-family: "Barlow Condensed", sans-serif;
}

.gfont0 {
  font-family: "Barlow", sans-serif;
  font-weight: 300;
}

.gfont1 {
  font-family: "Barlow", sans-serif;
  font-weight: 500;
}

.gfont2 {
  font-family: "Barlow", sans-serif;
  font-weight: 700;
}

.gfont3 {
  font-family: "Barlow", sans-serif;
  font-weight: 900;
}

.gfont4 {
  font-family: "Barlow Condensed", sans-serif;
  font-weight: 300;
}

.gfont5 {
  font-family: "Barlow Condensed", sans-serif;
  font-weight: 500;
}

.gfont6 {
  font-family: "Barlow Condensed", sans-serif;
  font-weight: 700;
}

.gfont7 {
  font-family: "Barlow Condensed", sans-serif;
  font-weight: 900;
}

-html, Javascript, WordPress, プログラミング