• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

naver / egjs-flicking / 10557177632

26 Aug 2024 09:22AM UTC coverage: 38.327% (-44.5%) from 82.855%
10557177632

Pull #886

github

daybrush
fix: recalculate camera offset
Pull Request #886: fix: recalculate camera offset

2039 of 7372 branches covered (27.66%)

Branch coverage included in aggregate %.

11 of 29 new or added lines in 2 files covered. (37.93%)

5575 existing lines in 46 files now uncovered.

5099 of 11252 relevant lines covered (45.32%)

10.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

23.31
/src/control/StrictControl.ts
1
/*
1✔
2
 * Copyright (c) 2015 NAVER Corp.
1✔
3
 * egjs projects are licensed under the MIT license
1✔
4
 */
1!
5
import { OnRelease } from "@egjs/axes";
6

×
7
import Panel from "../core/panel/Panel";
1✔
8
import FlickingError from "../core/FlickingError";
5✔
9
import { clamp, getFlickingAttached, getMinusCompensatedIndex, isBetween } from "../utils";
6✔
10
import * as ERROR from "../const/error";
6!
11

12
import Control from "./Control";
6✔
13
/**
1✔
14
 * An options for the {@link StrictControl}
1!
15
 * @ko {@link StrictControl} 생성시 사용되는 옵션
16
 * @interface
17
 * @property {number} count Maximum number of panels that can be moved at a time<ko>최대로 움직일 수 있는 패널의 개수</ko>
1!
18
 */
×
19
export interface StrictControlOptions {
20
  count: number;
21
}
×
22

23
/**
24
 * A {@link Control} that allow you to select the maximum number of panels to move at a time
1!
25
 * @ko 한번에 최대로 이동할 패널의 개수를 선택 가능한 {@link Control}
26
 */
