@egjs/flicking 4.7.3 → 4.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@egjs/flicking",
3
- "version": "4.7.3",
3
+ "version": "4.9.0",
4
4
  "description": "Everyday 30 million people experience. It's reliable, flexible and extendable carousel.",
5
5
  "main": "dist/flicking.js",
6
6
  "module": "dist/flicking.esm.js",
@@ -109,7 +109,7 @@
109
109
  "html-to-react": "^1.4.5",
110
110
  "http-serve": "^1.0.1",
111
111
  "husky": "^1.3.1",
112
- "jsdoc-to-mdx": "^1.0.5",
112
+ "jsdoc-to-mdx": "^1.1.2",
113
113
  "karma-typescript-es6-transform": "^5.5.2",
114
114
  "node-sass": "^7.0.1",
115
115
  "npm-run-all": "^4.1.5",
package/src/Flicking.ts CHANGED
@@ -78,6 +78,7 @@ export interface FlickingOptions {
78
78
  iOSEdgeSwipeThreshold: number;
79
79
  preventClickOnDrag: boolean;
80
80
  disableOnInit: boolean;
81
+ changeOnHold: boolean;
81
82
 
82
83
  // PERFORMANCE
83
84
  renderOnlyVisible: boolean;
@@ -89,6 +90,7 @@ export interface FlickingOptions {
89
90
  useResizeObserver: boolean;
90
91
  resizeDebounce: number;
91
92
  maxResizeDebounce: number;
93
+ useFractionalSize: boolean;
92
94
  externalRenderer: ExternalRenderer | null;
93
95
 
94
96
  // @deprecated
@@ -154,6 +156,7 @@ class Flicking extends Component<FlickingEvents> {
154
156
  private _iOSEdgeSwipeThreshold: FlickingOptions["iOSEdgeSwipeThreshold"];
155
157
  private _preventClickOnDrag: FlickingOptions["preventClickOnDrag"];
156
158
  private _disableOnInit: FlickingOptions["disableOnInit"];
159
+ private _changeOnHold: FlickingOptions["changeOnHold"];
157
160
 
158
161
  private _renderOnlyVisible: FlickingOptions["renderOnlyVisible"];
159
162
 
@@ -162,6 +165,7 @@ class Flicking extends Component<FlickingEvents> {
162
165
  private _useResizeObserver: FlickingOptions["useResizeObserver"];
163
166
  private _resizeDebounce: FlickingOptions["resizeDebounce"];
164
167
  private _maxResizeDebounce: FlickingOptions["maxResizeDebounce"];
168
+ private _useFractionalSize: FlickingOptions["useFractionalSize"];
165
169
  private _externalRenderer: FlickingOptions["externalRenderer"];
166
170
  private _renderExternal: FlickingOptions["renderExternal"];
167
171
 
@@ -570,6 +574,15 @@ class Flicking extends Component<FlickingEvents> {
570
574
  * @default false
571
575
  */
572
576
  public get disableOnInit() { return this._disableOnInit; }
577
+ /**
578
+ * Change active panel index on mouse/touch hold while animating.
579
+ * `index` of the `willChange`/`willRestore` event will be used as new index.
580
+ * @ko 애니메이션 도중 마우스/터치 입력시 현재 활성화된 패널의 인덱스를 변경합니다.
581
+ * `willChange`/`willRestore` 이벤트의 `index`값이 새로운 인덱스로 사용될 것입니다.
582
+ * @type {boolean}
583
+ * @default false
584
+ */
585
+ public get changeOnHold() { return this._changeOnHold; }
573
586
  // PERFORMANCE
574
587
  /**
575
588
  * Whether to render visible panels only. This can dramatically increase performance when there're many panels
@@ -654,6 +667,17 @@ class Flicking extends Component<FlickingEvents> {
654
667
  * @default 100
655
668
  */
656
669
  public get maxResizeDebounce() { return this._maxResizeDebounce; }
670
+ /**
671
+ * By enabling this, Flicking will calculate all internal size with CSS width computed with getComputedStyle.
672
+ * This can prevent 1px offset issue in some cases where panel size has the fractional part.
673
+ * All sizes will have the original size before CSS {@link https://developer.mozilla.org/en-US/docs/Web/CSS/transform transform} is applied on the element.
674
+ * @ko 이 옵션을 활성화할 경우, Flicking은 내부의 모든 크기를 {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect getBoundingClientRect}를 이용하여 계산합니다.
675
+ * 이를 통해, 패널 크기에 소수점을 포함할 경우에 발생할 수 있는 일부 1px 오프셋 이슈를 해결 가능합니다.
676
+ * 모든 크기는 CSS {@link https://developer.mozilla.org/en-US/docs/Web/CSS/transform transform}이 엘리먼트에 적용되기 이전의 크기를 사용할 것입니다.
677
+ * @type {boolean}
678
+ * @default false
679
+ */
680
+ public get useFractionalSize() { return this._useFractionalSize; }
657
681
  /**
658
682
  * This is an option for the frameworks(React, Vue, Angular, ...). Don't set it as it's automatically managed by Flicking.
659
683
  * @ko 프레임워크(React, Vue, Angular, ...)에서만 사용하는 옵션으로, 자동으로 설정되므로 따로 사용하실 필요 없습니다!
@@ -720,6 +744,7 @@ class Flicking extends Component<FlickingEvents> {
720
744
  }
721
745
 
722
746
  public set disableOnInit(val: FlickingOptions["disableOnInit"]) { this._disableOnInit = val; }
747
+ public set changeOnHold(val: FlickingOptions["changeOnHold"]) { this._changeOnHold = val; }
723
748
  // PERFORMANCE
724
749
  public set renderOnlyVisible(val: FlickingOptions["renderOnlyVisible"]) { this._renderOnlyVisible = val; }
725
750
  // OTHERS
@@ -794,6 +819,7 @@ class Flicking extends Component<FlickingEvents> {
794
819
  iOSEdgeSwipeThreshold = 30,
795
820
  preventClickOnDrag = true,
796
821
  disableOnInit = false,
822
+ changeOnHold = false,
797
823
  renderOnlyVisible = false,
798
824
  virtual = null,
799
825
  autoInit = true,
@@ -801,6 +827,7 @@ class Flicking extends Component<FlickingEvents> {
801
827
  useResizeObserver = true,
802
828
  resizeDebounce = 0,
803
829
  maxResizeDebounce = 100,
830
+ useFractionalSize = false,
804
831
  externalRenderer = null,
805
832
  renderExternal = null
806
833
  }: Partial<FlickingOptions> = {}) {
@@ -836,17 +863,19 @@ class Flicking extends Component<FlickingEvents> {
836
863
  this._iOSEdgeSwipeThreshold = iOSEdgeSwipeThreshold;
837
864
  this._preventClickOnDrag = preventClickOnDrag;
838
865
  this._disableOnInit = disableOnInit;
866
+ this._changeOnHold = changeOnHold;
839
867
  this._renderOnlyVisible = renderOnlyVisible;
840
868
  this._autoInit = autoInit;
841
869
  this._autoResize = autoResize;
842
870
  this._useResizeObserver = useResizeObserver;
843
871
  this._resizeDebounce = resizeDebounce;
844
872
  this._maxResizeDebounce = maxResizeDebounce;
873
+ this._useFractionalSize = useFractionalSize;
845
874
  this._externalRenderer = externalRenderer;
846
875
  this._renderExternal = renderExternal;
847
876
 
848
877
  // Create core components
849
- this._viewport = new Viewport(getElement(root));
878
+ this._viewport = new Viewport(this, getElement(root));
850
879
  this._autoResizer = new AutoResizer(this);
851
880
  this._renderer = this._createRenderer();
852
881
  this._camera = this._createCamera();
@@ -1275,6 +1304,8 @@ class Flicking extends Component<FlickingEvents> {
1275
1304
  camera.updateAlignPos();
1276
1305
  camera.updateRange();
1277
1306
  camera.updateAnchors();
1307
+ camera.updateAdaptiveHeight();
1308
+ camera.updateOffset();
1278
1309
  await renderer.render();
1279
1310
 
1280
1311
  if (control.animating) {
@@ -368,7 +368,7 @@ class AxesController {
368
368
  };
369
369
 
370
370
  private _onAxesChange = () => {
371
- this._dragged = true;
371
+ this._dragged = !!this._panInput?.isEnabled();
372
372
  };
373
373
 
374
374
  private _preventClickWhenDragged = (e: MouseEvent) => {
@@ -89,7 +89,7 @@ class SnapControl extends Control {
89
89
  const camera = flicking.camera;
90
90
  const activeAnchor = camera.findActiveAnchor();
91
91
  const anchorAtCamera = camera.findNearestAnchor(camera.position);
92
- const state = flicking.control.controller.state;
92
+ const state = this._controller.state;
93
93
 
94
94
  if (!activeAnchor || !anchorAtCamera) {
95
95
  return Promise.reject(new FlickingError(ERROR.MESSAGE.POSITION_NOT_REACHABLE(position), ERROR.CODE.POSITION_NOT_REACHABLE));
@@ -111,10 +111,13 @@ class SnapControl extends Control {
111
111
  targetAnchor = this._findSnappedAnchor(position, anchorAtCamera);
112
112
  } else if (absPosDelta >= flicking.threshold && absPosDelta > 0) {
113
113
  // Move to the adjacent panel
114
- targetAnchor = this._findAdjacentAnchor(posDelta, anchorAtCamera);
114
+ targetAnchor = this._findAdjacentAnchor(position, posDelta, anchorAtCamera);
115
115
  } else {
116
116
  // Restore to active panel
117
- targetAnchor = anchorAtCamera;
117
+ return this.moveToPanel(activeAnchor.panel, {
118
+ duration,
119
+ axesEvent
120
+ });
118
121
  }
119
122
 
120
123
  this._triggerIndexChangeEvent(targetAnchor.panel, position, axesEvent);
@@ -190,9 +193,18 @@ class SnapControl extends Control {
190
193
  }
191
194
  }
192
195
 
193
- private _findAdjacentAnchor(posDelta: number, anchorAtCamera: AnchorPoint): AnchorPoint {
196
+ private _findAdjacentAnchor(position: number, posDelta: number, anchorAtCamera: AnchorPoint): AnchorPoint {
194
197
  const flicking = getFlickingAttached(this._flicking);
195
198
  const camera = flicking.camera;
199
+
200
+ if (camera.circularEnabled) {
201
+ const anchorIncludePosition = camera.findAnchorIncludePosition(position);
202
+
203
+ if (anchorIncludePosition && anchorIncludePosition.position !== anchorAtCamera.position) {
204
+ return anchorIncludePosition;
205
+ }
206
+ }
207
+
196
208
  const adjacentAnchor = (posDelta > 0 ? camera.getNextAnchor(anchorAtCamera) : camera.getPrevAnchor(anchorAtCamera)) ?? anchorAtCamera;
197
209
 
198
210
  return adjacentAnchor;
@@ -194,6 +194,7 @@ class StrictControl extends Control {
194
194
  const axesRange = this._controller.range;
195
195
  const indexRange = this._indexRange;
196
196
  const cameraRange = camera.range;
197
+ const state = this._controller.state;
197
198
 
198
199
  const clampedPosition = clamp(camera.clampToReachablePosition(position), axesRange[0], axesRange[1]);
199
200
  const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
@@ -203,8 +204,11 @@ class StrictControl extends Control {
203
204
  }
204
205
 
205
206
  const prevPos = activePanel.position;
207
+ const posDelta = flicking.animating
208
+ ? state.delta
209
+ : position - camera.position;
206
210
 
207
- const isOverThreshold = Math.abs(position - prevPos) >= flicking.threshold;
211
+ const isOverThreshold = Math.abs(posDelta) >= flicking.threshold;
208
212
  const adjacentAnchor = (position > prevPos)
209
213
  ? camera.getNextAnchor(anchorAtPosition)
210
214
  : camera.getPrevAnchor(anchorAtPosition);
@@ -32,10 +32,16 @@ class AnimatingState extends State {
32
32
 
33
33
  public onHold(ctx: Parameters<State["onHold"]>[0]): void {
34
34
  const { flicking, axesEvent, transitTo } = ctx;
35
+ const targetPanel = this._targetPanel;
36
+ const control = flicking.control;
35
37
 
36
38
  this._delta = 0;
37
39
  flicking.control.updateInput();
38
40
 
41
+ if (flicking.changeOnHold && targetPanel) {
42
+ control.setActive(targetPanel, control.activePanel, axesEvent.isTrusted);
43
+ }
44
+
39
45
  const holdStartEvent = new ComponentEvent(EVENTS.HOLD_START, { axesEvent });
40
46
  flicking.trigger(holdStartEvent);
41
47
 
@@ -10,7 +10,7 @@ import { setPrototypeOf } from "../utils";
10
10
  * @ko Flicking 내부에서 알려진 오류 발생시 throw되는 에러
11
11
  * @property {number} code Error code<ko>에러 코드</ko>
12
12
  * @property {string} message Error message<ko>에러 메시지</ko>
13
- * @see {@link Constants.ERROR_CODE ERROR_CODE}
13
+ * @see {@link ERROR_CODE ERROR_CODE}
14
14
  * @example
15
15
  * ```ts
16
16
  * import Flicking, { FlickingError, ERROR_CODES } from "@egjs/flicking";
@@ -2,13 +2,15 @@
2
2
  * Copyright (c) 2015 NAVER Corp.
3
3
  * egjs projects are licensed under the MIT license
4
4
  */
5
- import { getStyle, isString } from "../utils";
5
+ import Flicking from "../Flicking";
6
+ import { getElementSize, getStyle, isString } from "../utils";
6
7
 
7
8
  /**
8
9
  * A component that manages viewport size
9
10
  * @ko 뷰포트 크기 정보를 담당하는 컴포넌트
10
11
  */
11
12
  class Viewport {
13
+ private _flicking: Flicking;
12
14
  private _el: HTMLElement;
13
15
  private _width: number;
14
16
  private _height: number;
@@ -57,7 +59,8 @@ class Viewport {
57
59
  /**
58
60
  * @param el A viewport element<ko>뷰포트 엘리먼트</ko>
59
61
  */
60
- public constructor(el: HTMLElement) {
62
+ public constructor(flicking: Flicking, el: HTMLElement) {
63
+ this._flicking = flicking;
61
64
  this._el = el;
62
65
  this._width = 0;
63
66
  this._height = 0;
@@ -120,9 +123,25 @@ class Viewport {
120
123
  public resize() {
121
124
  const el = this._el;
122
125
  const elStyle = getStyle(el);
126
+ const {
127
+ useFractionalSize
128
+ } = this._flicking;
129
+
130
+ this._width = getElementSize({
131
+ el,
132
+ horizontal: true,
133
+ useFractionalSize,
134
+ useOffset: false,
135
+ style: elStyle
136
+ });
137
+ this._height = getElementSize({
138
+ el,
139
+ horizontal: false,
140
+ useFractionalSize,
141
+ useOffset: false,
142
+ style: elStyle
143
+ });
123
144
 
124
- this._width = el.clientWidth;
125
- this._height = el.clientHeight;
126
145
  this._padding = {
127
146
  left: elStyle.paddingLeft ? parseFloat(elStyle.paddingLeft) : 0,
128
147
  right: elStyle.paddingRight ? parseFloat(elStyle.paddingRight) : 0,
@@ -3,7 +3,7 @@
3
3
  * egjs projects are licensed under the MIT license
4
4
  */
5
5
  import Flicking from "../../Flicking";
6
- import { getProgress, getStyle, parseAlign, setSize } from "../../utils";
6
+ import { getElementSize, getProgress, getStyle, parseAlign, setSize } from "../../utils";
7
7
  import { ALIGN, DIRECTION } from "../../const/external";
8
8
  import { LiteralUnion, ValueOf } from "../../type/internal";
9
9
 
@@ -313,7 +313,10 @@ class Panel {
313
313
  }): this {
314
314
  const el = this.element;
315
315
  const flicking = this._flicking;
316
- const horizontal = flicking.horizontal;
316
+ const {
317
+ horizontal,
318
+ useFractionalSize
319
+ } = flicking;
317
320
 
318
321
  if (cached) {
319
322
  this._size = cached.size;
@@ -322,7 +325,14 @@ class Panel {
322
325
  } else {
323
326
  const elStyle = getStyle(el);
324
327
 
325
- this._size = horizontal ? el.offsetWidth : el.offsetHeight;
328
+ this._size = getElementSize({
329
+ el,
330
+ horizontal,
331
+ useFractionalSize,
332
+ useOffset: true,
333
+ style: elStyle
334
+ });
335
+
326
336
  this._margin = horizontal
327
337
  ? {
328
338
  prev: parseFloat(elStyle.marginLeft || "0"),
@@ -331,7 +341,16 @@ class Panel {
331
341
  prev: parseFloat(elStyle.marginTop || "0"),
332
342
  next: parseFloat(elStyle.marginBottom || "0")
333
343
  };
334
- this._height = horizontal ? el.offsetHeight : this._size;
344
+
345
+ this._height = horizontal
346
+ ? getElementSize({
347
+ el,
348
+ horizontal: false,
349
+ useFractionalSize,
350
+ useOffset: true,
351
+ style: elStyle
352
+ })
353
+ : this._size;
335
354
  }
336
355
 
337
356
  this.updatePosition();
package/src/utils.ts CHANGED
@@ -300,6 +300,48 @@ export const range = (end: number): number[] => {
300
300
  return arr;
301
301
  };
302
302
 
303
+ export const getElementSize = ({
304
+ el,
305
+ horizontal,
306
+ useFractionalSize,
307
+ useOffset,
308
+ style
309
+ }: {
310
+ el: HTMLElement;
311
+ horizontal: boolean;
312
+ useFractionalSize: boolean;
313
+ useOffset: boolean;
314
+ style: CSSStyleDeclaration;
315
+ }): number => {
316
+ if (useFractionalSize) {
317
+ const baseSize = parseFloat(horizontal ? style.width : style.height);
318
+ const isBorderBoxSizing = style.boxSizing === "border-box";
319
+ const border = horizontal
320
+ ? parseFloat(style.borderLeftWidth || "0") + parseFloat(style.borderRightWidth || "0")
321
+ : parseFloat(style.borderTopWidth || "0") + parseFloat(style.borderBottomWidth || "0");
322
+
323
+ if (isBorderBoxSizing) {
324
+ return useOffset
325
+ ? baseSize
326
+ : baseSize - border;
327
+ } else {
328
+ const padding = horizontal
329
+ ? parseFloat(style.paddingLeft || "0") + parseFloat(style.paddingRight || "0")
330
+ : parseFloat(style.paddingTop || "0") + parseFloat(style.paddingBottom || "0");
331
+
332
+ return useOffset
333
+ ? baseSize + padding + border
334
+ : baseSize + padding;
335
+ }
336
+ } else {
337
+ const sizeStr = horizontal ? "Width" : "Height";
338
+
339
+ return useOffset
340
+ ? el[`offset${sizeStr}`]
341
+ : el[`client${sizeStr}`];
342
+ }
343
+ };
344
+
303
345
  export const setPrototypeOf = Object.setPrototypeOf || ((obj, proto) => {
304
346
  obj.__proto__ = proto;
305
347
  return obj;