Browse Source

Animated sticker panel

Fix lazyLoadQueue for panel
master
morethanwords 4 years ago
parent
commit
be8976e0d9
  1. 11
      src/components/animationIntersector.ts
  2. 35
      src/components/emoticonsDropdown/index.ts
  3. 116
      src/components/emoticonsDropdown/tabs/stickers.ts
  4. 2
      src/components/lazyLoadQueue.ts
  5. 2
      src/components/wrappers.ts

11
src/components/animationIntersector.ts

@ -50,16 +50,17 @@ export class AnimationIntersector {
}); });
} }
public getAnimation(element: HTMLElement) { public getAnimations(element: HTMLElement) {
for(let group in this.byGroups) { const found: AnimationItem[] = [];
for(let player of this.byGroups[group]) { for(const group in this.byGroups) {
for(const player of this.byGroups[group]) {
if(player.el == element) { if(player.el == element) {
return player; found.push(player);
} }
} }
} }
return null; return found;
} }
public addAnimation(animation: RLottiePlayer | HTMLVideoElement, group = '') { public addAnimation(animation: RLottiePlayer | HTMLVideoElement, group = '') {

35
src/components/emoticonsDropdown/index.ts

@ -40,6 +40,18 @@ export class EmoticonsDropdown {
public toggleEl: HTMLElement; public toggleEl: HTMLElement;
private displayTimeout: number; private displayTimeout: number;
public events: {
onClose: Array<() => void>,
onCloseAfter: Array<() => void>,
onOpen: Array<() => void>,
onOpenAfter: Array<() => void>
} = {
onClose: [],
onCloseAfter: [],
onOpen: [],
onOpenAfter: []
};
constructor() { constructor() {
this.element = document.getElementById('emoji-dropdown') as HTMLDivElement; this.element = document.getElementById('emoji-dropdown') as HTMLDivElement;
@ -174,26 +186,30 @@ export class EmoticonsDropdown {
} }
if((this.element.style.display && enable === undefined) || enable) { if((this.element.style.display && enable === undefined) || enable) {
this.element.style.display = ''; this.events.onOpen.forEach(cb => cb());
void this.element.offsetLeft; // reflow
this.element.classList.add('active');
EmoticonsDropdown.lazyLoadQueue.lockIntersection(); EmoticonsDropdown.lazyLoadQueue.lockIntersection();
//EmoticonsDropdown.lazyLoadQueue.unlock(); //EmoticonsDropdown.lazyLoadQueue.unlock();
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
this.element.style.display = '';
void this.element.offsetLeft; // reflow
this.element.classList.add('active');
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => { this.displayTimeout = setTimeout(() => {
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
EmoticonsDropdown.lazyLoadQueue.unlockIntersection(); EmoticonsDropdown.lazyLoadQueue.unlockIntersection();
this.events.onOpenAfter.forEach(cb => cb());
}, touchSupport ? 0 : 200); }, touchSupport ? 0 : 200);
/* if(touchSupport) { /* if(touchSupport) {
this.restoreScroll(); this.restoreScroll();
} */ } */
} else { } else {
this.element.classList.remove('active'); this.events.onClose.forEach(cb => cb());
EmoticonsDropdown.lazyLoadQueue.lockIntersection(); EmoticonsDropdown.lazyLoadQueue.lockIntersection();
//EmoticonsDropdown.lazyLoadQueue.lock(); //EmoticonsDropdown.lazyLoadQueue.lock();
@ -201,14 +217,17 @@ export class EmoticonsDropdown {
animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.lockIntersectionGroup(EMOTICONSSTICKERGROUP);
animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP); animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP);
this.element.classList.remove('active');
clearTimeout(this.displayTimeout); clearTimeout(this.displayTimeout);
this.displayTimeout = setTimeout(() => { this.displayTimeout = setTimeout(() => {
this.element.style.display = 'none'; this.element.style.display = 'none';
// теперь можно убрать visible, чтобы они не включились после фокуса // теперь можно убрать visible, чтобы они не включились после фокуса
animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP); animationIntersector.unlockIntersectionGroup(EMOTICONSSTICKERGROUP);
EmoticonsDropdown.lazyLoadQueue.unlockIntersection(); EmoticonsDropdown.lazyLoadQueue.unlockIntersection();
this.events.onCloseAfter.forEach(cb => cb());
}, touchSupport ? 0 : 200); }, touchSupport ? 0 : 200);
/* if(touchSupport) { /* if(touchSupport) {
@ -291,7 +310,9 @@ export class EmoticonsDropdown {
if(!target) return; if(!target) return;
let fileID = target.dataset.docID; const fileID = target.dataset.docID;
if(!fileID) return;
if(appImManager.chatInputC.sendMessageWithDocument(fileID)) { if(appImManager.chatInputC.sendMessageWithDocument(fileID)) {
/* dropdown.classList.remove('active'); /* dropdown.classList.remove('active');
toggleEl.classList.remove('active'); */ toggleEl.classList.remove('active'); */

116
src/components/emoticonsDropdown/tabs/stickers.ts

@ -1,4 +1,4 @@
import { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from ".."; import emoticonsDropdown, { EmoticonsTab, EMOTICONSSTICKERGROUP, EmoticonsDropdown } from "..";
import { MTDocument } from "../../../types"; import { MTDocument } from "../../../types";
import Scrollable from "../../scrollable_new"; import Scrollable from "../../scrollable_new";
import { wrapSticker } from "../../wrappers"; import { wrapSticker } from "../../wrappers";
@ -11,6 +11,8 @@ import { RichTextProcessor } from "../../../lib/richtextprocessor";
import { $rootScope } from "../../../lib/utils"; import { $rootScope } from "../../../lib/utils";
import apiManager from "../../../lib/mtproto/mtprotoworker"; import apiManager from "../../../lib/mtproto/mtprotoworker";
import StickyIntersector from "../../stickyIntersector"; import StickyIntersector from "../../stickyIntersector";
import appDocsManager from "../../../lib/appManagers/appDocsManager";
import animationIntersector from "../../animationIntersector";
export default class StickersTab implements EmoticonsTab { export default class StickersTab implements EmoticonsTab {
public content: HTMLElement; public content: HTMLElement;
@ -33,6 +35,9 @@ export default class StickersTab implements EmoticonsTab {
private stickyIntersector: StickyIntersector; private stickyIntersector: StickyIntersector;
private animatedDivs: Set<HTMLDivElement> = new Set();
private animatedIntersector: IntersectionObserver;
categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MTDocument[]>, prepend?: boolean) { categoryPush(categoryDiv: HTMLElement, categoryTitle: string, promise: Promise<MTDocument[]>, prepend?: boolean) {
//if((docs.length % 5) != 0) categoryDiv.classList.add('not-full'); //if((docs.length % 5) != 0) categoryDiv.classList.add('not-full');
@ -71,8 +76,16 @@ export default class StickersTab implements EmoticonsTab {
}); });
} }
renderSticker(doc: MTDocument) { renderSticker(doc: MTDocument, div?: HTMLDivElement) {
const div = document.createElement('div'); if(!div) {
div = document.createElement('div');
if(doc.sticker == 2) {
this.animatedDivs.add(div);
this.animatedIntersector.observe(div);
}
}
wrapSticker({ wrapSticker({
doc, doc,
div, div,
@ -242,6 +255,101 @@ export default class StickersTab implements EmoticonsTab {
this.mounted = true; this.mounted = true;
}); });
const checkAnimationDiv = (div: HTMLDivElement) => {
const players = animationIntersector.getAnimations(div);
players.forEach(player => {
if(!visible.has(div)) {
animationIntersector.checkAnimation(player, true, true);
} else {
animationIntersector.checkAnimation(player, false);
}
});
};
const processInvisibleDiv = (div: HTMLDivElement) => {
visible.delete(div);
//console.log('STICKER INvisible:', target, docID);
const docID = div.dataset.docID;
const doc = appDocsManager.getDoc(docID);
checkAnimationDiv(div);
div.innerHTML = '';
this.renderSticker(doc, div);
};
let locked = false;
const visible: Set<HTMLDivElement> = new Set();
this.animatedIntersector = new IntersectionObserver((entries) => {
if(locked) {
return;
}
entries.forEach(entry => {
const {target, isIntersecting} = entry;
const div = target as HTMLDivElement;
const docID = div.dataset.docID;
const doc = appDocsManager.getDoc(docID);
if(isIntersecting) {
//console.log('STICKER visible:', target, docID);
if(visible.has(div)) {
return;
}
visible.add(div);
wrapSticker({
doc,
div,
width: 80,
height: 80,
lazyLoadQueue: null,
group: EMOTICONSSTICKERGROUP,
onlyThumb: false,
play: true,
loop: true
}).then(() => {
checkAnimationDiv(div);
});
} else {
processInvisibleDiv(div);
}
});
//animationIntersector.checkAnimations(true, EMOTICONSSTICKERGROUP, false);
});
emoticonsDropdown.events.onClose.push(() => {
locked = true;
});
emoticonsDropdown.events.onCloseAfter.push(() => {
const divs = [...visible];
for(const div of divs) {
processInvisibleDiv(div);
}
});
emoticonsDropdown.events.onOpenAfter.push(() => {
locked = false;
// refresh
this.animatedIntersector.disconnect();
const divs = [...this.animatedDivs];
for(const div of divs) {
this.animatedIntersector.observe(div);
}
});
/* setInterval(() => {
// @ts-ignore
console.log('STICKERS RENDERED IN PANEL:', Object.values(lottieLoader.players).filter(p => p.width == 80).length);
}, .25e3); */
this.init = null; this.init = null;
} }
@ -255,7 +363,7 @@ export default class StickersTab implements EmoticonsTab {
div = this.renderSticker(doc); div = this.renderSticker(doc);
} }
const items = this.recentDiv.lastElementChild; const items = this.recentDiv.querySelector('.category-items');
items.prepend(div); items.prepend(div);
if(items.childElementCount > 20) { if(items.childElementCount > 20) {

2
src/components/lazyLoadQueue.ts

@ -24,7 +24,7 @@ export default class LazyLoadQueue {
if(noObserver) return; if(noObserver) return;
this.observer = new IntersectionObserver(entries => { this.observer = new IntersectionObserver(entries => {
if(this.lockPromise) return; if(this.lockPromise || this.intersectionLocked) return;
const intersecting = entries.filter(entry => entry.isIntersecting); const intersecting = entries.filter(entry => entry.isIntersecting);
intersecting.forEachReverse(entry => { intersecting.forEachReverse(entry => {

2
src/components/wrappers.ts

@ -578,7 +578,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o
//appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => { //appDocsManager.downloadDocNew(doc.id).promise.then(res => res.json()).then(async(json) => {
//fetch(doc.url).then(res => res.json()).then(async(json) => { //fetch(doc.url).then(res => res.json()).then(async(json) => {
appDocsManager.downloadDocNew(doc.id) await appDocsManager.downloadDocNew(doc.id)
.then(readBlobAsText) .then(readBlobAsText)
.then(JSON.parse) .then(JSON.parse)
.then(async(json) => { .then(async(json) => {

Loading…
Cancel
Save