質問編集履歴

1

質問の追加

2017/11/21 07:56

投稿

west826
west826

スコア14

test CHANGED
File without changes
test CHANGED
@@ -1,3 +1,27 @@
1
+ ###前提・実現したいこと
2
+
3
+ リアルタイムに動画の処理ができるVJの開発をしたいと考えています。
4
+
5
+ ※Firefoxを使用しています
6
+
7
+
8
+
9
+ ###発生している問題
10
+
11
+ - グレースケールボタンを押してから、セピアボタンを押すとグレースケールの設定が表示されないようにしたい。
12
+
13
+ →clearIntervalで試してみたがうまくいきませんでした
14
+
15
+ - グレースケールボタンを押してからもカラーの映像がたまに表示されてしまう
16
+
17
+ →setIntervalの秒数を変えたりしてみたが直りませんでした
18
+
19
+ - 同様に、ぼかしを押しても何も変化がありません。
20
+
21
+ →エラーが出ないので対処の仕方がわかりません
22
+
23
+
24
+
1
25
  ```html
2
26
 
3
27
  <!DOCTYPE html>
@@ -10,670 +34,630 @@
10
34
 
11
35
  <title>映像とキャンバスを同期</title>
12
36
 
13
-
14
-
15
-
16
-
17
- <style type="text/css">
18
-
19
- #v{
20
-
21
- display:none;
22
-
23
- }
24
-
25
- #screen{
26
-
27
- width:480px;
28
-
29
- height:270px;
30
-
31
- background-color:#000000;
32
-
33
- }
34
-
35
- button{
36
-
37
- width:100px;
38
-
39
- height:50px;
40
-
41
- }
42
-
43
- </style>
44
-
45
- </head>
37
+ </head> <body>
38
+
39
+ <video controls id="v" width="480" height="270">
40
+
41
+ <source src="movie.mp4" width="480" height="270">
42
+
43
+ </video>
44
+
45
+ <div id="screen">
46
+
47
+ <canvas id="c" width="480" height="270"></canvas>
48
+
49
+ </div>
50
+
51
+ <script type="text/javascript" src="script.js"></script>
52
+
53
+
54
+
55
+ <button onClick="playVideo()">play/stop</button>
56
+
57
+ <button onClick="restart()">restart</button>
58
+
59
+ <br>
60
+
61
+ <p>効果:</p>
62
+
63
+ <button onClick="grayscale()">グレースケール</button>
64
+
65
+ <button onClick="diffusion()">拡散</button>
66
+
67
+ <button onClick="sepia()">セピア</button>
68
+
69
+ <button onClick="inversion()">反転</button>
70
+
71
+ <button onClick="shading()">ぼかし</button>
72
+
73
+ <button onClick="heatmap()">疑似カラー</button>
74
+
75
+ <input id="color" type="color" value="#ff0000" />
76
+
77
+ <input id="distance" type="number" value="10" />
78
+
79
+ <button onClick="outline()">アウトライン</button>
80
+
81
+ </body>
82
+
83
+ </html>
84
+
85
+ ```
86
+
87
+ ```javascript
88
+
89
+ //play/stopボタンの設定
90
+
91
+ function playVideo(){
92
+
93
+ var video = document.getElementById("v");
94
+
95
+ if(video.paused){
96
+
97
+ video.play();
98
+
99
+ }else{
100
+
101
+ video.pause();
102
+
103
+ }
104
+
105
+ setInterval(function(){
106
+
107
+ var canvas = document.getElementById("c");
108
+
109
+ canvas.getContext("2d").drawImage(video, 0, 0, 480, 270);
110
+
111
+ }, 1000/30);
112
+
113
+ }
114
+
115
+
116
+
117
+ //restartボタンの設定
118
+
119
+ function restart() {
120
+
121
+ var video = document.getElementById("v");
122
+
123
+ video.currentTime = 0;
124
+
125
+ }
126
+
127
+
128
+
129
+ //グレースケールボタンの設定
130
+
131
+ function grayscale(){
132
+
133
+ //clearInterval(timer2);
134
+
135
+ timer1=setInterval(function(){
136
+
137
+
138
+
139
+ // 対象のCanvasを取得し、contextも取得する。
140
+
141
+ var canvas = document.getElementById("c");
142
+
143
+ var context = canvas.getContext("2d");
144
+
145
+ var video = document.getElementById("v");
146
+
147
+
148
+
149
+ // Canvasから描画内容を保持するimageDataを取得する。
150
+
151
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
152
+
153
+
154
+
155
+ // 描画内容に対して、式を当てはめながらrgbの値を計算する。
156
+
157
+ var d = imageData.data;
158
+
159
+ for (var i = 0; i < d.length; i+=4) {
160
+
161
+ var g = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722;
162
+
163
+ d[i] = d[i+1] = d[i+2] = g;
164
+
165
+ // d[i+3]に格納されたα値は変更しない
166
+
167
+ }
46
168
 
47
169
 
48
170
 
49
- <body>
50
-
51
- <video controls id="v" width="480" height="270">
52
-
53
- <source src="movie.mp4" width="480" height="270">
54
-
55
- </video>
171
+ // 計算結果でCanvasの表示内容を更新する。
56
-
57
-
58
-
172
+
59
- <div id="screen">
173
+ context.putImageData(imageData, 0, 0);
60
-
61
- <canvas id="c" width="480" height="270"></canvas>
62
-
63
- </div>
64
-
65
- <script type="text/javascript" src="script.js"></script>
66
-
67
-
68
-
69
- <button onClick="playVideo()">play/stop</button>
70
-
71
- <button onClick="restart()">restart</button>
72
-
73
- <br>
74
-
75
- <p>効果:</p>
76
-
77
- <button onClick="grayscale()">グレースケール</button>
78
-
79
- <button onClick="diffusion()">拡散</button>
80
-
81
- <button onClick="sepia()">セピア</button>
82
-
83
- <button onClick="inversion()">反転</button>
84
-
85
- <button onClick="shading()">ぼかし</button>
86
-
87
- <button onClick="heatmap()">疑似カラー</button>
88
-
89
- <input id="color" type="color" value="#ff0000" />
90
-
91
- <input id="distance" type="number" value="10" />
92
-
93
- <button onClick="outline()">アウトライン</button>
94
174
 
95
175
 
96
176
 
177
+ }, 1000/30);
178
+
179
+
180
+
181
+ }
182
+
183
+
184
+
185
+
186
+
187
+ //拡散
188
+
189
+ function diffusion(){
190
+
191
+
192
+
193
+ }
194
+
195
+
196
+
197
+
198
+
199
+ //セピア
200
+
201
+ function sepia(){
202
+
203
+ //他のボタンの処理を止める
204
+
205
+ //clearInterval(timer1);
206
+
207
+ timer2=setInterval(function(){
208
+
209
+ // 対象のCanvasを取得し、contextも取得する。
210
+
211
+ var canvas = document.getElementById("c");
212
+
213
+ var context = canvas.getContext("2d");
214
+
215
+ var video = document.getElementById("v");
216
+
217
+
218
+
219
+ // Canvasから描画内容を保持するimageDataを取得する。
220
+
221
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
222
+
223
+
224
+
225
+ // 描画内容に対して、式を当てはめながらrgbの値を計算する。
226
+
227
+ var d = imageData.data;
228
+
229
+ for (var i = 0; i < d.length; i+=4) {
230
+
231
+ var g =0.34*d[i] + 0.5*d[i+1] + 0.16*d[i+2];
232
+
233
+
234
+
235
+ // red
236
+
237
+ d[i] = (g/255)*240;
238
+
97
- </body>
239
+ // green
240
+
98
-
241
+ d[i + 1] = (g/255)*200;
242
+
99
- </html>
243
+ // blue
244
+
245
+ d[i + 2] = (g/255)*145;
246
+
247
+ }
248
+
249
+
250
+
251
+ // 計算結果でCanvasの表示内容を更新する。
252
+
253
+ context.putImageData(imageData, 0, 0);
254
+
255
+
256
+
257
+ }, 1000/30);
258
+
259
+
260
+
261
+ }
262
+
263
+
264
+
265
+
266
+
267
+ //ネガポジ反転
268
+
269
+ function inversion(){
270
+
271
+ //省略
272
+
273
+ }
274
+
275
+
276
+
277
+
278
+
279
+ //ぼかし(近傍8点)
280
+
281
+ function shading(context, width,height,mosaicSize){
282
+
283
+ setInterval(function(){
284
+
285
+ // 対象のCanvasを取得し、contextも取得する。
286
+
287
+ var canvas = document.getElementById("c");
288
+
289
+ var context = canvas.getContext("2d");
290
+
291
+
292
+
293
+ // Canvasから描画内容を保持するimageDataを取得する。
294
+
295
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
296
+
297
+
298
+
299
+ function BlurEffect(data,width){
300
+
301
+ for(var i = 0;i<data.length;i+=4){
302
+
303
+ var pre_line = i - width*4;
304
+
305
+ var next_line = i + width*4;
306
+
307
+
308
+
309
+ var red = (data[pre_line-4]+data[pre_line]+data[pre_line+4]
310
+
311
+ +data[i-4]+data[i]+data[i+4]
312
+
313
+ +data[next_line -4]+data[next_line]+data[next_line+4])/9;
314
+
315
+
316
+
317
+ var blue = (data[pre_line-3]+data[pre_line+1]+data[pre_line+5]
318
+
319
+ +data[i-3]+data[i+1]+data[i+5]
320
+
321
+ +data[next_line -3]+data[next_line+1]+data[next_line+5])/9;
322
+
323
+
324
+
325
+ var green = (data[pre_line-2]+data[pre_line+2]+data[pre_line+6]
326
+
327
+ +data[i-2]+data[i+2]+data[i+6]
328
+
329
+ +data[next_line -2]+data[next_line+2]+data[next_line+6])/9;
330
+
331
+
332
+
333
+
334
+
335
+ // red
336
+
337
+ data[i] = red;
338
+
339
+ // green
340
+
341
+ data[i + 1] = blue;
342
+
343
+ // blue
344
+
345
+ data[i + 2] = green;
346
+
347
+ }
348
+
349
+ }
350
+
351
+ // 生成したモノクロ画像データを適用
352
+
353
+ context.putImageData(imageData, 0, 0);
354
+
355
+ }, 1000/30);
356
+
357
+ }
358
+
359
+
360
+
361
+
362
+
363
+ //疑似カラー
364
+
365
+ function heatmap(){
366
+
367
+
368
+
369
+ }
370
+
371
+
372
+
373
+
374
+
375
+ //アウトライン
376
+
377
+ function outline(){
378
+
379
+
380
+
381
+ setInterval(function(){
382
+
383
+ // 対象のCanvasを取得し、contextも取得する。
384
+
385
+ var canvas = document.getElementById("c");
386
+
387
+ var context = canvas.getContext("2d");
388
+
389
+
390
+
391
+ // videoの映像をcanvasに描画する
392
+
393
+ var draw = function () {
394
+
395
+ // ここでアウトライン処理をする
396
+
397
+ outline();
398
+
399
+ requestAnimationFrame(draw);
400
+
401
+ };
402
+
403
+
404
+
405
+ // 境界線とする閾値
406
+
407
+ var outlineColor = {r: 255, g: 0, b: 0},
408
+
409
+ complementaryColor = {r: 0, g: 255, b: 255},
410
+
411
+ colorDistance = 10;
412
+
413
+
414
+
415
+ // アウトライン処理
416
+
417
+ var outline = function () {
418
+
419
+ // Canvasから描画内容を保持するimageDataを取得する
420
+
421
+ var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
422
+
423
+ var d = imageData.data;
424
+
425
+
426
+
427
+ // dはUint8ClampedArray
428
+
429
+ // 長さはcanvasの width * height * 4(r,g,b,a)
430
+
431
+ // 先頭から、一番左上のピクセルのr,g,b,aの値が順に入っており、
432
+
433
+ // 右隣のピクセルのr,g,b,aの値が続く
434
+
435
+ // n から n+4 までが1つのピクセルの情報となる
436
+
437
+
438
+
439
+ var currentOutlineColor = outlineColor;
440
+
441
+
442
+
443
+ for (var i = 0, l = d.length; i < l; i += 4) {
444
+
445
+
446
+
447
+ // この条件の時、currentは右端の色、nextは1px下の段の左端の色になるので透明にしてスキップする
448
+
449
+ if ((i / 4 + 1) % canvas.width === 0) {
450
+
451
+ d[i + 3] = 0;
452
+
453
+ continue;
454
+
455
+ }
456
+
457
+
458
+
459
+ // 段が変わったら色を変える
460
+
461
+ // 一段ずつoutlineColorからcomplementaryColorにグラデーションにする
462
+
463
+ if ((i / 4) % canvas.width === 0) {
464
+
465
+ var row = (i / 4) / canvas.width,
466
+
467
+ r = (outlineColor.r - complementaryColor.r) / canvas.height,
468
+
469
+ g = (outlineColor.g - complementaryColor.g) / canvas.height,
470
+
471
+ b = (outlineColor.b - complementaryColor.b) / canvas.height;
472
+
473
+
474
+
475
+ currentOutlineColor = {
476
+
477
+ r: outlineColor.r - (r * row),
478
+
479
+ g: outlineColor.g - (g * row),
480
+
481
+ b: outlineColor.b - (b * row)
482
+
483
+ };
484
+
485
+ }
486
+
487
+
488
+
489
+ var currentIndex = i,
490
+
491
+ nextIndex = currentIndex + 4,
492
+
493
+ underIndex = currentIndex + (canvas.width * 4),
494
+
495
+ // チェックするピクセルの色
496
+
497
+ current = {
498
+
499
+ r: d[currentIndex],
500
+
501
+ g: d[currentIndex + 1],
502
+
503
+ b: d[currentIndex + 2]
504
+
505
+ },
506
+
507
+ // 右隣の色
508
+
509
+ next = {
510
+
511
+ r: d[nextIndex],
512
+
513
+ g: d[nextIndex + 1],
514
+
515
+ b: d[nextIndex + 2]
516
+
517
+ },
518
+
519
+ // 下の色
520
+
521
+ under = {
522
+
523
+ r: d[underIndex],
524
+
525
+ g: d[underIndex + 1],
526
+
527
+ b: d[underIndex + 2]
528
+
529
+ };
530
+
531
+
532
+
533
+ // 現在のピクセルと右隣、下の色の三次元空間上の距離を閾値と比較する
534
+
535
+ // 閾値より大きい(色が遠い)場合、境界線とみなしそのピクセルをcurrentOutlineColorに変更
536
+
537
+ // 閾値より小さい(色が近い)場合、そのピクセルを消す
538
+
539
+ if (getColorDistance(current, next) > colorDistance || getColorDistance(current, under) > colorDistance) {
540
+
541
+ d[i] = currentOutlineColor.r;
542
+
543
+ d[i + 1] = currentOutlineColor.g;
544
+
545
+ d[i + 2] = currentOutlineColor.b;
546
+
547
+ } else {
548
+
549
+ // alpha値を0にすることで見えなくする
550
+
551
+ d[i + 3] = 0;
552
+
553
+ }
554
+
555
+ }
556
+
557
+
558
+
559
+ // 書き換えたdをimageDataにもどし、描画する
560
+
561
+ imageData.data = d;
562
+
563
+ context.putImageData(imageData, 0, 0);
564
+
565
+ };
566
+
567
+
568
+
569
+ // r,g,bというkeyを持ったobjectが第一引数と第二引数に渡される想定
570
+
571
+ var getColorDistance = function (rgb1, rgb2) {
572
+
573
+ // 三次元空間の距離が返る
574
+
575
+ return Math.sqrt(
576
+
577
+ Math.pow((rgb1.r - rgb2.r), 2) +
578
+
579
+ Math.pow((rgb1.g - rgb2.g), 2) +
580
+
581
+ Math.pow((rgb1.b - rgb2.b), 2)
582
+
583
+ );
584
+
585
+ };
586
+
587
+
588
+
589
+ var color = document.getElementById('color');
590
+
591
+ color.addEventListener('change', function () {
592
+
593
+ // フォームの値は16進カラーコードなのでrgb値に変換する
594
+
595
+ outlineColor = color2rgb(this.value);
596
+
597
+ complementaryColor = getComplementaryColor(outlineColor);
598
+
599
+ });
600
+
601
+
602
+
603
+ var color2rgb = function (color) {
604
+
605
+ color = color.replace(/^#/, '');
606
+
607
+ return {
608
+
609
+ r: parseInt(color.substr(0, 2), 16),
610
+
611
+ g: parseInt(color.substr(2, 2), 16),
612
+
613
+ b: parseInt(color.substr(4, 2), 16)
614
+
615
+ };
616
+
617
+ };
618
+
619
+
620
+
621
+ // 補色の計算
622
+
623
+ var getComplementaryColor = function (rgb) {
624
+
625
+ var max = Math.max(rgb.r, rgb.g, rgb.b),
626
+
627
+ min = Math.min(rgb.r, rgb.g, rgb.b),
628
+
629
+ sum = max + min;
630
+
631
+ return {
632
+
633
+ r: sum - rgb.r,
634
+
635
+ g: sum - rgb.g,
636
+
637
+ b: sum - rgb.b
638
+
639
+ };
640
+
641
+ };
642
+
643
+
644
+
645
+ var distance = document.getElementById('distance');
646
+
647
+ distance.style.textAlign = 'right';
648
+
649
+ distance.addEventListener('change', function () {
650
+
651
+ colorDistance = this.value;
652
+
653
+ });
654
+
655
+
656
+
657
+ }, 1000/30);
658
+
659
+
660
+
661
+ }
100
662
 
101
663
  ```
