diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 9c6b35081..c4bc52f94 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -264,6 +264,7 @@ export default class ChatBubbles { private setPeerTempId: number = 0; private renderNewPromises: Set> = new Set(); + private updateGradient: boolean; // private reactions: Map; @@ -887,18 +888,15 @@ export default class ChatBubbles { this.listenerSetter.add(rootScope)('history_append', async({storageKey, message}) => { if(storageKey !== this.chat.messagesStorageKey) return; + if(rootScope.settings.animationsEnabled) { + this.updateGradient = true; + } + if(!this.scrollable.loadedAll.bottom) { this.chat.setMessageId(); } else { this.renderNewMessage(message, true); } - - if(rootScope.settings.animationsEnabled) { - const gradientRenderer = this.chat.gradientRenderer; - if(gradientRenderer) { - gradientRenderer.toNextPosition(); - } - } }); this.listenerSetter.add(rootScope)('history_multiappend', (message) => { @@ -2375,6 +2373,12 @@ export default class ChatBubbles { startCallback: (dimensions) => { // this.onScroll(true, this.scrolledDown && dimensions.distanceToEnd <= SCROLLED_DOWN_THRESHOLD ? undefined : dimensions); this.onScroll(true, dimensions); + + if(this.updateGradient) { + const {gradientRenderer} = this.chat; + gradientRenderer?.toNextPosition(dimensions.getProgress); + this.updateGradient = undefined; + } } }); @@ -2604,6 +2608,7 @@ export default class ChatBubbles { this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined; this.fetchNewPromise = undefined; this.getSponsoredMessagePromise = undefined; + this.updateGradient = undefined; if(this.stickyIntersector) { this.stickyIntersector.disconnect(); diff --git a/src/components/chat/gradientRenderer.ts b/src/components/chat/gradientRenderer.ts index 4511adafb..4f2c96086 100644 --- a/src/components/chat/gradientRenderer.ts +++ b/src/components/chat/gradientRenderer.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import {animate} from '../../helpers/animation'; +import {animateSingle} from '../../helpers/animation'; import {hexToRgb} from '../../helpers/color'; const WIDTH = 50; @@ -19,13 +19,6 @@ export default class ChatBackgroundGradientRenderer { private readonly _scrollTails = 50; private _frames: ImageData[]; private _colors: {r: number, g: number, b: number}[]; - /* private readonly _curve = [ - 0, 25, 50, 75, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 800, 900, - 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1830, 1860, 1890, 1920, - 1950, 1980, 2010, 2040, 2070, 2100, 2130, 2160, 2190, 2220, 2250, 2280, 2310, - 2340, 2370, 2400, 2430, 2460, 2490, 2520, 2550, 2580, 2610, 2630, 2640, 2650, - 2660, 2670, 2680, 2690, 2700 - ]; */ private readonly _curve = [ 0, 0.25, 0.50, 0.75, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 18.3, 18.6, 18.9, 19.2, 19.5, 19.8, 20.1, 20.4, 20.7, @@ -59,6 +52,9 @@ export default class ChatBackgroundGradientRenderer { private _addedScrollListener: boolean; private _animatingToNextPosition: boolean; + private _nextPositionTail: number; + private _nextPositionTails: number; + private _nextPositionLeft: number; constructor() { const diff = this._tails / this._curve[this._curve.length - 1]; @@ -79,10 +75,7 @@ export default class ChatBackgroundGradientRenderer { private getPositions(shift: number) { const positions = this._positions.slice(); - while(shift > 0) { - positions.push(positions.shift()); - --shift; - } + positions.push(...positions.splice(0, shift)); const result: typeof positions = []; for(let i = 0; i < positions.length; i += 2) { @@ -151,31 +144,52 @@ export default class ChatBackgroundGradientRenderer { } }; + private changeTailAndDraw(diff: number) { + this.changeTail(diff); + const curPos = this.curPosition(this._phase, this._tail); + this.drawGradient(curPos); + } + private drawOnWheel = () => { - let diff = this._scrollDelta / this._scrollTails; + const value = this._scrollDelta / this._scrollTails; this._scrollDelta %= this._scrollTails; - diff = diff > 0 ? Math.floor(diff) : Math.ceil(diff); + const diff = value > 0 ? Math.floor(value) : Math.ceil(value); if(diff) { - this.changeTail(diff); - const curPos = this.curPosition(this._phase, this._tail); - this.drawGradient(curPos); + this.changeTailAndDraw(diff); } this._onWheelRAF = undefined; }; - private drawNextPositionAnimated = () => { - const frames = this._frames; - const id = frames.shift(); + private drawNextPositionAnimated = (getProgress?: () => number) => { + let done: boolean, id: ImageData; + if(getProgress) { + const value = getProgress(); + done = value >= 1; + const nextPositionTail = this._nextPositionTail ?? 0; + const tail = this._nextPositionTail = this._nextPositionTails * value; + const diff = tail - nextPositionTail; + if(diff) { + this._nextPositionLeft -= diff; + this.changeTailAndDraw(diff); + } + } else { + const frames = this._frames; + id = frames.shift(); + done = !frames.length; + } + if(id) { this.drawImageData(id); } - const leftLength = frames.length; - if(!leftLength) { + if(done) { + this._nextPositionLeft = undefined; + this._nextPositionTails = undefined; + this._nextPositionTail = undefined; this._animatingToNextPosition = undefined; } - return !!leftLength; + return !done; }; private getGradientImageData(positions: {x: number, y: number}[]) { @@ -303,11 +317,20 @@ export default class ChatBackgroundGradientRenderer { this.drawGradient(pos); } - public toNextPosition() { + public toNextPosition(getProgress?: () => number) { if(this._colors.length < 2) { return; } + if(getProgress) { + this._nextPositionLeft = this._tails + (this._nextPositionLeft ?? 0); + this._nextPositionTails = this._nextPositionLeft; + this._nextPositionTail = undefined; + this._animatingToNextPosition = true; + animateSingle(this.drawNextPositionAnimated.bind(this, getProgress), this); + return; + } + const tail = this._tail; const tails = this._tails; @@ -356,10 +379,14 @@ export default class ChatBackgroundGradientRenderer { }); this._animatingToNextPosition = true; - animate(this.drawNextPositionAnimated); + animateSingle(this.drawNextPositionAnimated, this); } + // public toNextPositionThrottled = throttle(this.toNextPosition.bind(this), 100, true); + public scrollAnimate(start?: boolean) { + // return; + if(this._colors.length < 2 && start) { return; } diff --git a/src/helpers/animation.ts b/src/helpers/animation.ts index c308bbb43..5a92b04d6 100644 --- a/src/helpers/animation.ts +++ b/src/helpers/animation.ts @@ -27,7 +27,9 @@ export function createAnimationInstance(key: AnimationInstanceKey) { instances.set(key, instance); instance.deferred.then(() => { - instances.delete(key); + if(getAnimationInstance(key) === instance) { + instances.delete(key); + } }); return instance; diff --git a/src/helpers/fastSmoothScroll.ts b/src/helpers/fastSmoothScroll.ts index aa826c101..a4f71f45f 100644 --- a/src/helpers/fastSmoothScroll.ts +++ b/src/helpers/fastSmoothScroll.ts @@ -32,6 +32,7 @@ export type ScrollStartCallbackDimensions = { duration: number, containerRect: DOMRect, elementRect: DOMRect, + getProgress: () => number }; export type ScrollOptions = { @@ -243,13 +244,16 @@ function scrollWithJs(options: ScrollOptions): Promise { */ const transition = absPath < SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition; - const tick = () => { + const getProgress = () => { const t = duration ? Math.min((Date.now() - startAt) / duration, 1) : 1; - - const currentPath = path * (1 - transition(t)); + return transition(t); + }; + const tick = () => { + const value = getProgress(); + const currentPath = path * (1 - value); container[scrollPositionKey] = Math.round(target - currentPath); - return t < 1; + return value < 1; }; if(!duration || !path) { @@ -285,7 +289,8 @@ function scrollWithJs(options: ScrollOptions): Promise { path, duration, containerRect, - elementRect + elementRect, + getProgress }); }