Skip to content

Commit 066a71e

Browse files
authored
fix: fix animation offset for deciaml point (naver#58)
* fix: fix animation offset for deciaml point * fix: fix lint
1 parent 08f76fd commit 066a71e

File tree

5 files changed

+84
-25
lines changed

5 files changed

+84
-25
lines changed

packages/conveyer/src/Conveyer.ts

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
ConveyerOptions, FindItemOptions, ConveyerReactiveState,
1414
ScrollIntoViewOptions,
1515
} from "./types";
16-
import { instanceOfElement, isString } from "./utils";
16+
import { instanceOfElement, isIntersection, isString } from "./utils";
1717

1818

1919
/**
@@ -45,6 +45,7 @@ class Conveyer extends Component<ConveyerEvents> {
4545
protected _options: ConveyerOptions;
4646
protected _animateParam: {
4747
expectedPos: number;
48+
offset: number;
4849
} | null = null;
4950

5051
private _resizeObserver: ResizeObserver | null = null;
@@ -54,6 +55,9 @@ class Conveyer extends Component<ConveyerEvents> {
5455
private _isAnimationScroll = false;
5556
private _scrollArea: string | HTMLElement | Ref<HTMLElement>;
5657

58+
private _panInput: PanInput | null = null;
59+
private _wheelInput: WheelInput | null = null;
60+
5761
/**
5862
* Whether the scroll has reached the start.
5963
* @ko 스크롤이 시작에 닿았는지 여부.
@@ -193,7 +197,7 @@ class Conveyer extends Component<ConveyerEvents> {
193197
const dist2 = dist + itemSize;
194198

195199
return (dist >= 0)
196-
|| (dist2 > 0 && intersection)
200+
|| (dist2 > 0 && isIntersection(dist, dist2, "end", intersection))
197201
|| (dist2 >= 0 && (!itemSize || Math.abs(dist2) / itemSize >= hitTest));
198202
});
199203

@@ -208,7 +212,7 @@ class Conveyer extends Component<ConveyerEvents> {
208212
const dist2 = dist - itemSize;
209213

210214
return dist <= 0
211-
|| (dist2 < 0 && intersection)
215+
|| (dist2 < 0 && isIntersection(dist2, dist, "start", intersection))
212216
|| (dist2 <= 0 && (!itemSize || Math.abs(dist2) / itemSize >= hitTest));
213217
}).reverse();
214218

@@ -220,19 +224,19 @@ class Conveyer extends Component<ConveyerEvents> {
220224
const dist2 = dist - itemSize;
221225

222226
return dist <= 0
223-
|| (dist2 < 0 && intersection)
227+
|| (dist2 < 0 && isIntersection(dist2, dist, "start", intersection))
224228
|| (dist2 <= 0 && (!itemSize || Math.abs(dist2) / itemSize >= hitTest));
225-
}).reverse()[0];
229+
}).reverse()[0] || startVirtualItem;
226230
} else if (target === "next") {
227231
selectedItem = items.filter(item => {
228232
const itemSize = item.size;
229233
const dist = item.pos - endPos;
230234
const dist2 = dist + itemSize;
231235

232236
return dist >= 0
233-
|| (dist2 > 0 && intersection)
237+
|| (dist2 > 0 && isIntersection(dist, dist2, "end", intersection))
234238
|| (dist2 >= 0 && (!itemSize || Math.abs(dist2) / itemSize >= hitTest));
235-
})[0];
239+
})[0] || endVirtualItem;
236240
} else {
237241
return this._getItem(target);
238242
}
@@ -292,7 +296,7 @@ class Conveyer extends Component<ConveyerEvents> {
292296
* @param - Duration to scroll by that position. <ko>해당 위치만큼 스크롤하는 시간</ko>
293297
*/
294298
public scrollBy(pos: number, duration = 0) {
295-
this._createAnimationParam();
299+
this._createAnimationParam(pos);
296300
this._axes!.setBy({ scroll: -pos }, duration);
297301
}
298302
/**
@@ -302,8 +306,7 @@ class Conveyer extends Component<ConveyerEvents> {
302306
* @param - Duration to scroll to that position. <ko>해당 위치로 스크롤하는 시간</ko>
303307
*/
304308
public scrollTo(pos: number, duration = 0) {
305-
this._createAnimationParam();
306-
this._axes!.setBy({ scroll: this._pos - pos }, duration);
309+
this.scrollBy(pos - this._pos, duration);
307310
}
308311
/**
309312
* Set the items directly to the Conveyer.
@@ -328,7 +331,7 @@ class Conveyer extends Component<ConveyerEvents> {
328331
);
329332
this.setItems(itemElements.map((el) => this._getItem(el)));
330333

331-
if (resizeObserver){
334+
if (resizeObserver) {
332335
const changed = diff(prevItemElements, itemElements);
333336
const removed = changed.removed;
334337
const added = changed.added;
@@ -378,6 +381,22 @@ class Conveyer extends Component<ConveyerEvents> {
378381
this.updateItems();
379382
this.updateContainer();
380383
}
384+
/**
385+
* Enables PanInput and WheelInput operations in mouse case.
386+
* @ko mouse 케이스에서 PanInput, WheelInput의 동작을 활성화한다.
387+
*/
388+
public enableInput() {
389+
this._panInput?.enable();
390+
this._wheelInput?.enable();
391+
}
392+
/**
393+
* Disables PanInput and WheelInput operations in mouse case.
394+
* @ko mouse 케이스에서 PanInput, WheelInput의 동작을 비활성화한다.
395+
*/
396+
public disableInput() {
397+
this._panInput?.disable();
398+
this._wheelInput?.disable();
399+
}
381400
/**
382401
* If you use the autoInit option as false, you can initialize it directly through the init method.
383402
* @ko autoInit 옵션을 false로 사용하는 경우 직접 init 메서드를 통해 초기화 할 수 있다.
@@ -446,9 +465,9 @@ class Conveyer extends Component<ConveyerEvents> {
446465
if (!e.isTrusted && animateParam) {
447466
animateParam.expectedPos -= scroll;
448467
if (options.horizontal) {
449-
scrollAreaElement.scrollLeft = animateParam.expectedPos;
468+
scrollAreaElement.scrollLeft = animateParam.expectedPos + animateParam.offset;
450469
} else {
451-
scrollAreaElement.scrollTop = animateParam.expectedPos;
470+
scrollAreaElement.scrollTop = animateParam.expectedPos + animateParam.offset;
452471
}
453472
} else {
454473
this._animateParam = null;
@@ -473,17 +492,19 @@ class Conveyer extends Component<ConveyerEvents> {
473492

474493
this._axes = axes;
475494
if (options.useDrag) {
476-
axes.connect(options.horizontal ? ["scroll", ""] : ["", "scroll"], new PanInput(scrollAreaElement, {
495+
this._panInput = new PanInput(scrollAreaElement, {
477496
preventClickOnDrag: options.preventClickOnDrag,
478497
preventDefaultOnDrag: options.preventDefaultOnDrag,
479498
inputType: ["mouse"],
480499
touchAction: "auto",
481-
}));
500+
});
501+
axes.connect(options.horizontal ? ["scroll", ""] : ["", "scroll"], this._panInput);
482502
}
483503
if (options.useSideWheel) {
484-
axes.connect(options.horizontal ? ["scroll", ""] : ["", "scroll"], new WheelInput(scrollAreaElement, {
504+
this._wheelInput = new WheelInput(scrollAreaElement, {
485505
useNormalized: false,
486-
}));
506+
});
507+
axes.connect(options.horizontal ? ["scroll", ""] : ["", "scroll"], this._wheelInput);
487508
}
488509
if (options.useResizeObserver && window.ResizeObserver) {
489510
this._resizeObserver = new ResizeObserver((entries: ResizeObserverEntry[]) => {
@@ -530,6 +551,8 @@ class Conveyer extends Component<ConveyerEvents> {
530551
window.removeEventListener("resize", this.update);
531552
}
532553
this.off();
554+
this._panInput = null;
555+
this._wheelInput = null;
533556
this._axes = null;
534557
this._resizeObserver = null;
535558
}
@@ -663,9 +686,12 @@ class Conveyer extends Component<ConveyerEvents> {
663686
}, this._options.scrollDebounce);
664687
}
665688

666-
private _createAnimationParam() {
689+
private _createAnimationParam(pos: number) {
690+
// Save a decimal point before starting the animation
691+
// and in case of animation (isTrusted: false), add the offset and scroll.
667692
this._animateParam = {
668693
expectedPos: this._pos,
694+
offset: pos % 1,
669695
};
670696
}
671697
}

packages/conveyer/src/consts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export const CONVEYER_METHODS = [
1414
"updateContainer",
1515
"updateItems",
1616
"init",
17+
"enableInput",
18+
"disableInput",
1719
] as const;
1820

1921
export const CONVEYER_EVENTS = [

packages/conveyer/src/types.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,13 @@ export interface FindItemOptions {
122122
*/
123123
hitTest?: number;
124124
/**
125-
* Whether to include items that intersect on the side
126-
* <ko>사이드에 교차하는 아이템까지 포함할지 여부.</ko>
125+
* A number indicating how much the items intersect on the side. (true is 1)
126+
* * If 0, there is no intersect. The closer it is to 1, the greater the intersect distance.
127+
* <ko>사이드에서 아이템이 얼마나 교차되는지 수치. (true의 경우 1)
128+
* 0인 경우 교차를 하지 않는다. 1에 가까울수록 교차거리가 늘어난다.</ko>
127129
* @default 0
128130
*/
129-
intersection?: boolean;
131+
intersection?: boolean | number;
130132
/**
131133
* The number of items next to the item's index to return.
132134
* <ko>해당 아이템의 index에서 얼마나 옆에 있는 아이템을 반환할지 개수.</ko>

packages/conveyer/src/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,27 @@ export function findIndex<T>(arr: T[], callback: (element: T, index: number) =>
2222
export function instanceOfElement(el: any): el is Element {
2323
return el instanceof Element || el instanceof Node;
2424
}
25+
26+
27+
export function isIntersection(
28+
pos1: number,
29+
pos2: number,
30+
target: "start" | "end",
31+
intersection?: boolean | number,
32+
) {
33+
if (!intersection) {
34+
return false;
35+
}
36+
const intersectionRatio = intersection === true ? 1 : intersection;
37+
const size = intersectionRatio * Math.abs(pos2 - pos1);
38+
39+
if (target === "end") {
40+
// pos1 위치를 고정하고 pos2를 pos1 + size으로 보정
41+
// Fix the position pos1 and adjust pos2 to pos1 + size
42+
return pos1 < 0 && pos1 + size > 0;
43+
}
44+
45+
// pos2 위치를 고정하고 pos1를 pos2 - size으로 보정
46+
// Fix the position pos2 and adjust pos1 to pos2 - size
47+
return pos2 - size < 0 && pos2 > 0;
48+
}

yarn.lock

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,10 +2299,10 @@
22992299
"@egjs/agent" "^2.2.1"
23002300
"@egjs/component" "^3.0.1"
23012301

2302-
"@egjs/axes@^3.9.0":
2303-
version "3.9.0"
2304-
resolved "https://registry.npmjs.org/@egjs/axes/-/axes-3.9.0.tgz#46932e449851414e7149826bee109b406d34b573"
2305-
integrity sha512-V+HxHxEGmlu/0yJ9kqlj2doiJt1yVeROTy/0Qf+IaJ9hMabjzUgfYffGR+3atcRpejjGbcpAIiBrGbAkDVaLjg==
2302+
"@egjs/axes@^3.9.1":
2303+
version "3.9.1"
2304+
resolved "https://registry.npmjs.org/@egjs/axes/-/axes-3.9.1.tgz#d7fef01768b2d23c3163dc601f062f7d4feeeecd"
2305+
integrity sha512-vaqUe/boRDk/A4TjiPtMv+lf7c4Q2jTER/Hw4I61NiJUyPftlZjaP4cteBCQejFyZmzimmA2W0cLmYZHTjBY8A==
23062306
dependencies:
23072307
"@cfcs/core" "^0.1.0"
23082308
"@egjs/agent" "^2.2.1"
@@ -4610,6 +4610,11 @@
46104610
"@types/scheduler" "*"
46114611
csstype "^3.0.2"
46124612

4613+
"@types/resize-observer-browser@^0.1.7":
4614+
version "0.1.11"
4615+
resolved "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.11.tgz#d3c98d788489d8376b7beac23863b1eebdd3c13c"
4616+
integrity sha512-cNw5iH8JkMkb3QkCoe7DaZiawbDQEUX8t7iuQaRTyLOyQCR2h+ibBD4GJt7p5yhUHrlOeL7ZtbxNHeipqNsBzQ==
4617+
46134618
46144619
version "0.0.8"
46154620
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"

0 commit comments

Comments
 (0)