"BOKU"のITな日常

テクノロジー以外にも、日常には、面白そうな”イット”がつまってるんだな

HTML5/CanvasAPIで白抜き文字やグラデーションで描く(1)

 f:id:arakan_no_boku:20210106233420p:plain

MDNのCanvasAPIのチュートリアルを参考に、CanvasAPIで遊んでみます。

developer.mozilla.org

英語です。

一応、日本語に切り替えることはできるのですが、日本語にするとリンクが「Not Found」になるページがあるので、英語のままのほうがストレスがないです。

 

そのまま動かすだけってのも芸がないので、CSSにBulmaを使って、以下のようなサンプル画面にしてみました。

f:id:arakan_no_boku:20210106235912p:plain

 一応、Bulmaのページもおいときます。bulma.io

なお、ソースコードとかが長いものがあるので、2回にわけて記事を書きます。

 

まずはレイアウト

 

こんな感じの枠を作ります。

f:id:arakan_no_boku:20210107001449p:plain

適当なフォルダに以下のようなサブフォルダを切ってます。

f:id:arakan_no_boku:20210107001620p:plain

imagesフォルダに数字の画像をそれぞれ、s01.JPG、s02.JPG、s03.JPG、s04.JPGという名前で保存し、CanvasAPIを記述したJavaScriptは、jsフォルダの下に「demo_p01.js」という名前で保存しています。

f:id:arakan_no_boku:20210110151518j:plainf:id:arakan_no_boku:20210110151530j:plainf:id:arakan_no_boku:20210110151547j:plainf:id:arakan_no_boku:20210110151558j:plain

もし、試す場合は画像ファイルだけ適当に用意してください。

そのうえで、sample01.htmlの中身はこんな感じです。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>BULMAとCanvasチュートリアル</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://use.fontawesome.com/releases/v5.3.1/js/all.js" defer ></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css" />
  </head>
  <body>
   <header>
        <div class="hero is-info is-bold">
            <div class="hero-body">
                <div class="container">
                    <h1 class="title">BulmaとCanvasを使ったチュートリアル</h1>
                    <h2 class="subtitle">@BOKU</h2>
                </div>
            </div>
        </div>
    </header>
    <div class="columns">
        <div class="column is-half">
            <article class="box media">
                <figure class="media-left">
                    <p class="image is-48x48">
                        <img src="images/s01.JPG">
                    </p>
                </figure>
                <div class="media-content">
                    <div class="content">
                        <p><strong>チュートリアル01</strong><br>
                            中心を白でぼかした四角のパレット<br>
                        </p>
                    </div>
                    <div class="content">
                        <canvas id="c01"></canvas>
                    </div>
                </div>
            </article>
      </div>
        <div class="column is-half">
            <article class="box media">
                <figure class="media-left">
                    <p class="image is-48x48">
                        <img src="images/s02.JPG">
                    </p>
                </figure>
                <div class="media-content">
                    <div class="content">
                        <p><strong>チュートリアル02</strong><br>
                            テキスト描画と太さを変えた下線<br>
                        </p>
                    </div>
                    <div class="content">
                        <canvas id="c02"></canvas>
                    </div>
                </div>
            </article>
      </div>
    </div>
     <div class="columns">
        <div class="column is-half">
            <article class="box media">
                <figure class="media-left">
                    <p class="image is-48x48">
                        <img src="images/s03.JPG">
                    </p>
                </figure>
                <div class="media-content">
                    <div class="content">
                        <p><strong>チュートリアル03</strong><br>
                            ニコニコマークをタートルグラフィックで描く<br>
                        </p>
                    </div>
                    <div class="content">
                        <canvas id="c03"></canvas>
                    </div>
                </div>
            </article>
      </div>
        <div class="column is-half">
            <article class="box media">
                <figure class="media-left">
                    <p class="image is-48x48">
                        <img src="images/s04.JPG">
                    </p>
                </figure>
                <div class="media-content">
                    <div class="content">
                        <p><strong>チュートリアル04</strong><br>
                            時計をCanvasだけでゴリゴリ描いてみる<br>
                        </p>
                    </div>
                    <div class="content">
                        <canvas id="c04"></canvas>
                    </div>
                </div>
            </article>
      </div>
    </div>
    <footer class="footer">
        <div class="container">
            <div class="content has-text-centered">
                2021 asakan-boku
            </div>
        </div>
    </footer>
    <script src="js/demo_p01.js"></script>
  </body>
</html>

 

チュートリアルその1 

 

まず「 <canvas id="c01"></canvas>」の部分に表示する処理です。

結果はこのような表示になります。

f:id:arakan_no_boku:20210110164617p:plain

IDを指定して、該当するCanvasタグの場所に描画する場合のオブジェクトの取得はこんな感じでします。。