27
class StrictControl extends Control {
5✔
28
  private _count: number;
29
  private _indexRange: { min: number; max: number };
1✔
30

1!
31
  /**
32
   * Maximum number of panels that can be moved at a time
×
33
   * @ko 최대로 움직일 수 있는 패널의 개수
34
   * @type {number}
35
   * @default 1
36
   */
1✔
37
  public get count() { return this._count; }
5!
38

×
39
  public set count(val: StrictControlOptions["count"]) { this._count = val; }
×
40

41
  /** */
×
UNCOV
42
  public constructor({
×
43
    count = 1
×
44
  }: Partial<StrictControlOptions> = {}) {
45
    super();
1!
46

×
UNCOV
47
    this._count = count;
×
UNCOV
48
    this._resetIndexRange();
×
49
  }
50

×
51
  /**
52
   * Destroy Control and return to initial state
×
53
   * @ko Control을 초기 상태로 되돌립니다
×
54
   * @return {void}
×
55
   */
56
  public destroy() {
5✔
57
    super.destroy();
×
58

59
    this._resetIndexRange();
60
  }
×
61

×
62
  /**
×
63
   * Update {@link Control#controller controller}'s state
×
64
   * @ko {@link Control#controller controller}의 내부 상태를 갱신합니다
×
65
   * @chainable
66
   * @return {this}
67
   */
68
  public updateInput(): this {
5✔
UNCOV
69
    const flicking = getFlickingAttached(this._flicking);
×
70
    const camera = flicking.camera;
71
    const renderer = flicking.renderer;
72
    const controller = this._controller;
1✔
73
    const controlParams = camera.controlParams;
2!
74
    const count = this._count;
75

1✔
76
    const activePanel = controller.state.animating
1!
77
      ? camera.findNearestAnchor(camera.position)?.panel
1!
78
      : this._activePanel;
1✔
79

1✔
80
    if (!activePanel) {
×
81
      controller.update(controlParams);
82
      this._resetIndexRange();
83
      return this;
84
    }
1✔
85

1✔
86
    const cameraRange = controlParams.range;
87
    const currentPos = activePanel.position;
UNCOV
88
    const currentIndex = activePanel.index;
×
UNCOV
89
    const panelCount = renderer.panelCount;
×
90

UNCOV
91
    let prevPanelIndex = currentIndex - count;
×
UNCOV
92
    let nextPanelIndex = currentIndex + count;
×
93

UNCOV
94
    if (prevPanelIndex < 0) {
×
UNCOV
95
      prevPanelIndex = flicking.circularEnabled
×
96
        ? getMinusCompensatedIndex((prevPanelIndex + 1) % panelCount - 1, panelCount)
97
        : clamp(prevPanelIndex, 0, panelCount - 1);
98
    }
1✔
99
    if (nextPanelIndex >= panelCount) {
×
100
      nextPanelIndex = flicking.circularEnabled
×
101
        ? nextPanelIndex % panelCount
102
        : clamp(nextPanelIndex, 0, panelCount - 1);
103
    }
104

105
    const prevPanel = renderer.panels[prevPanelIndex];
×
106
    const nextPanel = renderer.panels[nextPanelIndex];
×
107

108
    let prevPos = Math.max(prevPanel.position, cameraRange.min);
109
    let nextPos = Math.min(nextPanel.position, cameraRange.max);
110

111
    if (prevPos > currentPos) {
×
112
      prevPos -= camera.rangeDiff;
113
    }
114
    if (nextPos < currentPos) {
×
115
      nextPos += camera.rangeDiff;
1✔
116
    }
117

118
    controlParams.range = {
119
      min: prevPos,
120
      max: nextPos
121
    };
122

123
    if (controlParams.circular) {
×
124
      if (controlParams.position < prevPos) {
×
125
        controlParams.position += camera.rangeDiff;
1✔
126
      }
127

UNCOV
128
      if (controlParams.position > nextPos) {
×
UNCOV
129
        controlParams.position -= camera.rangeDiff;
×
130
      }
131
    }
132

UNCOV
133
    controlParams.circular = false;
×
134
    controller.update(controlParams);
×
135

UNCOV
136
    this._indexRange = {
×
137
      min: prevPanel.index,
138
      max: nextPanel.index
139
    };
140

UNCOV
141
    return this;
×
142
  }
143

144
  public async moveToPanel(panel: Panel, options: Parameters<Control["moveToPanel"]>[1]): Promise<void> {
5✔
UNCOV
145
    const flicking = getFlickingAttached(this._flicking);
×
UNCOV
146
    const camera = flicking.camera;
×
UNCOV
147
    const controller = this._controller;
×
148

×
149
    controller.update(camera.controlParams);
150

151
    return super.moveToPanel(panel, options);
152
  }
×
153

×
154
  /**
155
   * Move {@link Camera} to the given position
156
   * @ko {@link Camera}를 주어진 좌표로 이동합니다
157
   * @param {number} position The target position to move<ko>이동할 좌표</ko>
158
   * @param {number} duration Duration of the panel movement animation (unit: ms).<ko>패널 이동 애니메이션 진행 시간 (단위: ms)</ko>
159
   * @param {object} [axesEvent] {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} event of {@link https://naver.github.io/egjs-axes/ Axes}
160
   * <ko>{@link https://naver.github.io/egjs-axes/ Axes}의 {@link https://naver.github.io/egjs-axes/release/latest/doc/eg.Axes.html#event:release release} 이벤트</ko>
161
   * @fires Flicking#moveStart
×
162
   * @fires Flicking#move
163
   * @fires Flicking#moveEnd
164
   * @fires Flicking#willChange
×
165
   * @fires Flicking#changed
166
   * @fires Flicking#willRestore
167
   * @fires Flicking#restored
168
   * @fires Flicking#needPanel
169
   * @fires Flicking#visibleChange
170
   * @fires Flicking#reachEdge
171
   * @throws {FlickingError}
×
172
   * |code|condition|
×
173
   * |---|---|
174
   * |{@link ERROR_CODE POSITION_NOT_REACHABLE}|When the given panel is already removed or not in the Camera's {@link Camera#range range}|
175
   * |{@link ERROR_CODE NOT_ATTACHED_TO_FLICKING}|When {@link Control#init init} is not called before|
×
176
   * |{@link ERROR_CODE ANIMATION_INTERRUPTED}|When the animation is interrupted by user input|
177
   * |{@link ERROR_CODE STOP_CALLED_BY_USER}|When the animation is interrupted by user input|
178
   * <ko>
179
   *
180
   * |code|condition|
181
   * |---|---|
182
   * |{@link ERROR_CODE POSITION_NOT_REACHABLE}|주어진 패널이 제거되었거나, Camera의 {@link Camera#range range} 밖에 있을 경우|
183
   * |{@link ERROR_CODE NOT_ATTACHED_TO_FLICKING}|{@link Control#init init}이 이전에 호출되지 않은 경우|
184
   * |{@link ERROR_CODE ANIMATION_INTERRUPTED}|사용자 입력에 의해 애니메이션이 중단된 경우|
185
   * |{@link ERROR_CODE STOP_CALLED_BY_USER}|발생된 이벤트들 중 하나라도 `stop()`이 호출된 경우|
186
   *
187
   * </ko>
1✔
188
   * @return {Promise<void>} A Promise which will be resolved after reaching the target position<ko>해당 좌표 도달시에 resolve되는 Promise</ko>
189
   */
190
  public moveToPosition(position: number, duration: number, axesEvent?: OnRelease) {
5✔
UNCOV
191
    const flicking = getFlickingAttached(this._flicking);
×
UNCOV
192
    const camera = flicking.camera;
×
UNCOV
193
    const currentPanel = this._nextPanel ?? this._activePanel;
×
UNCOV
194
    const axesRange = this._controller.range;
×
UNCOV
195
    const indexRange = this._indexRange;
×
196
    const cameraRange = camera.range;
197
    const state = this._controller.state;
198

199
    const clampedPosition = clamp(camera.clampToReachablePosition(position), axesRange[0], axesRange[1]);
200
    const anchorAtPosition = camera.findAnchorIncludePosition(clampedPosition);
201

202
    if (!anchorAtPosition || !currentPanel) {
×
203
      return Promise.reject(new FlickingError(ERROR.MESSAGE.POSITION_NOT_REACHABLE(position), ERROR.CODE.POSITION_NOT_REACHABLE));
204
    }
205

206
    const prevPos = currentPanel.position;
207
    const posDelta = flicking.animating
×
208
      ? state.delta
209
      : position - camera.position;
210

211
    const isOverThreshold = Math.abs(posDelta) >= flicking.threshold;
212
    const adjacentAnchor = (position > prevPos)
×
213
      ? camera.getNextAnchor(anchorAtPosition)
214
      : camera.getPrevAnchor(anchorAtPosition);
215

216
    let targetPos: number;
217
    let targetPanel: Panel;
218

219
    const anchors = camera.anchorPoints;
220
    const firstAnchor = anchors[0];
221
    const lastAnchor = anchors[anchors.length - 1];
222

223
    const shouldBounceToFirst = position <= cameraRange.min && isBetween(firstAnchor.panel.index, indexRange.min, indexRange.max);
×
224
    const shouldBounceToLast = position >= cameraRange.max && isBetween(lastAnchor.panel.index, indexRange.min, indexRange.max);
×
225

226
    const isAdjacent = adjacentAnchor && (indexRange.min <= indexRange.max
×
227
      ? isBetween(adjacentAnchor.index, indexRange.min, indexRange.max)
228
      : adjacentAnchor.index >= indexRange.min || adjacentAnchor.index <= indexRange.max);
×
229

230
    if (shouldBounceToFirst || shouldBounceToLast) {
×
231
      // In bounce area
232
      const targetAnchor = position < cameraRange.min ? firstAnchor : lastAnchor;
×
233

234
      targetPanel = targetAnchor.panel;
235
      targetPos = targetAnchor.position;
1✔
236
    } else if (isOverThreshold && anchorAtPosition.position !== currentPanel.position) {
×
237
      // Move to anchor at position
UNCOV
238
      targetPanel = anchorAtPosition.panel;
×
UNCOV
239
      targetPos = anchorAtPosition.position;
×
UNCOV
240
    } else if (isOverThreshold && isAdjacent) {
×
241
      // Move to adjacent anchor
UNCOV
242
      targetPanel = adjacentAnchor!.panel;
×
UNCOV
243
      targetPos = adjacentAnchor!.position;
×
244
    } else {
245
      // Fallback to nearest panel from current camera
UNCOV
246
      const anchorAtCamera = camera.findNearestAnchor(camera.position);
×
UNCOV
247
      if (!anchorAtCamera) {
×
248
        return Promise.reject(new FlickingError(ERROR.MESSAGE.POSITION_NOT_REACHABLE(position), ERROR.CODE.POSITION_NOT_REACHABLE));
249
      }
UNCOV
250
      return this.moveToPanel(anchorAtCamera.panel, {
×
251
        duration,
252
        axesEvent
253
      });
254
    }
×
255

256
    this._triggerIndexChangeEvent(targetPanel, position, axesEvent);
257

258
    return this._animateToPosition({
259
      position: targetPos,
260
      duration,
261
      newActivePanel: targetPanel,
262
      axesEvent
×
263
    });
×
264
  }
×
265

266
  public setActive = (newActivePanel: Panel, prevActivePanel: Panel | null, isTrusted: boolean) => {
×
UNCOV
267
    super.setActive(newActivePanel, prevActivePanel, isTrusted);
×
268
    this.updateInput();
269
  };
×
270

271
  private _resetIndexRange() {
5✔
272
    this._indexRange = { min: 0, max: 0 };
273
  }
×
274
}
5✔
275

276
export default StrictControl;
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc