깃허브 코드를 참고해서 따라하면서 학습해보는 시간을 가져보려고 한다.
왜냐?
swiper에 한이 맺힌게 있다.
// src/core/loop/loopCreate.mjs
import {createElement, elementChildren , showWarning} from "../../shared/utils.mjs";
export default function loopCreate(slideRealIndex, initial) {
const swiper = this;
const { params, slidesEl} = swiper;
if(!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return;
const initSlides = () => {
/**
* params.slideClass 클래스명이거나 swiper-slide태그인 요소를 배열로 추출
* 왜 > Swiper 같은 라이브러리는 사용자 정의 태그(<swiper-slide>)도 지원하고 클래스 기반 슬라이드(<div class="swiper-slide">)도 지원하기 때문에
*/
const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
slides.forEach((el, index) => {
el.setAttribute('data-swiper-slide-index', index)
})
};
//grid 모드면 행 개수만큼 슬라이드 그룹당 슬라이드 개수를 곱해줌
const gridEnabled = swiper.grid && params.grid && params.grid.row > 1;
const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1);
//슬라이드 수가 group 단위로 안 맞으면 → group 보정 필요
//grid 모드인데 행 수가 안 맞으면 → grid 보정 필요
//무슨 소리인지 모르겠음
const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0;
const shouldFillGrid = gridEnabled && swiper.slides.length % params.grid.rows !== 0;
//빈 슬라이드를 지정된 개수만큼 만들어 DOM에 붙임
//swiper.isElement는 <swiper-container>를 사용하는지 여부
const addBlankSlides = (amountOfSlides) => {
for(let i = 0; i< amountOfSlides; i +=1){
const slideEl = swiper.isElement ?
createElement('swiper-slide', [params.slideBlankClass]) :
createElement('div', [params.slideClass, params.slideBlankClass]);
swiper.slidesEl.append(slideEl)
}
}
//빈 슬라이드를 채워 넣고, 슬라이드를 재계산
if(shouldFillGroup){
if(params.loopAddBlankSlides) {
const slidesToAdd = slidesPerGroup - (swiper.slides.length % slidesPerGroup);
addBlankSlides(slidesToAdd);
swiper.recalcSlides();
swiper.updateSlides();
} else {
showWarning(
'Swiper Loop Warning: The number of slides is not even to slidesPerGroup, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)',
)
}
initSlides();
} else if(shouldFillGrid){
if(params.loopAddBlankSlides){
const slidesToAdd = params.grid.rows - (swiper.slides.length % params.grid.rows);
addBlankSlides(slidesToAdd);
swiper.recalcSlides();
swiper.updateSlides();
} else {
showWarning( 'Swiper Loop Warning: The number of slides is not even to grid.rows, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)',)
}
initSlides();
}
else {
initSlides();
}
//복제된 슬라이드까지 다 세팅된 뒤,
//실제 슬라이드 위치를 올바르게 조정 (loopFix 함수가 loop offset 계산함)
swiper.loopFix({
slideRealIndex,
direction : params.centeredSlides? undefined : 'next',
initial
})
}
if (!params.loop || (swiper.virtual && swiper.params.virtual.enabled)) return;
const initSlides = () => {
const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
slides.forEach((el, index) => {
el.setAttribute('data-swiper-slide-index', index);
});
};
const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1);
grid, group부분은 잘 모르곘음. grid모드도 있었나.
const addBlankSlides = (amountOfSlides) => {
for (...) {
const slideEl = swiper.isElement
? createElement('swiper-slide', [params.slideBlankClass])
: createElement('div', [params.slideClass, params.slideBlankClass]);
swiper.slidesEl.append(slideEl);
}
};
if (shouldFillGroup) {
if (params.loopAddBlankSlides) {
const slidesToAdd = slidesPerGroup - (swiper.slides.length % slidesPerGroup);
addBlankSlides(slidesToAdd);
swiper.recalcSlides();
swiper.updateSlides();
} else {
showWarning(...);
}
initSlides();
}
const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0;
슬라이드 수가 slidesPerGroup으로 나누어 떨어지지 않으면 마지막 그룹이 불완전한 슬라이드 묶음이 된다.
-> 루프 모드에서 마지막 남는 슬라이드 그룹이 불완전하다면, 복제할 때 문제가 생기므로 빈 슬라이드를 채워줘야 한다. 예를 들어서 slides의 길이가 10이고 slidesPerGroup이 3이면 마지막 슬라이드에서 1개만 남기 때문에 2개를 추가로 붙인다는 뜻.
swiper.loopFix({
slideRealIndex,
direction: params.centeredSlides ? undefined : 'next',
initial,
});
//src/shared/utils.mjs
import { getWindow, getDocument } from 'ssr-window';
function showWarning(text){
try {
console.warn(text);
return;
} catch(err){
//err
}
}
function createElement(tag, classes = []){
const el = document.createElement(tag);
el.classList.add(...(Array.isArray(classes) ? classes : classesToToken(classes)));
return el;
}
/**
* 브라우저 환경(ssr대응 포함)에서 DOM요소의 자식 요소들을 선택적으로 가져오는 함수
* 얘( elementChildren(div, '.my-class') // → div 자식 중 .my-class만 반환
*/
function elementChildren(element, selector=''){
const window = getWindow(); //브라우저 환경에서는 실제 window, 아니면 모킹 객체
const children = [...element.children]; //HTML요소중에서 Element노드만 가져온다.
if(window.HTMLSlotElement && element instanceof HTMLSlotElement){ //해당 요소가 <slot></slot>일때 처리
children.push(...element.assignedElements()); //실제 렌더링되는 자식들은 assignedElements()로 접근해야함
}
if(!selector){
//선택자가 없다면 전체 자식 배열 반환
return children;
}
// 있다면 .matches로 해당 셀렉터에 일치하는 요소만 필터링
return children.filter((el)=> el.matches(selector));
}
export {
showWarning,
createElement,
elementChildren
}
import { getWindow, getDocument } from 'ssr-window';
function showWarning(text){
try {
console.warn(text);
return;
} catch(err){
//err
}
}
function createElement(tag, classes = []) {
const el = document.createElement(tag);
el.classList.add(...(Array.isArray(classes) ? classes : classesToToken(classes)));
return el;
}
function elementChildren(element, selector = '') {
const window = getWindow();
const children = [...element.children];
if (window.HTMLSlotElement && element instanceof HTMLSlotElement) {
children.push(...element.assignedElements());
}
if (!selector) return children;
return children.filter((el) => el.matches(selector));
}