ChatGPTを用いて既存のプログラムを改良した話
普段コーディングしか行っていない人にとっては、JavaScriptを読むだけならまだしも、自分で書くことは中々手を出しづらいです。
その上、ライブラリ依存の記述になると、元のライブラリを理解することから必要となるため、一般人には到底手を出せない領域に入ってきます。
その領域に詳しい知人がいれば質問できますが、いない場合には自分で検索する他なく、検索しても分からない場合には諦めてしまうことも多いでしょう。
この記事では、検索しても分からなかったプログラム記述を、筆者がChatGPTを使用して改良した方法と、その際に学んだ安定した結果を返してもらうためのポイントについて紹介していきます。
今回の改良の動機
筆者は個人で趣味のサイトを運営していますが、そのサイト内でFabricを利用した画像に書き込みする機能を持っています。
元々使用していたコードでは、1ページにつき1つしかcanvasを設置できないという不満点がありました。
改良したいとは常々思っていましたが、この機能実装の参考元であるサイトは既に閉鎖されており、Fabricは日本語リファレンスや参考文献が少ないこともあり、自分の力では改良できない状態が続いていました。
最近登場したChatGPTは、英語やプログラミング系に関して強いという噂を聞いたため、質問しながらであれば改良できるのかどうかについて検証することにしました。
Fabric.jsとは
Fabric.jsは、canvas要素にマウスで線を描く、画像を置く、文字を入力するなどの処理を可能にするjavascriptのライブラリの1つです。
詳しくは公式サイトをご覧ください。
初期のコードと改善したい点
元々は下記のコードでした。
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="oNeaKem" data-preview="true" data-user="shiwehi" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/shiwehi/pen/oNeaKem">
flex message01</a> by shiwehi (<a href="https://codepen.io/shiwehi">@shiwehi</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
先述したように、こちらのコードでは「同一ページに複数のcanvasが存在すると、2つ目以降のcanvasを操作することができない」という問題が発生していました。
また、せっかくなら同時に「できれば改良したい」と思っていた下記2点についても改良するようにしました。
- canvas上でマウスポインタが十字カーソルになっているものをペンに応じたものに変更する。
- メニューをいちいちベタで記述するのが面倒なのでjsで生成するようにする。
ChatGPTを使用して改良する
質問は筆者の理解が悪いのもあって、質問の表現の修正も含めると100往復くらいしているため、ここでは一部のみ掲載します。
ChatGPTへの質問①
fabric.jsで1ページに複数canvasを作成して、背景にそれぞれ別々の画像をセットしています。
data-urlで設定してる背景用の画像について、縦横比を維持したままcanvasのサイズに合わせて拡大縮小しようと思っていますが、この書き方だと、拡大縮小される場合とされない場合があります。
何が原因でしょうか? また、どのように記述を変更すれば解決しますか?
<div class="image2 canvas-wrapper">
<canvas data-name="arukoi21" data-url="./img/q1.png"></canvas>
</div>
<div class="image2 canvas-wrapper">
<canvas data-name="arukoi22" data-url="./img/q2.png"></canvas>
</div>
<div class="image2 canvas-wrapper">
<canvas data-name="arukoi23" data-url="./img/q3.png"></canvas>
</div>
<script>
$(window).on('load', function(){
// Canvasの数
let canvasNum = $('.canvas-wrapper').length;
// 各Canvasの設定
for (let i=0; i<canvasNum; i++) {
// Canvas要素を取得
let canvasElem = document.querySelector('.canvas-wrapper:nth-child('+(i+1)+') canvas[data-name="arukoi2'+(i+1)+'"]');
let canvas = new fabric.Canvas(canvasElem);
// 背景画像を設定
let bgImgUrl = canvasElem.dataset.url;
fabric.Image.fromURL($('canvas.lower-canvas:eq('+i+')').data('url'), function(img){
let imgSize = [img.width,img.height];
let canvasWidth = $('.canvas-wrapper').eq(i).outerWidth();
let canvasHeight = $('.canvas-wrapper').eq(i).outerWidth() * imgSize[1] / imgSize[0];
canvas.setWidth(canvasWidth);
canvas.setHeight(canvasHeight);
img.scale($('.canvas-wrapper').eq(i).outerWidth() / imgSize[0]);
canvas.setBackgroundImage(img);
});
canvas.setBackgroundImage(bgImgUrl, canvas.renderAll.bind(canvas), {});
};
});
</script>
ChatGPTの答え①
背景画像の縦横比を維持しながら、Canvasのサイズに合わせて拡大縮小するためには、以下のように修正することができます。
$(window).on('load', function(){
// Canvasの数
let canvasNum = $('.canvas-wrapper').length;
// 各Canvasの設定
for (let i=0; i<canvasNum; i++) {
// Canvas要素を取得
let canvasElem = document.querySelector('.canvas-wrapper:nth-child('+(i+1)+') canvas[data-name="arukoi2'+(i+1)+'"]');
let canvas = new fabric.Canvas(canvasElem);
// 背景画像を設定
let bgImgUrl = canvasElem.dataset.url;
fabric.Image.fromURL(bgImgUrl, function(img){
// 画像の縦横比を取得する
let imgAspectRatio = img.width / img.height;
// Canvasのサイズを設定する
let canvasWidth = $('.canvas-wrapper').eq(i).outerWidth();
let canvasHeight = canvasWidth / imgAspectRatio;
canvas.setWidth(canvasWidth);
canvas.setHeight(canvasHeight);
// 画像を拡大縮小する
img.scaleToWidth(canvasWidth);
canvas.setBackgroundImage(img);
});
};
});
この修正では、Canvasのサイズを計算するために、画像の縦横比を取得し、それに基づいてCanvasの高さを設定しています。
また、`img.scaleToWidth()`メソッドを使用して、画像をCanvasの幅に合わせて拡大縮小しています。これにより、画像の縦横比が維持されたまま、Canvasにぴったりと合わせて表示されます。
なお、背景画像の読み込みが非同期で行われるため、`fabric.Image.fromURL()`メソッドによって画像がロードされる前にCanvasのサイズが設定される場合があります。
この場合、画像が表示されずに白い背景が表示される可能性があるため、`canvas.setBackgroundImage()`メソッドの第2引数に、画像がロードされた後にCanvasを再描画する処理を設定することが重要です。
(中略)
ChatGPTへの質問②
先ほどうまく行ったコードについて、それぞれのcanvas-wrapperの直前に同じdiv要素を追加したいです。
.before関数を用いて追加しようと思ったらエラーが出てしまいました。
何が原因でしょうか? また、どのように記述を変更すれば解決しますか?
ChatGPTの答え②
エラーの原因は、.before()メソッドが引数に渡された文字列を新しい要素として作成し、既存の要素の前に挿入するという動作をするためです。そのため、.before()を実行した時点で、追加されたdiv要素はまだDOMに存在しないため、.canvas-wrapper:eq(‘+i+’)はnullになり、エラーが発生します。
解決する方法としては、.before()の前に新しいdiv要素を作成しておくことが考えられます。以下は、それを実現するためのコード例です。
(コード省略)
ChatGPTへの質問③
div.function内にあるundo,redo,pencil,wpen,marker,edit,reset,downloadを押すと、fabric.jsを利用した機能が使えるようになっています。
しかし、この関数はcanvasが1つのときに作成したものなので、canvasが複数あると上手く動きません。
修正したいですが、一度に修正すると量が多いため、まずはpencilについてどのようにしたら良いか修正内容を提示していただけますでしょうか?
(コード省略)
ChatGPTの答え③
この場合、pencil機能がクリックされたときに実行される setPencil 関数内で、各Canvasに対して別々にイベントを設定する必要があります。
以下は、修正案の例です。 setPencil 関数に、現在選択されているCanvasに応じてイベントを設定するコードを追加しました。
(コード省略)
このコードでは、setPencil 関数内で、$(this).closest(‘.canvas-wrapper’).find(‘canvas’)[0] を使用して、現在選択されているCanvasを取得します。
次に、既存のイベントを削除し、新しいイベントを設定します。新しいイベントでは、Canvasにマウスを押す、マウスを動かす、マウスを離す、Canvasの外に出る(mouseleave)という4つのイベントを設定し、それぞれ canvas.freeDrawingBrush を使用して描画します。
このように修正することで、複数のCanvasがあっても各Canvasに対して個別に描画することができます。wpen や marker などの他の機能についても同様に修正する必要があります。
ChatGPTに質問して気づいたこと
こちら側もある程度用語の理解ができないと欲しい回答が来ないため、書き直しや追加質問を含めると基本的には解決までに質問1つにつき5往復くらいしています。
ただし、基本的な質問の仕方としては、下記のことを意識すると安定するように個人的には感じました。
- ソースは基本的に全て貼る。
- 「何が起きていて、何が原因で、どう修正すれば良いのか?」という質問形式で聞く。
- 貰った回答に対して再質問する場合は、「上のソースにおいて、」などの続きの質問であることを明示する。
最終的なコード
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="zYJwmLJ" data-preview="true" data-user="shiwehi" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/shiwehi/pen/zYJwmLJ">
paint.js</a> by shiwehi (<a href="https://codepen.io/shiwehi">@shiwehi</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
最終的にはこのようになり、改良したかった下記3点についてはすべて達成することができました!(CodePen上では違いが分かりにくいかもしれません)
詳しい機能については個人の記事にて解説していますので、そちらを参照して下さい。
実際にchatGPTを使用した感想とまとめ
今回はchatGPTを使用して既存のプログラムを改良することに成功しました。
この件以外にも「文章だけで1からプログラムを組んでもらう」ことにも挑戦してみましたが、今のところまだ安定して生成してもらえないことがほとんどです。
おそらく、指示文の日本語の書き方が安定していないんだと思います。
SEO関連ではハルシネーション(間違った情報を記述してしまう現象)の観点からまだまだ実用性があるとは言えない状態ですが、簡単な単体プログラムの開発や修正であればかなり有用なものだと感じました。
文章やイラスト系の生成では著作権の側面でもいろいろな議論がされていますが、迅速に整備されていくことを期待します。
ファブリカコミュニケーションズで働いてみませんか?
あったらいいな、をカタチに。人々を幸せにする革新的なサービスを、私たちと一緒に創っていくメンバーを募集しています。
ファブリカコミュニケーションズの社員は「全員がクリエイター」。アイデアの発信に社歴や部署の垣根はありません。
“自分から発信できる人に、どんどんチャンスが与えられる“そんな環境で活躍してみませんか?ご興味のある方は、以下の採用ページをご覧ください。