Animated sticker panel
Fix lazyLoadQueue for panel
This commit is contained in:
parent
369536b9b7
commit
be8976e0d9
@ -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 = '') {
|
||||||
|
@ -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,25 +186,29 @@ 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'); */
|
||||||
|
@ -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) {
|
||||||
|
@ -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 => {
|
||||||
|
@ -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…
Reference in New Issue
Block a user