- HTML
- CSS
/* colors */ html { width: 100%; height: 100%; } body { background-color: #eff3f4; position: relative; width: 100%; height: 100%; font-size: 16px; font-family: "Source Sans Pro", sans-serif; font-weight: 400; -webkit-font-smoothing: antialiased; } form { position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); display: block; width: 100%; max-width: 400px; background-color: #fff; margin: 0; padding: 2.25em; -webkit-box-sizing: border-box; box-sizing: border-box; border: solid 1px #ddd; border-radius: 0.5em; font-family: "Source Sans Pro", sans-serif; } form .svgContainer { position: relative; width: 200px; height: 200px; margin: 0 auto 1em; border-radius: 50%; background: none; border: solid 2.5px #3a5e77; overflow: hidden; pointer-events: none; } form .svgContainer div { position: relative; width: 100%; height: 0; overflow: hidden; padding-bottom: 100%; } form .svgContainer .mySVG { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; } form .inputGroup { margin: 0 0 2em; padding: 0; position: relative; } form .inputGroup:last-of-type { margin-bottom: 0; } form label { margin: 0 0 12px; display: block; font-size: 1.25em; color: #217093; font-weight: 700; font-family: inherit; } form input[type="email"], form input[type="text"], form input[type="password"] { display: block; margin: 0; padding: 0 1em 0; background-color: #f3fafd; border: solid 2px #217093; border-radius: 4px; -webkit-appearance: none; -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; height: 65px; font-size: 1.55em; color: #353538; font-weight: 600; font-family: inherit; -webkit-transition: border-color 0.25s ease-out, -webkit-box-shadow 0.2s linear; transition: border-color 0.25s ease-out, -webkit-box-shadow 0.2s linear; transition: box-shadow 0.2s linear, border-color 0.25s ease-out; transition: box-shadow 0.2s linear, border-color 0.25s ease-out, -webkit-box-shadow 0.2s linear; } form input[type="email"]:focus, form input[type="text"]:focus, form input[type="password"]:focus { outline: none; -webkit-box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1); border: solid 2px #4eb8dd; } form input[type="email"], form input[type="text"] { padding: 14px 1em 0px; } form button { display: block; margin: 0; padding: 0.65em 1em 1em; background-color: #4eb8dd; border: none; border-radius: 4px; -webkit-box-sizing: border-box; box-sizing: border-box; -webkit-box-shadow: none; box-shadow: none; width: 100%; height: 65px; font-size: 1.55em; color: #fff; font-weight: 600; font-family: inherit; -webkit-transition: background-color 0.2s ease-out; transition: background-color 0.2s ease-out; } form button:hover, form button:active { background-color: #217093; } form .inputGroup1 .helper { position: absolute; z-index: 1; font-family: inherit; } form .inputGroup1 .helper1 { top: 0; left: 0; -webkit-transform: translate(1.4em, 2.6em) scale(1); transform: translate(1.4em, 2.6em) scale(1); -webkit-transform-origin: 0 0; transform-origin: 0 0; color: #217093; font-size: 1.25em; font-weight: 400; opacity: 0.65; pointer-events: none; -webkit-transition: opacity 0.2s linear, -webkit-transform 0.2s ease-out; transition: opacity 0.2s linear, -webkit-transform 0.2s ease-out; transition: transform 0.2s ease-out, opacity 0.2s linear; transition: transform 0.2s ease-out, opacity 0.2s linear, -webkit-transform 0.2s ease-out; } form .inputGroup1.focusWithText .helper { /*input[type='email']:focus + .helper {*/ -webkit-transform: translate(1.4em, 2em) scale(0.65); transform: translate(1.4em, 2em) scale(0.65); opacity: 1; }
- JS
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script> 파일 가져와야함
var email = document.querySelector('#email'), password = document.querySelector('#password'), mySVG = document.querySelector('.svgContainer'), armL = document.querySelector('.armL'), armR = document.querySelector('.armR'), eyeL = document.querySelector('.eyeL'), eyeR = document.querySelector('.eyeR'), nose = document.querySelector('.nose'), mouth = document.querySelector('.mouth'), mouthBG = document.querySelector('.mouthBG'), mouthSmallBG = document.querySelector('.mouthSmallBG'), mouthMediumBG = document.querySelector('.mouthMediumBG'), mouthLargeBG = document.querySelector('.mouthLargeBG'), mouthMaskPath = document.querySelector('#mouthMaskPath'), mouthOutline = document.querySelector('.mouthOutline'), tooth = document.querySelector('.tooth'), tongue = document.querySelector('.tongue'), chin = document.querySelector('.chin'), face = document.querySelector('.face'), eyebrow = document.querySelector('.eyebrow'), outerEarL = document.querySelector('.earL .outerEar'), outerEarR = document.querySelector('.earR .outerEar'), earHairL = document.querySelector('.earL .earHair'), earHairR = document.querySelector('.earR .earHair'), hair = document.querySelector('.hair'); var caretPos, curEmailIndex, screenCenter, svgCoords, eyeMaxHorizD = 20, eyeMaxVertD = 10, noseMaxHorizD = 23, noseMaxVertD = 10, dFromC, eyeDistH, eyeLDistV, eyeRDistV, eyeDistR, mouthStatus = "small"; function getCoord(e) { var carPos = email.selectionEnd, div = document.createElement('div'), span = document.createElement('span'), copyStyle = getComputedStyle(email), emailCoords = {}, caretCoords = {}, centerCoords = {} ; [].forEach.call(copyStyle, function(prop){ div.style[prop] = copyStyle[prop]; }); div.style.position = 'absolute'; document.body.appendChild(div); div.textContent = email.value.substr(0, carPos); span.textContent = email.value.substr(carPos) || '.'; div.appendChild(span); emailCoords = getPosition(email); //console.log("emailCoords.x: " + emailCoords.x + ", emailCoords.y: " + emailCoords.y); caretCoords = getPosition(span); //console.log("caretCoords.x " + caretCoords.x + ", caretCoords.y: " + caretCoords.y); centerCoords = getPosition(mySVG); //console.log("centerCoords.x: " + centerCoords.x); svgCoords = getPosition(mySVG); screenCenter = centerCoords.x + (mySVG.offsetWidth / 2); //console.log("screenCenter: " + screenCenter); caretPos = caretCoords.x + emailCoords.x; //console.log("caretPos: " + caretPos); dFromC = screenCenter - caretPos; //console.log("dFromC: " + dFromC); var pFromC = Math.round((caretPos / screenCenter) * 100) / 100; if(pFromC < 1) { } else if(pFromC > 1) { pFromC -= 2; pFromC = Math.abs(pFromC); } eyeDistH = -dFromC * .05; if(eyeDistH > eyeMaxHorizD) { eyeDistH = eyeMaxHorizD; } else if(eyeDistH < -eyeMaxHorizD) { eyeDistH = -eyeMaxHorizD; } var eyeLCoords = {x: svgCoords.x + 84, y: svgCoords.y + 76}; var eyeRCoords = {x: svgCoords.x + 113, y: svgCoords.y + 76}; var noseCoords = {x: svgCoords.x + 97, y: svgCoords.y + 81}; var mouthCoords = {x: svgCoords.x + 100, y: svgCoords.y + 100}; var eyeLAngle = getAngle(eyeLCoords.x, eyeLCoords.y, emailCoords.x + caretCoords.x, emailCoords.y + 25); var eyeLX = Math.cos(eyeLAngle) * eyeMaxHorizD; var eyeLY = Math.sin(eyeLAngle) * eyeMaxVertD; var eyeRAngle = getAngle(eyeRCoords.x, eyeRCoords.y, emailCoords.x + caretCoords.x, emailCoords.y + 25); var eyeRX = Math.cos(eyeRAngle) * eyeMaxHorizD; var eyeRY = Math.sin(eyeRAngle) * eyeMaxVertD; var noseAngle = getAngle(noseCoords.x, noseCoords.y, emailCoords.x + caretCoords.x, emailCoords.y + 25); var noseX = Math.cos(noseAngle) * noseMaxHorizD; var noseY = Math.sin(noseAngle) * noseMaxVertD; var mouthAngle = getAngle(mouthCoords.x, mouthCoords.y, emailCoords.x + caretCoords.x, emailCoords.y + 25); var mouthX = Math.cos(mouthAngle) * noseMaxHorizD; var mouthY = Math.sin(mouthAngle) * noseMaxVertD; var mouthR = Math.cos(mouthAngle) * 6; var chinX = mouthX * .8; var chinY = mouthY * .5; var chinS = 1 - ((dFromC * .15) / 100); if(chinS > 1) {chinS = 1 - (chinS - 1);} var faceX = mouthX * .3; var faceY = mouthY * .4; var faceSkew = Math.cos(mouthAngle) * 5; var eyebrowSkew = Math.cos(mouthAngle) * 25; var outerEarX = Math.cos(mouthAngle) * 4; var outerEarY = Math.cos(mouthAngle) * 5; var hairX = Math.cos(mouthAngle) * 6; var hairS = 1.2; TweenMax.to(eyeL, 1, {x: -eyeLX , y: -eyeLY, ease: Expo.easeOut}); TweenMax.to(eyeR, 1, {x: -eyeRX , y: -eyeRY, ease: Expo.easeOut}); TweenMax.to(nose, 1, {x: -noseX, y: -noseY, rotation: mouthR, transformOrigin: "center center", ease: Expo.easeOut}); TweenMax.to(mouth, 1, {x: -mouthX , y: -mouthY, rotation: mouthR, transformOrigin: "center center", ease: Expo.easeOut}); TweenMax.to(chin, 1, {x: -chinX, y: -chinY, scaleY: chinS, ease: Expo.easeOut}); TweenMax.to(face, 1, {x: -faceX, y: -faceY, skewX: -faceSkew, transformOrigin: "center top", ease: Expo.easeOut}); TweenMax.to(eyebrow, 1, {x: -faceX, y: -faceY, skewX: -eyebrowSkew, transformOrigin: "center top", ease: Expo.easeOut}); TweenMax.to(outerEarL, 1, {x: outerEarX, y: -outerEarY, ease: Expo.easeOut}); TweenMax.to(outerEarR, 1, {x: outerEarX, y: outerEarY, ease: Expo.easeOut}); TweenMax.to(earHairL, 1, {x: -outerEarX, y: -outerEarY, ease: Expo.easeOut}); TweenMax.to(earHairR, 1, {x: -outerEarX, y: outerEarY, ease: Expo.easeOut}); TweenMax.to(hair, 1, {x: hairX, scaleY: hairS, transformOrigin: "center bottom", ease: Expo.easeOut}); document.body.removeChild(div); }; function onEmailInput(e) { getCoord(e); var value = e.target.value; curEmailIndex = value.length; // very crude email validation for now to trigger effects if(curEmailIndex > 0) { if(mouthStatus == "small") { mouthStatus = "medium"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthMediumBG, shapeIndex: 8, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {x: 0, y: 1, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .85, scaleY: .85, ease: Expo.easeOut}); } if(value.includes("@")) { mouthStatus = "large"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthLargeBG, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 3, y: -2, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {y: 2, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .65, scaleY: .65, ease: Expo.easeOut, transformOrigin: "center center"}); } else { mouthStatus = "medium"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthMediumBG, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {x: 0, y: 1, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: .85, scaleY: .85, ease: Expo.easeOut}); } } else { mouthStatus = "small"; TweenMax.to([mouthBG, mouthOutline, mouthMaskPath], 1, {morphSVG: mouthSmallBG, shapeIndex: 9, ease: Expo.easeOut}); TweenMax.to(tooth, 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(tongue, 1, {y: 0, ease: Expo.easeOut}); TweenMax.to([eyeL, eyeR], 1, {scaleX: 1, scaleY: 1, ease: Expo.easeOut}); } } function onEmailFocus(e) { e.target.parentElement.classList.add("focusWithText"); getCoord(); } function onEmailBlur(e) { if(e.target.value == "") { e.target.parentElement.classList.remove("focusWithText"); } resetFace(); } function onPasswordFocus(e) { coverEyes(); } function onPasswordBlur(e) { uncoverEyes(); } function coverEyes() { TweenMax.to(armL, .45, {x: -93, y: 2, rotation: 0, ease: Quad.easeOut}); TweenMax.to(armR, .45, {x: -93, y: 2, rotation: 0, ease: Quad.easeOut, delay: .1}); } function uncoverEyes() { TweenMax.to(armL, 1.35, {y: 220, ease: Quad.easeOut}); TweenMax.to(armL, 1.35, {rotation: 105, ease: Quad.easeOut, delay: .1}); TweenMax.to(armR, 1.35, {y: 220, ease: Quad.easeOut}); TweenMax.to(armR, 1.35, {rotation: -105, ease: Quad.easeOut, delay: .1}); } function resetFace() { TweenMax.to([eyeL, eyeR], 1, {x: 0, y: 0, ease: Expo.easeOut}); TweenMax.to(nose, 1, {x: 0, y: 0, scaleX: 1, scaleY: 1, ease: Expo.easeOut}); TweenMax.to(mouth, 1, {x: 0, y: 0, rotation: 0, ease: Expo.easeOut}); TweenMax.to(chin, 1, {x: 0, y: 0, scaleY: 1, ease: Expo.easeOut}); TweenMax.to([face, eyebrow], 1, {x: 0, y: 0, skewX: 0, ease: Expo.easeOut}); TweenMax.to([outerEarL, outerEarR, earHairL, earHairR, hair], 1, {x: 0, y: 0, scaleY: 1, ease: Expo.easeOut}); } function getAngle(x1, y1, x2, y2) { var angle = Math.atan2(y1 - y2, x1 - x2); return angle; } function getPosition(el) { var xPos = 0; var yPos = 0; while (el) { if (el.tagName == "BODY") { // deal with browser quirks with body/window/document and page scroll var xScroll = el.scrollLeft || document.documentElement.scrollLeft; var yScroll = el.scrollTop || document.documentElement.scrollTop; xPos += (el.offsetLeft - xScroll + el.clientLeft); yPos += (el.offsetTop - yScroll + el.clientTop); } else { // for all other non-BODY elements xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft); yPos += (el.offsetTop - el.scrollTop + el.clientTop); } el = el.offsetParent; } return { x: xPos, y: yPos }; } email.addEventListener('focus', onEmailFocus); email.addEventListener('blur', onEmailBlur); email.addEventListener('input', onEmailInput); password.addEventListener('focus', onPasswordFocus); password.addEventListener('blur', onPasswordBlur); TweenMax.set(armL, {x: -93, y: 220, rotation: 105, transformOrigin: "top left"}); TweenMax.set(armR, {x: -93, y: 220, rotation: -105, transformOrigin: "top right"});
출처 : https://codepen.io/dsenneff/pen/QajVxO?editors=0100#0