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

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

新規登録して質問してみよう
ただいま回答率
85.48%
canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

3273閲覧

canvasで画像保存がうまくいきません

kihokutarou

総合スコア59

canvas

HTML5の<canvas>要素用のタグです。CanvasはHTML5から導入された、二次元の図形描写が可能な要素です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2018/01/25 14:53

canvasでローカル画像を呼び出し、加工(絵画)、これを画像として出力したいのですがうまくいきません。chromeエラーによると
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
とありますので、「汚れている(?)」ということなのか、つまりcanvasに画像を載せている段階で汚れている判定なのでしょうか。。。完全にローカル環境だけで完結させたいのでそのせいもあるのか、まったくわかりません。

ちなみにchromeの場合、canvas領域を右クリックで画像をpng形式で保存できます。結局これと同様に画像化したものを貼り付けたいだけなのですが・・・。

コードにはオブジェクトを動かす記述など不必要なものもたくさん含まれます。
なにが悪いか、もしくは修正点はどこかご指摘いただけるとありがたいです。

js

1onload = function() { 2 draw(); 3}; 4function draw() { 5 var canvas = document.getElementById("drawArea"); 6 if ( ! canvas || ! canvas.getContext ) { return false; } 7 var ctx = canvas.getContext("2d"); 8 /* Imageオブジェクトを生成 */ 9 var img = new Image(); 10 img.src = "a.jpg?" + new Date().getTime(); 11 /* 画像が読み込まれるのを待ってから処理を続行 */ 12 img.onload = function() { 13 ctx.drawImage(img, 0, 0,ctx.canvas.width, ctx.canvas.height); 14 } 15} 16 17var canvas = document.getElementById("drawArea"); 18var ctx = canvas.getContext("2d"); 19var width = window.innerWidth; 20var height = window.innerHeight; 21var x = 0, y = 0; 22var color = "#000000"; 23var line = 13; 24canvas.width = width; 25canvas.height = height - 70; 26 27canvas.addEventListener("mousedown", touchStartHandler, false); 28canvas.addEventListener("mouseup", touchEndHandler, false); 29 30canvas.addEventListener("touchstart", ttouchStartHandler, false); 31canvas.addEventListener("touchend", ttouchEndHandler, false); 32 33function touchStartHandler(e) { 34 e.preventDefault(); 35 getTouchPoint(e); 36 ctx.beginPath(); 37 ctx.lineCap = "round"; 38 ctx.lineJoin = "round"; 39 ctx.moveTo(x, y); 40 canvas.addEventListener("mousemove", touchMoveHandler, false); 41} 42 43function touchMoveHandler(e) { 44 e.preventDefault(); 45 getTouchPoint(e); 46 ctx.lineWidth = line; //線の太さ 47 ctx.strokeStyle = color; //線の色 48 ctx.lineTo(x, y); 49 ctx.stroke(); 50} 51 52function touchEndHandler(e) { 53 e.preventDefault(); 54 ctx.closePath(); 55 canvas.removeEventListener("mousemove", touchMoveHandler, false); 56} 57 58function getTouchPoint(e) { 59 x = e.clientX - canvas.offsetLeft; 60 y = e.clientY - canvas.offsetTop; 61} 62 63function ttouchStartHandler(e) { 64 e.preventDefault(); 65 getTTouchPoint(e); 66 ctx.beginPath(); 67 ctx.lineCap = "round"; 68 ctx.lineJoin = "round"; 69 ctx.moveTo(x, y); 70 canvas.addEventListener("touchmove", ttouchMoveHandler, false); 71} 72 73function ttouchMoveHandler(e) { 74 e.preventDefault(); 75 getTTouchPoint(e); 76 ctx.lineWidth = line; //線の太さ 77 ctx.strokeStyle = color; //線の色 78 ctx.lineTo(x, y); 79 ctx.stroke(); 80} 81 82function ttouchEndHandler(e) { 83 e.preventDefault(); 84 ctx.closePath(); 85 canvas.removeEventListener("touchmove", ttouchMoveHandler, false); 86} 87 88function getTTouchPoint(e) { 89 var touch = e.touches[0]; 90 x = touch.pageX - canvas.offsetLeft; 91 y = touch.pageY - canvas.offsetTop; 92} 93 94function spread() { 95 ctx.fillStyle = color; 96 ctx.fillRect(0, 0, canvas.width, canvas.height); 97 document.getElementById("ClickSound").play(); 98} 99 100function clr() { 101 cntx = canvas.getContext("2d"); 102 cntx.clearRect(0, 0, canvas.width, canvas.height); 103 document.getElementById("ClickSound").play(); 104} 105 106function change_black() { 107 color = "#000000"; 108 document.getElementById("ClickSound").play(); 109} 110function change_red() { 111 color = "#ff0000"; 112 document.getElementById("ClickSound").play(); 113} 114function change_green() { 115 color = "#00ff00"; 116 document.getElementById("ClickSound").play(); 117} 118function change_blue() { 119 color = "#0000ff"; 120 document.getElementById("ClickSound").play(); 121} 122function change_white() { 123 color = "#ffffff"; 124 document.getElementById("ClickSound").play(); 125} 126function change_8() { 127 line = 8; 128 document.getElementById("ClickSound").play(); 129} 130function change_16() { 131 line = 16; 132 document.getElementById("ClickSound").play(); 133} 134//canvasデータを画像に変換にする関数 135function chgImg() 136{ 137 var png = canvas.toDataURL("image/png"); 138 img.crossOrigin = 'Anonymous' 139 document.getElementById("newImg").src = png; 140} 141 142

html

1<!DOCTYPE html> 2<html lang="ja"> 3<head> 4 <meta charset="UTF-8"> 5 <title>汎用お絵かきチャレンジ</title> 6 <link rel="stylesheet" type="text/css" href="wboard.css"> 7</head> 8<body> 9 10<div class="wrapper"> 11<form> 12<p> 13&nbsp;&nbsp;&nbsp; 14<input type="button" class="b1" style="background: #000000;" value=" " onclick="change_black()"> 15<input type="button" class="b1" style="background: #ff0000;" value=" " onclick="change_red()"> 16<input type="button" class="b1" style="background: #00ff00;" value=" " onclick="change_green()"> 17<input type="button" class="b1" style="background: #0000ff;" value=" " onclick="change_blue()"> 18<input type="button" class="b1" style="background: #ffffff;" value=" " onclick="change_white()"> 19&nbsp;&nbsp;線幅&nbsp; 20<input type="button" class="b2" value="8px" onclick="change_8()"> 21<input type="button" class="b2" value="16px" onclick="change_16()"> 22&nbsp;&nbsp; 23<input type="button" class="b2" value="全面" onclick="spread()"> 24<input type="button" class="b2" value="消去" onclick="clr()"> 25<font size="5" color="#ff0000"><a href="javascript:location.reload();">最初から</a></font> 26&nbsp;&nbsp;画像&nbsp; 27<input id="ufile" name="ufile" type="file" accept="image/jpeg,image/png"> 28<input type="button" value="戻る" id="backBtn"> 29<input type="button" value="進む" id="forwardBtn"> 30<input type="button" value="画像に変換" onclick="chgImg()"> 31</form> 32 33 34<canvas id="drawArea"></canvas> 35 36 37<p><font size="6" color="#ff0000"> 38<a href="#" onClick="toggle_view1();return false;">カードの表示</a> 39<a href="#" onClick="toggle_hidden1();return false;">カードの非表示</a> 40</font> 41</p> 42<div id="card" style="display: none;"> 43<table><tr> 44<td><p id="move1"><img src="img/1.JPG" width="150" height="120"></p></td> 45 46<td><p id="move2"><img src="img/2.JPG" width="150" height="120"></p></td> 47 48<td><p id="move3"><img src="img/3.JPG" width="150" height="120"></p></td> 49 50<td><p id="move4"><img src="img/4.JPG" width="150" height="120"></p></td> 51 52<td><p id="move5"><img src="img/5.JPG" width="150" height="120"></p></td> 53 54<td><p id="move6"><img src="img/6.JPG" width="150" height="120"></p></td> 55 56<td><p id="move7"><img src="img/7.JPG" width="150" height="120"></p></td> 57 58<td><p id="move8"><img src="img/8.png" width="80" height="80"></p></td> 59 60</tr></table> 61 62<div><img id="newImg"></div> 63 64 65<script src="wboard.js"></script> 66<script src="jquery.min.js"></script> 67<script type="text/javascript" src="jquery-ui-1.8.12.custom.min.js"></script> 68<script type="text/javascript" src="jquery.ui.touch-punch.js"></script> 69<script type="text/javascript" src="move.js"></script> 70 71 <script> 72 $(function(){ 73 74 // id="ufile"の変化でコールバック 75 $("#ufile").change(function(){ 76 // 選択ファイルの有無をチェック 77 if (!this.files.length) { 78 alert('ファイルが選択されていません'); 79 return; 80 } 81 82 // Formからファイルを取得 83 var file = this.files[0]; 84 85 // (1) HTMLのCanvas要素の取得 86 var canvas = $("#drawArea"); 87 88 // (2) getContext()メソッドで描画機能を有効にする 89 var ctx = canvas[0].getContext('2d'); 90 91 // 描画イメージインスタンス化 92 var image = new Image(); 93 94 // File API FileReader Objectでローカルファイルにアクセス 95 var fr = new FileReader(); 96 97 // ファイル読み込み読み込み完了後に実行 [非同期処理] 98 fr.onload = function(evt) { 99 100 // 画像がロードされた後にcanvasに描画を行う [非同期処理] 101 image.onload = function() { 102 // (3) プレビュー(Cnavas)のサイズを指定 103 var cnvsH = ctx.canvas.height; 104 var cnvsW = image.naturalWidth*cnvsH/image.naturalHeight; 105 // (4) Cnavasにサイズアトリビュートを設定する 106 canvas.attr('width', cnvsW); 107 canvas.attr('height', cnvsH); 108 // (5) 描画 109 ctx.drawImage(image, 0, 0, cnvsW, cnvsH); 110 } 111 // 読み込んだ画像をimageのソースに設定 112 image.src = evt.target.result; 113 } 114 115 // fileを読み込む データはBase64エンコードされる 116 fr.readAsDataURL(file); 117 }) 118 }) 119</script> 120<script language="JavaScript" type="text/javascript"> 121<!-- 122var elem1 = document.getElementById("card"); 123function toggle_view1() { 124 elem1.style.display = ""; 125} 126function toggle_hidden1() { 127 elem1.style.display = "none"; 128} 129--> 130</script> 131</body> 132</html> 133

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

chromeではローカル環境(HTML文書のURLがfile://から始まるもの)におけるtoDataURL(toBlob,getImageData,captureStream)メソッドの実行が許可されていません.(実行するとセキュリティエラーが発生する)

これは悪意あるスクリプトをローカル環境で実行することによる情報漏えい(ローカル画像のパスを直接指定し, canvas要素を使ってURL文字列化して外部に送信する手法)を防ぐためのものです.

NOTE:
canvas要素ではこの「危険な画像の出力」をorigin clean flagと呼ばれる内部フラグで管理しており, 同一生成元ポリシーに反する操作(例えば異なるオリジンにある画像を書き込む)を行うことでこのフラグがfalseに設定され, この状態をしばしばオリジンクリーンフラグが汚染されたと形容します. オリジンクリーンフラグが汚染されたcanvas要素に対して上記メソッド群を実行するとエラーとなります.

NOTE:
Fileオブジェクトを介したローカル画像の書き込みはユーザーの操作によって書き込まれたことが明らかであることからオリジンクリーンフラグを汚染しません.

NOTE:
ローカル環境での動作はブラウザごとに対処が異なります. 例えばFireFoxではHTML文書が保管されているパスよりも上位のディレクトリの画像を参照した場合に限り同様の問題が発生します.


解決策としては, 次の二つが考えられます.

  • HTML文書をWEBサーバー上に配置して実行する.
  • chromeを--allow-file-access-from-filesフラグ付きで起動し, HTML文書を実行する.

NOTE:
右クリックによる画像の保存はユーザーが実行したことが明らかであるため, エラーは発生しません.

投稿2018/01/25 18:16

編集2018/01/25 18:50
defghi1977

総合スコア4756

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問