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