# SVG, JS, CSS로 만드는 라이언 로그인 폼
### [원본 : https://taegon.kim/archives/9658](https://taegon.kim/archives/9658)
##### [source : https://jsfiddle.net/softm/by01n23u/ ](https://jsfiddle.net/softm/by01n23u/)
```html
Ryan Login
```
```css
body {
background: #3d516b;
}
form {
width: 320px;
padding: 60px 25px 80px;
margin: 50px auto;
background: #f8d348;
display: flex;
flex-direction: column;
}
svg {
width: 70%;
border-radius: 50%;
background: #fff;
margin-bottom: 40px;
align-self: center;
transform-style: preserve-3d;
}
input {
font-size: 16px;
border: 0;
border-radius: 5px;
outline: 0;
padding: 10px 15px;
margin-top: 15px;
}
@keyframes lookaway-up {
from {
transform: rotate3d(.2, 0, -.01, 20deg);
}
to {
transform: rotate3d(.2, -.05, -.01, 20deg);
}
}
@keyframes lookaway-down {
0% {
transform: rotate3d(-.2, 0, -.01, 20deg);
}
100% {
transform: rotate3d(-.2, -.05, .01, 20deg);
}
}
.ears {
transform-origin: 50% 50% 5px;
}
.eyes {
transform-origin: 50% 50% -40px;
}
.muzzle {
transform-origin: 50% 50% -44px;
}
.ears, .eyes, .muzzle {
transition: transform .5s;
}
.focused {
transition: transform .1s;
}
.look-away .ears,
.look-away .eyes,
.look-away .muzzle {
transition-duration: .3s;
animation: 3s infinite alternate;
}
.look-away.up .ears,
.look-away.up .eyes,
.look-away.up .muzzle {
transform: rotate3d(.2, 0, 0, 20deg) !important;
}
.look-away.down .ears,
.look-away.down .eyes,
.look-away.down .muzzle {
transform: rotate3d(-.2, 0, 0, 20deg) !important;
}
.look-away.playing.up .ears,
.look-away.playing.up .eyes,
.look-away.playing.up .muzzle {
animation-name: lookaway-up;
}
.look-away.playing.down .ears,
.look-away.playing.down .eyes,
.look-away.playing.down .muzzle {
animation-name: lookaway-down;
}
```
```javasript
(function(){
const ryan = document.querySelector('#ryan');
const face = document.querySelectorAll('.ears, .eyes, .muzzle');
const email = document.querySelector('input[type="text"]');
const password = document.querySelector('input[type="password"]');
const fauxInput = document.createElement('div');
const span = document.createElement('span');
let timer = null;
function rotate3d(x, y, z, rad) {
const value = `rotate3d(${x}, ${y}, ${z}, ${rad}rad)`;
for (let i=0; i < face.length; i++) {
face[i].style.transform = value;
}
}
function focus(event) {
event.target.classList.add('focused');
copyStyles(event.target);
event.target.type === 'password' ? lookAway(event) : look(event);
}
function reset(event) {
event.target.classList.remove('focused');
ryan.classList.remove('playing');
clearTimeout(timer);
timer = setTimeout( () => {
ryan.classList.remove('look-away', 'down', 'up');
rotate3d(0,0,0,0);
}, 1 );
}
function copyStyles(el) {
const props = window.getComputedStyle(el, null);
if ( fauxInput.parentNode === document.body ) {
document.body.removeChild(fauxInput);
}
fauxInput.style.visibility = 'hidden';
fauxInput.style.position = 'absolute';
fauxInput.style.top = Math.min(el.offsetHeight * -2, -999) + 'px';
for(let i=0; i < props.length; i++) {
if (['visibility','display','opacity','position','top','left','right','bottom'].indexOf(props[i]) !== -1) {
continue;
}
fauxInput.style[props[i]] = props.getPropertyValue(props[i]);
}
document.body.appendChild(fauxInput);
}
function look(event) {
const el = event.target;
const text = el.value.substr(0, el.selectionStart);
span.innerText = text || '.';
const ryanRect = ryan.getBoundingClientRect();
const inputRect = el.getBoundingClientRect();
const caretRect = span.getBoundingClientRect();
const caretPos = caretRect.left + Math.min(caretRect.width, inputRect.width) * !!text;
const distCaret = ryanRect.left + ryanRect.width/2 - inputRect.left - caretPos;
const distInput = ryanRect.top + ryanRect.height/2 - inputRect.top;
const y = Math.atan2(-distCaret, Math.abs(distInput)*3);
const x = Math.atan2(distInput, Math.abs(distInput)*3 / Math.cos(y));
const angle = Math.max(Math.abs(x), Math.abs(y));
rotate3d(x/angle, y/angle, y/angle/2, angle);
}
function lookAway(event) {
const el = event.target;
const ryanRect = ryan.getBoundingClientRect();
const inputRect = el.getBoundingClientRect();
const distInput = ryanRect.top + ryanRect.height/2 - inputRect.top;
ryan.classList.add( 'look-away', distInput < 0 ? 'up' : 'down' );
clearTimeout(timer);
timer = setTimeout( () => {
ryan.classList.add( 'playing' );
}, 300);
}
fauxInput.appendChild(span);
email.addEventListener( 'focus', focus, false );
email.addEventListener( 'keyup', look, false );
email.addEventListener( 'click', look, false );
email.addEventListener( 'blur', reset, false );
password.addEventListener( 'focus', lookAway, false );
password.addEventListener( 'blur', reset, false );
})();
```
'web' 카테고리의 다른 글
Web Url Protocol (0) | 2019.07.16 |
---|---|
node javascript async waterfall (0) | 2019.07.12 |
Selenium을 이용한 Web 어플리케이션 테스트 자동화 (0) | 2019.07.12 |
Chrome 개발자도구 (Command Line API & Console API ) (0) | 2019.01.30 |
Selenium 을 이용한 Web 어플리케이션 테스트 자동화 (0) | 2019.01.30 |
댓글