質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

Canvasの保存(塗り絵アプリで線画と描画のふたつのCanvasを合成したものを保存したい)

受付中

回答 0

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 274
退会済みユーザー

退会済みユーザー

前提・実現したいこと

ブラウザ上で起動する塗り絵を作成しています。
見様見真似ですが、htmlとjavascriptで、
線画を表示するcanvasの下に描画用のcanvasを置いています。
最終的には、作成者がその完成画像をダウンロードできるようにすることです。

発生している問題・エラーメッセージ

画像を保存させる方法がわかりません。
調べて、以下のコードを入れ込みました。
html↓

<button onclick="downloadCanvas()">ダウンロード</button>
   <a id="hiddenLink" download="canvas.png">link</a>
   <p>ダウンロードできない場合、下図を右クリックして保存してください。</p>
   <img id="canvasImage" src="dummy.png">


script↓

   function downloadCanvas() {
       let canvas = document.getElementById('canvas')
       let link = document.getElementById('hiddenLink')
       link.href = canvas.toDataURL()
       document.getElementById('canvasImage').src = canvas.toDataURL()
        link.click()


しかし、線画用のcanvasと描画用のcanvasが別々なので、
それを合成した完成形をダウンロードさせることができません。
IdのところをいずれかのIdにすれば、どちらかをダウンロードさせることはできます。
合成した第3のcanvasを作る必要があるのかと思うのですが、やり方がわかりません。
なにかお分かりになる方お教えいただけば幸いです。
よろしくお願いいたします。

ソースコード全体

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <title>ぬり絵テスト</title>
   <style type="text/css">
       /* h1のCSS */
       h1 {
           margin-left: 50px;
           color: #cccccc;
       }
       /* キャンバスのCSS */
       #canvas {
           border: solid 1px #cccccc;
           margin: 0px 50px;
           width: 690px;
           height: 380px;
           position: relative;
       }
       #coloring {
           top: 0px;
           left: 0px;
           position: absolute;
           z-index: 1;
       }
       /* 上に重ねる画像 */
       #overImage {
           top: 0px;
           left: 0px;
           position: absolute;
           z-index: 2;
       }
       /* カラーパレットのCSS */
       #palette {
           margin: 0px 50px;
           padding: 0px;
           width: 690px;
       }
       /* 色ボタンのCSS */
       .Color {
           margin: 5px;
           padding: 0px;
           width: 30px;
           height: 30px;
           border: solid 1px #CCCCCC;
       }
       /* ペンサイズ */
       #penWidth {
           margin: 50px;
           padding: 0px;
           float: left;
       }
       #penWidth button {
           border: none;
           background-color: #FFFFFF;
           width: 30px;
           height: 30px;
           padding: 0px;
       }
       .penSize {
           border-radius: 50%;
           background-color: #000000;
           border: solid 1px #CCCCCC;
           margin: auto;
       }
       #penSizeL {
           width: 20px;
           height: 20px;
       }
       #penSizeM {
           width: 10px;
           height: 10px;
       }
       #penSizeS {
           width: 5px;
           height: 5px;
       }
       /* ペン濃度 */
       #penAlpha {
           margin: 50px;
           padding: 0px;
           float: left;
       }
       #hiddenLink{
           display: none;
       }
   </style>
</head>
<body>
   <!-- タイトルを表示する -->
   <h1>ぬり絵テスト</h1>
   <!-- キャンバスを置く -->
   <div id="canvas">
   <canvas id="overImage" width="690" height="380"></canvas>
   <canvas id="coloring" width="690" height="380"></canvas>
   </div>
   <!-- カラーパレット -->
   <div id="palette">カラーパレット<br />
   </div>
   <blockquote id="penWidth">ペン先<br />
       <button type="button" onClick="setPenSize('20')"><div class="penSize" id="penSizeL"></div></button>
       <button type="button" onClick="setPenSize('10')"><div class="penSize" id="penSizeM"></div></button>
       <button type="button" onClick="setPenSize('3' )"><div class="penSize" id="penSizeS"></div></button>
   </blockquote>
   <canvas id="coloring"></canvas>
<div>   
<button onclick="downloadCanvas()">ダウンロード</button>

   <a id="hiddenLink" download="canvas.png">link</a>
   <!-- CSSで「display: none;」して非表示 -->
   <p>ダウンロードできない場合、下図を右クリックして保存してください。</p>
   <img id="canvasImage" src="dummy.png">
   <!-- CSSで小さめサイズに調整 -->
