なでしこさんのキャンバスを使って簡単なメディアアート。
あるいは、描画中コンテキストからキャンバスの色情報を取得したり、描画中コンテキストのプロパティを変更したり参照したりなど、なでしこさんの描画命令だけではできないことをやってみる。
なでしことは
日本語プログラミング言語「なでしこ」は、その名の通り日本語でプログラミングできる、素敵プログラミング言語です❣️
なんと、この2025年で開発から20周年を迎えたとのことです。素晴らしいですね🎊
日本語であることによる分かりやすさや親しみやすさ、そして気軽に始められるとっつきやすさが息の長い人気の秘訣でしょうか。
なでしこ3なら、プログラム貯蔵庫で誰でも気軽にプログラムを動かして実際の動作を確認できますし、他の方の作ったプログラムで遊んだり、自分で作ったプログラムを投稿することもできます✨️
👇詳しくは公式ホームページで!
とにかくめでたい節目の年ですので、お祝いメッセージを電光掲示板に流したいと思います。
とりあえず文字を流れさせます
電光掲示板の設定
電光掲示板(canvas)の幅と高さ、流すメッセージと流れる速さを定義します。
掲示板幅=400。
掲示板高さ=100。
メッセージ=「祝 なでしこ 20周年!」
速さ=2。
キャンバス
電光掲示板用のキャンバスは、なでしこ3のプログラム貯蔵庫に標準で設置されているキャンバスを利用することにします。
貯蔵庫のキャンバスはUIで簡単にサイズの設定が出来るようになっていますが、プログラム中で指定しておいたほうがコピペしたコードでも同様に動かせるし間違いないですね。
描画中キャンバス.幅属性=掲示板幅。
描画中キャンバス.高さ属性=掲示板高さ。
フォント
流すメッセージの色やフォントを定義します。
背景色=黒色。
文字色=金色。
フォント=「bold 80px sans-serif」
フォントに描画フォント設定。
「描画フォント設定」は、cssのfont設定と同様の形式です。
cssだって、font-styleやfont-sizeがあるんだから、それぞれ別々に設定したいよぅ…(fontでまとめて設定するとfont-familyが必須なので、めんどくさいのです😅️)
とはいえ、実は数値だけ入れれば、フォントサイズのみが設定できるという簡単仕様が搭載されているのですが、今回は太字設定もしたいのでまとめて設定しなきゃです。
📖 なでしこ3 マニュアル > plugin_browser/描画フォント設定
なでしこはナゼか、v1の時代からフォントスタイルを設定したい人にキビシイ💧️
描画位置
文字を右から左に流すので、文字描画のxの初期値は右端。つまり掲示板幅と同じで良いですね。
そして、yですが・・・
なでしこ1の文字描画で指定するx,yの位置は文字の左肩でしたが、なでしこ3では左下なので、v1の感覚で[0,0]に文字描画しようとすると、あれ? 描画されない!? ってなります。
また、正確にはジャバスクリプトの規定値であるalphabeticなので、アルファベットのベースラインの左端。英小文字の g や y なんかは、その下にはみ出しますのでご注意です。
キャンバスの下端ギリギリに設定したりすると、今度は英小文字の足が切れてしまうわけですよよよ。
今回のメッセージは日本語だから関係ないけど、「Happy new year」とかにしたくなるかもしれないからね!
日本語の縦位置を中央寄せにしたい場合も、ディセンダ(ベースライン下のはみ出し部分)を考慮に入れないと上に寄って見えちゃいます。
描画X=掲示板幅。
描画Y=掲示板高さ-20。
ちゃんとする方法はいろいろあるのだろうと思いますが、今回は雑にキャンバスの高さからてきとーにマイナスしておきます😅️
ディセンダは、だいたいフォントサイズの1/4くらいみておけば大丈夫そう?🤔️
メッセージの描画幅を取得
メッセージが流れて左端に消えたら再び右側から出てくるようにするために、メッセージの実際の描画幅を取得します。
描画中キャンバスに設定されているフォント情報でのサイズです。
メッセージ幅=(メッセージの文字描画幅取得).width。
なでしこ1では、「文字幅取得」「文字高さ取得」で簡単に取得することができました。
ところが、なでしこ3には「文字描画幅取得」という命令があるのですが、返り値は「TextMetricsオブジェクト」となっており、TextMetricsオブジェクトの内容を知らないと使えないという、なぜかちょっと賢い人向けの命令となっています。
すべてのプロパティを利用できるようにということなのか、両方取得するときにいちいちTextMetricsオブジェクト取得しなおすのもったいないということなのか知らんが、正直ちょっと使いにくい😓️
まあ、幅はwidthだからまだいいんですけど、高さはheightが無いんだよね~。
こんなの綴れるわけないじゃーん💦️ って感じです。
マニュアルにはv1と同じようにできるサンプルを載せています。
📖 なでしこ3 マニュアル > plugin_browser/文字描画幅取得
文字を流すアニメーション
いったん背景色に塗りつぶし、描画Xを減らしながらメッセージを描画することを「画面更新時実行」でループさせるだけ。
そして、描画Xがメッセージ幅だけマイナスになったら描画Xを右端に戻します。
電光掲示板描画。
●電光掲示板描画
背景色に塗り色設定。
[0,0,掲示板幅,掲示板高さ]に四角描画。
文字色に塗り色設定。
[描画X,描画Y]にメッセージを文字描画。
描画Xを速さだけ減らす。
もし、描画X≦メッセージ幅*-1ならば、描画X=掲示板幅。
「電光掲示板描画」を画面更新時実行。
ここまで。
できました!
👇️動作確認
簡単ですね✨️
でも、これは単に文字が流れているというだけのことです。
もっとちゃんと電光掲示板っぽくしたいよね~。
冒頭の画像みたいにLEDのドットで文字が描かれている的な?
リアル電光掲示板
決まった文字を流すだけなら自分でドットのデータを作るという手もありますが、好きに入力したどんなメッセージでも流せるようにしたいです。
というわけで、メッセージ文字列をドットに変換してみます。
LEDメッセージ作成
まず、LEDのサイズと間隔を決めて、ドットのデータを収める配列と、電光掲示板とは別に、LEDメッセージを作成する裏キャンバスを作成します。
裏キャンバスはユーザーから見えないように可視をオフにしておきます。
LEDサイズ=3。
LED間隔=6。
LEDデータ=空配列。
LEDキャンバス=[メッセージ幅,掲示板高さ]のキャンバス作成。
LEDキャンバス.可視=オフ。
そして、黒地に白でメッセージを描画して、その色データを取得し、LED間隔置きにデータを確認して、白だったら(一応中間値の128以上としています)LEDデータに配列追加していけばドットのデータが出来上がり!
今度はそのデータをもとに円描画していけば、LED風のメッセージが出来上がるって寸法です。
# 黒地に白でメッセージを描画
黒色に塗り色設定。
[0,0,メッセージ幅,掲示板高さ]に四角描画。
白色に塗り色設定。
フォントに描画フォント設定。
[0,描画Y]にメッセージを文字描画。
# 色データを取得し、白い部分からドットを作成
変数 色データ=[0,0,メッセージ幅,掲示板高さ]の色データ取得。
LEDデータ=空配列。
yを0から掲示板高さまでLED間隔ずつ増やし繰り返す。
xを0からメッセージ幅までLED間隔ずつ増やし繰り返す。
ID=(y*メッセージ幅+x)*4。
もし、色データ[ID]>128ならば、LEDデータに[x,y]を配列追加。
ここまで。
ここまで。
# いったんクリアして背景を透明にし、LEDデータに従って円を描く。
全描画クリア。
文字色に塗り色設定。背景色に線色設定。
LEDデータを反復
対象にLEDサイズの円描画。
ここまで。
でも、色データの取得?🤔️
色データの取得
なでしこの命令にはありませんが、描画中コンテキストのgetImageDataというメソッドを使って、キャンバスの色データをまるっと取得することができます。
●(xywhの|xywhを)色データ取得
(描画中コンテキストの「getImageData」をxywhでJSメソッド実行).dataを戻す。
ここまで。
内容は1ピクセルごとにRGBAの順に値が入った一次元配列です。
[0,0,0,1,255,255,255,1...]
見た目には区切りも何もなく数値の並んだ一次元配列ですが、
[R,G,B,A,R,G,B,A...]
となっていますから、四つ飛びでアクセスしていけば、高速に任意の点のRGBA値を取得したり、変更したりもできるって寸法です。
色データをまるっと取得したり戻したりする命令は、あって良いと思うんだよねぇ~。
思えばv1にも、画像から色情報を取得できる命令が、壊滅的に遅い「点取得」しかなくて、とてもとても辛かった記憶が・・・😢️
消灯しているLEDの表示
実際の電光掲示板って背景は真っ黒じゃなく、点灯していない部分もうっすら見えているじゃないですか?
背景を丸で埋め尽くすのは簡単です。
xとyをLED間隔ずつ増やしながらひたすら円描画していけばよいだけ!
yを0から掲示板高さまでLED間隔ずつ増やし繰り返す。
xを0からLEDキャンバス幅までLED間隔ずつ増やし繰り返す。
[x,y]にLEDサイズの円描画。
ここまで。
ここまで。
円の色は、文字色、つまり点灯したLEDの色の透明度を落として薄くした感じにしたいなぁ~。
・・・ところが‼️
文字色は「金色」と設定しており、このなでしこさんの色定数はカラーネームを日本語にしたものなので、金色には「gold」が設定されています。
v1の場合は赤とか黒とかの色定数は「0xff0000」とか「0x000000」とかの十六進数が設定されており、rgbの値は計算で求めることができるのですが、なでしこ3で色定数を使い、その色を色々しようとすると、あれれ~? ということになってしまうんです。
いやベツにgoldは「rgb(255,215,0)」と分かっているんですから、最初から文字色にそれを設定するなり、消灯したLEDの色をrgba(255,215,0,0.2)とでも設定してやればよいだけの話なんだけどもさー。
なにしろとにかく日本語で設定したいワケですよ🤣️
調べてみると描画中コンテキストのglobalAlphaの値を設定してやるのがカンタンらしい。
一度設定すると、以降の描画はすべてその透明度で描かれるということなので、文字色は金色のままで、
描画中コンテキスト.globalAlpha=0.2
とか設定して消灯LEDを描画し、その後1に戻すだけでOKそう。
また、もう一つの方法として、描画中コンテキストのfillStyleの値を参照すれば、カラーネームを設定していても16進数のカラーコードになっているようです。
金色に塗り色設定。
描画中コンテキスト.fillStyleを表示。 //#ffd700
これなら、透明度の設定のみならず、色を色々したい場合にも使えそうです。
globalAlphaを設定すると描画のすべてがその透明度になってしまうのですが、輪郭線を黒色とすることでLEDの境界がくっきりとするようにしたいため、線色は半透明にしたくないので、今回は塗り色を取得し、16進数のカラーコードをRGB値に分解し、透明度を足してRGBAのカラーコードとして塗り色設定することにしてみます。
透明度=0.2。
文字色に塗り色設定。
塗り色=描画中コンテキスト.fillStyle。
塗り色をRGB分解。
消灯LED色=「rgba({それ[0]},{それ[1]},{それ[2]},{透明度})」
●(色を|色の)RGB分解
分色とは変数。分色=空配列。
色=色の「#」を「0x」に置換。
色=色を整数変換。
数を2から0まで繰り返す
分色[数]=色%256。
色=(色-分色[数])/256
ここまで。
分色で戻る。
ここまで。
できました。
日本語でやろうとしてムダに大変になってる感がありますが、わざとです。わざとなんですぅ~🤣️🤣️🤣️
色を色々することや、描画中コンテキストのプロパティについて書きたかったのでっ。
こういったキャンバスのコンテキストのプロパティは、なでしこのマニュアルでは用が足りず、ちょっとこんなこと出来ないかな? と思うとjavascriptのcanvas操作について検索しまくらなきゃならなくなるのでツライ~💧️
検索して知ることができれば、オブジェクトプロパティ構文によりjavascriptのと同じように描画中コンテキストの参照や設定ができるので良いのですが、なんかもうちょっとこうDOMの和属性や和スタイルのように日本語でなんとかならないものかな?
描画中コンテキスト.透明度
描画中コンテキスト.塗り色
みたいに!!!
できました
あとは黒く塗りつぶして文字描画する代わりに、背景キャンバスを画像描画してLEDキャンバスを画像描画していけばOK!
#--- 電光掲示板の設定 ----------
掲示板幅=400。
掲示板高さ=100。
メッセージ=「祝 なでしこ 20周年!」
速さ=3。
# キャンバス
電光掲示板=描画中キャンバス。
電光掲示板.幅属性=掲示板幅。
電光掲示板.高さ属性=掲示板高さ。
# フォントと描画位置
背景色=黒色。
文字色=金色。
フォント=「bold 80px sans-serif」
# 描画位置
描画X=掲示板幅。
描画Y=掲示板高さ-20。
# メッセージの描画幅
フォントに描画フォント設定。
メッセージ幅=(メッセージの文字描画幅取得).widthを整数変換。
#--- LEDメッセージ作成 ----------
LEDサイズ=3。
LED間隔=6。
LEDデータ=空配列。
LEDキャンバス=[メッセージ幅,掲示板高さ]のキャンバス作成。
LEDキャンバス.可視=オフ。
# 黒地に白でメッセージを描画
黒色に塗り色設定。
[0,0,メッセージ幅,掲示板高さ]に四角描画。
白色に塗り色設定。
フォントに描画フォント設定。
[0,描画Y]にメッセージを文字描画。
# 色データを取得し、白い部分からドットを作成
変数 色データ=[0,0,メッセージ幅,掲示板高さ]の色データ取得。
LEDデータ=空配列。
yを0から掲示板高さまでLED間隔ずつ増やし繰り返す。
xを0からメッセージ幅までLED間隔ずつ増やし繰り返す。
ID=(y*メッセージ幅+x)*4。
もし、色データ[ID]>128ならば、LEDデータに[x,y]を配列追加。
ここまで。
ここまで。
# いったんクリアして背景を透明にし、LEDデータに従って円を描く。
全描画クリア。
文字色に塗り色設定。背景色に線色設定。
LEDデータを反復
対象にLEDサイズの円描画。
ここまで。
#--- 背景作成 ----------
背景キャンバス=[掲示板幅,掲示板高さ]のキャンバス作成。
背景キャンバス.可視=オフ。
透明度=0.2。
文字色に塗り色設定。
塗り色=描画中コンテキスト.fillStyle。
塗り色をRGB分解。
消灯LED色=「rgba({それ[0]},{それ[1]},{それ[2]},{透明度})」
背景色に塗り色設定。
[0,0,掲示板幅,掲示板高さ]に四角描画。
消灯LED色に塗り色設定。
yを0から掲示板高さまでLED間隔ずつ増やし繰り返す。
xを0からメッセージ幅までLED間隔ずつ増やし繰り返す。
[x,y]にLEDサイズの円描画。
ここまで。
ここまで。
#--- 電光掲示板描画アニメーション ----------
電光掲示板へ描画開始。
電光掲示板描画。
●電光掲示板描画
[0,0]へ背景キャンバスを画像描画。
[描画X,0]にLEDキャンバスを画像描画。
描画XをLED間隔だけ減らす。
もし、描画X≦メッセージ幅*-1ならば、描画X=掲示板幅。
(0.1/速さ)秒待つ。
「電光掲示板描画」を画面更新時実行。
ここまで。
#-----------------------------------------------
●(xywhの|xywhを)色データ取得
(描画中コンテキストの「getImageData」をxywhでJSメソッド実行).dataを戻す。
ここまで。
●(色を|色の)RGB分解
分色とは変数。分色=空配列。
色=色の「#」を「0x」に置換。
色=色を整数変換。
数を2から0まで繰り返す
分色[数]=色%256。
色=(色-分色[数])/256
ここまで。
分色で戻る。
ここまで。
#-----------------------------------------------
👇️動作確認
おわります
今年もアドベントカレンダー始まりました。
めでたい節目の年です。
みなさん奮ってご参加ください!

