最新のNoodlでCanvasを扱う


Noodl2.3以降CanvasノードがなくなりNoodlでのCanvasの扱い方が変わっています。
そのためNoodl上でChart.jsを使おうにも、以前紹介したやり方はできなくなくなっています。
調べてみるとScriptノードでDOM要素を作成するというやり方が、
公式のドキュメントに紹介されていましたので、
最新のNoodで通用するCanvasの扱いについてまとめました。

## 最小限の手順
- 外部ライブラリを読み込む(必要に応じて)
- CanvasをScriptノードで追加する
- Canvasのサイズを変える
- Chart.jsを使つかう

### 1. 外部ライブラリを読み込む
以前はScriptノードとScriptDownloaderノードを使用していましたが、
現在はProjectSettings>CustomCode>HeadCode：Editを使います。
>Headerにライブラリの読み込みを記述するので、どこかのページを開いた時には
ライブラリが読み込まれていることを前提に記述することができます。

![header.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/fbac80b7-df82-8ebc-9441-7fd60b385051.png)

必要に応じて外部ライブラリをNoodlに読み込みます。

```html:Noodlのフォルダ内にライブラリを置く場合
<script src="./mermaid_10.4.0_mermaid.min.js"></script>
```

```html:CDNを使う場合
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs"></script>
```

![headerEdit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/3cd2fd41-e2eb-ce31-e9a9-373355b13825.png)

### 2.ノードの配置
最低限必要なのはGroupノードとScriptノードです
- Groupノードで描画したい部分を配置します
- Scriptノードには描画するコード記述します
![nodeEditer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/5fc30dae-5f10-5aa3-970d-1b6be986b7cb.png)

### 3.Scriptノードの記述

```js:Scriptノード例
Script.Inputs = {
    group: 'reference',
}

Script.Signals = {
    didMount() {
        let canvas = document.createElement("canvas");
        const domElement = Script.Inputs.group.getDOMElement()
        const canvasElem = domElement.appendChild(canvas)
        let context = canvasElem.getContext('2d');
        //ここに描画に必要なコードを記述する

    },
    willUnmount() {
        //終了時の処理
    },
}
```

![scriptEdit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/c02714e7-6e09-f4e1-29fc-7620a2ea0855.png)

### 解説
Scriptノード内ではCanvasエレメントの生成と、生成したエレメントをGroupに配置をしています

### 入力：Inputs
- Groupノードの「This」出力を受けるため、Scriptノードの「group:'reference'」で定義します

## 初期化処理：Script.Signals.didMount()
- Canvasエレメントを生成します
- 入力されたgroupのエレメントを取得
- groupエレメントに先ほど作成したCanvasエレメントを追加
- Canvasエレメント描画用のContext関数を取得

### 4.　ノードの接続
下記のようにつなぎます
Groupのサイズ変更に追従する場合はGroupからwidth,heightを取得します
Scriptノードの実行はGroupノードがマウントされた時としています

![nodeEditer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/3f1a1b99-ea35-9e88-6d4f-659797a1851a.png)

--------------
## Canvas描画の例1
Group:thie->Script:group
Group:width>Script:width
Group:height>Script:height

```js：最小限のコードでCanvasに描画する例
Script.Inputs = {
    group: 'reference',
    width: 'number',
    height: 'number'
}

Script.Signals = {
    didMount() {
        let canvas = document.createElement("canvas");
        const domElement = Script.Inputs.group.getDOMElement()

        canvas.width = Math.floor(Script.Inputs.width);
        canvas.height = Math.floor(Script.Inputs.height);
        const canvasElem = domElement.appendChild(canvas)
        let context = canvasElem.getContext('2d');

        // パスをリセット
        context.beginPath();
        //context.rect( 50, 50, 75, 50 );
        context.arc(100, 80, 50, 0 * Math.PI / 180, 360 * Math.PI / 180, false);
        context.fillStyle = "rgba(255,0,0,0.8)";

        context.fill();        // 塗りつぶしを実行
        context.strokeStyle = "purple";        // 線の色
        context.lineWidth = 8;        // 線の太さ
        context.stroke();        // 線を描画
    },
    willUnmount() {
    },
}
```

![canvas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192442/8509c519-d7e9-396b-9a55-f31aac5bf061.png)

## Canvas描画の例2
Group:thie->Script:group
Group:width>Script:width
Group:height>Script:height

```js:Scriptノードでベジエ曲線を描く例
// ベジエ曲線の制御点の座標
const startX = 0;
const startY = 0;
const controlX1 = 100;
const controlY1 = 150;
const controlX2 = 200;
const controlY2 = 150;
const endX = 250;
const endY = 50;

let canvas
let ctx;

Script.Inputs = {
    group: 'reference',
    width: 'number',
    height: 'number'
}

Script.Signals = {
    didMount() {
        // キャンバス要素を取得        
        canvas = document.createElement("canvas");
        canvas.width = Math.floor(Script.Inputs.width);
        canvas.height = Math.floor(Script.Inputs.height);
        const canvasElem = Script.Inputs.group.getDOMElement().appendChild(canvas)
        ctx = canvasElem.getContext('2d');

        drawBezierCurve(ctx, startX, startY, controlX1, controlY1, controlX2, controlY2, endX, endY) 

    },
    willUnmount() {
    },
}

Node.OnInit = function () {
    document.body.addEventListener('mousemove', setPosition)
    document.body.addEventListener('mousedown', setPosition)
}

// ベジエ曲線を描く関数
function drawBezierCurve(context, startX, startY, controlX1, controlY1, controlX2, controlY2, endX, endY) {
    context.clearRect(0, 0, Script.Inputs.width, Script.Inputs.height);
    context.beginPath();
    context.moveTo(startX, startY);
    context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
    context.stroke();
}

function setPosition(e) {
    Node.Outputs.PointerX = e.clientX
    Node.Outputs.PointerY = e.clientY
        canvas.width = Math.floor(Script.Inputs.width);
        canvas.height = Math.floor(Script.Inputs.height);
    //drawBezierCurve(ctx, startX, startY, controlX1, controlY1, controlX2, controlY2, endX, endY) 
    drawBezierCurve(ctx, startX, startY, controlX1, controlY1, controlX2, controlY2, e.clientX, e.clientY)

}
```
---
## まとめ
[Noodl：Canvasノードを使ってJavascriptライブラリをいろいろと実装する](https://qiita.com/macole/items/ad61bc9887e7bdd1e9be)
として紹介していたのですが、Canvasノードがなくなったため、こちらの書き方も若干変わっています。
ライブラリを使用する例も引き続き解説していきます。