@@ -3,3 +3,164 @@ const canvas = document.querySelector('.photo');
33const ctx = canvas . getContext ( '2d' ) ;
44const strip = document . querySelector ( '.strip' ) ;
55const snap = document . querySelector ( '.snap' ) ;
6+ const rgb = document . querySelector ( '.rgb' ) ;
7+ const booth = document . querySelector ( '.photobooth' ) ;
8+
9+ let videoFilter = noFilter ;
10+ const ghostingValue = 1.0 ;
11+
12+ function getVideo ( ) {
13+ navigator . mediaDevices . getUserMedia ( { video : true , audio : false } )
14+ . then ( stream => {
15+ video . src = window . URL . createObjectURL ( stream ) ;
16+ video . play ( ) ;
17+ } )
18+ . catch ( error => console . error ( "Oh snap!" , error ) ) ;
19+ }
20+
21+ function paintToCanavas ( ) {
22+ const width = video . videoWidth ;
23+ const height = video . videoHeight ;
24+ canvas . width = width ;
25+ canvas . height = height ;
26+
27+ return setInterval ( ( ) => {
28+ ctx . drawImage ( video , 0 , 0 , width , height ) ;
29+ // take the pixels out
30+ let pixels = ctx . getImageData ( 0 , 0 , width , height ) ;
31+ // mess with them
32+ pixels = videoFilter ( pixels ) ;
33+ ctx . globalAlpha = ghostingValue ;
34+ // put them back
35+ ctx . putImageData ( pixels , 0 , 0 ) ;
36+ } , 16 ) ;
37+ }
38+
39+ function takePhoto ( ) {
40+ // played the sound
41+ snap . currentTime = 0 ;
42+ snap . play ( ) ;
43+
44+ // take the data out of the canvas
45+ const dataURI = canvas . toDataURL ( 'image/jpeg' ) ;
46+ const link = document . createElement ( 'a' ) ;
47+ link . href = dataURI ;
48+ link . setAttribute ( 'download' , 'handsome' ) ;
49+ link . innerHTML = `<img src="${ dataURI } " alt="Video still" />` ;
50+ strip . insertBefore ( link , strip . firsChild ) ;
51+ }
52+
53+ function noFilter ( pixels ) { return me ( pixels ) ; }
54+ me = i => i ;
55+ plus = x => i => i + x ;
56+ minus = x => i => i - x ;
57+ multiply = x => i => i * x ;
58+ divide = x => i => i / x ;
59+ redIndex = me ;
60+ greenIndex = plus ( 1 ) ;
61+ blueIndex = plus ( 2 ) ;
62+ alphaIndex = plus ( 3 ) ;
63+ /**
64+ * pixels - our pixels for the video filter effect
65+ * all other params are functions
66+ * params beginning with r deal with the Red channel
67+ * params beginning with g deal with the Green channel
68+ * params beginning with g deal with the Blue channel
69+ * params beginning with a deal with the Alpha channel
70+ * params ending in i take the iterator from the for loop as a param and return
71+ * an index number for use in the pixel data array
72+ * params ending in t are transformation functions meant to consume a value from
73+ * the pixel data array and transform it into some new value
74+ * the middle letter for the 3 letter params deal with direction
75+ * r for Right and l for Left
76+ * the line for the Red channel can be read as:
77+ * Choose a value from the pixel data array using the index from our Red Right
78+ * Index function
79+ * Calcualte a new value from the selected value using the Red Transformation
80+ * function
81+ * Store the newly calculated value in our pixel data array at the index
82+ * indicated by our Red Left Index function
83+ */
84+ function simpleFilter ( pixels , rli , rri , rt , gli , gri , gt , bli , bri , bt , ali , ari , at ) {
85+ for ( let i = 0 ; i < pixels . data . length ; i += 4 ) {
86+ pixels . data [ rli ( i ) ] = rt ( pixels . data [ rri ( i ) ] ) ; // Red
87+ pixels . data [ gli ( i ) ] = gt ( pixels . data [ gri ( i ) ] ) ; // Green
88+ pixels . data [ bli ( i ) ] = bt ( pixels . data [ bri ( i ) ] ) ; // Blue
89+ pixels . data [ ali ( i ) ] = at ( pixels . data [ ari ( i ) ] ) ; // Alpha
90+ }
91+ return pixels ;
92+ }
93+
94+ function redEffect ( pixels ) {
95+ return simpleFilter (
96+ pixels ,
97+ redIndex , redIndex , plus ( 200 ) ,
98+ greenIndex , greenIndex , minus ( 50 ) ,
99+ blueIndex , blueIndex , multiply ( 0.5 ) ,
100+ alphaIndex , alphaIndex , me
101+ ) ;
102+ }
103+ function rgbSplit ( pixels ) {
104+ return simpleFilter (
105+ pixels ,
106+ minus ( 150 ) , redIndex , me ,
107+ plus ( 500 ) , greenIndex , me ,
108+ minus ( 550 ) , blueIndex , me ,
109+ alphaIndex , alphaIndex , me
110+ ) ;
111+ }
112+ function myFilter ( pixels ) {
113+ return simpleFilter (
114+ pixels ,
115+ redIndex , greenIndex , me ,
116+ greenIndex , blueIndex , me ,
117+ blueIndex , redIndex , me ,
118+ alphaIndex , alphaIndex , me
119+ ) ;
120+ }
121+ function greenScreen ( pixels ) {
122+ const levels = { } ;
123+
124+ rgb . querySelectorAll ( 'input' ) . forEach ( ( input ) => {
125+ levels [ input . name ] = input . value ;
126+ } ) ;
127+
128+ for ( i = 0 ; i < pixels . data . length ; i = i + 4 ) {
129+ red = pixels . data [ i + 0 ] ;
130+ green = pixels . data [ i + 1 ] ;
131+ blue = pixels . data [ i + 2 ] ;
132+ alpha = pixels . data [ i + 3 ] ;
133+
134+ if ( red >= levels . rmin
135+ && green >= levels . gmin
136+ && blue >= levels . bmin
137+ && red <= levels . rmax
138+ && green <= levels . gmax
139+ && blue <= levels . bmax ) {
140+ // take it out!
141+ pixels . data [ i + 3 ] = 0 ;
142+ }
143+ }
144+
145+ return pixels ;
146+ }
147+
148+ function handleButtons ( event ) {
149+ if ( event . target . matches ( '#TakePhoto' ) ) {
150+ takePhoto ( ) ;
151+ } else if ( event . target . matches ( '#RedEffect' ) ) {
152+ videoFilter = redEffect ;
153+ } else if ( event . target . matches ( '#SplitEffect' ) ) {
154+ videoFilter = rgbSplit ;
155+ } else if ( event . target . matches ( '#GreenscreenEffect' ) ) {
156+ videoFilter = greenScreen ;
157+ } else if ( event . target . matches ( '#ClearEffect' ) ) {
158+ videoFilter = noFilter ;
159+ } else if ( event . target . matches ( '#MyEffect' ) ) {
160+ videoFilter = myFilter ;
161+ } // else do nothing
162+ }
163+
164+ getVideo ( ) ;
165+ video . addEventListener ( 'canplay' , paintToCanavas ) ;
166+ booth . addEventListener ( 'click' , handleButtons ) ;
0 commit comments