102
-
103
- ```javascript
104
-
105
- //play/stopボタンの設定
106
-
107
- function playVideo(){
108
-
109
- var video = document.getElementById("v");
110
-
111
- if(video.paused){
112
-
113
- video.play();
114
-
115
- }else{
116
-
117
- video.pause();
118
-
119
- }
120
-
121
- setInterval(function(){
122
-
123
- var canvas = document.getElementById("c");
124
-
125
- canvas.getContext("2d").drawImage(video, 0, 0, 480, 270);
126
-
127
- }, 1000/30);
128
-
129
- }
130
-
131
-
132
-
133
- //restartボタンの設定
134
-
135
- function restart() {
136
-
137
- var video = document.getElementById("v");
138
-
139
- video.currentTime = 0;
140
-
141
- }
142
-
143
-
144
-
145
- //グレースケールボタンの設定
146
-
147
- function grayscale(){
148
-
149
- //clearInterval(timer2);
150
-
151
- timer1=setInterval(function(){
152
-
153
-
154
-
155
- // 対象のCanvasを取得し、contextも取得する。
156
-
157
- var canvas = document.getElementById("c");
158
-
159
- var context = canvas.getContext("2d");
160
-
161
- var video = document.getElementById("v");
162
-
163
-
164
-
165
- // Canvasから描画内容を保持するimageDataを取得する。
166
-
167
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
168
-
169
-
170
-
171
- // 描画内容に対して、式を当てはめながらrgbの値を計算する。
172
-
173
- var d = imageData.data;
174
-
175
- for (var i = 0; i < d.length; i+=4) {
176
-
177
- var g = d[i] * 0.2126 + d[i+1] * 0.7152 + d[i+2] * 0.0722;
178
-
179
- d[i] = d[i+1] = d[i+2] = g;
180
-
181
- // d[i+3]に格納されたα値は変更しない
182
-
183
- }
184
-
185
-
186
-
187
- // 計算結果でCanvasの表示内容を更新する。
188
-
189
- context.putImageData(imageData, 0, 0);
190
-
191
-
192
-
193
- }, 1000/30);
194
-
195
-
196
-
197
- }
198
-
199
-
200
-
201
-
202
-
203
- //拡散
204
-
205
- function diffusion(){
206
-
207
-
208
-
209
- }
210
-
211
-
212
-
213
-
214
-
215
- //セピア
216
-
217
- function sepia(){
218
-
219
- //他のボタンの処理を止める
220
-
221
- //clearInterval(timer1);
222
-
223
- timer2=setInterval(function(){
224
-
225
- // 対象のCanvasを取得し、contextも取得する。
226
-
227
- var canvas = document.getElementById("c");
228
-
229
- var context = canvas.getContext("2d");
230
-
231
- var video = document.getElementById("v");
232
-
233
-
234
-
235
- // Canvasから描画内容を保持するimageDataを取得する。
236
-
237
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
238
-
239
-
240
-
241
- // 描画内容に対して、式を当てはめながらrgbの値を計算する。
242
-
243
- var d = imageData.data;
244
-
245
- for (var i = 0; i < d.length; i+=4) {
246
-
247
- var g =0.34*d[i] + 0.5*d[i+1] + 0.16*d[i+2];
248
-
249
-
250
-
251
- // red
252
-
253
- d[i] = (g/255)*240;
254
-
255
- // green
256
-
257
- d[i + 1] = (g/255)*200;
258
-
259
- // blue
260
-
261
- d[i + 2] = (g/255)*145;
262
-
263
- }
264
-
265
-
266
-
267
- // 計算結果でCanvasの表示内容を更新する。
268
-
269
- context.putImageData(imageData, 0, 0);
270
-
271
-
272
-
273
- }, 1000/30);
274
-
275
-
276
-
277
- }
278
-
279
-
280
-
281
-
282
-
283
- //ネガポジ反転
284
-
285
- function inversion(){
286
-
287
- //省略
288
-
289
- }
290
-
291
-
292
-
293
-
294
-
295
- //ぼかし(近傍8点)
296
-
297
- function shading(context, width,height,mosaicSize){
298
-
299
- setInterval(function(){
300
-
301
- // 対象のCanvasを取得し、contextも取得する。
302
-
303
- var canvas = document.getElementById("c");
304
-
305
- var context = canvas.getContext("2d");
306
-
307
-
308
-
309
- // Canvasから描画内容を保持するimageDataを取得する。
310
-
311
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
312
-
313
-
314
-
315
- function BlurEffect(data,width){
316
-
317
- for(var i = 0;i<data.length;i+=4){
318
-
319
- var pre_line = i - width*4;
320
-
321
- var next_line = i + width*4;
322
-
323
-
324
-
325
- var red = (data[pre_line-4]+data[pre_line]+data[pre_line+4]
326
-
327
- +data[i-4]+data[i]+data[i+4]
328
-
329
- +data[next_line -4]+data[next_line]+data[next_line+4])/9;
330
-
331
-
332
-
333
- var blue = (data[pre_line-3]+data[pre_line+1]+data[pre_line+5]
334
-
335
- +data[i-3]+data[i+1]+data[i+5]
336
-
337
- +data[next_line -3]+data[next_line+1]+data[next_line+5])/9;
338
-
339
-
340
-
341
- var green = (data[pre_line-2]+data[pre_line+2]+data[pre_line+6]
342
-
343
- +data[i-2]+data[i+2]+data[i+6]
344
-
345
- +data[next_line -2]+data[next_line+2]+data[next_line+6])/9;
346
-
347
-
348
-
349
-
350
-
351
- // red
352
-
353
- data[i] = red;
354
-
355
- // green
356
-
357
- data[i + 1] = blue;
358
-
359
- // blue
360
-
361
- data[i + 2] = green;
362
-
363
- }
364
-
365
- }
366
-
367
- // 生成したモノクロ画像データを適用
368
-
369
- context.putImageData(imageData, 0, 0);
370
-
371
- }, 1000/30);
372
-
373
- }
374
-
375
-
376
-
377
-
378
-
379
- //疑似カラー
380
-
381
- function heatmap(){
382
-
383
-
384
-
385
- }
386
-
387
-
388
-
389
-
390
-
391
- //アウトライン
392
-
393
- function outline(){
394
-
395
-
396
-
397
- setInterval(function(){
398
-
399
- // 対象のCanvasを取得し、contextも取得する。
400
-
401
- var canvas = document.getElementById("c");
402
-
403
- var context = canvas.getContext("2d");
404
-
405
-
406
-
407
- // videoの映像をcanvasに描画する
408
-
409
- var draw = function () {
410
-
411
- // ここでアウトライン処理をする
412
-
413
- outline();
414
-
415
- requestAnimationFrame(draw);
416
-
417
- };
418
-
419
-
420
-
421
- // 境界線とする閾値
422
-
423
- var outlineColor = {r: 255, g: 0, b: 0},
424
-
425
- complementaryColor = {r: 0, g: 255, b: 255},
426
-
427
- colorDistance = 10;
428
-
429
-
430
-
431
- // アウトライン処理
432
-
433
- var outline = function () {
434
-
435
- // Canvasから描画内容を保持するimageDataを取得する
436
-
437
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
438
-
439
- var d = imageData.data;
440
-
441
-
442
-
443
- // dはUint8ClampedArray
444
-
445
- // 長さはcanvasの width * height * 4(r,g,b,a)
446
-
447
- // 先頭から、一番左上のピクセルのr,g,b,aの値が順に入っており、
448
-
449
- // 右隣のピクセルのr,g,b,aの値が続く
450
-
451
- // n から n+4 までが1つのピクセルの情報となる
452
-
453
-
454
-
455
- var currentOutlineColor = outlineColor;
456
-
457
-
458
-
459
- for (var i = 0, l = d.length; i < l; i += 4) {
460
-
461
-
462
-
463
- // この条件の時、currentは右端の色、nextは1px下の段の左端の色になるので透明にしてスキップする
464
-
465
- if ((i / 4 + 1) % canvas.width === 0) {
466
-
467
- d[i + 3] = 0;
468
-
469
- continue;
470
-
471
- }
472
-
473
-
474
-
475
- // 段が変わったら色を変える
476
-
477
- // 一段ずつoutlineColorからcomplementaryColorにグラデーションにする
478
-
479
- if ((i / 4) % canvas.width === 0) {
480
-
481
- var row = (i / 4) / canvas.width,
482
-
483
- r = (outlineColor.r - complementaryColor.r) / canvas.height,
484
-
485
- g = (outlineColor.g - complementaryColor.g) / canvas.height,
486
-
487
- b = (outlineColor.b - complementaryColor.b) / canvas.height;
488
-
489
-
490
-
491
- currentOutlineColor = {
492
-
493
- r: outlineColor.r - (r * row),
494
-
495
- g: outlineColor.g - (g * row),
496
-
497
- b: outlineColor.b - (b * row)
498
-
499
- };
500
-
501
- }
502
-
503
-
504
-
505
- var currentIndex = i,
506
-
507
- nextIndex = currentIndex + 4,
508
-
509
- underIndex = currentIndex + (canvas.width * 4),
510
-
511
- // チェックするピクセルの色
512
-
513
- current = {
514
-
515
- r: d[currentIndex],
516
-
517
- g: d[currentIndex + 1],
518
-
519
- b: d[currentIndex + 2]
520
-
521
- },
522
-
523
- // 右隣の色
524
-
525
- next = {
526
-
527
- r: d[nextIndex],
528
-
529
- g: d[nextIndex + 1],
530
-
531
- b: d[nextIndex + 2]
532
-
533
- },
534
-
535
- // 下の色
536
-
537
- under = {
538
-
539
- r: d[underIndex],
540
-
541
- g: d[underIndex + 1],
542
-
543
- b: d[underIndex + 2]
544
-
545
- };
546
-
547
-
548
-
549
- // 現在のピクセルと右隣、下の色の三次元空間上の距離を閾値と比較する
550
-
551
- // 閾値より大きい(色が遠い)場合、境界線とみなしそのピクセルをcurrentOutlineColorに変更
552
-
553
- // 閾値より小さい(色が近い)場合、そのピクセルを消す
554
-
555
- if (getColorDistance(current, next) > colorDistance || getColorDistance(current, under) > colorDistance) {
556
-
557
- d[i] = currentOutlineColor.r;
558
-
559
- d[i + 1] = currentOutlineColor.g;
560
-
561
- d[i + 2] = currentOutlineColor.b;
562
-
563
- } else {
564
-
565
- // alpha値を0にすることで見えなくする
566
-
567
- d[i + 3] = 0;
568
-
569
- }
570
-
571
- }
572
-
573
-
574
-
575
- // 書き換えたdをimageDataにもどし、描画する
576
-
577
- imageData.data = d;
578
-
579
- context.putImageData(imageData, 0, 0);
580
-
581
- };
582
-
583
-
584
-
585
- // r,g,bというkeyを持ったobjectが第一引数と第二引数に渡される想定
586
-
587
- var getColorDistance = function (rgb1, rgb2) {
588
-
589
- // 三次元空間の距離が返る
590
-
591
- return Math.sqrt(
592
-
593
- Math.pow((rgb1.r - rgb2.r), 2) +
594
-
595
- Math.pow((rgb1.g - rgb2.g), 2) +
596
-
597
- Math.pow((rgb1.b - rgb2.b), 2)
598
-
599
- );
600
-
601
- };
602
-
603
-
604
-
605
- var color = document.getElementById('color');
606
-
607
- color.addEventListener('change', function () {
608
-
609
- // フォームの値は16進カラーコードなのでrgb値に変換する
610
-
611
- outlineColor = color2rgb(this.value);
612
-
613
- complementaryColor = getComplementaryColor(outlineColor);
614
-
615
- });
616
-
617
-
618
-
619
- var color2rgb = function (color) {
620
-
621
- color = color.replace(/^#/, '');
622
-
623
- return {
624
-
625
- r: parseInt(color.substr(0, 2), 16),
626
-
627
- g: parseInt(color.substr(2, 2), 16),
628
-
629
- b: parseInt(color.substr(4, 2), 16)
630
-
631
- };
632
-
633
- };
634
-
635
-
636
-
637
- // 補色の計算
638
-
639
- var getComplementaryColor = function (rgb) {
640
-
641
- var max = Math.max(rgb.r, rgb.g, rgb.b),
642
-
643
- min = Math.min(rgb.r, rgb.g, rgb.b),
644
-
645
- sum = max + min;
646
-
647
- return {
648
-
649
- r: sum - rgb.r,
650
-
651
- g: sum - rgb.g,
652
-
653
- b: sum - rgb.b
654
-
655
- };
656
-
657
- };
658
-
659
-
660
-
661
- var distance = document.getElementById('distance');
662
-
663
- distance.style.textAlign = 'right';
664
-
665
- distance.addEventListener('change', function () {
666
-
667
- colorDistance = this.value;
668
-
669
- });
670
-
671
-
672
-
673
- }, 1000/30);
674
-
675
-
676
-
677
- }
678
-
679
- ```