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

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

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

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

JavaScript

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

Q&A

解決済

1回答

1999閲覧

clearInterval・setInterval等のJavaScriptについて

west826

総合スコア14

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

JavaScript

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

0グッド

0クリップ

投稿2017/11/21 07:52

編集2017/11/21 07:56

###前提・実現したいこと
リアルタイムに動画の処理ができるVJの開発をしたいと考えています。
※Firefoxを使用しています

###発生している問題

  • グレースケールボタンを押してから、セピアボタンを押すとグレースケールの設定が表示されないようにしたい。

→clearIntervalで試してみたがうまくいきませんでした

  • グレースケールボタンを押してからもカラーの映像がたまに表示されてしまう

→setIntervalの秒数を変えたりしてみたが直りませんでした

  • 同様に、ぼかしを押しても何も変化がありません。

→エラーが出ないので対処の仕方がわかりません

html

1<!DOCTYPE html> 2<html lang="ja"> 3 <head> 4 <meta charset="utf-8"> 5 <title>映像とキャンバスを同期</title> 6 </head> <body> 7 <video controls id="v" width="480" height="270"> 8 <source src="movie.mp4" width="480" height="270"> 9 </video> 10 <div id="screen"> 11 <canvas id="c" width="480" height="270"></canvas> 12 </div> 13 <script type="text/javascript" src="script.js"></script> 14 15 <button onClick="playVideo()">play/stop</button> 16 <button onClick="restart()">restart</button> 17 <br> 18 <p>効果:</p> 19 <button onClick="grayscale()">グレースケール</button> 20 <button onClick="diffusion()">拡散</button> 21 <button onClick="sepia()">セピア</button> 22 <button onClick="inversion()">反転</button> 23 <button onClick="shading()">ぼかし</button> 24 <button onClick="heatmap()">疑似カラー</button> 25 <input id="color" type="color" value="#ff0000" /> 26 <input id="distance" type="number" value="10" /> 27 <button onClick="outline()">アウトライン</button> 28 </body> 29</html>

javascript

