Resize On Contents Ready
resizeOnContentsReady 옵션으로 이미지/비디오 로드 후 자동으로 패널 크기와 위치를 재계산합니다.
- JavaScript
- React
- Vue@3
import Flicking from "@egjs/flicking"; import "@egjs/flicking/dist/flicking.css"; import "./styles.css"; const t = Date.now(); const HEIGHTS_WIDTHS = [ [300, 150], [200, 150], [400, 150], [250, 150] ]; // Set image src before Flicking init, so resizeOnContentsReady can detect them document.querySelectorAll("#flick-auto .flicking-panel img").forEach((img, i) => { img.src = `https://picsum.photos/${HEIGHTS_WIDTHS[i][0]}/${HEIGHTS_WIDTHS[i][1]}?t=${t}&r=${i + 1}`; }); document.querySelectorAll("#flick-manual .flicking-panel img").forEach((img, i) => { img.src = `https://picsum.photos/${HEIGHTS_WIDTHS[i][0]}/${HEIGHTS_WIDTHS[i][1]}?t=${t}&r=${i + 5}`; }); const autoFlicking = new Flicking("#flick-auto", { align: "prev", resizeOnContentsReady: true, preventDefaultOnDrag: true, bound: true }); const manualFlicking = new Flicking("#flick-manual", { align: "prev", resizeOnContentsReady: false, preventDefaultOnDrag: true, bound: true }); function updateSizes() { setTimeout(() => { try { document.getElementById("auto-sizes").textContent = autoFlicking.panels.map(p => Math.round(p.size)).join(", "); document.getElementById("manual-sizes").textContent = manualFlicking.panels .map(p => Math.round(p.size)) .join(", "); } catch (_e) { /* ignore */ } }, 500); } autoFlicking.on("ready", updateSizes); manualFlicking.on("ready", updateSizes); document.querySelectorAll("#flick-auto img").forEach(img => { img.addEventListener("load", updateSizes); }); document.querySelectorAll("#flick-manual img").forEach(img => { img.addEventListener("load", updateSizes); }); document.querySelectorAll(".move-btn").forEach(btn => { btn.addEventListener("click", () => { const idx = parseInt(btn.dataset.index, 10); autoFlicking.moveTo(idx, 500).catch(() => {}); manualFlicking.moveTo(idx, 500).catch(() => {}); }); });
import Flicking from "@egjs/react-flicking"; import { useRef, useState } from "react"; import "@egjs/react-flicking/dist/flicking.css"; import "./styles.css"; // Use various widths so panel size depends on images // Cache-busting to load new images each time (no difference when cached) const t = Date.now(); const IMAGES = [ `https://picsum.photos/300/150?t=${t}&r=1`, `https://picsum.photos/200/150?t=${t}&r=2`, `https://picsum.photos/400/150?t=${t}&r=3`, `https://picsum.photos/250/150?t=${t}&r=4` ]; export default function App() { const autoRef = useRef(null); const manualRef = useRef(null); const [autoSizes, setAutoSizes] = useState("-"); const [manualSizes, setManualSizes] = useState("-"); const updateSizes = () => { setTimeout(() => { try { if (autoRef.current) { setAutoSizes(autoRef.current.panels.map(p => Math.round(p.size)).join(", ")); } if (manualRef.current) { setManualSizes(manualRef.current.panels.map(p => Math.round(p.size)).join(", ")); } } catch (_e) { /* ignore */ } }, 500); }; const moveToPanel = index => { if (autoRef.current) autoRef.current.moveTo(index, 500).catch(() => {}); if (manualRef.current) manualRef.current.moveTo(index, 500).catch(() => {}); }; return ( <div> <div className="demo-container"> <div className="demo-label">resizeOnContentsReady: true</div> <div className="demo-info">Auto-recalculates panel size & position after image load → correct scrolling</div> <Flicking ref={autoRef} align="prev" resizeOnContentsReady={true} preventDefaultOnDrag={true} bound={true} onReady={() => updateSizes()} > {IMAGES.map((src, i) => ( <div key={i} className="flicking-panel"> <img src={src} alt={`Panel ${i + 1}`} onLoad={() => updateSizes()} /> <div className="panel-label">Panel {i + 1}</div> </div> ))} </Flicking> <div className="status-display"> Flicking internal panel widths: <strong>[{autoSizes}]</strong> </div> </div> <div className="demo-container"> <div className="demo-label">resizeOnContentsReady: false (default)</div> <div className="demo-info">Panel sizes not recalculated → size mismatch, incorrect scroll position</div> <Flicking ref={manualRef} align="prev" resizeOnContentsReady={false} preventDefaultOnDrag={true} bound={true} onReady={() => updateSizes()} > {IMAGES.map((src, i) => ( <div key={i} className="flicking-panel"> <img src={src} alt={`Panel ${i + 1}`} onLoad={() => updateSizes()} /> <div className="panel-label">Panel {i + 1}</div> </div> ))} </Flicking> <div className="status-display"> Flicking internal panel widths: <strong>[{manualSizes}]</strong> </div> </div> <div className="controls"> <button className="button" onClick={() => moveToPanel(0)}> Panel 1 </button> <button className="button" onClick={() => moveToPanel(1)}> Panel 2 </button> <button className="button" onClick={() => moveToPanel(2)}> Panel 3 </button> <button className="button" onClick={() => moveToPanel(3)}> Panel 4 </button> </div> </div> ); }
<template> <div> <div class="demo-container"> <div class="demo-label">resizeOnContentsReady: true</div> <div class="demo-info">Auto-recalculates panel size & position after image load → correct scrolling</div> <Flicking ref="autoFlicking" :options="{ align: 'prev', resizeOnContentsReady: true, preventDefaultOnDrag: true, bound: true }" @ready="updateSizes" > <div v-for="(src, i) in images" :key="'auto-' + i" class="flicking-panel"> <img :src="src" :alt="'Panel ' + (i + 1)" @load="updateSizes" /> <div class="panel-label">Panel {{ i + 1 }}</div> </div> </Flicking> <div class="status-display"> Flicking internal panel widths: <strong>[{{ autoSizes }}]</strong> </div> </div> <div class="demo-container"> <div class="demo-label">resizeOnContentsReady: false (default)</div> <div class="demo-info">Panel sizes not recalculated → size mismatch, incorrect scroll position</div> <Flicking ref="manualFlicking" :options="{ align: 'prev', resizeOnContentsReady: false, preventDefaultOnDrag: true, bound: true }" @ready="updateSizes" > <div v-for="(src, i) in images" :key="'manual-' + i" class="flicking-panel"> <img :src="src" :alt="'Panel ' + (i + 1)" @load="updateSizes" /> <div class="panel-label">Panel {{ i + 1 }}</div> </div> </Flicking> <div class="status-display"> Flicking internal panel widths: <strong>[{{ manualSizes }}]</strong> </div> </div> <div class="controls"> <button class="button" @click="moveToPanel(0)">Panel 1</button> <button class="button" @click="moveToPanel(1)">Panel 2</button> <button class="button" @click="moveToPanel(2)">Panel 3</button> <button class="button" @click="moveToPanel(3)">Panel 4</button> </div> </div> </template> <script setup> import Flicking from "@egjs/vue3-flicking"; import { ref } from "vue"; import "@egjs/vue3-flicking/dist/flicking.css"; const t = Date.now(); const images = [ `https://picsum.photos/300/150?t=${t}&r=1`, `https://picsum.photos/200/150?t=${t}&r=2`, `https://picsum.photos/400/150?t=${t}&r=3`, `https://picsum.photos/250/150?t=${t}&r=4` ]; const autoFlicking = ref(null); const manualFlicking = ref(null); const autoSizes = ref("-"); const manualSizes = ref("-"); const updateSizes = () => { setTimeout(() => { try { if (autoFlicking.value) { autoSizes.value = autoFlicking.value.panels.map(p => Math.round(p.size)).join(", "); } if (manualFlicking.value) { manualSizes.value = manualFlicking.value.panels.map(p => Math.round(p.size)).join(", "); } } catch (e) { /* ignore */ } }, 500); }; const moveToPanel = index => { if (autoFlicking.value) autoFlicking.value.moveTo(index, 500).catch(() => {}); if (manualFlicking.value) manualFlicking.value.moveTo(index, 500).catch(() => {}); }; </script> <style> .flicking-viewport.vertical { display: block; width: 100%; } .flicking-panel { width: 200px; height: 150px; margin-right: 10px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 24px; font-weight: bold; color: white; } .panel-1 { background: #3e8ed0; } .panel-2 { background: #00d1b2; } .panel-3 { background: #f14668; } .panel-4 { background: #ffe08a; color: #333; } .panel-5 { background: #48c78e; } .demo-container { margin-bottom: 24px; } .demo-label { font-weight: bold; margin-bottom: 8px; color: #666; } .button { padding: 8px 16px; margin: 4px; border: 2px solid #3498db; background: transparent; color: #3498db; border-radius: 4px; cursor: pointer; font-size: 14px; } .button:hover { background: #3498db; color: white; } .controls { display: flex; justify-content: center; margin-top: 16px; gap: 8px; } .flicking-panel { width: auto; height: auto; margin-right: 10px; border-radius: 8px; overflow: hidden; background: #f5f5f5; } .flicking-panel img { height: 150px; width: auto; display: block; } .panel-label { padding: 4px 8px; text-align: center; font-size: 12px; color: #333; background: #e0e0e0; white-space: nowrap; } .demo-container { margin-bottom: 32px; } .demo-info { font-size: 14px; color: #888; margin-bottom: 12px; } .status-display { margin-top: 8px; padding: 8px 12px; background: #f5f5f5; border-radius: 4px; font-size: 14px; color: #333; } .status-display strong { color: #3e8ed0; } </style>
요약
주요 옵션
| 옵션 | 타입 | 기본값 | 설명 |
|---|---|---|---|
resizeOnContentsReady | boolean | false | 콘텐츠 로드 시 패널 크기 자동 재계산 |
모드별 비교
| resizeOnContentsReady | 이미지 로드 후 동작 |
|---|---|
false | Flicking 내부 패널 크기가 초기화 시점 그대로 → 스크롤 범위, 스냅 위치 불일치 |
true | 패널 크기 재계산 → 스크롤 범위, 스냅 위치 정상 |
상세 설명
동작 원리
- Flicking 초기화 시점에 패널 크기 계산
- 이미지/비디오가 아직 로드되지 않아 크기가 0 또는 placeholder 크기
- 이미지 로드 완료 후 실제 크기로 변경
resizeOnContentsReady: true면@egjs/imready를 통해 자동으로 패널 크기 재계산 및 위치 업데이트
<Flicking
resizeOnContentsReady={true}
>
<div className="panel">
<img src="image.jpg" /> {/* 로드 완료 시 패널 크기 자동 재계산 */}
</div>
</Flicking>
재계산 대상
resizeOnContentsReady가 이미지/비디오 로드를 감지하면 다음을 업데이트합니다:
- 패널 크기 (
panel.resize()) - 뒤따르는 패널 위치 (
updatePosition()) - 카메라 스크롤 범위 (
camera.updateRange()) - 스냅 포인트 (
camera.updateAnchors())
감지 대상 콘텐츠
패널 내의 다음 요소의 load 이벤트를 감지합니다:
<img>이미지<video>비디오
사용 시나리오
언제 resizeOnContentsReady를 사용하나요?
사용 권장:
- 패널 크기가 이미지/비디오에 의존하는 경우
- CSS로 패널 크기가 미리 고정되지 않은 경우
- 네트워크에서 이미지를 로드하는 경우
사용하지 않아도 되는 경우:
- 패널 크기가 CSS로 미리 고정된 경우 (예:
width: 200px) - 텍스트만 있는 패널
- 이미지가 base64 인라인으로 포함된 경우
수동 resize 대안
resizeOnContentsReady: false일 때 수동으로 resize를 호출할 수 있습니다:
// 이미지 로드 완료 후 수동 resize
img.addEventListener("load", () => {
flicking.resize();
});
관련 링크
관련 옵션
adaptive: 뷰포트 높이 자동 조정autoResize: 윈도우 리사이즈 시 자동 resize
관련 메서드
resize: 수동 레이아웃 재계산
관련 데모
- Adaptive: adaptive 옵션 데모