일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 미로 생성 알고리즘
- 풀스택
- mybatis
- MVC
- spring
- 웹페이지
- 제이쿼리
- 마이바티스
- jsp
- 웹서비스
- Binding
- dbms
- css3
- 서블릿
- 스프링
- 백엔드
- 로그인
- javascript
- Ajax
- jQuery
- 웹개발
- html5
- 네비게이터
- 프론트엔드
- 오라클
- c programming
- 회원가입
- 프레임워크
- 비밀번호찾기
- Linked List
- Today
- Total
Programmer's Progress
포트폴리오 사이트 프로젝트 - 1 본문
포트폴리오 사이트의 기본적인 형태는 One Page Scroll의 형태인데, Vertical + Horizontal 방식이다.
위의 One Page Scroll은 Vertical하기 때문에 위아래로만 동작하는 것을 확인할 수 있다.
나는 이를 Horizontal하게 움직일수도 있게 설계하기 위해서 방법을 생각했고, position : fixed를 통해 이를 해결했다.
HTML : Source Code
<!DOCTYPE html>
<html lang="en">
<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">
<link rel="stylesheet" href="Vertical Horizontal One Page Scroll.css">
<script src="jquery.js"></script>
<script src="jquery-ui.js"></script>
<script src="Vertical Horizontal One Page Scroll.js"></script>
<title>Document</title>
</head>
<body>
<div class="verticalFullPage">
<div class="horizontalFullPage verticalSelected">
<div class="page horizontalSelected" style="background-color: red;"><div class="box"><h1 id="anime-v0-h0-i0">Programmer's progress</h1><p id="anime-v0-h0-i1">Let's study</p></div></div>
<div class="page" style="background-color:green"><div id="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(124, 185, 124)"><div class="box"><h1 id="anime-v1-h0-i0">Hello World!</p></div></div>
<div class="page" style="background-color:rgb(179, 255, 0)"><div id="box"></div></div>
<div class="page" style="background-color:rgb(0, 68, 255)"><div id="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(226, 79, 255)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(0, 140, 255)"><div class="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(210, 255, 107)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(117, 192, 117)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(200, 163, 224)"><div class="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(2, 0, 128)"><div class="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(162, 203, 250)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(192, 54, 135)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(255, 52, 52)"><div class="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(23, 247, 255)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(58, 175, 58)"><div class="box"></div></div>
</div>
<div class="horizontalFullPage">
<div class="page horizontalSelected" style="background-color:rgb(210, 255, 107)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(117, 192, 117)"><div class="box"></div></div>
<div class="page" style="background-color:rgb(200, 163, 224)"><div class="box"></div></div>
</div>
<div class="verticalNav"><ul></ul></div>
<table class="horizontalNav"></table>
</div>
</body>
</html>
전체적으로는 내부에 <tr>과 <td>태그가 삽입되는 <table>과 같은 구조이다.
horizontalFullPage는 내부에 page라는 기본 단위를 가지고 있으며, 이 page중 가장 첫 page는 기본적으로
horizonSelected된 상태이므로, 초기 화면이 된다. verticalSelected된 horizonFullPage도 기본적으로는 첫 페이지이다.
background-color을 일부러 주었는데 이는 제대로 동작하는지 확인하기 위해 임시로 추가했다.
최하단의 verticalNav와 horizontalNav는 나중에 ul, li태그로 처리할지 table로 처리할지 고민중에 있다.
page에는 box클래스를 가진 div가 존재하는데, 이는 page가 fixed이므로 하위 요소들의 position : absolute 사용시
위치를 정할 수 없게 되므로, relative position을 가진 div를 임시로 추가했다. 또한 하위요소들의 id를 주고, 이 id는
아래에서 소개할 animation과 관련이 있다.
CSS : Source Code
*{
margin: 0;
padding: 0;
}
html, body{
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.verticalFullPage{
top: 0px;
width: 100%;
height: 100%;
position: fixed;
}
.horizontalFullPage{
width: 100%;
height: 100%;
position: fixed;
}
.page{
width: 100%;
height: 100%;
position: fixed;
}
ul,li{
list-style-type: none;
}
.horizontalNav{
width: 100%;
height: 15px;
top: 100%;
left: 50%;
transform: translate(-50%,-100%);
position: fixed;
}
.nav{
background-color: white
}
.horizontalNavSelected{
background-color: black;
}
.box{
width: 100%;
height: 100%;
position: relative;
}
*[id^="anime-"]{
position: absolute;
}
@keyframes anime-v0-h0-i0{
0%{
color: black;
}
100%{
color: chartreuse;
}
}
@keyframes anime-v0-h0-i1{
0%{
color: black;
}
100%{
color: blue;
}
}
@keyframes anime-v1-h0-i0{
0%{
color: black;
}
100%{
color: rgb(255, 0, 0);
}
}
#anime-v0-h0-i0{
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
#anime-v0-h0-i1{
top: 55%;
left: 50%;
transform: translate(-50%,-55%);
}
#anime-v1-h0-i0{
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
@keyframes들의 이름이 상당히 특이한데 v는 vertical, h는 horizontal, i는 index의 약자로 애니메이션을 동적으로
할당해주기 위해서 이런식으로 이름을 지었다.
JavaScript : Source Code
$(document).ready(function(){
var i,j;
var pageWidth = parseInt($(".page").css("width"),10);
var pageHeight = parseInt($(".page").css("height"),10);
var verticalLength = $(".verticalFullPage > .horizontalFullPage").length;
var horizontalLength = new Array();
var currentVerticalIndex = 0;
var currentHorizontalndex = 0;
//각 행마다 가진 페이지 수 저장
for(i=0; i<verticalLength; i++){
horizontalLength.push($(".verticalFullPage .horizontalFullPage").eq(i).find(".page").length);
}
//각 페이지의 초기 위치 설정
for(i=0; i<verticalLength; i++){
for(j=0; j<horizontalLength[i]; j++){
$(".horizontalFullPage").eq(i).find(".page").eq(j).css({
"top" : (pageHeight * i)+"px",
"left" : (pageWidth * j)+"px"
})
}
}
//수평내비게이터 생성 초기화
for(i=0; i<horizontalLength[0]; i++){
$(".horizontalNav").append("<td class='nav'></td>");
}
$(".horizontalNav td.nav").css({
"width": (100/horizontalLength[0])+"%",
"height":"100%"
});
$(".horizontalNav td").eq(0).addClass("horizontalNavSelected");
//첫 화면 애니메이션 실행
animateFocusedPage();
//이벤트등록
$(document).on("mousedown",$(".verticalFullPage"),function(e1){
$(document).on("mousemove",$(".verticalFullPage"),function(e2){
var i, temp = $(".horizontalFullPage").eq(currentVerticalIndex).find(".page.horizontalSelected").index();
if(!$(".page").is(":animated")){
if(temp < horizontalLength[currentVerticalIndex] - 1 && e1.clientX - e2.clientX > 100){
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(temp++).removeClass("horizontalSelected");
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(temp).addClass("horizontalSelected");
currentHorizontalndex = $(".horizontalFullPage.verticalSelected").find(".page.horizontalSelected").index();
for(i=0; i<horizontalLength[currentVerticalIndex]; i++){
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(i).animate({
"left" : (i-currentHorizontalndex)*pageWidth+"px"
},1000,"easeInOutExpo",reLayout);
}
$(".horizontalNav td").siblings().removeClass("horizontalNavSelected");
$(".horizontalNav td").eq(temp).addClass("horizontalNavSelected");
animateFocusedPage();
$(this).off("mousemove");
}else if(temp > 0 && e2.clientX - e1.clientX > 100){
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(temp--).removeClass("horizontalSelected");
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(temp).addClass("horizontalSelected");
currentHorizontalndex = $(".horizontalFullPage.verticalSelected").find(".page.horizontalSelected").index();
for(i=0; i<horizontalLength[currentVerticalIndex]; i++){
$(".horizontalFullPage").eq(currentVerticalIndex).find(".page").eq(i).animate({
"left" : (i-currentHorizontalndex)*pageWidth+"px"
},1000,"easeInOutExpo",reLayout);
}
$(".horizontalNav td").siblings().removeClass("horizontalNavSelected");
$(".horizontalNav td").eq(temp).addClass("horizontalNavSelected");
animateFocusedPage();
$(this).off("mousemove");
}else if(currentVerticalIndex < verticalLength - 1 && e1.clientY - e2.clientY > 100){
$(".horizontalFullPage").eq(currentVerticalIndex).removeClass("verticalSelected");
currentVerticalIndex++;
$(".horizontalFullPage").eq(currentVerticalIndex).addClass("verticalSelected");
for(i=0; i<verticalLength; i++){
$(".horizontalFullPage").eq(i).find(".page").animate({
"top" : (i-currentVerticalIndex)*pageHeight+"px"
},1000,"easeInOutExpo",reLayout);
}
animateFocusedPage();
horizontalNavFactory();
$(this).off("mousemove");
}else if(currentVerticalIndex > 0 && e2.clientY - e1.clientY > 100){
$(".horizontalFullPage").eq(currentVerticalIndex).removeClass("verticalSelected");
currentVerticalIndex--;
$(".horizontalFullPage").eq(currentVerticalIndex).addClass("verticalSelected");
for(i=0; i<verticalLength; i++){
$(".horizontalFullPage").eq(i).find(".page").animate({
"top" : (i-currentVerticalIndex)*pageHeight+"px"
},1000,"easeInOutExpo",reLayout);
}
animateFocusedPage();
horizontalNavFactory();
$(this).off("mousemove");
}
}
})
})
//마우스 중간에 떼면 동작 X하도록 설정
$(document).on("mouseup",$(".verticalFullPage"),function(){
$(this).off("mousemove");
});
//브라우저 리사이즈시에 각 페이지 크기 및 위치 재설정
$(window).resize(reLayout);
function reLayout(){
pageWidth = parseInt($(window).width(),10);
pageHeight = parseInt($(window).height(),10);
var temp;
$(this).queue(function(){
//각 페이지 수평 위치
for(i=0; i<verticalLength; i++){
for(j=0; j<horizontalLength[i]; j++){
temp = $(".horizontalFullPage").eq(i).find(".page.horizontalSelected").index();
if( j != temp){
$(".horizontalFullPage").eq(i).find(".page").eq(j).css({
"left" : (j - temp)*pageWidth+"px"
})
}
}
}
//각 페이지 수직 위치
for(i=0; i<verticalLength; i++){
temp = $(".horizontalFullPage.verticalSelected").index();
if( i != temp){
$(".horizontalFullPage").eq(i).find(".page").css({
"top" : (i - temp)*pageHeight+"px"
})
}
}
$(this).dequeue();
})
}
//수평 내비게이터 생성 메소드
function horizontalNavFactory(){
$(".horizontalNav").empty(true);
for(i=0; i<horizontalLength[currentVerticalIndex]; i++){
$(".horizontalNav").append("<td class='nav'></td>");
}
$(".horizontalNav td").eq($(".horizontalFullPage.verticalSelected").find(".page.horizontalSelected").index()).addClass("horizontalNavSelected");
}
function animateFocusedPage(){
currentHorizontalndex = $(".horizontalFullPage.verticalSelected").find(".page.horizontalSelected").index();
var page = $(".horizontalFullPage.verticalSelected").find(".page.horizontalSelected");
var i;
setTimeout(function(){
$.each($("*[id^='anime-']"),function(index, obj){
$(obj).css({
"animation-name" : "none"
})
console.log(index);
})
$.each($(page).find("*[id^='anime-']"),function(index,obj){
$(obj).css({
"animation-name" : "anime-v"+currentVerticalIndex+"-h"+currentHorizontalndex+"-i"+index,
"animation-duration" : "3s",
"animation-fill-mode" : "both"
});
})
},1000)
}
});
현재 mousedown과 mousemove이벤트를 이용해서 PC에서 모바일 기기를 사용하는 것 마냥
스와이프 하면 페이지가 넘어가지도록 설계했다. 하지만 모바일에서도 동작하려면 다른 이벤트도 추가해야 한다.
이 고민은 나중에 해결하도록 하겠다.
Document
jrw9215.dothome.co.kr
'JavaScript + JQuery > Project' 카테고리의 다른 글
포트폴리오 사이트 프로젝트 - 0 (0) | 2021.02.10 |
---|