1//play/stopボタンの設定 2function playVideo(){ 3 var video = document.getElementById("v"); 4 if(video.paused){ 5 video.play(); 6 }else{ 7 video.pause(); 8 } 9 setInterval(function(){ 10 var canvas = document.getElementById("c"); 11 canvas.getContext("2d").drawImage(video, 0, 0, 480, 270); 12 }, 1000/30); 13} 14 15//restartボタンの設定 16function restart() { 17 var video = document.getElementById("v"); 18 video.currentTime = 0; 19} 20 21//グレースケールボタンの設定 22function grayscale(){ 23 //clearInterval(timer2); 24 timer1=setInterval(function(){ 25 26 // 対象のCanvasを取得し、contextも取得する。 27 var canvas = document.getElementById("c"); 28 var context = canvas.getContext("2d"); 29 var video = document.getElementById("v"); 30 31 // Canvasから描画内容を保持するimageDataを取得する。 32 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 33 34 // 描画内容に対して、式を当てはめながらrgbの値を計算する。 35 var d = imageData.data; 36 for (var i = 0; i < d.length; i+=4) { 37 var g = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722; 38 d[i] = d[i+1] = d[i+2] = g; 39 // d[i+3]に格納されたα値は変更しない 40 } 41 42 // 計算結果でCanvasの表示内容を更新する。 43 context.putImageData(imageData, 0, 0); 44 45 }, 1000/30); 46 47} 48 49 50//拡散 51function diffusion(){ 52 53} 54 55 56//セピア 57function sepia(){ 58 //他のボタンの処理を止める 59 //clearInterval(timer1); 60 timer2=setInterval(function(){ 61 // 対象のCanvasを取得し、contextも取得する。 62 var canvas = document.getElementById("c"); 63 var context = canvas.getContext("2d"); 64 var video = document.getElementById("v"); 65 66 // Canvasから描画内容を保持するimageDataを取得する。 67 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 68 69 // 描画内容に対して、式を当てはめながらrgbの値を計算する。 70 var d = imageData.data; 71 for (var i = 0; i < d.length; i+=4) { 72 var g =0.34*d[i] + 0.5*d[i+1] + 0.16*d[i+2]; 73 74 // red 75 d[i] = (g/255)*240; 76 // green 77 d[i + 1] = (g/255)*200; 78 // blue 79 d[i + 2] = (g/255)*145; 80 } 81 82 // 計算結果でCanvasの表示内容を更新する。 83 context.putImageData(imageData, 0, 0); 84 85 }, 1000/30); 86 87} 88 89 90//ネガポジ反転 91function inversion(){ 92 //省略 93} 94 95 96//ぼかし(近傍8点) 97function shading(context, width,height,mosaicSize){ 98 setInterval(function(){ 99 // 対象のCanvasを取得し、contextも取得する。 100 var canvas = document.getElementById("c"); 101 var context = canvas.getContext("2d"); 102 103 // Canvasから描画内容を保持するimageDataを取得する。 104 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 105 106 function BlurEffect(data,width){ 107 for(var i = 0;i<data.length;i+=4){ 108 var pre_line = i - width*4; 109 var next_line = i + width*4; 110 111 var red = (data[pre_line-4]+data[pre_line]+data[pre_line+4] 112 +data[i-4]+data[i]+data[i+4] 113 +data[next_line -4]+data[next_line]+data[next_line+4])/9; 114 115 var blue = (data[pre_line-3]+data[pre_line+1]+data[pre_line+5] 116 +data[i-3]+data[i+1]+data[i+5] 117 +data[next_line -3]+data[next_line+1]+data[next_line+5])/9; 118 119 var green = (data[pre_line-2]+data[pre_line+2]+data[pre_line+6] 120 +data[i-2]+data[i+2]+data[i+6] 121 +data[next_line -2]+data[next_line+2]+data[next_line+6])/9; 122 123 124 // red 125 data[i] = red; 126 // green 127 data[i + 1] = blue; 128 // blue 129 data[i + 2] = green; 130 } 131 } 132 // 生成したモノクロ画像データを適用 133 context.putImageData(imageData, 0, 0); 134 }, 1000/30); 135} 136 137 138//疑似カラー 139function heatmap(){ 140 141} 142 143 144//アウトライン 145function outline(){ 146 147 setInterval(function(){ 148 // 対象のCanvasを取得し、contextも取得する。 149 var canvas = document.getElementById("c"); 150 var context = canvas.getContext("2d"); 151 152 // videoの映像をcanvasに描画する 153 var draw = function () { 154 // ここでアウトライン処理をする 155 outline(); 156 requestAnimationFrame(draw); 157 }; 158 159 // 境界線とする閾値 160 var outlineColor = {r: 255, g: 0, b: 0}, 161 complementaryColor = {r: 0, g: 255, b: 255}, 162 colorDistance = 10; 163 164 // アウトライン処理 165 var outline = function () { 166 // Canvasから描画内容を保持するimageDataを取得する 167 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); 168 var d = imageData.data; 169 170 // dはUint8ClampedArray 171 // 長さはcanvasの width * height * 4(r,g,b,a) 172 // 先頭から、一番左上のピクセルのr,g,b,aの値が順に入っており、 173 // 右隣のピクセルのr,g,b,aの値が続く 174 // n から n+4 までが1つのピクセルの情報となる 175 176 var currentOutlineColor = outlineColor; 177 178 for (var i = 0, l = d.length; i < l; i += 4) { 179 180 // この条件の時、currentは右端の色、nextは1px下の段の左端の色になるので透明にしてスキップする 181 if ((i / 4 + 1) % canvas.width === 0) { 182 d[i + 3] = 0; 183 continue; 184 } 185 186 // 段が変わったら色を変える 187 // 一段ずつoutlineColorからcomplementaryColorにグラデーションにする 188 if ((i / 4) % canvas.width === 0) { 189 var row = (i / 4) / canvas.width, 190 r = (outlineColor.r - complementaryColor.r) / canvas.height, 191 g = (outlineColor.g - complementaryColor.g) / canvas.height, 192 b = (outlineColor.b - complementaryColor.b) / canvas.height; 193 194 currentOutlineColor = { 195 r: outlineColor.r - (r * row), 196 g: outlineColor.g - (g * row), 197 b: outlineColor.b - (b * row) 198 }; 199 } 200 201 var currentIndex = i, 202 nextIndex = currentIndex + 4, 203 underIndex = currentIndex + (canvas.width * 4), 204 // チェックするピクセルの色 205 current = { 206 r: d[currentIndex], 207 g: d[currentIndex + 1], 208 b: d[currentIndex + 2] 209 }, 210 // 右隣の色 211 next = { 212 r: d[nextIndex], 213 g: d[nextIndex + 1], 214 b: d[nextIndex + 2] 215 }, 216 // 下の色 217 under = { 218 r: d[underIndex], 219 g: d[underIndex + 1], 220 b: d[underIndex + 2] 221 }; 222 223 // 現在のピクセルと右隣、下の色の三次元空間上の距離を閾値と比較する 224 // 閾値より大きい(色が遠い)場合、境界線とみなしそのピクセルをcurrentOutlineColorに変更 225 // 閾値より小さい(色が近い)場合、そのピクセルを消す 226 if (getColorDistance(current, next) > colorDistance || getColorDistance(current, under) > colorDistance) { 227 d[i] = currentOutlineColor.r; 228 d[i + 1] = currentOutlineColor.g; 229 d[i + 2] = currentOutlineColor.b; 230 } else { 231 // alpha値を0にすることで見えなくする 232 d[i + 3] = 0; 233 } 234 } 235 236 // 書き換えたdをimageDataにもどし、描画する 237 imageData.data = d; 238 context.putImageData(imageData, 0, 0); 239 }; 240 241 // r,g,bというkeyを持ったobjectが第一引数と第二引数に渡される想定 242 var getColorDistance = function (rgb1, rgb2) { 243 // 三次元空間の距離が返る 244 return Math.sqrt( 245 Math.pow((rgb1.r - rgb2.r), 2) + 246 Math.pow((rgb1.g - rgb2.g), 2) + 247 Math.pow((rgb1.b - rgb2.b), 2) 248 ); 249 }; 250 251 var color = document.getElementById('color'); 252 color.addEventListener('change', function () { 253 // フォームの値は16進カラーコードなのでrgb値に変換する 254 outlineColor = color2rgb(this.value); 255 complementaryColor = getComplementaryColor(outlineColor); 256 }); 257 258 var color2rgb = function (color) { 259 color = color.replace(/^#/, ''); 260 return { 261 r: parseInt(color.substr(0, 2), 16), 262 g: parseInt(color.substr(2, 2), 16), 263 b: parseInt(color.substr(4, 2), 16) 264 }; 265 }; 266 267 // 補色の計算 268 var getComplementaryColor = function (rgb) { 269 var max = Math.max(rgb.r, rgb.g, rgb.b), 270 min = Math.min(rgb.r, rgb.g, rgb.b), 271 sum = max + min; 272 return { 273 r: sum - rgb.r, 274 g: sum - rgb.g, 275 b: sum - rgb.b 276 }; 277 }; 278 279 var distance = document.getElementById('distance'); 280 distance.style.textAlign = 'right'; 281 distance.addEventListener('change', function () { 282 colorDistance = this.value; 283 }); 284 285}, 1000/30); 286 287}

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

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

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

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

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

