pagePhotograph.js 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. let webcam;
  2. class PagePhotograph extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = {
  6. isCameraEnable: false,
  7. isPhotoReady: false,
  8. myImage: null,
  9. loading: false,
  10. };
  11. this.refVideo = React.createRef();
  12. this.refCamvas = React.createRef();
  13. this.refFileInput = React.createRef();
  14. this.refVideoContainer = React.createRef();
  15. }
  16. goNext = (base64) => {
  17. this.props.next(base64);
  18. }
  19. goBack = () => {
  20. this.props.back();
  21. }
  22. onLoadedmetadata = () => {
  23. const { refVideoContainer, refVideo } = this;
  24. refVideoContainer.current.style = '';
  25. refVideo.current.width = refVideo.current.videoWidth;
  26. refVideo.current.height = refVideo.current.videoHeight;
  27. refVideo.current.defaultMuted = true;
  28. refVideo.current.muted = true;
  29. refVideo.current.play();
  30. const VIDEO__Width = refVideo.current.videoWidth;
  31. const VIDEO__Height = refVideo.current.videoHeight;
  32. console.log('videoContainer', refVideoContainer.current.clientHeight);
  33. const _scale = refVideoContainer.current.clientHeight / Math.min(VIDEO__Width, VIDEO__Height);
  34. this.setState({isCameraEnable: true});
  35. console.log('isCameraEnable',this.state.isCameraEnable);
  36. $(refVideoContainer.current).css({
  37. width: VIDEO__Width,
  38. height: VIDEO__Height,
  39. top: '50%',
  40. left: '50%',
  41. 'margin-top': VIDEO__Height * -0.5,
  42. 'margin-left': VIDEO__Width * -0.5,
  43. });
  44. gsap.set(refVideoContainer.current, {
  45. scaleX: _scale,
  46. scaleY: _scale,
  47. transformOrigin: 'center center',
  48. });
  49. }
  50. initWebcam = () => {
  51. const { refVideo, refCamvas } = this;
  52. this.setState({isCameraEnable: false});
  53. webcam = new Webcam(refVideo.current, 'user', refCamvas.current);
  54. webcam
  55. .start()
  56. .then(() => {})
  57. .catch((error) => {
  58. webcam.stop();
  59. console.log(error);
  60. alert('妳的瀏覽器不支援相機哦! 請改用左下方按鍵上傳照片,或設定瀏覽器開啟相機。');
  61. });
  62. }
  63. onSnapshot = () => {
  64. const { refVideoContainer } = this;
  65. const { isCameraEnable } = this.state;
  66. if (!isCameraEnable) {
  67. return;
  68. }
  69. this.setState({loading: true});
  70. webcam
  71. .snap()
  72. .then(async (base64) => {
  73. this.setState({myImage: base64});
  74. const image = await ImageLoader(base64);
  75. const canvas = Image2Canvas(image, window.innerWidth / window.innerHeight * refVideoContainer.current.clientHeight, refVideoContainer.current.clientHeight);
  76. const detectSrc = canvas.toDataURL('image/jpg', 1.0);
  77. const detectImage = await ImageLoader(detectSrc);
  78. detectFace(detectImage).then(() => {
  79. this.setState({loading: false});
  80. this.goNext(detectSrc);
  81. }).catch((error) => {
  82. this.setState({loading: false});
  83. console.log('DetectFace Error\nName:'+error.name+'\nMessage:'+error.message);
  84. alert(error.message);
  85. this.onRemake();
  86. });
  87. })
  88. .catch((error) => {
  89. this.setState({loading: false});
  90. console.log(`Snapshot Error\nName: ${error.name}\nMessage: ${error.message}`);
  91. });
  92. }
  93. flipCamera = () => {
  94. this.setState({isCameraEnable: false});
  95. webcam.flip();
  96. webcam.start();
  97. }
  98. onRemake = () => {
  99. this.setState({myImage: ''});
  100. this.setState({isPhotoReady: false});
  101. this.setState({isCameraEnable: false});
  102. this.initWebcam();
  103. }
  104. onFileChange = (event) => {
  105. const file = event.target.files[0];
  106. if (file) {
  107. const reader = new FileReader();
  108. reader.onload = (e) => {
  109. const image = new Image();
  110. image.onload = () => {
  111. const canvas = document.createElement('canvas');
  112. canvas.width = image.width < 640 ? image.width : 640;
  113. canvas.height = (image.height / image.width) * canvas.width;
  114. const context = canvas.getContext('2d');
  115. context.drawImage(image, 0, 0, canvas.width, canvas.height);
  116. const base64 = canvas.toDataURL('image/jpg', 1.0);
  117. ImageLoader(base64).then((_img) => {
  118. this.setState({myImage: base64});
  119. this.setState({isPhotoReady: true});
  120. this.setState({loading: true});
  121. detectFace(_img).then(() => {
  122. this.setState({loading: false});
  123. this.goNext(base64);
  124. }).catch((error) => {
  125. this.setState({loading: false});
  126. console.log('DetectFace Error\nName:'+error.name+'\nMessage:'+error.message);
  127. alert(error.message);
  128. });
  129. });
  130. };
  131. image.src = e.target.result;
  132. };
  133. reader.readAsDataURL(file);
  134. } else {
  135. console.log('failed');
  136. }
  137. }
  138. componentDidMount() {
  139. this.refVideo.current.addEventListener('loadedmetadata', this.onLoadedmetadata);
  140. this.initWebcam();
  141. }
  142. componentWillUnmount() {
  143. this.refVideo.current.removeEventListener("loadedmetadata", this.onLoadedmetadata);
  144. if (webcam) {
  145. webcam.stop();
  146. webcam = null;
  147. }
  148. }
  149. render() {
  150. const { goBack, onFileChange, onSnapshot, flipCamera, refVideo, refCamvas, refFileInput, refVideoContainer } = this;
  151. const { isPhotoReady, myImage, isCameraEnable, loading} = this.state;
  152. return (
  153. <div class="photographPage">
  154. <div class="photographPage__header">
  155. <div class="btn__back" onClick={goBack}>
  156. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/btn-back.png" alt="" />
  157. </div>
  158. </div>
  159. <div class="photographPage__body">
  160. <div ref={refVideoContainer} class="video__container">
  161. <video ref={refVideo} autoPlay={true} muted playsInline></video>
  162. <canvas id='canvas' ref={refCamvas} ></canvas>
  163. </div>
  164. {
  165. (isPhotoReady) ? (
  166. <img class="photographPage__userImage" src={myImage} />
  167. ) : ('')
  168. }
  169. <div class="photographPage__facemasker">
  170. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/masker.png" alt="" />
  171. <div class="photographPage__facemaskerText">請在明亮環境,將臉部對準虛線範圍<br/>確保臉部合成準確,請避免頭髮遮蔽五官!</div>
  172. </div>
  173. </div>
  174. <div class="photographPage__toolbar">
  175. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/tollbar-bg.png" alt="" />
  176. <div class="photographPage__toolbar__wrapper">
  177. <div class="btn__insert">
  178. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/icon-insert-photo.png" alt="" />
  179. <input ref={refFileInput} type="file" accept="image/jpg, image/jpeg, image/png" onChange={onFileChange}/>
  180. </div>
  181. <div className={"btn__camera " + (isCameraEnable ? 'is__show' : 'is__hidden')} onClick={onSnapshot}>
  182. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/icon-camera.png" alt="" />
  183. </div>
  184. <div className={"btn__flip__camera " + (isCameraEnable ? 'is__show' : 'is__hidden')} onClick={flipCamera}>
  185. <img src="https://d1xzlli46wohoc.cloudfront.net/assets/images/icon-flip-camera.png" alt="" />
  186. </div>
  187. </div>
  188. </div>
  189. <LoaderComponent show={loading}></LoaderComponent>
  190. </div>
  191. );
  192. }
  193. }