• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16type VisibleRangeChangedCallback = () => void;
17
18interface IItemsOnScreenProvider {
19  register(callback: VisibleRangeChangedCallback): void;
20  get visibleRange(): IndexRange;
21  get meanValue(): number;
22  get direction(): ScrollDirection;
23  update(minVisible: number, maxVisible: number): void;
24}
25
26class ItemsOnScreenProvider implements IItemsOnScreenProvider {
27  private firstScreen = true;
28  private meanImagesOnScreen = 0;
29  private minVisible = 0;
30  private maxVisible = 0;
31  private _direction: ScrollDirection = 'UNKNOWN';
32  private _visibleRange: IndexRange = new IndexRange(0, 0);
33
34  private callbacks: VisibleRangeChangedCallback[] = [];
35
36  register(callback: VisibleRangeChangedCallback): void {
37    this.callbacks.push(callback);
38  }
39
40  get visibleRange(): IndexRange {
41    return this._visibleRange;
42  }
43
44  get meanValue(): number {
45    return this.meanImagesOnScreen;
46  }
47
48  get direction(): ScrollDirection {
49    return this._direction;
50  }
51
52  update(minVisible: number, maxVisible: number): void {
53    if (minVisible === this.minVisible && maxVisible === this.maxVisible) {
54      // Direction not changed
55    } else if (
56      Math.max(minVisible, this.minVisible) === minVisible &&
57      Math.max(maxVisible, this.maxVisible) === maxVisible
58    ) {
59      this._direction = 'DOWN';
60    } else if (
61      Math.min(minVisible, this.minVisible) === minVisible &&
62      Math.min(maxVisible, this.maxVisible) === maxVisible
63    ) {
64      this._direction = 'UP';
65    }
66    this.minVisible = minVisible;
67    this.maxVisible = maxVisible;
68
69    let imagesOnScreen = maxVisible - minVisible + 1;
70    if (this.firstScreen) {
71      this.meanImagesOnScreen = imagesOnScreen;
72      this.firstScreen = false;
73    } else {
74      const weight = 0.95;
75      this.meanImagesOnScreen = this.meanImagesOnScreen * weight + (1 - weight) * imagesOnScreen;
76      imagesOnScreen = Math.ceil(this.meanImagesOnScreen);
77    }
78
79    const visibleRangeSizeChanged = this.visibleRange.length !== imagesOnScreen;
80    this._visibleRange = new IndexRange(minVisible, maxVisible + 1);
81
82    if (visibleRangeSizeChanged) {
83      this.notifyObservers();
84    }
85  }
86
87  private notifyObservers(): void {
88    this.callbacks.forEach((callback) => callback());
89  }
90}
91