defghi1977

2017/11/21 08:11

video要素にfilterを掛けるだけでは駄目なのですか?canvas要素を用いる(用いなければならない)理由はありますか?
west826

2017/11/21 08:15

輪郭を抽出したいと思っているため、filterだけではできないと考えてたのでcanvas要素を用いることにしました。canvas要素を用いなくても輪郭の抽出は可能なのでしょうか?
defghi1977

2017/11/21 08:16

おそらくSVGフィルタを用いれば可能でしょう
guest

回答1

0

ベストアンサー

エラーが出ないので対処の仕方がわかりません

コードそのものに問題がなければエラーになりません. 真にロジックの問題だからです.

おそらく, 複数ヶ所でタイマーを用いているため先頭のsetIntervalが悪さをしています. このようにvideo要素の内容をcanvas要素で編集する場合は常に一つのタイマーでグラフィックの内容を転写するようにします.

JavaScript

1setInterval(function(){ 2 var canvas = document.getElementById("c"); 3 canvas.getContext("2d").drawImage(video, 0, 0, 480, 270); 4 //ここにグラフィックを編集するコードを挿入する. 5 filter(canvas); 6}, 1000/30); 7 8//フィルタ関数群 9function none(canvas){ 10} 11function grayFilter(canvas){ 12} 13function sepiaFilter(canvas){ 14} 15 16//現在のフィルタ関数 17let filter = none; 18//クリックイベントでフィルタ関数を書き換える 19gray.onclick = function(e){ 20 filter = grayFilter; 21}; 22sepia.onclick = function(e){ 23 filter = sepiaFilter; 24};

その上で, このタイマー処理の中にグレイスケース化やセピア化等の画像編集処理を記述していきます. (UI操作でその処理の中身を切り替えるわけです)


NOTE:
なお, getImageDataメソッドによるピクセル操作をリアルタイムで行うのは非常にコストの高い処理です. 表示内容を加工するだけの目的であればCSSのfilterプロパティにSVGフィルタを適用するだけで十分実現できるものと思います.


NOTE:
このようにスクリーン描画に関わる処理を記述する場合はsetIntervalメソッドよりもrequestAnimationFrameメソッドの方が適しています.


[コメントに対する回答]
これは, 「null値にonclickを設定しようとしている」から発生するエラーで,null値はgetElementByIdメソッドが引数grayに対応するノードが見つからなかったために取得されています. ではなぜgrayに対応するノードが見つからないかというと, documentノードにまだgrayノードが追加されていないのにscript要素の内容を実行したからです. 要するに, grayの前にscript要素が配置されているので発生しています.

NOTE:
WEBブラウザはHTMLを読み込んだ際, HTMLコードを先頭から順に解釈して現れたノードをDOMに追加していきます.途中script要素が見つかった場合はその時点でスクリプトを実行します.

ではどうすべきかというと,

  • grayの後にscriptを配置する
  • documentの発するDOMContentLoadedイベントに初期化処理を登録する
  • そのままgrayノードのonclick属性に処理を記述する

のように修正します.

投稿2017/11/21 08:32

編集2017/11/24 08:01
defghi1977

総合スコア4756

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

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

west826

2017/11/24 07:35

```Javascript setInterval(function(){ var canvas = document.getElementById("c"); var video = document.getElementById("v"); canvas.getContext("2d").drawImage(video, 0, 0, 480, 270); //ここにグラフィックを編集するコードを挿入する. function grayscale(){ // Canvasから描画内容を保持するimageDataを取得する。 var imageData = context.getImageData(0, 0, canvas.width, canvas.height); // 描画内容に対して、式を当てはめながらrgbの値を計算する。 var d = imageData.data; for (var i = 0; i < d.length; i+=4) { var g = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722; d[i] = d[i+1] = d[i+2] = g; // d[i+3]に格納されたα値は変更しない } // 計算結果でCanvasの表示内容を更新する。 context.putImageData(imageData, 0, 0); } filter(canvas); }, 1000/30); //フィルタ関数群 function none(canvas){ } function grayFilter(canvas){ } //現在のフィルタ関数 let filter = none; //クリックイベントでフィルタ関数を書き換える var gray=document.getElementById("gray"); gray.onclick = function(e){ filter = grayFilter; }; ``` と以上のようにすると ”Cannot set property 'onclick' of null” というエラーが出てきます。  この場合はどうしたら良いのですか? クリックしたときの処理はどこに記載したらよいのでしょうか
west826

2017/11/28 03:30

そうです。 そのようなものを作ろうとしています。
defghi1977

2017/11/28 03:49

であればなおのことCSS/SVGフィルタの方が適しているように感じます. (確かに習得コストはそれなりに掛かりますが)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問