|
|
23번째 줄: |
23번째 줄: |
| ------ | | ------ |
| <span style="padding: 0 7px; padding-bottom: 3px; border-left: 5px solid #466DFA; color: #3757C7; font-size: 13.8pt;">'''이브위키 추천 세계관'''</span> {{글씨 색|#929294|이브위키 운영진이 추천하는 세계관입니다.}} | | <span style="padding: 0 7px; padding-bottom: 3px; border-left: 5px solid #466DFA; color: #3757C7; font-size: 13.8pt;">'''이브위키 추천 세계관'''</span> {{글씨 색|#929294|이브위키 운영진이 추천하는 세계관입니다.}} |
| | <div style="margin-bottom: 0.75rem;"></div> |
| <html> | | <html> |
| <head> | | <embed class="banner recommend" src="https://evewiki.kr/etc/recmd.html" type="text/html" style="width: 100%; height: 300px; overflow: hidden; border-radius: 1.0rem; box-shadow: 0 0 1.0rem #808080"></embed> |
| <meta charset="UTF-8" />
| |
| <meta http-equiv="X-UA-Compatible" content="IE=edge" />
| |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" />
| |
| <title>Vanilla js auto loop slide</title>
| |
| </head>
| |
| <body>
| |
| <div class="container">
| |
| <div class="slide slide_wrap">
| |
| <div class="slide_item item1">
| |
| <div id="banner" class="flora" onclick="location.href='/w/index.php/분류:플로라_유니버스'">
| |
| <div class="main-banner-body">
| |
| <div class="flora-1">
| |
| <img id="img-550" alt="플로라.svg" src="https://evewiki.kr/w/images/1/18/플로라.svg" style="width: auto; height: 5.5vw;">
| |
| </div>
| |
| <div class="flora-2">
| |
| <span style="font-size: 1.75rem; font-weight: 700;">플로라 유니버스</span><br>
| |
| <span style="font-size: 1.0rem; font-weight: 700;">한국 정치를 우리들 생각대로 만들어보자!</span>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| <div class="slide_item item2">
| |
| <div id="banner" class="hannara" onclick="location.href='/w/index.php/한나라닷컴'">
| |
| <div class="main-banner-body">
| |
| <div class="hannara-1">
| |
| <img id="img-225" alt="한나라닷컴.png" src="https://evewiki.kr/w/images/2/23/Hannara_w.png" style="width: auto; height: 2.25vw;">
| |
| </div>
| |
| <div class="hannara-2">
| |
| <span style="font-size: 1.0rem; font-weight: 700;">종신집권을 향해 나아가는 여전사 나경원의 국회 분투기</span>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| <div class="slide_item item3">
| |
| <div id="banner" class="ana" onclick="location.href='/w/index.php/아름다운_나라'">
| |
| <div class="main-banner-body">
| |
| <div class="ana-1">
| |
| <img id="img-550" alt="아름다운나라.png" src="/w/images/f/fc/아나_로고.png" style="width: auto; height: 5.5vw;">
| |
| </div>
| |
| <div class="ana-2">
| |
| <span style="font-size: 1.0rem; font-weight: 700;">새로운 설정, 새로운 아나</span>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| </div>
| |
| <div class="slide_prev_button slide_button"></div>
| |
| <div class="slide_next_button slide_button"></div>
| |
| <ul class="slide_pagination"></ul>
| |
| </div>
| |
| </div>
| |
| </body> | |
| <style> | | <style> |
| #catlinks { display: none; }
| | /* 다크모드 */ |
| .container {
| | @media (prefers-color-scheme: dark) { |
| width: calc(100% + 30px);
| | .banner { box-shadow: none !important; } |
| margin-left: -1.0rem;
| |
| }
| |
| .container {
| |
| padding: 0;
| |
| list-style: none;
| |
| font-size: 13px;
| |
| font-weight: 500;
| |
| box-sizing: border-box;
| |
| }
| |
| @media screen and (max-width: 1023px) { .container { margin-left: -0.5rem !important; } }
| |
| | |
| .slide {
| |
| /* layout */
| |
| display: flex;
| |
| flex-wrap: nowrap;
| |
| /* 컨테이너의 내용물이 컨테이너 크기(width, height)를 넘어설 때 보이지 않도록 하기 위해 hidden을 준다. */
| |
| overflow: hidden;
| |
| | |
| /* position */
| |
| /* slide_button의 position absolute가 컨테이너 안쪽에서 top, left, right offset이 적용될 수 있도록 relative를 준다. (기본값이 static인데, static인 경우 그 상위 컨테이너로 나가면서 현재 코드에선 html을 기준으로 offset을 적용시키기 때문) */
| |
| position: relative;
| |
| | |
| /* size */
| |
| width: 100%;
| |
| | |
| /* slide drag를 위해 DOM요소가 드래그로 선택되는것을 방지 */
| |
| user-select: none;
| |
| }
| |
| .slide_item {
| |
| /* layout */
| |
| display: flex;
| |
| align-items: center;
| |
| justify-content: center;
| |
| | |
| /* position - 버튼 클릭시 left offset값을 적용시키기 위해 */
| |
| position: relative;
| |
| left: 0px;
| |
| | |
| /* size */
| |
| width: 100%;
| |
| height: 300px;
| |
| /* flex item의 flex-shrink는 기본값이 1이므로 컨테이너 크기에 맞게 줄어드는데, 슬라이드를 구현할 것이므로 줄어들지 않도록 0을 준다. */
| |
| flex-shrink: 0;
| |
| | |
| /* transition */
| |
| transition: left 300ms;
| |
| }
| |
| .slide_button {
| |
| /* layout */
| |
| display: flex;
| |
| justify-content: center;
| |
| align-items: center;
| |
| | |
| /* position */
| |
| position: absolute;
| |
| /* 버튼이 중앙에 위치하게 하기위해 계산 */
| |
| top: calc(50% - 16px);
| |
| | |
| /* size */
| |
| width: 32px;
| |
| height: 32px;
| |
| | |
| /* style */
| |
| border-radius: 100%;
| |
| background-color: #FFF;
| |
| color: #222;
| |
| border: 1px solid #bbb;
| |
| cursor: pointer;
| |
| }
| |
| | |
| .slide_prev_button {
| |
| left: 25px;
| |
| }
| |
| .slide_next_button {
| |
| right: 25px;
| |
| }
| |
| .slide_prev_button:after {
| |
| content: '◀';
| |
| }
| |
| .slide_next_button:after {
| |
| content: '▶';
| |
| }
| |
| | |
| /* 슬라이드 */
| |
| #banner {
| |
| cursor: pointer;
| |
| }
| |
| | |
| .flora {
| |
| width: 100%;
| |
| height: 100%;
| |
| background-color: #00A366;
| |
| color: #FFF;
| |
| }
| |
| | |
| .main-banner-body {
| |
| width: 1290px;
| |
| height: 100%;
| |
| margin: 0 auto;
| |
| position: relative;
| |
| }
| |
| .flora-1 {
| |
| margin: 3.5vw 5.25vw 0 5.25vw;
| |
| width: 100%;
| |
| height: 5.5vw;
| |
| }
| |
| .flora-2 {
| |
| margin: 0.5vw 5.25vw;
| |
| }
| |
| | |
| .hannara {
| |
| width: 100%;
| |
| height: 100%;
| |
| background: linear-gradient(to right, rgba(0, 46, 109, 1) 0%, rgba(0, 46, 109, 0) 50%), url(/w/images/2/2d/나경원_내각총리대신_취임연설.jpg);
| |
| background-repeat: no-repeat;
| |
| background-size: cover;
| |
| color: #FFF;
| |
| }
| |
| .hannara-1 {
| |
| margin: 3.5vw 5.25vw 0 5.25vw;
| |
| width: 100%;
| |
| height: 2.25vw;
| |
| }
| |
| .hannara-2 {
| |
| margin: 0.5vw 5.25vw;
| |
| }
| |
| | |
| .ana {
| |
| width: 100%;
| |
| height: 100%;
| |
| background-color: #000070;
| |
| color: #FFF;
| |
| }
| |
| .ana-1 {
| |
| margin: 3.5vw 5.25vw 0 5.25vw;
| |
| width: 100%;
| |
| height: 5.5vw;
| |
| }
| |
| .ana-2 {
| |
| margin: 0.5vw 5.25vw;
| |
| }
| |
| | |
| @media screen and (max-width: 1023px) { .slide_button { display: none !important; } } | |
| @media screen and (max-width: 1023px) { #img-550 { height: 50px !important; } .flora-1, .ana-1 { height: 60px !important; } }
| |
| @media screen and (max-width: 1023px) { #img-225 { height: 20px !important; } .hannara-1 { height: 30px !important; } }
| |
| @media screen and (max-width: 1023px) { .slide_item { height: auto !important; } #banner { padding-top: 15px !important; padding-bottom: 20px !important; } }
| |
| | |
| /* 페이지네이션 스타일 */
| |
| ul,
| |
| li {
| |
| list-style: none;
| |
| padding: 0;
| |
| margin: 0;
| |
| }
| |
| .slide_pagination {
| |
| /* layout */
| |
| margin: 0 !important;
| |
| display: flex;
| |
| gap: 5px;
| |
|
| |
| /* left:50%, translateX(-50%)를 하면 가로 가운데로 위치시킬 수 있다. */
| |
| left: 50%;
| |
| transform: translateX(-50%);
| |
|
| |
| /* position */
| |
| position: absolute;
| |
| bottom: 0.25rem;
| |
| }
| |
| .slide_pagination > li {
| |
| /* 현재 슬라이드가 아닌 것은 투명도 부여 */
| |
| color: #FFF;
| |
| cursor: pointer;
| |
| font-size: 30px;
| |
| }
| |
| .slide_pagination > li.active {
| |
| /* 현재 슬라이드 색상은 투명도 없이 */
| |
| color: red;
| |
| } | |
| | |
| .slide_item_duplicate {
| |
| display: flex;
| |
| align-items: center;
| |
| justify-content: center;
| |
| position: relative;
| |
| left: 0px;
| |
| width: 100%;
| |
| height: 300px;
| |
| flex-shrink: 0;
| |
| transition: left 300ms;
| |
| } | | } |
| | /* 모바일 */ |
| | @media screen and (max-width: 768px) { .banner { height: 150px !important; } } |
| </style> | | </style> |
| <script>
| |
| // 슬라이크 전체 크기(width 구하기)
| |
| const slide = document.querySelector(".slide");
| |
| let slideWidth = slide.clientWidth;
| |
|
| |
| // 버튼 엘리먼트 선택하기
| |
| const prevBtn = document.querySelector(".slide_prev_button");
| |
| const nextBtn = document.querySelector(".slide_next_button");
| |
|
| |
| // 슬라이드 전체를 선택해 값을 변경해주기 위해 슬라이드 전체 선택하기
| |
| let slideItems = document.querySelectorAll(".slide_item");
| |
| // 현재 슬라이드 위치가 슬라이드 개수를 넘기지 않게 하기 위한 변수
| |
| const maxSlide = slideItems.length;
| |
|
| |
| // 버튼 클릭할 때 마다 현재 슬라이드가 어디인지 알려주기 위한 변수
| |
| let currSlide = 1;
| |
|
| |
| // 페이지네이션 생성
| |
| const pagination = document.querySelector(".slide_pagination");
| |
|
| |
| for (let i = 0; i < maxSlide; i++) {
| |
| if (i === 0) pagination.innerHTML += `<li class="active">•</li>`;
| |
| else pagination.innerHTML += `<li>•</li>`;
| |
| }
| |
|
| |
| const paginationItems = document.querySelectorAll(".slide_pagination > li");
| |
|
| |
| // 무한 슬라이드를 위해 start, end 슬라이드 복사하기
| |
| const startSlide = slideItems[0];
| |
| const endSlide = slideItems[slideItems.length - 1];
| |
| const startElem = document.createElement("div");
| |
| const endElem = document.createElement("div");
| |
|
| |
| endSlide.classList.forEach((c) => endElem.classList.add(c));
| |
| endElem.innerHTML = endSlide.innerHTML;
| |
|
| |
| startSlide.classList.forEach((c) => startElem.classList.add(c));
| |
| startElem.innerHTML = startSlide.innerHTML;
| |
|
| |
| // 각 복제한 엘리먼트 추가하기
| |
| slideItems[0].before(endElem);
| |
| slideItems[slideItems.length - 1].after(startElem);
| |
|
| |
| // 슬라이드 전체를 선택해 값을 변경해주기 위해 슬라이드 전체 선택하기
| |
| slideItems = document.querySelectorAll(".slide_item");
| |
| //
| |
| let offset = slideWidth + currSlide;
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `left: ${-offset}px`);
| |
| });
| |
|
| |
| function nextMove() {
| |
| currSlide++;
| |
| // 마지막 슬라이드 이상으로 넘어가지 않게 하기 위해서
| |
| if (currSlide <= maxSlide) {
| |
| // 슬라이드를 이동시키기 위한 offset 계산
| |
| const offset = slideWidth * currSlide;
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `left: ${-offset}px`);
| |
| });
| |
| // 슬라이드 이동 시 현재 활성화된 pagination 변경
| |
| paginationItems.forEach((i) => i.classList.remove("active"));
| |
| paginationItems[currSlide - 1].classList.add("active");
| |
| } else {
| |
| // 무한 슬라이드 기능 - currSlide 값만 변경해줘도 되지만 시각적으로 자연스럽게 하기 위해 아래 코드 작성
| |
| currSlide = 0;
| |
| let offset = slideWidth * currSlide;
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `transition: ${0}s; left: ${-offset}px`);
| |
| });
| |
| currSlide++;
| |
| offset = slideWidth * currSlide;
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| setTimeout(() => {
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| // i.setAttribute("style", `transition: ${0}s; left: ${-offset}px`);
| |
| i.setAttribute("style", `transition: ${0.15}s; left: ${-offset}px`);
| |
| });
| |
| }, 0);
| |
| // // 슬라이드 이동 시 현재 활성화된 pagination 변경
| |
| paginationItems.forEach((i) => i.classList.remove("active"));
| |
| paginationItems[currSlide - 1].classList.add("active");
| |
| }
| |
| }
| |
| function prevMove() {
| |
| currSlide--;
| |
| // 1번째 슬라이드 이하로 넘어가지 않게 하기 위해서
| |
| if (currSlide > 0) {
| |
| // 슬라이드를 이동시키기 위한 offset 계산
| |
| const offset = slideWidth * currSlide;
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `left: ${-offset}px`);
| |
| });
| |
| // 슬라이드 이동 시 현재 활성화된 pagination 변경
| |
| paginationItems.forEach((i) => i.classList.remove("active"));
| |
| paginationItems[currSlide - 1].classList.add("active");
| |
| } else {
| |
| // 무한 슬라이드 기능 - currSlide 값만 변경해줘도 되지만 시각적으로 자연스럽게 하기 위해 아래 코드 작성
| |
| currSlide = maxSlide + 1;
| |
| let offset = slideWidth * currSlide;
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `transition: ${0}s; left: ${-offset}px`);
| |
| });
| |
| currSlide--;
| |
| offset = slideWidth * currSlide;
| |
| setTimeout(() => {
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| // i.setAttribute("style", `transition: ${0}s; left: ${-offset}px`);
| |
| i.setAttribute("style", `transition: ${0.15}s; left: ${-offset}px`);
| |
| });
| |
| }, 0);
| |
| // 슬라이드 이동 시 현재 활성화된 pagination 변경
| |
| paginationItems.forEach((i) => i.classList.remove("active"));
| |
| paginationItems[currSlide - 1].classList.add("active");
| |
| }
| |
| }
| |
|
| |
| // 버튼 엘리먼트에 클릭 이벤트 추가하기
| |
| nextBtn.addEventListener("click", () => {
| |
| // 이후 버튼 누를 경우 현재 슬라이드를 변경
| |
| nextMove();
| |
| });
| |
| // 버튼 엘리먼트에 클릭 이벤트 추가하기
| |
| prevBtn.addEventListener("click", () => {
| |
| // 이전 버튼 누를 경우 현재 슬라이드를 변경
| |
| prevMove();
| |
| });
| |
|
| |
| // 브라우저 화면이 조정될 때 마다 slideWidth를 변경하기 위해
| |
| window.addEventListener("resize", () => {
| |
| slideWidth = slide.clientWidth;
| |
| });
| |
|
| |
| // 각 페이지네이션 클릭 시 해당 슬라이드로 이동하기
| |
| for (let i = 0; i < maxSlide; i++) {
| |
| // 각 페이지네이션마다 클릭 이벤트 추가하기
| |
| paginationItems[i].addEventListener("click", () => {
| |
| // 클릭한 페이지네이션에 따라 현재 슬라이드 변경해주기(currSlide는 시작 위치가 1이기 때문에 + 1)
| |
| currSlide = i + 1;
| |
| // 슬라이드를 이동시키기 위한 offset 계산
| |
| const offset = slideWidth * currSlide;
| |
| // 각 슬라이드 아이템의 left에 offset 적용
| |
| slideItems.forEach((i) => {
| |
| i.setAttribute("style", `left: ${-offset}px`);
| |
| });
| |
| // 슬라이드 이동 시 현재 활성화된 pagination 변경
| |
| paginationItems.forEach((i) => i.classList.remove("active"));
| |
| paginationItems[currSlide - 1].classList.add("active");
| |
| });
| |
| }
| |
|
| |
| // 드래그(스와이프) 이벤트를 위한 변수 초기화
| |
| let startPoint = 0;
| |
| let endPoint = 0;
| |
|
| |
| // PC 클릭 이벤트 (드래그)
| |
| slide.addEventListener("mousedown", (e) => {
| |
| startPoint = e.pageX; // 마우스 드래그 시작 위치 저장
| |
| });
| |
|
| |
| slide.addEventListener("mouseup", (e) => {
| |
| endPoint = e.pageX; // 마우스 드래그 끝 위치 저장
| |
| if (startPoint < endPoint) {
| |
| // 마우스가 오른쪽으로 드래그 된 경우
| |
| prevMove();
| |
| } else if (startPoint > endPoint) {
| |
| // 마우스가 왼쪽으로 드래그 된 경우
| |
| nextMove();
| |
| }
| |
| });
| |
|
| |
| // 모바일 터치 이벤트 (스와이프)
| |
| slide.addEventListener("touchstart", (e) => {
| |
| startPoint = e.touches[0].pageX; // 터치가 시작되는 위치 저장
| |
| });
| |
| slide.addEventListener("touchend", (e) => {
| |
| endPoint = e.changedTouches[0].pageX; // 터치가 끝나는 위치 저장
| |
| if (startPoint < endPoint) {
| |
| // 오른쪽으로 스와이프 된 경우
| |
| prevMove();
| |
| } else if (startPoint > endPoint) {
| |
| // 왼쪽으로 스와이프 된 경우
| |
| nextMove();
| |
| }
| |
| });
| |
|
| |
| // 기본적으로 슬라이드 루프 시작하기
| |
| let loopInterval = setInterval(() => {
| |
| nextMove();
| |
| }, 3000);
| |
|
| |
| // 슬라이드에 마우스가 올라간 경우 루프 멈추기
| |
| slide.addEventListener("mouseover", () => {
| |
| clearInterval(loopInterval);
| |
| });
| |
|
| |
| // 슬라이드에서 마우스가 나온 경우 루프 재시작하기
| |
| slide.addEventListener("mouseout", () => {
| |
| loopInterval = setInterval(() => {
| |
| nextMove();
| |
| }, 3000);
| |
| });
| |
| </script>
| |
| </html> | | </html> |
| ------ | | ------ |