파일명 : teacherable.html
<!------- 메인 화면 ------->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- 백그라운드 비디오 시작 -->
<div class="video-background">
<div class="video-foreground">
<iframe frameborder="0" height="100%" width="100%"
src="<https://www.youtube.com/embed/c1mzlGjb0gQ?controls=0&mute=1&loop=1&autoplay=1&rel=0&controls=0&showinfo=0&playlist=c1mzlGjb0gQ>"
title="YouTube video player" frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
</div>
<!-- 백그라운드 비디오 끝 -->
<div class="cont">
<div class="tit">FACE ID 구현하기</div>
<div class="txt">
[인증 시작] 버튼을 눌러
<br>
인증을 시작하세요!
</div>
<video id="player" autoplay playsinline muted></video>
<div id="webcam-container"></div>
<button type="button" onclick="init()" class="btn-start">인증 시작</button>
<strong class="title_result">인증 결과</strong>
<div id="label-container"></div>
</div>
<div class="cover">
<img src="loading.gif" alt="">
</div>
<script src="myScript.js"></script>
<script src="<https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js>"></script>
<script src="<https://cdn.jsdelivr.net/npm/@teachablemachine/[email protected]/dist/teachablemachine-image.min.js>"></script>
<script type="text/javascript">
// More API functions here:
// <https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/image>
// the link to your model provided by Teachable Machine export panel
const URL = "./my_model/";
let model, webcam, labelContainer, maxPredictions;
// Load the image model and setup the webcam
async function init() {
const modelURL = URL + "model.json";
const metadataURL = URL + "metadata.json";
// load the model and metadata
// Refer to tmImage.loadFromFiles() in the API to support files from a file picker
// or files from your local hard drive
// Note: the pose library adds "tmImage" object to your window (window.tmImage)
model = await tmImage.load(modelURL, metadataURL);
maxPredictions = model.getTotalClasses();
// Convenience function to setup a webcam
const flip = true; // whether to flip the webcam
webcam = new tmImage.Webcam(390, 390, flip); // width, height, flip
await webcam.setup(); // request access to the webcam
await webcam.play();
window.requestAnimationFrame(loop);
// append elements to the DOM
document.getElementById("webcam-container").appendChild(webcam.canvas);
labelContainer = document.getElementById("label-container");
for (let i = 0; i < maxPredictions; i++) { // and class labels
labelContainer.appendChild(document.createElement("div"));
}
}
async function loop() {
webcam.update(); // update the webcam frame
await predict();
window.requestAnimationFrame(loop);
}
// run the webcam image through the image model
async function predict() {
// predict can take in an image, video or canvas html element
const prediction = await model.predict(webcam.canvas);
if (prediction[0].className == 'myFace' && prediction[0].probability.toFixed(2) > 0.80) {
window.location.href = "./success.html";
} else {
window.location.href = "./fail.html";
}
}
</script>
</body>
</html>
파일명 : success.html
<!------- 인증 성공 화면 ------->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body class="cont-card">
<div class="item-card">
<span class="txt-result">인증이 완료되었습니다!</span>
<a href="teacherable.html" class="link-return">다시 인증하기</a>
<a href="#none" class="link-return">블로그로 이동하기</a>
</div>
</body>
<script>
setTimeout(function () {
document.body.classList.add('id-card-active');
}, 100);
</script>
</html
파일명 : fail.html
<!------- 인증 실패 화면 ------->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body class="cont-card fail">
<div class="item-card">
<span class="txt-result">인증에 실패했습니다!</span>
<a href="teacherable.html" class="link-return">다시 인증하기</a>
</div>
</body>
<script>
setTimeout(function () {
document.body.classList.add('id-card-active');
}, 100);
</script>
</html>
파일명 : style.css
@charset "utf-8";
.cont{
display: inline-block;
position: relative;
top:117px;
left:50%;
transform: translateX(-50%);
margin:0 auto;
text-align: center;
}
.cont .tit{
font-weight: 900;
font-size: 24px;
line-height: 28px;
color:#F2994A;
margin-bottom:30px;
}
#player{
width: 500px;
height: 376px;
border: 2px solid #828282;
box-sizing: border-box;
border-radius: 10px;;
text-align: center;
}
.cont .txt{
margin-bottom:50px;
color:#828282;
}
.cont .btn-start{
margin-top:20px;
display: block;
width:100%;
height: 60px;
background: #F2994A;
border-radius: 10px;
font-size:20px;
color:#fff;
transition: 0.15s;
}
.cont .btn-start:hover{
transform:scale(1.1);
cursor: pointer;
}
.title_result{
display: none;
}
.cont-card{
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
perspective: 800px;
}
.item-card{
width: 390px;
height: 500px;
background:#F2994A;
color:#fff;
text-align: center;
position: relative;
transform: scale(0.01) rotateY(720deg);
transform-style: preserve-3d;
transition: 1.2s;
font-size:24px;
border-radius: 10px;
}
.id-card-active .item-card {
transform: scale(1) rotateY(0deg);
}
.fail .item-card{
transform: scale(0.01) rotateZ(720deg);
background:#828282;
}
.id-card-active.fail .item-card {
animation-duration: 1s, 1s;
animation-name: animation-fail, animation-fail-finish;
animation-delay: 0s, 1s;
animation-fill-mode: forwards;
}
.cover{
position: fixed;
top:0;
left:0;
right:0;
bottom:0;
background:black;
display: none;
}
.cover img{
display: block;
position: relative;
width: 100px;
margin:0 auto;
position:relative;
top:50%;
transform:translateY(-50%);
}
.cont-card .txt-result{
display: inline-block;
margin-top:200px;
margin:200px 0 100px;
}
.cont-card .link-return{
display: block;
height:50px;
line-height:50px;
width:80%;
margin:0 auto 20px;
text-decoration: none;
background:#fff;
border-radius: 10px;
font-weight:bold;
color:black;
}
.cont-card.id-card-active .item-card:hover{
background: #ea124f;
transform: scale(1.1) rotateZ(3deg);
cursor: pointer;
}
@keyframes animation-fail {
100%{
transform: scale(1) rotateZ(-50deg);
}
}
@keyframes animation-fail-finish {
0%{
transform: scale(1) rotateZ(-50deg);
}
10%{
transform: scaleY(1.3) scaleX(0.7) rotateZ(2deg);
}
40%{
transform: scale(1) rotateZ(-40deg);
}
50%{
transform: scale(1.2) scaleX(0.8) rotateZ(0deg);
}
65%{
transform: scale(1) rotateZ(-30deg);
}
75%{
transform: scale(1.1) scaleX(0.9) rotateZ(0deg);
}
85%{
transform: scale(1) rotateZ(-10deg);
}
90%{
transform: scale(1) rotateZ(0deg);
}
95%{
transform: scale(1) rotateZ(-5deg);
}
97%{
transform: scale(1) rotateZ(0deg);
}
99%{
transform: scale(1) rotateZ(-2deg);
}
100%{
transform: scale(1) rotateZ(0deg);
}
}
/* 영상 배경 스타일 */
.video-background {
background: #000;
position: fixed;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -99;
}
.video-foreground,
.video-background iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
@media (min-aspect-ratio: 16/9) {
.video-foreground { height: 300%; top: -100%; }
}
@media (max-aspect-ratio: 16/9) {
.video-foreground { width: 300%; left: -100%; }
}
파일명 : myScript.js
/**************** 자바스크립트 파일 *****************/
let btn = document.querySelector(".btn-start");
let resultTxt = document.querySelector(".title_result");
let player = document.querySelector("#player");
let cover = document.querySelector(".cover");
btn.addEventListener('click', function () {
resultTxt.style.display = "block";
player.style.display = "none";
cover.style.display = "block";
});
// video tag의 DOM element를 player로 지정합니다.
// 웹캠 사용권한이 승인되는 경우 스트리밍 영상을 player의 재생대상으로 지정
var handleSuccess = function (stream) {
player.srcObject = stream;
};
navigator.mediaDevices.getUserMedia({ video: true }).then(handleSuccess);
// 현재 사용중인 브라우저 객체(navigator)의 mediaDevices 인터페이스를
// 이용하여 사용자의 미디어 입력장치 사용권한을 받습니다.