var c01 = document.getElementById('c01');
if (c01.getContext){
    var ctx = c01.getContext("2d")
    // ここに処理を書く
    }  
}else{
    console.log('canvasオブジェクトがNullです。');
}

ID「C01」のElementを取得して、getContextでNULLでなければ、2dのCanvasオブジェクトを取得して、それを使って描画するパターンです。

この中で。

  • 矩形(rectangle)を色を変えながら6×6個描く
  • 白い円弧(Arc)を径を変えながら複数描く

の2つをやってます。

 Canvasは「内部を塗りつぶす=fill」か「塗りつぶさないか=stroke」の2種類のメソッドが用意されてます。

なので、塗りつぶしの矩形は「fillRect」、塗りつぶさない矩形は「strokeRect」で、他の図形も基本同じで、一度規則を覚えると、とてもやりやすい命名規則になってます。

なので、「矩形(rectangle)を色を変えながら6×6個描く」処理は以下になります。

   for (var i = 0; i < 6; i++) {
        for (var j = 0; j < 6; j++) {
          ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ', ' +
                           Math.floor(255 - 42.5 * j) + ', 0)';
          ctx.fillRect(j * 25, i * 25, 25, 25);
        }
    }

わかりやすいです。

続いて、「白い円弧(Arc)を径を変えながら複数描く」です。

こちらでは「globalAlpha」を使って、円に透明度を設定しています。

それによって中心からライトがあたっているような透明感をだすわけですね。

透明度を設定した円弧を径を変えながら描くソースはこんな感じ。

    ctx.globalAlpha = 0.2;
    ctx.fillStyle = '#FFF';
       for (var i = 0; i < 7; i++) {
        ctx.beginPath();
        ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
        ctx.fill();
    }  

forループ の中でPathを使ってます。

beginPath()を実行したら、以後の命令は「Path」に対して描画されます。

つまり、即時画面更新は発生しなくなるわけです。

そして、塗りつぶす場合は「fill()」、塗りつぶさない場合は「stroke()」を呼ぶことで、Pathの内容で画面にレンダリングするわけです。

まとめて処理するときに、即時レンダリングする処理ばかりだと非効率だということなんでしょう。

チュートリアルを見ていても、基本はこのPathを使った処理になってます。

 

チュートリアルその2

今度は文字を描画する例です。

こんな感じにします。

f:id:arakan_no_boku:20210111010038p:plain

文字を描くのも、塗りつぶし(fill)か塗りつぶさないか(stroke)なのは一緒です。

上記の文字を描いている部分のソースはこんな感じです。

    ctx.font = '48px serif';
    ctx.fillText('Hello world', 10, 50);
    ctx.strokeText('Hello world', 30, 70);

フォントの種類とサイズを指定して、位置と文字列を指定して描くだけです。

それだけだとあまりに芸がないので、文字の下に太さを変えながら横線を5本描いてみました。

線だけを描くのは「stroke」なので、以下のような感じになります。

    ctx.strokeStyle = '#F22'
    for (var i = 0; i < 5; i++) {
        ctx.lineWidth = 1 + i;
        ctx.beginPath();
        ctx.moveTo(30,10 * i + 70);
        ctx.lineTo(300,10 * i + 70);
        ctx.stroke();
    }  

始点に移動(moveTo)して線を引く(lineTo)だけなので、悩むところはありません。

 

残りは次回にします

 

行数が多くなりすぎるので「その3」と「その4」は次回にします。

最後に、その1とその2のJavaScript部分全体をのせておきます。

demo_p01.js

var c01 = document.getElementById('c01');
if (c01.getContext){
    var ctx = c01.getContext("2d")
    for (var i = 0; i < 6; i++) {
        for (var j = 0; j < 6; j++) {
          ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ', ' +
                           Math.floor(255 - 42.5 * j) + ', 0)';
          ctx.fillRect(j * 25, i * 25, 25, 25);
        }
    }
    ctx.globalAlpha = 0.2;
    ctx.fillStyle = '#FFF';
       for (var i = 0; i < 7; i++) {
        ctx.beginPath();
        ctx.arc(75, 75, 10 + 10 * i, 0, Math.PI * 2, true);
        ctx.fill();
    }  
}else{
    console.log('canvasオブジェクトがNullです。');
}

var c02 = document.getElementById('c02');
if (c02.getContext){
    var ctx = c02.getContext("2d")
    ctx.font = '48px serif';
    ctx.fillText('Hello world', 10, 50);
    ctx.strokeText('Hello world', 30, 70);
    ctx.strokeStyle = '#F22'
    for (var i = 0; i < 5; i++) {
        ctx.lineWidth = 1 + i;
        ctx.beginPath();
        ctx.moveTo(30,10 * i + 70);
        ctx.lineTo(300,10 * i + 70);
        ctx.stroke();
    }  
 
}else{
    console.log('canvasオブジェクトがNullです。');
}

今回はこんなところで。 

ではでは。