</div>
</body>
<script type="text/javascript">
   // キャンバスを取得
   var canvas = document.getElementById("canvas");
   // 色塗り用
   var coloring = document.getElementById('coloring');
   // コンテキストを取得
   var ctx   = coloring.getContext('2d');
   // カラーパレット取得
   var palette = document.getElementById('palette');
   // ペンサイズ
   var penSizeButtons = document.getElementsByClassName('penSize');
   // マウス位置
   var mouse = {x:0, y:0, bx:0, by:0};
   // クリック中フラグ
   var isClicked = false;
   // ペン色
   var penColor = "#000000";
   // ペン幅
   var penWidth = 10;
   // ペン濃度
   var penAlpha = 1.0;
   // 画像をキャンバスの上に描画する
   setImage()
   // 塗り絵の元を表示
   function setImage() {
       var overCanvas = document.getElementById('overImage');
       var overCtx = overCanvas.getContext('2d');
       /* Imageオブジェクトを生成 */
       var img = new Image();
       img.src="/div/shisetsu1/img/nanjamonja.png";
       img.onload = function() {
           overCtx.drawImage(img, 0, 0)
       }
       console.log("setImage")
   }
   // 実行
   setColorPalette()
   var suportTouch = 'ontouchend' in document;
   var startEvent = suportTouch ? "touchstart" : "mousedown";
   var moveEvent = suportTouch ? "touchmove" : "mousemove";
   var endEvent = suportTouch ? "touchend" : "mouseup";
   // マウスイベントを取得
   // マウスクリック
   canvas.addEventListener(startEvent, function(e){
       if (suportTouch) {
           event.preventDefault(); // タッチによる画面スクロールを止める
       }
       startDraw(e);
   });
   // マウス移動
   canvas.addEventListener(moveEvent, function(e){
       drawing(e);
   });
   // マウスクリック解除
   canvas.addEventListener(endEvent, function(e){
       endDraw(e)
   });
   // ポジション閑散
   function getPosition(e) {
       // キャンバスの位置とサイズを取得
       var rect = e.target.getBoundingClientRect();
       if (suportTouch) {
           // マウスの位置
           mouse.x = e.touches[0].clientX - rect.left;
           mouse.y = e.touches[0].clientY - rect.top;
       } else {
           // マウスの位置
           mouse.x = e.clientX - rect.left;
           mouse.y = e.clientY - rect.top;
       }
   }
   // 線引き開始
   function startDraw(e) {
       // キャンバスの位置とサイズを取得
       getPosition(e);
       // 描画の開始
       drarLineStart();
       // クリック中フラグ
       isClicked = true;
   };
   // 線引き続行
   function drawing(e) {
       // クリック中以外の時は無視
       if(!isClicked) {
           return;
       }
       // 一つ前の位置
       mouse.bx = mouse.x;
       mouse.by = mouse.y;
       // キャンバスの位置とサイズを取得
       getPosition(e);
       // クリック中なら線を引く
       drawLine();
   };
   // 線引き完了
   function endDraw(e) {
       // クリック終了
       isClicked = false;
   };
   // 開始位置を指定
   function drarLineStart() {
       // 線の太さを指定
       ctx.lineWidth = penWidth;
       // 線の色を指定
       ctx.strokeStyle = penColor;
       ctx.globalAlpha = penAlpha;
       // 先端を丸くする
       ctx.lineCap = "round"
       // つなぎ目を丸くする
       ctx.lineJoint = "round"
   }
   // 線を引く
   function drawLine() {
       // 今からパスを書きますよと云う宣言
       ctx.beginPath();
       // パスの開始点に移動
       ctx.moveTo(mouse.bx, mouse.by);
       // 指定の位置までパスを引く
       ctx.lineTo(mouse.x, mouse.y);
       // パスに線を載せる
       ctx.stroke();
   }
   // カラーパレットを配置する
   function setColorPalette() {
       var colors = ['#FFFFFF', '#000000', '#B45F04', '#FE2E2E', '#FE642E', '#FE9A2E', '#FACC2E',
                '#F7FE2E', '#C8FE2E', '#64FE2E', '#2EFEC8', '#2ECCFA', '#2E9AFE',
                '#642EFE', '#9A2EFE', '#CC2EFA', '#FE2EC8',
                '#F5A9A9', '#F5BCA9', '#F5D0A9', '#F3E2A9', '#E1F5A9', '#A9F5A9', '#A9F5A9', '#A9F5E1',
                '#A9E2F3', '#A9D0F5', '#A9BCF5', '#A9A9F5', '#BCA9F5', '#D0A9F5',
                '#F5A9F2', '#F5A9D0', '#F5A9BC'];
       for(var i = 0; i < 34; i++) {
           var btn = "<button class='Color' style='background-color: " + colors[i] + ";' onClick='setPenColor(\"" + colors[i] + "\");'></button>"
           palette.innerHTML += btn;
       }
   }
   // 色を変更する
   function setPenColor(setColor) {
       penColor = setColor;
       // ペンの色を変える
       for(var i = 0; i < penSizeButtons.length; i++) {
           penSizeButtons[i].style.backgroundColor = setColor;
       }
   }
   // 線幅を変更する
   function setPenSize(size) {
       penWidth = size;
   }
   // 線の濃さを変更する
   function setPenAlpha(alpha) {
       penAlpha = alpha;
   }
</script>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • 退会済みユーザー

    2020/07/21 14:56

    複数のユーザーから「意図的に内容が抹消された質問」という意見がありました
    解決後に編集機能を用いて質問内容を改変し関係のない内容にしたり、内容を削除する行為は禁止しています。
    投稿していただいた質問は、後に他の誰かが困ったときに助けになる情報資産になると考えるからです。
    「質問を編集する」ボタンから編集を行い、他のユーザにも質問内容が見えるように修正してください。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る