2025年9月18日木曜日

p5.jsのコードを実行するAPEXアプリケーションを作成する

ブラウザ上で手軽に図形描画やアニメーションを実現できるJavaScriptライブラリとして、p5.jsがあります。p5.jsを使って図形を描くときはJavaScriptでコードを書きます。

本記事ではp5.jsのJavaScriptのコードを、REPL(Read-Eval-Print Loop)風に実行するAPEXアプリケーションを作成します。JavaScriptコードの入力には、Ronny Weissさん作成のリージョン・プラグインAPEX-Monaco-Editor-and-Diff-Viewerを使用します。

作成したAPEXアプリケーションは以下のように動作します。


以下よりAPEXアプリケーションの作成手順を紹介します。

最初にクイックSQLの以下のモデルより表P5_SCRIPTSを作成します。この表に図形やアニメーションを描画するJavaScriptのコードを保存します。
# pk: identity
p5_scripts
    title  vc80 /nn
    script clob;

空のAPEXアプリケーションを作成します。名前p5.jsとします。


図形を描画するコードの入力はホーム・ページで行い、そのコードを実行して図形やアニメーションを描画するのは、非モーダル・ダイアログで行うことにします。

図形を描画するページを作成します。ページの作成をクリックします。


空白ページを選択します。


新規に作成するページ番号2名前Sketchとします。このページは非モーダル・ダイアログにする予定ですが、ページ・モードの選択肢に非モーダル・ダイアログはありません。形式が一番近いモーダル・ダイアログを選択し、ページ作成後にモードを変更します。

ページの作成をクリックします。


新規にページが作成されます。

外観ページ・モード非モーダル・ダイアログに変更します。ダイアログ800高さ800としています。この値は便宜的なものなので、それぞれ、使い勝手のよい値を設定します。必須ではありませんが、チェーンオフにしています。


JavaScriptファイルURLに以下を設定します。p5.jsのライブラリをAPEXのページにロードします。

https://cdn.jsdelivr.net/npm/p5@2.0.5/lib/p5.min.js


スクリプトが保存されている表P5_SCRIPTSの列IDの値を保持するページ・アイテムを、P2_IDとして作成します。ページ・アイテムのタイプ非表示です。


P5_SCRIPTSに保存されているJavaScriptのコードを保持するページ・アイテムを、P2_CODEとして作成します。こちらもタイプ非表示です。

セッション・ステートデータ型CLOBストレージリクエストごと(メモリーのみ)を選択します。


ダイアログが開いたときに、P2_IDの値を主キーとして表P5_SCRIPTSよりJavaScriptコード(列SCRIPTの値)を取り出し、P2_CODEに設定するプロセスを作成します。

一般的にはフォームを作成して、そのソースを元にフォームの初期化プロセスを実行することにより、フォームに紐づくページ・アイテムに値を設定します。今回は初期化するページ・アイテムはP2_CODEのみなので、フォームの作成は省略しています。

プロセスのソースPL/SQLコードに以下を記述します。

select script into :P2_CODE from p5_scripts where id = :P2_ID;


JavaScriptのコードを実行し、p5.jsによる図形やアニメーションを描画するリージョンを作成します。

名前Sketchタイプ静的コンテンツを選択します。

ソースHTMLコードに以下を記述します。


外観テンプレートに装飾の少ないBlank with Attributes (No Grid)を選択します。


以上で、図形やアニメーションを描画する非モーダル・ダイアログのページができました。

続いて、コードを記述する機能をホーム・ページに実装します。

APEX-Monaco-Editor-and-Diff-Viewerのページより、region_type_plugin_rw_apex_vs_monaco_editor.sqlをダウンロードします。


共有コンポーネントプラグインを開きます。


プラグインの一覧画面よりインポートをクリックします。


インポートするファイルとして、先ほどダウンロードしたregion_type_plugin_rw_apex_vs_monaco_editor.sqlを選択します。プラグインよりインポート画面を開いているため、ファイル・タイププラグインが選択済みです。

へ進みます。


プラグインをインストールします。へ進みます。


プラグインのインストールを実行します。インストールするアプリケーションには、インポート処理を開始したアプリケーションが設定されています。


Monaco EditorのJavaScriptライブラリの参照先となるCDNのURLを確認します。Monaco Editorの最新版は0.53.0のようですが、0.53.0に置き換えるとプラグインが動作しないので、このBase URLは変更しない方が良いでしょう。

https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.51.0

変更の適用をクリックします。


以上でプラグインのインストールは完了です。

ホーム・ページにコード・エディタを組み込みます。

非モーダル・ダイアログを開くボタンOPEN_SKETCHを作成します。

ボタン名OPEN_SKETCHラベルOpen Sketchとします。非モーダル・ダイアログのページを開くために、動作アクションこのアプリケーションのページにリダイレクトを選択し、ターゲットページ2を設定します。


