2023. 2. 23. 19:17ㆍ_Project/React_HAEDAL_introduction
나를 믿어라. 인생에서 최대의 성과와 기쁨을 수확하는 비결은 위험한 삶을 사는 데 있다.
-02.23. 프레드리히 니체
전체코드
.icon_form.png 에 해당하는 그림 바꿔서 쓸것
클릭하면 바뀌는 URL 수정할 것
import React from "react";
import "./css/apply.css";
import { gsap } from "gsap";
import { useState, useEffect } from "react";
const applyFunc = () => {
document.querySelectorAll(".apply-button").forEach((button) => {
let getVar = (variable) =>
getComputedStyle(button).getPropertyValue(variable);
//앞의 함수가 무조건 실행 되어야 함
//complie 한번당/ 렌더링 당 한 사이트만 실행되는거 같음
//const timer = setTimeout(() => console.log("Initial timeout!"), 1000);
//
button.addEventListener("click", (e) => {
if (!button.classList.contains("active")) {
button.classList.add("active");
gsap.to(button, {
keyframes: [
{
"--left-wing-first-x": 50,
"--left-wing-first-y": 100,
"--right-wing-second-x": 50,
"--right-wing-second-y": 100,
duration: 0.2,
onComplete() {
gsap.set(button, {
"--left-wing-first-y": 0,
"--left-wing-second-x": 40,
"--left-wing-second-y": 100,
"--left-wing-third-x": 0,
"--left-wing-third-y": 100,
"--left-body-third-x": 40,
"--right-wing-first-x": 50,
"--right-wing-first-y": 0,
"--right-wing-second-x": 60,
"--right-wing-second-y": 100,
"--right-wing-third-x": 100,
"--right-wing-third-y": 100,
"--right-body-third-x": 60,
});
},
},
{
"--left-wing-third-x": 20,
"--left-wing-third-y": 90,
"--left-wing-second-y": 90,
"--left-body-third-y": 90,
"--right-wing-third-x": 80,
"--right-wing-third-y": 90,
"--right-body-third-y": 90,
"--right-wing-second-y": 90,
duration: 0.2,
},
{
"--rotate": 50,
"--left-wing-third-y": 95,
"--left-wing-third-x": 27,
"--right-body-third-x": 45,
"--right-wing-second-x": 45,
"--right-wing-third-x": 60,
"--right-wing-third-y": 83,
duration: 0.25,
},
{
"--rotate": 60,
"--plane-x": -8,
"--plane-y": 40,
duration: 0.2,
},
{
"--rotate": 40,
"--plane-x": 45,
"--plane-y": -300,
"--plane-opacity": 0,
duration: 0.375,
onComplete() {
setTimeout(() => {
button.removeAttribute("style");
gsap.fromTo(
button,
{
opacity: 0,
y: -8,
},
{
opacity: 1,
y: 0,
clearProps: true,
duration: 0.3,
onComplete() {
button.classList.remove("active");
},
}
);
}, 1800);
},
},
],
});
gsap.to(button, {
keyframes: [
{
"--text-opacity": 0,
"--border-radius": 0,
"--left-wing-background": getVar("--primary-dark"),
"--right-wing-background": getVar("--primary-dark"),
duration: 0.11,
},
{
"--left-wing-background": getVar("--primary"),
"--right-wing-background": getVar("--primary"),
duration: 0.14,
},
{
"--left-body-background": getVar("--primary-dark"),
"--right-body-background": getVar("--primary-darkest"),
duration: 0.25,
delay: 0.1,
},
{
"--trails-stroke": 171,
duration: 0.22,
delay: 0.22,
},
{
"--success-opacity": 1,
"--success-x": 0,
duration: 0.2,
delay: 0.15,
},
{
"--success-stroke": 0,
duration: 0.15,
},
],
});
}
});
});
};
//10이 왜 안들어갈까요/
function reload(check) {
//if (parent.location.href.indexOf('reloaded')==-1) parent.location.replace(parent.location.href+'?reloaded');
if (check === 10) {
alert("5");
//alert("신청 완료");
} else {
//window.location.reload(true);
//alert("신청 완료"); // 버그수정을 위해 적어둠
}
}
const link = () => {
setTimeout(
() => window.open(url, "_blank", `height=${height}, width=${width}`),
3000
);
let check = 10;
setTimeout((check) => reload(), 7000);
};
// 화면 사이즈 가져오기
const height = window?.screen?.height;
const width = window?.screen?.width;
const url = "https://forms.gle/1SEDZKgNMrHQHUGTA";
function Apply() {
const [isActive, setIsActive] = useState(false);
const timer = setTimeout(() => console.log("Initial timeout!"), 2000);
clearTimeout(timer);
useEffect(applyFunc, [isActive]);
let check = 0;
return (
<div>
<div className="apply">
<div className="apply-view">
<button
className="apply-button"
img
src="img/icon_form.png"
alt="plane"
onClick={() => {
link();
setIsActive(true);
setTimeout(() => {
setIsActive(false);
}, 1800);
}}
>
<span className="apply-contents">
<span>지원하기 </span>
<img
className="apply-plane-img"
src="img/icon_form.png"
alt="plane"
/>
</span>
<span class="success">
<svg viewBox="0 0 16 16">
<polyline points="3.75 9 7 12 13 5"></polyline>
</svg>
<span>신청 완료</span>
</span>
<svg class="trails" viewBox="0 0 33 64">
<path d="M26,4 C28,13.3333333 29,22.6666667 29,32 C29,41.3333333 28,50.6666667 26,60"></path>
<path d="M6,4 C8,13.3333333 9,22.6666667 9,32 C9,41.3333333 8,50.6666667 6,60"></path>
</svg>
<div class="plane">
<div class="left"></div>
<div class="right"></div>
</div>
</button>
</div>
</div>
</div>
);
//clearTimeout(timer);
}
export default Apply;
Apply.css
@import url('https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap');
.apply{
display: flex;
justify-content: center;
}
.apply-plane-img{
display: var(--right-body-background);
z-index: 1;
position: relative;
bottom: 4px;
}
.apply-view{
margin-top: 10%;
margin-bottom: 10%;
}
.apply-contents{
font-family: 'Do+Hyeon';
margin: 0;
line-height: 24px;
font-weight: 600;
font-size: 24px;
bottom: 10px;
left: 10px;
}
.apply-button:hover {
background: #ffe071bb;
border-radius: 5%;
}
.apply-button {
--primary: #000D58;
--primary-dark: #000940;
--primary-darkest: #00072E;
--shadow: rgba(0, 0, 0, 0.3);
--text: #FFE171;
--text-opacity: 1;
--success: #000D58;
--success-x: -12;
--success-stroke: 14;
--success-opacity: 0;
--border-radius: 2;
--overflow: hidden;
--x: 0;
--y: 0;
--rotate: 0;
--plane-x: 0;
--plane-y: 0;
--plane-opacity: 1;
--trails: rgba(255, 255, 255, 0.15);
--trails-stroke: 57;
--left-wing-background: var(--primary);
--left-wing-first-x: 0;
--left-wing-first-y: 0;
--left-wing-second-x: 50;
--left-wing-second-y: 0;
--left-wing-third-x: 0;
--left-wing-third-y: 100;
--left-body-background: var(--primary);
--left-body-first-x: 51;
--left-body-first-y: 0;
--left-body-second-x: 51;
--left-body-second-y: 100;
--left-body-third-x: 0;
--left-body-third-y: 100;
--right-wing-background: var(--primary);
--right-wing-first-x: 49;
--right-wing-first-y: 0;
--right-wing-second-x: 100;
--right-wing-second-y: 0;
--right-wing-third-x: 100;
--right-wing-third-y: 100;
--right-body-background: var(--primary);
--right-body-first-x: 49;
--right-body-first-y: 0;
--right-body-second-x: 49;
--right-body-second-y: 100;
--right-body-third-x: 100;
--right-body-third-y: 100;
display: block;
cursor: pointer;
position: relative;
border: 0;
padding: 8px 0;
min-width: 203px;
max-height: 50px;
border-right: #FFE171;
border-bottom: #FFE171;
border-right-style: solid;
border-right-width: 3px;
border-bottom-style: solid;
border-bottom-width: 3px;
text-align: center;
font-family: 'Do+Hyeon';
margin: 0;
line-height: 24px;
font-weight: 600;
font-size: 24px;
transition: all 0.2s ;
background: #FFE171;
outline: none;
color: var(--text);
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
}
.apply-button .plane,
.apply-button .trails {
pointer-events: none;
position: absolute;
}
.apply-button .plane {
left: 0;
top: 0;
right: 0;
bottom: 0;
filter: drop-shadow(0 3px 6px var(--shadow));
transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) rotate(calc(var(--rotate) * 1deg)) translateZ(0);
}
.apply-button .plane .left,
.apply-button .plane .right {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
opacity: var(--plane-opacity);
transform: translate(calc(var(--plane-x) * 1px), calc(var(--plane-y) * 1px)) translateZ(0);
}
.apply-button .plane .left:before, .apply-button .plane .left:after,
.apply-button .plane .right:before,
.apply-button .plane .right:after {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: calc(var(--border-radius) * 1px);
transform: translate(var(--part-x, 0.4%), var(--part-y, 0)) translateZ(0);
z-index: var(--z-index, 2);
background: var(--background, var(--left-wing-background));
clip-path: polygon(calc(var(--first-x, var(--left-wing-first-x)) * 1%) calc(var(--first-y, var(--left-wing-first-y)) * 1%), calc(var(--second-x, var(--left-wing-second-x)) * 1%) calc(var(--second-y, var(--left-wing-second-y)) * 1%), calc(var(--third-x, var(--left-wing-third-x)) * 1%) calc(var(--third-y, var(--left-wing-third-y)) * 1%));
}
.apply-button .plane .left:after {
--part-x: -1%;
--z-index: 1;
--background: var(--left-body-background);
--first-x: var(--left-body-first-x);
--first-y: var(--left-body-first-y);
--second-x: var(--left-body-second-x);
--second-y: var(--left-body-second-y);
--third-x: var(--left-body-third-x);
--third-y: var(--left-body-third-y);
}
.apply-button .plane .right:before {
--part-x: -1%;
--z-index: 2;
--background: var(--right-wing-background);
--first-x: var(--right-wing-first-x);
--first-y: var(--right-wing-first-y);
--second-x: var(--right-wing-second-x);
--second-y: var(--right-wing-second-y);
--third-x: var(--right-wing-third-x);
--third-y: var(--right-wing-third-y);
}
.apply-button .plane .right:after {
--part-x: 0;
--z-index: 1;
--background: var(--right-body-background);
--first-x: var(--right-body-first-x);
--first-y: var(--right-body-first-y);
--second-x: var(--right-body-second-x);
--second-y: var(--right-body-second-y);
--third-x: var(--right-body-third-x);
--third-y: var(--right-body-third-y);
}
.apply-button .trails {
display: block;
width: 33px;
height: 64px;
top: -4px;
left: 16px;
fill: none;
stroke: var(--trails);
stroke-linecap: round;
stroke-width: 2;
stroke-dasharray: 57px;
stroke-dashoffset: calc(var(--trails-stroke) * 1px);
transform: rotate(68deg) translateZ(0);
}
.apply-button > span {
display: flex;
align-items: center;
justify-content: center;
position: relative;
z-index: 4;
opacity: var(--text-opacity);
}
.apply-button span.success {
z-index: 0;
position: relative;
bottom: 46px;
transform: translateX(calc(var(--success-x) * 1px)) translateZ(0);
opacity: var(--success-opacity);
color: var(--success);
}
.apply-button span.success svg {
display: inline-block;
vertical-align: top;
width: 16px;
height: 16px;
margin: 4px 8px 0 0;
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 14px;
stroke: var(--success);
stroke-dashoffset: calc(var(--success-stroke) * 1px);
}
html {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: inherit;
}
*:before, *:after {
box-sizing: inherit;
}
이미지가 있는 버튼으로, 누르면 종이비행기처럼 애니메이션이 3초간 실행된 후 외부링크가 새 페이지로 뜬다
1. 버튼 기능 구현 CSS + JS + HTML
아래 링크를 참고하여 작성하였다.
https://codepen.io/aaroniker/pen/BajabVN
document.querySelectorAll(".apply-button").forEach((button) => {
let getVar = (variable) =>
getComputedStyle(button).getPropertyValue(variable);
//앞의 함수가 무조건 실행 되어야 함
//complie 한번당/ 렌더링 당 한 사이트만 실행되는거 같음
//const timer = setTimeout(() => console.log("Initial timeout!"), 1000);
button.addEventListener("click", (e) => {
if (!button.classList.contains("active")) {
button.classList.add("active");
gsap.to(button, {
keyframes: [
{
"--left-wing-first-x": 50,
"--left-wing-first-y": 100,
"--right-wing-second-x": 50,
"--right-wing-second-y": 100,
duration: 0.2,
onComplete() {
gsap.set(button, {
"--left-wing-first-y": 0,
"--left-wing-second-x": 40,
"--left-wing-second-y": 100,
"--left-wing-third-x": 0,
"--left-wing-third-y": 100,
"--left-body-third-x": 40,
"--right-wing-first-x": 50,
"--right-wing-first-y": 0,
"--right-wing-second-x": 60,
"--right-wing-second-y": 100,
"--right-wing-third-x": 100,
"--right-wing-third-y": 100,
"--right-body-third-x": 60,
});
},
},
{
"--left-wing-third-x": 20,
"--left-wing-third-y": 90,
"--left-wing-second-y": 90,
"--left-body-third-y": 90,
"--right-wing-third-x": 80,
"--right-wing-third-y": 90,
"--right-body-third-y": 90,
"--right-wing-second-y": 90,
duration: 0.2,
},
{
"--rotate": 50,
"--left-wing-third-y": 95,
"--left-wing-third-x": 27,
"--right-body-third-x": 45,
"--right-wing-second-x": 45,
"--right-wing-third-x": 60,
"--right-wing-third-y": 83,
duration: 0.25,
},
{
"--rotate": 60,
"--plane-x": -8,
"--plane-y": 40,
duration: 0.2,
},
{
"--rotate": 40,
"--plane-x": 45,
"--plane-y": -300,
"--plane-opacity": 0,
duration: 0.375,
onComplete() {
setTimeout(() => {
button.removeAttribute("style");
gsap.fromTo(
button,
{
opacity: 0,
y: -8,
},
{
opacity: 1,
y: 0,
clearProps: true,
duration: 0.3,
onComplete() {
button.classList.remove("active");
},
}
);
}, 1800);
},
},
],
});
gsap.to(button, {
keyframes: [
{
"--text-opacity": 0,
"--border-radius": 0,
"--left-wing-background": getVar("--primary-dark"),
"--right-wing-background": getVar("--primary-dark"),
duration: 0.11,
},
{
"--left-wing-background": getVar("--primary"),
"--right-wing-background": getVar("--primary"),
duration: 0.14,
},
{
"--left-body-background": getVar("--primary-dark"),
"--right-body-background": getVar("--primary-darkest"),
duration: 0.25,
delay: 0.1,
},
{
"--trails-stroke": 171,
duration: 0.22,
delay: 0.22,
},
{
"--success-opacity": 1,
"--success-x": 0,
duration: 0.2,
delay: 0.15,
},
{
"--success-stroke": 0,
duration: 0.15,
},
],
});
}
});
});
디자인은 해달 디자인팀에서 제작한 피그마 디자인으로 작성하였다
2. 링크 연결
아래 링크를 참고하여 연결하였다.
const link = () => {
setTimeout(
() => window.open(url, "_blank", `height=${height}, width=${width}`),
3000
);
};
// 화면 사이즈 가져오기
const height = window?.screen?.height;
const width = window?.screen?.width;
const url =
"https://github.com/DingX2";
window.open 함수를 통해 새 창으로 페이지를 열 수 있으며 url을 수정하면 주소를 바꿀 수 있다.
다만 렌더링 할 때마다 함수가 호출되는 바람에 3개씩 자동으로 뜨고 애니메이션이 보이지 않는 문제점이 발생하여 타이머를 추가하였다.
const timer = setTimeout(() => console.log("Initial timeout!"), 2000);
clearTimeout(timer);
setTimeout( 함수 , delay) 함수는 delay 만큼 타이머가 지난 후, 실행된다.
timer을 초기화하면 렌더링 될 때 마다 창이 뜨지 않는다.
3. 모션 오류 수정, 컴파일 1번 당? 실행되는 오류 고치기
컴파일 한 번한 새 페이지에서만 모션이 작동하고 다음부터는 모션이 작동하지 않아서 찾아봤다.
링크는 정상 작동하지만,,, 애써 가져온 비행기 애니메이션이 포인트인데!
수정 전 코드
import React from "react";
import "./css/Apply.css";
import { gsap } from "gsap";
document.querySelectorAll(".apply-button").forEach((button) => {
let getVar = (variable) =>
getComputedStyle(button).getPropertyValue(variable);
//앞의 함수가 무조건 실행 되어야 함
//complie 한번당/ 렌더링 당 한 사이트만 실행되는거 같음
//const timer = setTimeout(() => console.log("Initial timeout!"), 1000);
button.addEventListener("click", (e) => {
if (!button.classList.contains("active")) {
button.classList.add("active");
gsap.to(button, {
keyframes: [
{
"--left-wing-first-x": 50,
"--left-wing-first-y": 100,
"--right-wing-second-x": 50,
"--right-wing-second-y": 100,
duration: 0.2,
onComplete() {
gsap.set(button, {
"--left-wing-first-y": 0,
"--left-wing-second-x": 40,
"--left-wing-second-y": 100,
"--left-wing-third-x": 0,
"--left-wing-third-y": 100,
"--left-body-third-x": 40,
"--right-wing-first-x": 50,
"--right-wing-first-y": 0,
"--right-wing-second-x": 60,
"--right-wing-second-y": 100,
"--right-wing-third-x": 100,
"--right-wing-third-y": 100,
"--right-body-third-x": 60,
});
},
},
{
"--left-wing-third-x": 20,
"--left-wing-third-y": 90,
"--left-wing-second-y": 90,
"--left-body-third-y": 90,
"--right-wing-third-x": 80,
"--right-wing-third-y": 90,
"--right-body-third-y": 90,
"--right-wing-second-y": 90,
duration: 0.2,
},
{
"--rotate": 50,
"--left-wing-third-y": 95,
"--left-wing-third-x": 27,
"--right-body-third-x": 45,
"--right-wing-second-x": 45,
"--right-wing-third-x": 60,
"--right-wing-third-y": 83,
duration: 0.25,
},
{
"--rotate": 60,
"--plane-x": -8,
"--plane-y": 40,
duration: 0.2,
},
{
"--rotate": 40,
"--plane-x": 45,
"--plane-y": -300,
"--plane-opacity": 0,
duration: 0.375,
onComplete() {
setTimeout(() => {
button.removeAttribute("style");
gsap.fromTo(
button,
{
opacity: 0,
y: -8,
},
{
opacity: 1,
y: 0,
clearProps: true,
duration: 0.3,
onComplete() {
button.classList.remove("active");
},
}
);
}, 1800);
},
},
],
});
gsap.to(button, {
keyframes: [
{
"--text-opacity": 0,
"--border-radius": 0,
"--left-wing-background": getVar("--primary-dark"),
"--right-wing-background": getVar("--primary-dark"),
duration: 0.11,
},
{
"--left-wing-background": getVar("--primary"),
"--right-wing-background": getVar("--primary"),
duration: 0.14,
},
{
"--left-body-background": getVar("--primary-dark"),
"--right-body-background": getVar("--primary-darkest"),
duration: 0.25,
delay: 0.1,
},
{
"--trails-stroke": 171,
duration: 0.22,
delay: 0.22,
},
{
"--success-opacity": 1,
"--success-x": 0,
duration: 0.2,
delay: 0.15,
},
{
"--success-stroke": 0,
duration: 0.15,
},
],
});
}
});
});
const link = () => {
setTimeout(
() => window.open(url, "_blank", `height=${height}, width=${width}`),
3000
);
};
// 화면 사이즈 가져오기
const height = window?.screen?.height;
const width = window?.screen?.width;
const url =
"https://docs.google.com/forms/d/e/1FAIpQLSchlsp4vKxJ92fUYguzvnbvqjzgVVp1_6gr3-iTNgEXUpc9Pw/viewform?usp=sf_link";
function Apply() {
const { useState, useRef, useEffect } = React;
const timer = setTimeout(() => console.log("Initial timeout!"), 2000);
clearTimeout(timer);
return (
<div>
<div className="apply">
<div className="apply-view">
<button
class="apply-button"
img
src="img/icon_form.png"
alt="plane"
onClick={link}
>
<span className="apply-contents">
지원하기{" "}
<img
className="apply-plane-img"
src="img/icon_form.png"
alt="plane"
/>
</span>
<span class="success">
<svg viewBox="0 0 16 16">
<polyline points="3.75 9 7 12 13 5"></polyline>
</svg>
신청 완료
</span>
<p />
<svg class="trails" viewBox="0 0 33 64">
<path d="M26,4 C28,13.3333333 29,22.6666667 29,32 C29,41.3333333 28,50.6666667 26,60"></path>
<path d="M6,4 C8,13.3333333 9,22.6666667 9,32 C9,41.3333333 8,50.6666667 6,60"></path>
</svg>
<div class="plane">
<div class="left"></div>
<div class="right"></div>
</div>
</button>
</div>
</div>
</div>
);
//clearTimeout(timer);
}
export default Apply;
에서 removeAttribute로 style을 제거해버리기 때문에.... 라고 일단 추측해본다
아무래도 코드내의 리소스를 초기화해야 할 거 같다
자바스크립트 새로고침을 참고
https://7942yongdae.tistory.com/53
새로고침을 하려고 reload를 사용했더니 무한 새로고침오류가 생기고 먼저 새로고침이되어 애니메이션이 실행되지 않는 오류가 발생했다.
3. 모션 오류 수정, 컴파일 1번 당? 실행되는 오류 고치기
밋밋한거같아서 마우스를 올리면 효과를 주는 hover을 추가하기로했다
예상과 다른 결과지만 오히려 조아 할 수는 없어서
지원하기가 효과를 받을 수는 없을까??
무언가 아무래도 종이비행기가 접히다보니까 앞 화면이..... button 인거 같은데 싶은데....