- 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