一般的にページを開く場合は、遷移先のページに主キーの値を引き継ぎます。今回の実装を例にとると、リンク・ビルダーアイテムの設定名前P2_ID&P1_ID.といった設定を行います。

この設定は行なっていません。&P1_ID.ですが、ボタンのアクションリダイレクトの場合、ホーム・ページが開いた時点で設定されているP1_IDの値で置換されます。ページ上の操作によってP1_IDの値が変更されても、リダイレクト時に渡される値には反映されません。

今回はホーム・ページが表示された後にページ・アイテムP1_IDの値を変更するため、変更されたP1_IDの値でページ・アイテムP2_IDを直接更新することにより、リンクの引数による値の引き渡しを省略しました。


P5_SCRIPTS内で、更新の対象としているスクリプトのIDを保持するページ・アイテムとして、P1_IDを作成します。タイプポップアップLOVLOVタイプSQL問合せを選択し、SQL問合せとして以下を記述します。

select title d, id r from p5_scripts order by r asc

追加値の表示オフにします。


新規に登録するスクリプトのタイトルを入力するページ・アイテムとして、P1_TITLEを作成します。タイプはテキスト・フィールドです。

ラベルTitleセッション・ステートストレージリクエストごと(メモリーのみ)を設定します。


P5_SCRIPTSに新規行を保存するボタンとして、CREATEを作成します。P1_TITLEを列TITLEに保存し、自動採番されたIDP1_IDに戻します。列SCRIPTに保存するスクリプトはスクリプト・エディタから保存します。そのため、ボタンCREATEではスクリプトを保存しません。

動作アクションはデフォルトのページの送信です。


ボタンCREATEをクリックしたときに実行されるプロセスを作成します。名前Createとします。

ソースPL/SQLコードとして以下を記述します。

insert into p5_scripts(title) values(:P1_TITLE) returning id into :P1_ID;

サーバー側の条件ボタン押下時CREATEを設定します。


コード・エディタのリージョンを作成します。

名前Editorとし、タイプにプラグインのAPEX-VS-Monaco-Editorを選択します。プラグインのソースSQL問合せとして以下を記述します。


外観テンプレートに装飾の少ないBlank with Attributesを選択します。


属性を開き、ButtonsSwitch between Single Editor and Diff-Editorのチェックを外します。

Execute on Saveに以下を記述します。
begin
    update p5_scripts set script = :CLOB where id = :P1_ID;
end;

この時点でアプリケーションを実行すると、新規スクリプトの作成ができます。

下段のページ・アイテムにTitleを入力してボタンCreateをクリックすると、表P5_SCRIPTSに1行追加されます。


コード・エディタの保存アイコンをクリックすると、上段のTitleで選択されているスクリプトとして保存されます。

ページ・アイテムP1_IDに動的アクションを作成して、選択したIDのスクリプトがコード・エディタにロードされるようにします。

作成する動的アクションの名前onChange P1_IDとします。タイミングイベントはページ・アイテムのデフォルトである変更です。


ページ・アイテムP1_IDの値が変更されたときにページ・アイテムP2_IDに同じ値を設定するよう、TRUEアクションとしてサーバー側のコードを実行します。

設定PL/SQLコードとして以下を記述します。
begin
    apex_session_state.set_value('P2_ID', :P1_ID);
end;
P1_IDが変更したときに限らず、ページが表示された時点でのP1_IDの値もP2_IDに保存するため、実行初期化時に実行オンにします。


変更されたP1_IDのスクリプトを読み込むため、コード・エディタのリージョンであるEditorを、TRUEアクションでリフレッシュします。


以上でポップアップLOVで選択したスクリプトが、コード・エディタに表示されるようになりました。また、選択されたスクリプトがコード・エディタでの保存先になります。

選択されたスクリプトを表P5_SCRIPTSより削除するボタンとして、DELETEを作成します。

動作アクションページの送信です。削除は注意が必要な処理なので、動作確認の要求オンにします。確認メッセージに「削除しますか?」を記述し、スタイル警告を設定します。


ボタンDELETEをクリックしたときに実行されるプロセスを作成します。名前はDeleteとします。

ソースPL/SQLコードに以下を記述します。

delete from p5_scripts where id = :P1_ID;

サーバー側の条件ボタン押下時DELETEを設定します。


以上で、アプリケーションの動作に関しては実装が完了しました。

少し見栄えを調整します。

静的コンテンツのリージョンとしてSelect Scriptを作成し、外観テンプレートItem Containerを選択します。そのリージョンのItemのスロットにページ・アイテムP1_IDButton EndのスロットにボタンDELETEを配置します。


同様に静的コンテンツのリージョンとしてNew Scriptを作成し、そのリージョンのItemのスロットにページ・アイテムP1_TITLEButton EndのスロットにボタンCREATEを配置します。New ScriptはSelect Scriptの右隣に配置するため、レイアウトの新規行の開始はオフにしします。


以上でアプリケーションは完成です。実行すると記事の先頭のGIF動画のように動作します。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-repl-p5-js.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。