@egjs/flicking 4.12.1-beta.5 → 4.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core-packages-link.js +8 -7
- package/declaration/Flicking.d.ts +10 -2
- package/declaration/core/AutoResizer.d.ts +5 -0
- package/dist/flicking.cjs.js +160 -43
- package/dist/flicking.cjs.js.map +1 -1
- package/dist/flicking.esm.js +160 -43
- package/dist/flicking.esm.js.map +1 -1
- package/dist/flicking.js +160 -43
- package/dist/flicking.js.map +1 -1
- package/dist/flicking.min.js +2 -2
- package/dist/flicking.min.js.map +1 -1
- package/dist/flicking.pkgd.js +160 -43
- package/dist/flicking.pkgd.js.map +1 -1
- package/dist/flicking.pkgd.min.js +2 -2
- package/dist/flicking.pkgd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/Flicking.ts +191 -83
- package/src/core/AutoResizer.ts +75 -22
- package/src/renderer/Renderer.ts +13 -0
- package/src/utils.ts +6 -1
- package/panel-demo.html +0 -323
- package/panel-visual.md +0 -328
- package/scratch/dist/flicking.css +0 -11
- package/scratch/dist/flicking.js +0 -9059
- package/scratch/dist/flicking.js.map +0 -1
package/src/core/AutoResizer.ts
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
* Copyright (c) 2015 NAVER Corp.
|
|
3
3
|
* egjs projects are licensed under the MIT license
|
|
4
4
|
*/
|
|
5
|
-
import { getElementSize, getStyle } from "
|
|
5
|
+
import { getElementSize, getStyle } from "../utils";
|
|
6
6
|
import Flicking from "../Flicking";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* A component that detects size change and trigger resize method when the autoResize option is used
|
|
10
|
+
* @ko autoResize 옵션을 사용할 때 크기 변화를 감지하고 Flicking의 resize를 호출하는 컴포넌트
|
|
11
|
+
*/
|
|
8
12
|
class AutoResizer {
|
|
9
13
|
private _flicking: Flicking;
|
|
10
14
|
private _enabled: boolean;
|
|
@@ -16,6 +20,7 @@ class AutoResizer {
|
|
|
16
20
|
return this._enabled;
|
|
17
21
|
}
|
|
18
22
|
|
|
23
|
+
/** */
|
|
19
24
|
public constructor(flicking: Flicking) {
|
|
20
25
|
this._flicking = flicking;
|
|
21
26
|
this._enabled = false;
|
|
@@ -36,14 +41,18 @@ class AutoResizer {
|
|
|
36
41
|
const viewportSizeNot0 = viewport.width !== 0 || viewport.height !== 0;
|
|
37
42
|
|
|
38
43
|
const resizeObserver = viewportSizeNot0
|
|
39
|
-
? new ResizeObserver(
|
|
40
|
-
: new ResizeObserver(
|
|
41
|
-
|
|
42
|
-
resizeObserver.observe(flicking.viewport.element);
|
|
44
|
+
? new ResizeObserver(this._skipFirstResize)
|
|
45
|
+
: new ResizeObserver(this._onResize);
|
|
43
46
|
|
|
44
47
|
this._resizeObserver = resizeObserver;
|
|
48
|
+
|
|
49
|
+
this.observe(flicking.viewport.element);
|
|
50
|
+
|
|
51
|
+
if (flicking.observePanelResize) {
|
|
52
|
+
this.observePanels();
|
|
53
|
+
}
|
|
45
54
|
} else {
|
|
46
|
-
window.addEventListener("resize",
|
|
55
|
+
window.addEventListener("resize", this._onResizeWrapper);
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
this._enabled = true;
|
|
@@ -51,6 +60,44 @@ class AutoResizer {
|
|
|
51
60
|
return this;
|
|
52
61
|
}
|
|
53
62
|
|
|
63
|
+
public observePanels(): this {
|
|
64
|
+
this._flicking.panels.forEach(panel => {
|
|
65
|
+
this.observe(panel.element);
|
|
66
|
+
});
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public unobservePanels(): this {
|
|
71
|
+
this._flicking.panels.forEach(panel => {
|
|
72
|
+
this.unobserve(panel.element);
|
|
73
|
+
});
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public observe(element: HTMLElement): this {
|
|
78
|
+
const resizeObserver = this._resizeObserver;
|
|
79
|
+
|
|
80
|
+
if (!resizeObserver) return this;
|
|
81
|
+
|
|
82
|
+
resizeObserver.observe(element);
|
|
83
|
+
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public unobserve(element: HTMLElement): this {
|
|
88
|
+
const resizeObserver = this._resizeObserver;
|
|
89
|
+
|
|
90
|
+
if (!resizeObserver) return this;
|
|
91
|
+
|
|
92
|
+
resizeObserver.unobserve(element);
|
|
93
|
+
|
|
94
|
+
if (this._flicking.observePanelResize) {
|
|
95
|
+
this.unobservePanels();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
|
|
54
101
|
public disable(): this {
|
|
55
102
|
if (!this._enabled) return this;
|
|
56
103
|
|
|
@@ -59,7 +106,7 @@ class AutoResizer {
|
|
|
59
106
|
resizeObserver.disconnect();
|
|
60
107
|
this._resizeObserver = null;
|
|
61
108
|
} else {
|
|
62
|
-
window.removeEventListener("resize",
|
|
109
|
+
window.removeEventListener("resize", this._onResizeWrapper);
|
|
63
110
|
}
|
|
64
111
|
|
|
65
112
|
this._enabled = false;
|
|
@@ -67,44 +114,50 @@ class AutoResizer {
|
|
|
67
114
|
return this;
|
|
68
115
|
}
|
|
69
116
|
|
|
117
|
+
private _onResizeWrapper = () => {
|
|
118
|
+
this._onResize([]);
|
|
119
|
+
};
|
|
120
|
+
|
|
70
121
|
private _onResize = (entries: ResizeObserverEntry[]) => {
|
|
71
122
|
const flicking = this._flicking;
|
|
72
123
|
const resizeDebounce = flicking.resizeDebounce;
|
|
73
124
|
const maxResizeDebounce = flicking.maxResizeDebounce;
|
|
74
125
|
|
|
75
|
-
|
|
76
|
-
|
|
126
|
+
const resizedViewportElement = flicking.element;
|
|
127
|
+
// 현재 구현에서 리사이즈 옵저빙 대상은 패널과 뷰포트 2개만 존재.
|
|
128
|
+
// 아래는 뷰포트만 변경되었을 때 동작해야하는 로직이 있으므로 아래와 같이 조건문을 건다.
|
|
129
|
+
// 패널 쪽에서는 리사이즈 감지에 resizeObserver를 사용하지 않는 경우가 없으므로 이 조건은 곧 뷰포트만 리사이즈가 된 경우를 의미한다.
|
|
130
|
+
const isResizedViewportOnly = entries.find(e => e.target === flicking.element) && entries.length === 1;
|
|
131
|
+
|
|
132
|
+
// 참고: resizeObserver를 사용하지 않은 경우에는 entries.length가 0으로 오는데 이 경우에는 그냥 항상 리사이즈가 진행되도록 한다.
|
|
133
|
+
// (vw, vh 등을 사용하는 경우 이상 동작이 발생할 여지가 있기 때문이다)
|
|
134
|
+
if (isResizedViewportOnly) {
|
|
135
|
+
// resize 이벤트가 발생했으나 이전과 width, height의 변화가 없다면 이후 로직을 진행하지 않는다.
|
|
77
136
|
const beforeSize = {
|
|
78
137
|
width: flicking.viewport.width,
|
|
79
138
|
height: flicking.viewport.height
|
|
80
139
|
};
|
|
81
140
|
|
|
82
141
|
const afterSize = {
|
|
83
|
-
width: resizeEntryInfo.width,
|
|
84
|
-
height: resizeEntryInfo.height
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const new_afterSize = {
|
|
88
142
|
width: getElementSize({
|
|
89
|
-
el:
|
|
143
|
+
el: resizedViewportElement,
|
|
90
144
|
horizontal: true,
|
|
91
145
|
useFractionalSize: this._flicking.useFractionalSize,
|
|
92
146
|
useOffset: false,
|
|
93
|
-
style: getStyle(
|
|
147
|
+
style: getStyle(resizedViewportElement)
|
|
94
148
|
}),
|
|
95
149
|
height: getElementSize({
|
|
96
|
-
el:
|
|
150
|
+
el: resizedViewportElement,
|
|
97
151
|
horizontal: false,
|
|
98
152
|
useFractionalSize: this._flicking.useFractionalSize,
|
|
99
153
|
useOffset: false,
|
|
100
|
-
style: getStyle(
|
|
154
|
+
style: getStyle(resizedViewportElement)
|
|
101
155
|
})
|
|
102
|
-
}
|
|
156
|
+
};
|
|
103
157
|
|
|
104
|
-
// resize 이벤트가 발생했으나 이전과 width, height의 변화가 없다면 이후 로직을 진행하지 않는다.
|
|
105
158
|
if (
|
|
106
|
-
beforeSize.height ===
|
|
107
|
-
beforeSize.width ===
|
|
159
|
+
beforeSize.height === afterSize.height &&
|
|
160
|
+
beforeSize.width === afterSize.width
|
|
108
161
|
) {
|
|
109
162
|
return;
|
|
110
163
|
}
|
package/src/renderer/Renderer.ts
CHANGED
|
@@ -335,6 +335,19 @@ abstract class Renderer {
|
|
|
335
335
|
// Update camera & control
|
|
336
336
|
this._updateCameraAndControl();
|
|
337
337
|
|
|
338
|
+
if (flicking.autoResize && flicking.useResizeObserver) {
|
|
339
|
+
panelsAdded.forEach((panel) => {
|
|
340
|
+
if (panel.element) {
|
|
341
|
+
flicking.autoResizer.observe(panel.element);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
panelsRemoved.forEach((panel) => {
|
|
345
|
+
if (panel.element) {
|
|
346
|
+
flicking.autoResizer.unobserve(panel.element);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
338
351
|
void this.render();
|
|
339
352
|
|
|
340
353
|
if (!flicking.animating) {
|
package/src/utils.ts
CHANGED
|
@@ -256,7 +256,12 @@ export const findIndex = <T>(array: T[], checker: (val: T) => boolean): number =
|
|
|
256
256
|
export const getProgress = (pos: number, prev: number, next: number) => (pos - prev) / (next - prev);
|
|
257
257
|
|
|
258
258
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
259
|
-
export const getStyle = (el: HTMLElement): CSSStyleDeclaration =>
|
|
259
|
+
export const getStyle = (el: HTMLElement): CSSStyleDeclaration => {
|
|
260
|
+
if (!el) {
|
|
261
|
+
return {} as CSSStyleDeclaration;
|
|
262
|
+
}
|
|
263
|
+
return window.getComputedStyle(el) || (el as any).currentStyle as CSSStyleDeclaration;
|
|
264
|
+
};
|
|
260
265
|
|
|
261
266
|
export const setSize = (el: HTMLElement, { width, height }: Partial<{
|
|
262
267
|
width: number | string;
|
package/panel-demo.html
DELETED
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="ko">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Flicking Panel 시각화 데모</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
box-sizing: border-box;
|
|
10
|
-
margin: 0;
|
|
11
|
-
padding: 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
body {
|
|
15
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
16
|
-
line-height: 1.6;
|
|
17
|
-
padding: 20px;
|
|
18
|
-
background: #f5f5f5;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.container {
|
|
22
|
-
max-width: 1200px;
|
|
23
|
-
margin: 0 auto;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.demo-section {
|
|
27
|
-
background: white;
|
|
28
|
-
border-radius: 8px;
|
|
29
|
-
padding: 20px;
|
|
30
|
-
margin-bottom: 20px;
|
|
31
|
-
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
.demo-title {
|
|
35
|
-
font-size: 1.5em;
|
|
36
|
-
margin-bottom: 15px;
|
|
37
|
-
color: #333;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.demo-description {
|
|
41
|
-
margin-bottom: 20px;
|
|
42
|
-
color: #666;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
.panel-container {
|
|
46
|
-
position: relative;
|
|
47
|
-
width: 100%;
|
|
48
|
-
height: 300px;
|
|
49
|
-
background: #eee;
|
|
50
|
-
border: 2px solid #ccc;
|
|
51
|
-
margin: 20px 0;
|
|
52
|
-
overflow: hidden;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.panel {
|
|
56
|
-
position: absolute;
|
|
57
|
-
background: #4CAF50;
|
|
58
|
-
color: white;
|
|
59
|
-
padding: 20px;
|
|
60
|
-
transition: all 0.3s ease;
|
|
61
|
-
cursor: pointer;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
.panel:hover {
|
|
65
|
-
background: #45a049;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.panel-info {
|
|
69
|
-
position: absolute;
|
|
70
|
-
background: rgba(0,0,0,0.8);
|
|
71
|
-
color: white;
|
|
72
|
-
padding: 10px;
|
|
73
|
-
border-radius: 4px;
|
|
74
|
-
font-size: 12px;
|
|
75
|
-
pointer-events: none;
|
|
76
|
-
z-index: 1000;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.controls {
|
|
80
|
-
display: flex;
|
|
81
|
-
gap: 10px;
|
|
82
|
-
margin-bottom: 20px;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
button {
|
|
86
|
-
padding: 8px 16px;
|
|
87
|
-
background: #2196F3;
|
|
88
|
-
color: white;
|
|
89
|
-
border: none;
|
|
90
|
-
border-radius: 4px;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
button:hover {
|
|
95
|
-
background: #1976D2;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
.size-controls {
|
|
99
|
-
display: flex;
|
|
100
|
-
gap: 10px;
|
|
101
|
-
align-items: center;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
input[type="range"] {
|
|
105
|
-
width: 200px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.margin-visual {
|
|
109
|
-
position: absolute;
|
|
110
|
-
background: rgba(255, 193, 7, 0.3);
|
|
111
|
-
pointer-events: none;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.margin-label {
|
|
115
|
-
position: absolute;
|
|
116
|
-
font-size: 12px;
|
|
117
|
-
color: #666;
|
|
118
|
-
}
|
|
119
|
-
</style>
|
|
120
|
-
</head>
|
|
121
|
-
<body>
|
|
122
|
-
<div class="container">
|
|
123
|
-
<div class="demo-section">
|
|
124
|
-
<h2 class="demo-title">1. 패널 크기와 마진 시각화</h2>
|
|
125
|
-
<p class="demo-description">
|
|
126
|
-
패널의 기본 크기(_size)와 마진(prev, next)을 시각적으로 확인할 수 있습니다.
|
|
127
|
-
슬라이더를 조절하여 크기와 마진을 변경해보세요.
|
|
128
|
-
</p>
|
|
129
|
-
<div class="controls">
|
|
130
|
-
<div class="size-controls">
|
|
131
|
-
<label>패널 크기:</label>
|
|
132
|
-
<input type="range" id="sizeSlider" min="100" max="400" value="200">
|
|
133
|
-
<span id="sizeValue">200px</span>
|
|
134
|
-
</div>
|
|
135
|
-
<div class="size-controls">
|
|
136
|
-
<label>이전 마진:</label>
|
|
137
|
-
<input type="range" id="prevMarginSlider" min="0" max="50" value="10">
|
|
138
|
-
<span id="prevMarginValue">10px</span>
|
|
139
|
-
</div>
|
|
140
|
-
<div class="size-controls">
|
|
141
|
-
<label>다음 마진:</label>
|
|
142
|
-
<input type="range" id="nextMarginSlider" min="0" max="50" value="10">
|
|
143
|
-
<span id="nextMarginValue">10px</span>
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
<div class="panel-container" id="sizeDemo">
|
|
147
|
-
<div class="panel" id="demoPanel">
|
|
148
|
-
패널
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
|
|
153
|
-
<div class="demo-section">
|
|
154
|
-
<h2 class="demo-title">2. 패널 위치와 정렬</h2>
|
|
155
|
-
<p class="demo-description">
|
|
156
|
-
패널의 위치(_pos)와 정렬(_alignPos)을 시각적으로 확인할 수 있습니다.
|
|
157
|
-
정렬 옵션을 변경하여 패널의 위치가 어떻게 변하는지 확인해보세요.
|
|
158
|
-
</p>
|
|
159
|
-
<div class="controls">
|
|
160
|
-
<button onclick="changeAlign('prev')">시작점 정렬</button>
|
|
161
|
-
<button onclick="changeAlign('center')">중앙 정렬</button>
|
|
162
|
-
<button onclick="changeAlign('next')">끝점 정렬</button>
|
|
163
|
-
<div class="size-controls">
|
|
164
|
-
<label>정렬 오프셋:</label>
|
|
165
|
-
<input type="range" id="alignSlider" min="-100" max="100" value="0">
|
|
166
|
-
<span id="alignValue">0px</span>
|
|
167
|
-
</div>
|
|
168
|
-
</div>
|
|
169
|
-
<div class="panel-container" id="positionDemo">
|
|
170
|
-
<div class="panel" id="positionPanel">
|
|
171
|
-
패널
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
<div class="demo-section">
|
|
177
|
-
<h2 class="demo-title">3. 가시성 계산</h2>
|
|
178
|
-
<p class="demo-description">
|
|
179
|
-
패널의 가시성(visibleRatio)을 시각적으로 확인할 수 있습니다.
|
|
180
|
-
패널을 드래그하여 뷰포트 내에서의 가시성 변화를 확인해보세요.
|
|
181
|
-
</p>
|
|
182
|
-
<div class="panel-container" id="visibilityDemo">
|
|
183
|
-
<div class="panel" id="visibilityPanel">
|
|
184
|
-
패널
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
<div id="visibilityInfo" style="margin-top: 10px;">
|
|
188
|
-
가시성 비율: <span id="visibilityRatio">100%</span>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
</div>
|
|
192
|
-
|
|
193
|
-
<script>
|
|
194
|
-
// 1. 크기와 마진 시각화
|
|
195
|
-
const sizeSlider = document.getElementById('sizeSlider');
|
|
196
|
-
const prevMarginSlider = document.getElementById('prevMarginSlider');
|
|
197
|
-
const nextMarginSlider = document.getElementById('nextMarginSlider');
|
|
198
|
-
const demoPanel = document.getElementById('demoPanel');
|
|
199
|
-
const sizeValue = document.getElementById('sizeValue');
|
|
200
|
-
const prevMarginValue = document.getElementById('prevMarginValue');
|
|
201
|
-
const nextMarginValue = document.getElementById('nextMarginValue');
|
|
202
|
-
|
|
203
|
-
function updatePanelSize() {
|
|
204
|
-
const size = sizeSlider.value;
|
|
205
|
-
const prevMargin = prevMarginSlider.value;
|
|
206
|
-
const nextMargin = nextMarginSlider.value;
|
|
207
|
-
|
|
208
|
-
demoPanel.style.width = `${size}px`;
|
|
209
|
-
demoPanel.style.marginLeft = `${prevMargin}px`;
|
|
210
|
-
demoPanel.style.marginRight = `${nextMargin}px`;
|
|
211
|
-
|
|
212
|
-
sizeValue.textContent = `${size}px`;
|
|
213
|
-
prevMarginValue.textContent = `${prevMargin}px`;
|
|
214
|
-
nextMarginValue.textContent = `${nextMargin}px`;
|
|
215
|
-
|
|
216
|
-
// 마진 시각화
|
|
217
|
-
updateMarginVisual();
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function updateMarginVisual() {
|
|
221
|
-
let marginVisual = document.querySelector('.margin-visual');
|
|
222
|
-
if (!marginVisual) {
|
|
223
|
-
marginVisual = document.createElement('div');
|
|
224
|
-
marginVisual.className = 'margin-visual';
|
|
225
|
-
demoPanel.parentNode.appendChild(marginVisual);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const panelRect = demoPanel.getBoundingClientRect();
|
|
229
|
-
const containerRect = demoPanel.parentNode.getBoundingClientRect();
|
|
230
|
-
|
|
231
|
-
marginVisual.style.left = `${panelRect.left - containerRect.left - parseInt(prevMarginSlider.value)}px`;
|
|
232
|
-
marginVisual.style.top = `${panelRect.top - containerRect.top}px`;
|
|
233
|
-
marginVisual.style.width = `${parseInt(sizeSlider.value) + parseInt(prevMarginSlider.value) + parseInt(nextMarginSlider.value)}px`;
|
|
234
|
-
marginVisual.style.height = `${panelRect.height}px`;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
sizeSlider.addEventListener('input', updatePanelSize);
|
|
238
|
-
prevMarginSlider.addEventListener('input', updatePanelSize);
|
|
239
|
-
nextMarginSlider.addEventListener('input', updatePanelSize);
|
|
240
|
-
|
|
241
|
-
// 2. 위치와 정렬
|
|
242
|
-
const positionPanel = document.getElementById('positionPanel');
|
|
243
|
-
const alignSlider = document.getElementById('alignSlider');
|
|
244
|
-
const alignValue = document.getElementById('alignValue');
|
|
245
|
-
let currentAlign = 'center';
|
|
246
|
-
|
|
247
|
-
function changeAlign(align) {
|
|
248
|
-
currentAlign = align;
|
|
249
|
-
updatePanelPosition();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function updatePanelPosition() {
|
|
253
|
-
const container = positionPanel.parentNode;
|
|
254
|
-
const containerWidth = container.offsetWidth;
|
|
255
|
-
const panelWidth = positionPanel.offsetWidth;
|
|
256
|
-
const alignOffset = parseInt(alignSlider.value);
|
|
257
|
-
|
|
258
|
-
let position = 0;
|
|
259
|
-
switch (currentAlign) {
|
|
260
|
-
case 'prev':
|
|
261
|
-
position = 0;
|
|
262
|
-
break;
|
|
263
|
-
case 'center':
|
|
264
|
-
position = (containerWidth - panelWidth) / 2;
|
|
265
|
-
break;
|
|
266
|
-
case 'next':
|
|
267
|
-
position = containerWidth - panelWidth;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
position += alignOffset;
|
|
272
|
-
positionPanel.style.left = `${position}px`;
|
|
273
|
-
alignValue.textContent = `${alignOffset}px`;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
alignSlider.addEventListener('input', updatePanelPosition);
|
|
277
|
-
|
|
278
|
-
// 3. 가시성 계산
|
|
279
|
-
const visibilityPanel = document.getElementById('visibilityPanel');
|
|
280
|
-
const visibilityRatio = document.getElementById('visibilityRatio');
|
|
281
|
-
let isDragging = false;
|
|
282
|
-
let startX = 0;
|
|
283
|
-
let startLeft = 0;
|
|
284
|
-
|
|
285
|
-
visibilityPanel.addEventListener('mousedown', (e) => {
|
|
286
|
-
isDragging = true;
|
|
287
|
-
startX = e.clientX;
|
|
288
|
-
startLeft = parseInt(visibilityPanel.style.left || '0');
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
document.addEventListener('mousemove', (e) => {
|
|
292
|
-
if (!isDragging) return;
|
|
293
|
-
|
|
294
|
-
const deltaX = e.clientX - startX;
|
|
295
|
-
const newLeft = startLeft + deltaX;
|
|
296
|
-
visibilityPanel.style.left = `${newLeft}px`;
|
|
297
|
-
|
|
298
|
-
updateVisibility();
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
document.addEventListener('mouseup', () => {
|
|
302
|
-
isDragging = false;
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
function updateVisibility() {
|
|
306
|
-
const container = visibilityPanel.parentNode;
|
|
307
|
-
const containerRect = container.getBoundingClientRect();
|
|
308
|
-
const panelRect = visibilityPanel.getBoundingClientRect();
|
|
309
|
-
|
|
310
|
-
const visibleWidth = Math.min(panelRect.right, containerRect.right) -
|
|
311
|
-
Math.max(panelRect.left, containerRect.left);
|
|
312
|
-
|
|
313
|
-
const ratio = Math.max(0, Math.min(100, (visibleWidth / panelRect.width) * 100));
|
|
314
|
-
visibilityRatio.textContent = `${Math.round(ratio)}%`;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// 초기화
|
|
318
|
-
updatePanelSize();
|
|
319
|
-
updatePanelPosition();
|
|
320
|
-
updateVisibility();
|
|
321
|
-
</script>
|
|
322
|
-
</body>
|
|
323
|
-
</html>
|