Blur on images
Preloader with attach & detach animation Fixed preloader layout
This commit is contained in:
parent
5b0f0286d9
commit
09ca70ecb7
@ -880,6 +880,10 @@ class AppMediaViewerBase<ContentAdditionType extends string, ButtonsAdditionType
|
|||||||
//const maxWidth = appPhotosManager.windowW - 16;
|
//const maxWidth = appPhotosManager.windowW - 16;
|
||||||
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
|
const maxWidth = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
|
||||||
const maxHeight = appPhotosManager.windowH - 100;
|
const maxHeight = appPhotosManager.windowH - 100;
|
||||||
|
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media);
|
||||||
|
if(gotThumb) {
|
||||||
|
container.append(gotThumb.image);
|
||||||
|
}
|
||||||
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
|
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
|
||||||
|
|
||||||
// need after setAttachmentSize
|
// need after setAttachmentSize
|
||||||
|
@ -20,7 +20,7 @@ import LazyLoadQueue from "./lazyLoadQueue";
|
|||||||
import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc";
|
import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc";
|
||||||
import { ripple } from "./ripple";
|
import { ripple } from "./ripple";
|
||||||
import Scrollable, { ScrollableX } from "./scrollable";
|
import Scrollable, { ScrollableX } from "./scrollable";
|
||||||
import { wrapDocument } from "./wrappers";
|
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
|
||||||
|
|
||||||
const testScroll = false;
|
const testScroll = false;
|
||||||
|
|
||||||
@ -351,99 +351,36 @@ export default class AppSearchSuper {
|
|||||||
div.classList.add('grid-item');
|
div.classList.add('grid-item');
|
||||||
//this.log(message, photo);
|
//this.log(message, photo);
|
||||||
|
|
||||||
const isPhoto = media._ === 'photo';
|
let wrapped: ReturnType<typeof wrapPhoto>;
|
||||||
|
if(media._ !== 'photo') {
|
||||||
const photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
wrapped = wrapVideo({
|
||||||
let isDownloaded: boolean;
|
doc: media,
|
||||||
if(photo) {
|
message,
|
||||||
isDownloaded = photo.downloaded > 0;
|
container: div,
|
||||||
|
boxWidth: 0,
|
||||||
|
boxHeight: 0,
|
||||||
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
|
middleware,
|
||||||
|
onlyPreview: true,
|
||||||
|
withoutPreloader: true
|
||||||
|
}).thumb;
|
||||||
} else {
|
} else {
|
||||||
const cachedThumb = appPhotosManager.getDocumentCachedThumb(media.id);
|
wrapped = wrapPhoto({
|
||||||
isDownloaded = cachedThumb?.downloaded > 0;
|
photo: media,
|
||||||
}
|
message,
|
||||||
|
container: div,
|
||||||
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
boxWidth: 0,
|
||||||
|
boxHeight: 0,
|
||||||
if(!isPhoto) {
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
const span = document.createElement('span');
|
middleware,
|
||||||
span.classList.add('video-time');
|
withoutPreloader: true
|
||||||
div.append(span);
|
|
||||||
|
|
||||||
if(media.type !== 'gif') {
|
|
||||||
span.innerText = (media.duration + '').toHHMMSS(false);
|
|
||||||
|
|
||||||
/* const spanPlay = document.createElement('span');
|
|
||||||
spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center');
|
|
||||||
div.append(spanPlay); */
|
|
||||||
} else {
|
|
||||||
span.innerText = 'GIF';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const load = () => appPhotosManager.preloadPhoto(isPhoto ? media.id : media, appPhotosManager.choosePhotoSize(media, 200, 200))
|
|
||||||
.then(() => {
|
|
||||||
if(!middleware()) {
|
|
||||||
//this.log.warn('peer changed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = (photo && photo.url) || appPhotosManager.getDocumentCachedThumb(media.id).url;
|
|
||||||
if(url) {
|
|
||||||
//if(needBlur) return;
|
|
||||||
|
|
||||||
const needBlurCallback = needBlur ? () => {
|
|
||||||
//void img.offsetLeft; // reflow
|
|
||||||
img.style.opacity = '';
|
|
||||||
|
|
||||||
if(thumb) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
thumb.remove();
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
} : undefined;
|
|
||||||
renderImageFromUrl(img, url, needBlurCallback);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let thumb: HTMLImageElement;
|
|
||||||
const sizes = media.sizes || media.thumbs;
|
|
||||||
|
|
||||||
const willHaveThumb = !isDownloaded && sizes && sizes[0].bytes;
|
|
||||||
if(willHaveThumb) {
|
|
||||||
thumb = new Image();
|
|
||||||
thumb.classList.add('grid-item-media', 'thumbnail');
|
|
||||||
thumb.dataset.mid = '' + message.mid;
|
|
||||||
appPhotosManager.setAttachmentPreview(sizes[0].bytes, thumb, false, false);
|
|
||||||
div.append(thumb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const needBlur = (!isDownloaded || !willHaveThumb) && rootScope.settings.animationsEnabled;
|
wrapped.images.thumb && wrapped.images.thumb.classList.add('grid-item-media');
|
||||||
const img = new Image();
|
wrapped.images.full && wrapped.images.full.classList.add('grid-item-media');
|
||||||
img.dataset.mid = '' + message.mid;
|
|
||||||
img.classList.add('grid-item-media');
|
|
||||||
if(needBlur) img.style.opacity = '0';
|
|
||||||
div.append(img);
|
|
||||||
|
|
||||||
if(isDownloaded || willHaveThumb) {
|
promises.push(wrapped.loadPromises.thumb);
|
||||||
const promise = new Promise<void>((resolve, reject) => {
|
|
||||||
(thumb || img).addEventListener('load', () => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
//this.log('didn\'t load', thumb, media, isDownloaded, sizes);
|
|
||||||
reject();
|
|
||||||
}, 1e3);
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sizes?.length) {
|
|
||||||
if(isDownloaded) load();
|
|
||||||
else this.lazyLoadQueue.push({div, load});
|
|
||||||
}
|
|
||||||
|
|
||||||
elemsToAppend.push({element: div, message});
|
elemsToAppend.push({element: div, message});
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimatio
|
|||||||
import { fastRaf } from "../../helpers/schedulers";
|
import { fastRaf } from "../../helpers/schedulers";
|
||||||
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
||||||
|
|
||||||
|
const USE_MEDIA_TAILS = false;
|
||||||
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
|
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
|
||||||
|
|
||||||
const TEST_SCROLL_TIMES: number = undefined;
|
const TEST_SCROLL_TIMES: number = undefined;
|
||||||
@ -1392,7 +1393,7 @@ export default class ChatBubbles {
|
|||||||
this.chatInner.classList.toggle('is-channel', isChannel);
|
this.chatInner.classList.toggle('is-channel', isChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderMessagesQueue(message: any, bubble: HTMLDivElement, reverse: boolean) {
|
public renderMessagesQueue(message: any, bubble: HTMLDivElement, reverse: boolean, promises: Promise<any>[]) {
|
||||||
/* let dateMessage = this.getDateContainerByMessage(message, reverse);
|
/* let dateMessage = this.getDateContainerByMessage(message, reverse);
|
||||||
if(reverse) dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling);
|
if(reverse) dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling);
|
||||||
else dateMessage.container.append(bubble);
|
else dateMessage.container.append(bubble);
|
||||||
@ -1400,47 +1401,6 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
//this.log('renderMessagesQueue');
|
//this.log('renderMessagesQueue');
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
|
||||||
(Array.from(bubble.querySelectorAll('img, video')) as HTMLImageElement[]).forEach(el => {
|
|
||||||
if(el instanceof HTMLVideoElement) {
|
|
||||||
if(!el.src) {
|
|
||||||
//this.log.warn('no source', el, source, 'src', source.src);
|
|
||||||
return;
|
|
||||||
} else if(el.readyState >= 4) return;
|
|
||||||
} else if(el.complete || !el.src) return;
|
|
||||||
|
|
||||||
let promise = new Promise<void>((resolve, reject) => {
|
|
||||||
let r: () => boolean;
|
|
||||||
let onLoad = () => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
resolve();
|
|
||||||
|
|
||||||
// lol
|
|
||||||
el.removeEventListener(el instanceof HTMLVideoElement ? 'canplay' : 'load', onLoad);
|
|
||||||
};
|
|
||||||
|
|
||||||
if(el instanceof HTMLVideoElement) {
|
|
||||||
el.addEventListener('canplay', onLoad);
|
|
||||||
r = () => el.readyState >= 1;
|
|
||||||
} else {
|
|
||||||
el.addEventListener('load', onLoad);
|
|
||||||
r = () => el.complete;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for safari
|
|
||||||
let c = () => r() ? onLoad() : window.requestAnimationFrame(c);
|
|
||||||
window.requestAnimationFrame(c);
|
|
||||||
|
|
||||||
let timeout = setTimeout(() => {
|
|
||||||
// @ts-ignore
|
|
||||||
//this.log.error('did not called', el, el.parentElement, el.complete, el.readyState, src);
|
|
||||||
resolve();
|
|
||||||
}, 1500);
|
|
||||||
});
|
|
||||||
|
|
||||||
promises.push(promise);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.messagesQueue.push({message, bubble, reverse, promises});
|
this.messagesQueue.push({message, bubble, reverse, promises});
|
||||||
|
|
||||||
this.setMessagesQueuePromise();
|
this.setMessagesQueuePromise();
|
||||||
@ -1624,6 +1584,8 @@ export default class ChatBubbles {
|
|||||||
bubble.dataset.mid = message.mid;
|
bubble.dataset.mid = message.mid;
|
||||||
bubble.dataset.timestamp = message.date;
|
bubble.dataset.timestamp = message.date;
|
||||||
|
|
||||||
|
const loadPromises: Promise<any>[] = [];
|
||||||
|
|
||||||
if(message._ === 'messageService') {
|
if(message._ === 'messageService') {
|
||||||
let action = message.action;
|
let action = message.action;
|
||||||
let _ = action._;
|
let _ = action._;
|
||||||
@ -1636,7 +1598,7 @@ export default class ChatBubbles {
|
|||||||
bubbleContainer.innerHTML = `<div class="service-msg">${message.rReply}</div>`;
|
bubbleContainer.innerHTML = `<div class="service-msg">${message.rReply}</div>`;
|
||||||
|
|
||||||
if(updatePosition) {
|
if(updatePosition) {
|
||||||
this.renderMessagesQueue(message, bubble, reverse);
|
this.renderMessagesQueue(message, bubble, reverse, loadPromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bubble;
|
return bubble;
|
||||||
@ -1832,13 +1794,14 @@ export default class ChatBubbles {
|
|||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
isOut: our,
|
isOut: our,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
chat: this.chat
|
chat: this.chat,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const withTail = !isAndroid && !message.message && !withReplies;
|
const withTail = !isAndroid && !message.message && !withReplies && USE_MEDIA_TAILS;
|
||||||
if(withTail) bubble.classList.add('with-media-tail');
|
if(withTail) bubble.classList.add('with-media-tail');
|
||||||
wrapPhoto({
|
wrapPhoto({
|
||||||
photo,
|
photo,
|
||||||
@ -1847,7 +1810,8 @@ export default class ChatBubbles {
|
|||||||
withTail,
|
withTail,
|
||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware()
|
middleware: this.getMiddleware(),
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -1858,7 +1822,7 @@ export default class ChatBubbles {
|
|||||||
|
|
||||||
let webpage = messageMedia.webpage;
|
let webpage = messageMedia.webpage;
|
||||||
////////this.log('messageMediaWebPage', webpage);
|
////////this.log('messageMediaWebPage', webpage);
|
||||||
if(webpage._ == 'webPageEmpty') {
|
if(webpage._ === 'webPageEmpty') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1895,7 +1859,8 @@ export default class ChatBubbles {
|
|||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
isOut,
|
isOut,
|
||||||
group: CHAT_ANIMATION_GROUP
|
group: CHAT_ANIMATION_GROUP,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
//}
|
//}
|
||||||
} else {
|
} else {
|
||||||
@ -1945,7 +1910,7 @@ export default class ChatBubbles {
|
|||||||
bubble.classList.add('photo');
|
bubble.classList.add('photo');
|
||||||
|
|
||||||
const size = webpage.photo.sizes[webpage.photo.sizes.length - 1];
|
const size = webpage.photo.sizes[webpage.photo.sizes.length - 1];
|
||||||
if(size.w == size.h && quoteTextDiv.childElementCount) {
|
if(size.w === size.h && quoteTextDiv.childElementCount) {
|
||||||
bubble.classList.add('is-square-photo');
|
bubble.classList.add('is-square-photo');
|
||||||
} else if(size.h > size.w) {
|
} else if(size.h > size.w) {
|
||||||
bubble.classList.add('is-vertical-photo');
|
bubble.classList.add('is-vertical-photo');
|
||||||
@ -1959,7 +1924,8 @@ export default class ChatBubbles {
|
|||||||
boxHeight: mediaSizes.active.webpage.height,
|
boxHeight: mediaSizes.active.webpage.height,
|
||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware()
|
middleware: this.getMiddleware(),
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1986,7 +1952,7 @@ export default class ChatBubbles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let size = bubble.classList.contains('emoji-big') ? 140 : 200;
|
let size = bubble.classList.contains('emoji-big') ? 140 : 200;
|
||||||
this.appPhotosManager.setAttachmentSize(doc, attachmentDiv, size, size, true);
|
this.appPhotosManager.setAttachmentSize(doc, attachmentDiv, size, size);
|
||||||
//let preloader = new ProgressivePreloader(attachmentDiv, false);
|
//let preloader = new ProgressivePreloader(attachmentDiv, false);
|
||||||
bubbleContainer.style.height = attachmentDiv.style.height;
|
bubbleContainer.style.height = attachmentDiv.style.height;
|
||||||
bubbleContainer.style.width = attachmentDiv.style.width;
|
bubbleContainer.style.width = attachmentDiv.style.width;
|
||||||
@ -2019,10 +1985,11 @@ export default class ChatBubbles {
|
|||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
isOut: our,
|
isOut: our,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
chat: this.chat
|
chat: this.chat,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message && !withReplies;
|
const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message && !withReplies && USE_MEDIA_TAILS;
|
||||||
if(withTail) bubble.classList.add('with-media-tail');
|
if(withTail) bubble.classList.add('with-media-tail');
|
||||||
wrapVideo({
|
wrapVideo({
|
||||||
doc,
|
doc,
|
||||||
@ -2034,7 +2001,8 @@ export default class ChatBubbles {
|
|||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue: this.lazyLoadQueue,
|
lazyLoadQueue: this.lazyLoadQueue,
|
||||||
middleware: this.getMiddleware(),
|
middleware: this.getMiddleware(),
|
||||||
group: CHAT_ANIMATION_GROUP
|
group: CHAT_ANIMATION_GROUP,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2045,7 +2013,8 @@ export default class ChatBubbles {
|
|||||||
message,
|
message,
|
||||||
bubble,
|
bubble,
|
||||||
messageDiv,
|
messageDiv,
|
||||||
chat: this.chat
|
chat: this.chat,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
if(newNameContainer) {
|
if(newNameContainer) {
|
||||||
@ -2260,7 +2229,7 @@ export default class ChatBubbles {
|
|||||||
if(updatePosition) {
|
if(updatePosition) {
|
||||||
this.bubbleGroups.addBubble(bubble, message, reverse);
|
this.bubbleGroups.addBubble(bubble, message, reverse);
|
||||||
|
|
||||||
this.renderMessagesQueue(message, bubble, reverse);
|
this.renderMessagesQueue(message, bubble, reverse, loadPromises);
|
||||||
} else {
|
} else {
|
||||||
this.bubbleGroups.updateGroupByMessageId(message.mid);
|
this.bubbleGroups.updateGroupByMessageId(message.mid);
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ export default class GifsMasonry {
|
|||||||
const doc = appDocsManager.getDoc(docId);
|
const doc = appDocsManager.getDoc(docId);
|
||||||
|
|
||||||
const promise = this.scrollPromise.then(() => {
|
const promise = this.scrollPromise.then(() => {
|
||||||
const promise = wrapVideo({
|
const res = wrapVideo({
|
||||||
doc,
|
doc,
|
||||||
container: div as HTMLDivElement,
|
container: div as HTMLDivElement,
|
||||||
lazyLoadQueue: null,
|
lazyLoadQueue: null,
|
||||||
@ -73,6 +73,7 @@ export default class GifsMasonry {
|
|||||||
noInfo: true,
|
noInfo: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const promise = res.loadPromise;
|
||||||
promise.finally(() => {
|
promise.finally(() => {
|
||||||
const video = div.querySelector('video');
|
const video = div.querySelector('video');
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { debounce } from "../helpers/schedulers";
|
import { debounce } from "../helpers/schedulers";
|
||||||
import { logger, LogLevels } from "../lib/logger";
|
import { logger, LogLevels } from "../lib/logger";
|
||||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||||
|
import { DEBUG } from "../lib/mtproto/mtproto_config";
|
||||||
|
import { findAndSpliceAll } from "../helpers/array";
|
||||||
|
|
||||||
type LazyLoadElementBase = {
|
type LazyLoadElementBase = {
|
||||||
load: () => Promise<any>
|
load: () => Promise<any>
|
||||||
@ -42,14 +44,16 @@ export class LazyLoadQueueBase {
|
|||||||
public lock() {
|
public lock() {
|
||||||
if(this.lockPromise) return;
|
if(this.lockPromise) return;
|
||||||
|
|
||||||
const perf = performance.now();
|
//const perf = performance.now();
|
||||||
this.lockPromise = new Promise((resolve, reject) => {
|
this.lockPromise = new Promise((resolve, reject) => {
|
||||||
this.unlockResolve = resolve;
|
this.unlockResolve = resolve;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* if(DEBUG) {
|
||||||
this.lockPromise.then(() => {
|
this.lockPromise.then(() => {
|
||||||
this.log('was locked for:', performance.now() - perf);
|
this.log('was locked for:', performance.now() - perf);
|
||||||
});
|
});
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
public unlock() {
|
public unlock() {
|
||||||
@ -68,7 +72,9 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
this.inProcess.add(item);
|
this.inProcess.add(item);
|
||||||
|
|
||||||
|
/* if(DEBUG) {
|
||||||
this.log('will load media', this.lockPromise, item);
|
this.log('will load media', this.lockPromise, item);
|
||||||
|
} */
|
||||||
|
|
||||||
try {
|
try {
|
||||||
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
||||||
@ -81,7 +87,9 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
this.inProcess.delete(item);
|
this.inProcess.delete(item);
|
||||||
|
|
||||||
|
/* if(DEBUG) {
|
||||||
this.log('loaded media', item);
|
this.log('loaded media', item);
|
||||||
|
} */
|
||||||
|
|
||||||
this.processQueue();
|
this.processQueue();
|
||||||
}
|
}
|
||||||
@ -104,7 +112,7 @@ export class LazyLoadQueueBase {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
if(item) {
|
if(item) {
|
||||||
this.queue.findAndSplice(i => i == item);
|
this.queue.findAndSplice(i => i === item);
|
||||||
} else {
|
} else {
|
||||||
item = this.getItem();
|
item = this.getItem();
|
||||||
}
|
}
|
||||||
@ -168,12 +176,12 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {
|
protected addElement(method: 'push' | 'unshift', el: LazyLoadElement) {
|
||||||
const item = this.queue.find(i => i.div == el.div);
|
const item = this.queue.find(i => i.div === el.div && i.load === el.load);
|
||||||
if(item) {
|
if(item) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
for(const item of this.inProcess) {
|
for(const item of this.inProcess) {
|
||||||
if(item.div == el.div) {
|
if(item.div === el.div && item.load === el.load) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,7 +209,8 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public unobserve(el: HTMLElement) {
|
public unobserve(el: HTMLElement) {
|
||||||
this.queue.findAndSplice(i => i.div === el);
|
findAndSpliceAll(this.queue, (i) => i.div === el);
|
||||||
|
|
||||||
this.intersector.unobserve(el);
|
this.intersector.unobserve(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,12 +227,11 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
|||||||
this.log('isIntersecting', target);
|
this.log('isIntersecting', target);
|
||||||
|
|
||||||
// need for set element first if scrolled
|
// need for set element first if scrolled
|
||||||
const item = this.queue.findAndSplice(i => i.div == target);
|
findAndSpliceAll(this.queue, (i) => i.div === target).forEach(item => {
|
||||||
if(item) {
|
|
||||||
item.wasSeen = true;
|
item.wasSeen = true;
|
||||||
this.queue.unshift(item);
|
this.queue.unshift(item);
|
||||||
//this.processQueue(item);
|
//this.processQueue(item);
|
||||||
}
|
});
|
||||||
|
|
||||||
this.setProcessQueueTimeout();
|
this.setProcessQueueTimeout();
|
||||||
}
|
}
|
||||||
@ -261,11 +269,11 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
|||||||
super(parallelLimit);
|
super(parallelLimit);
|
||||||
|
|
||||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||||
|
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||||
if(visible) {
|
if(visible) {
|
||||||
const item = this.queue.findAndSplice(i => i.div == target);
|
spliced.forEach(item => {
|
||||||
this.queue.unshift(item || this._queue.get(target));
|
this.queue.unshift(item || this._queue.get(target));
|
||||||
} else {
|
});
|
||||||
this.queue.findAndSplice(i => i.div == target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||||
@ -298,9 +306,11 @@ export class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
|
|||||||
super(parallelLimit);
|
super(parallelLimit);
|
||||||
|
|
||||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||||
const item = this.queue.findAndSplice(i => i.div == target);
|
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||||
if(visible && item) {
|
if(visible && spliced.length) {
|
||||||
|
spliced.forEach(item => {
|
||||||
this.queue.unshift(item);
|
this.queue.unshift(item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { isInDOM, cancelEvent, attachClickEvent } from "../helpers/dom";
|
import { isInDOM, cancelEvent, attachClickEvent } from "../helpers/dom";
|
||||||
import { CancellablePromise } from "../helpers/cancellablePromise";
|
import { CancellablePromise } from "../helpers/cancellablePromise";
|
||||||
|
import SetTransition from "./singleTransition";
|
||||||
|
|
||||||
|
const TRANSITION_TIME = 200;
|
||||||
|
|
||||||
export default class ProgressivePreloader {
|
export default class ProgressivePreloader {
|
||||||
public preloader: HTMLDivElement;
|
public preloader: HTMLDivElement;
|
||||||
@ -20,8 +23,8 @@ export default class ProgressivePreloader {
|
|||||||
|
|
||||||
this.preloader.innerHTML = `
|
this.preloader.innerHTML = `
|
||||||
<div class="you-spin-me-round">
|
<div class="you-spin-me-round">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
|
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="${streamable ? '25 25 50 50' : '27 27 54 54'}">
|
||||||
<circle class="preloader-path-new" cx="50" cy="50" r="${streamable ? 19 : 23}" fill="none" stroke-miterlimit="10"/>
|
<circle class="preloader-path-new" cx="${streamable ? '50' : '54'}" cy="${streamable ? '50' : '54'}" r="${streamable ? 19 : 24}" fill="none" stroke-miterlimit="10"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
@ -95,16 +98,20 @@ export default class ProgressivePreloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.detached = false;
|
this.detached = false;
|
||||||
window.requestAnimationFrame(() => {
|
/* window.requestAnimationFrame(() => {
|
||||||
if(this.detached) return;
|
if(this.detached) return;
|
||||||
this.detached = false;
|
this.detached = false; */
|
||||||
|
|
||||||
elem[this.attachMethod](this.preloader);
|
elem[this.attachMethod](this.preloader);
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME);
|
||||||
|
});
|
||||||
|
|
||||||
if(this.cancelable && reset) {
|
if(this.cancelable && reset) {
|
||||||
this.setProgress(0);
|
this.setProgress(0);
|
||||||
}
|
}
|
||||||
});
|
//});
|
||||||
}
|
}
|
||||||
|
|
||||||
public detach() {
|
public detach() {
|
||||||
@ -113,14 +120,18 @@ export default class ProgressivePreloader {
|
|||||||
//return;
|
//return;
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
if(this.preloader.parentElement) {
|
||||||
/* setTimeout(() => */window.requestAnimationFrame(() => {
|
/* setTimeout(() => *///window.requestAnimationFrame(() => {
|
||||||
if(!this.detached) return;
|
/* if(!this.detached) return;
|
||||||
this.detached = true;
|
this.detached = true; */
|
||||||
|
|
||||||
if(this.preloader.parentElement) {
|
//if(this.preloader.parentElement) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
SetTransition(this.preloader, 'is-visible', false, TRANSITION_TIME, () => {
|
||||||
this.preloader.remove();
|
this.preloader.remove();
|
||||||
}
|
});
|
||||||
})/* , 5e3) */;
|
});
|
||||||
|
//}
|
||||||
|
//})/* , 5e3) */;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,7 +148,7 @@ export default class ProgressivePreloader {
|
|||||||
try {
|
try {
|
||||||
const totalLength = this.circle.getTotalLength();
|
const totalLength = this.circle.getTotalLength();
|
||||||
//console.log('setProgress', (percents / 100 * totalLength));
|
//console.log('setProgress', (percents / 100 * totalLength));
|
||||||
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', 200';
|
this.circle.style.strokeDasharray = '' + Math.max(5, percents / 100 * totalLength) + ', ' + totalLength;
|
||||||
} catch(err) {}
|
} catch(err) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,11 @@ import RichTextProcessor from '../lib/richtextprocessor';
|
|||||||
import appImManager from '../lib/appManagers/appImManager';
|
import appImManager from '../lib/appManagers/appImManager';
|
||||||
import Chat from './chat/chat';
|
import Chat from './chat/chat';
|
||||||
import { SearchSuperContext } from './appSearchSuper.';
|
import { SearchSuperContext } from './appSearchSuper.';
|
||||||
|
import rootScope from '../lib/rootScope';
|
||||||
|
|
||||||
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
|
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
|
||||||
|
|
||||||
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: {
|
export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group, onlyPreview, withoutPreloader, loadPromises}: {
|
||||||
doc: MyDocument,
|
doc: MyDocument,
|
||||||
container?: HTMLElement,
|
container?: HTMLElement,
|
||||||
message?: any,
|
message?: any,
|
||||||
@ -43,7 +44,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
middleware?: () => boolean,
|
middleware?: () => boolean,
|
||||||
lazyLoadQueue?: LazyLoadQueue,
|
lazyLoadQueue?: LazyLoadQueue,
|
||||||
noInfo?: true,
|
noInfo?: true,
|
||||||
group?: string
|
group?: string,
|
||||||
|
onlyPreview?: boolean,
|
||||||
|
withoutPreloader?: boolean,
|
||||||
|
loadPromises?: Promise<any>[]
|
||||||
}) {
|
}) {
|
||||||
const isAlbumItem = !(boxWidth && boxHeight);
|
const isAlbumItem = !(boxWidth && boxHeight);
|
||||||
const canAutoplay = doc.type != 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem);
|
const canAutoplay = doc.type != 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem);
|
||||||
@ -71,8 +75,13 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let res: {
|
||||||
|
thumb?: typeof photoRes,
|
||||||
|
loadPromise: Promise<any>
|
||||||
|
} = {} as any;
|
||||||
|
|
||||||
if(doc.mime_type == 'image/gif') {
|
if(doc.mime_type == 'image/gif') {
|
||||||
return wrapPhoto({
|
const photoRes = wrapPhoto({
|
||||||
photo: doc,
|
photo: doc,
|
||||||
message,
|
message,
|
||||||
container,
|
container,
|
||||||
@ -81,8 +90,14 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
withTail,
|
withTail,
|
||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue,
|
lazyLoadQueue,
|
||||||
middleware
|
middleware,
|
||||||
|
withoutPreloader,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.thumb = photoRes;
|
||||||
|
res.loadPromise = photoRes.loadPromises.full;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* const video = doc.type == 'round' ? appMediaPlaybackController.addMedia(doc, message.mid) as HTMLVideoElement : document.createElement('video');
|
/* const video = doc.type == 'round' ? appMediaPlaybackController.addMedia(doc, message.mid) as HTMLVideoElement : document.createElement('video');
|
||||||
@ -91,6 +106,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
const video = document.createElement('video');
|
const video = document.createElement('video');
|
||||||
|
video.classList.add('media-video');
|
||||||
video.muted = true;
|
video.muted = true;
|
||||||
video.setAttribute('playsinline', 'true');
|
video.setAttribute('playsinline', 'true');
|
||||||
if(doc.type == 'round') {
|
if(doc.type == 'round') {
|
||||||
@ -156,10 +172,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
video.autoplay = true; // для safari
|
video.autoplay = true; // для safari
|
||||||
}
|
}
|
||||||
|
|
||||||
let img: HTMLImageElement;
|
let photoRes: ReturnType<typeof wrapPhoto>;
|
||||||
if(message) {
|
if(message) {
|
||||||
if(!canAutoplay) {
|
photoRes = wrapPhoto({
|
||||||
return wrapPhoto({
|
|
||||||
photo: doc,
|
photo: doc,
|
||||||
message,
|
message,
|
||||||
container,
|
container,
|
||||||
@ -168,40 +183,25 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
withTail,
|
withTail,
|
||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue,
|
lazyLoadQueue,
|
||||||
middleware
|
middleware,
|
||||||
|
withoutPreloader: true,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res.thumb = photoRes;
|
||||||
|
|
||||||
|
if(!canAutoplay || onlyPreview) {
|
||||||
|
res.loadPromise = photoRes.loadPromises.full;
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(withTail) {
|
if(withTail) {
|
||||||
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
|
const foreignObject = (photoRes.images.thumb || photoRes.images.full).parentElement;
|
||||||
} else {
|
|
||||||
if(boxWidth && boxHeight) { // !album
|
|
||||||
appPhotosManager.setAttachmentSize(doc, container, boxWidth, boxHeight, false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(doc.thumbs?.length && 'bytes' in doc.thumbs[0]) {
|
|
||||||
appPhotosManager.setAttachmentPreview(doc.thumbs[0].bytes, container, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
img = container.lastElementChild as HTMLImageElement;
|
|
||||||
if(img?.tagName != 'IMG') {
|
|
||||||
container.append(img = new Image());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(img) {
|
|
||||||
img.classList.add('thumbnail');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(withTail) {
|
|
||||||
const foreignObject = img.parentElement;
|
|
||||||
video.width = +foreignObject.getAttributeNS(null, 'width');
|
video.width = +foreignObject.getAttributeNS(null, 'width');
|
||||||
video.height = +foreignObject.getAttributeNS(null, 'height');
|
video.height = +foreignObject.getAttributeNS(null, 'height');
|
||||||
foreignObject.append(video);
|
foreignObject.append(video);
|
||||||
}
|
}
|
||||||
}
|
} else { // * gifs masonry
|
||||||
|
|
||||||
if(!img?.parentElement) {
|
|
||||||
const gotThumb = appDocsManager.getThumb(doc, false);
|
const gotThumb = appDocsManager.getThumb(doc, false);
|
||||||
if(gotThumb) {
|
if(gotThumb) {
|
||||||
gotThumb.promise.then(() => {
|
gotThumb.promise.then(() => {
|
||||||
@ -253,14 +253,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
|
|
||||||
//if(doc.type == 'gif'/* || true */) {
|
//if(doc.type == 'gif'/* || true */) {
|
||||||
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => {
|
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => {
|
||||||
if(img?.parentElement) {
|
|
||||||
img.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if(!video.paused) {
|
/* if(!video.paused) {
|
||||||
video.pause();
|
video.pause();
|
||||||
} */
|
} */
|
||||||
if(doc.type != 'round' && group) {
|
if(doc.type !== 'round' && group) {
|
||||||
animationIntersector.addAnimation(video, group);
|
animationIntersector.addAnimation(video, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +323,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
|||||||
return;
|
return;
|
||||||
} */
|
} */
|
||||||
|
|
||||||
return /* doc.downloaded || */!lazyLoadQueue/* && false */ ? loadVideo() : (lazyLoadQueue.push({div: container, load: loadVideo/* , wasSeen: true */}), Promise.resolve());
|
res.loadPromise = !lazyLoadQueue ? loadVideo() : (lazyLoadQueue.push({div: container, load: loadVideo}), Promise.resolve());
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
export const formatDate = (timestamp: number, monthShort = false, withYear = true) => {
|
||||||
@ -344,13 +342,14 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru
|
|||||||
return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
|
return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext}: {
|
export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showSender, searchContext, loadPromises}: {
|
||||||
message: any,
|
message: any,
|
||||||
withTime?: boolean,
|
withTime?: boolean,
|
||||||
fontWeight?: number,
|
fontWeight?: number,
|
||||||
voiceAsMusic?: boolean,
|
voiceAsMusic?: boolean,
|
||||||
showSender?: boolean,
|
showSender?: boolean,
|
||||||
searchContext?: SearchSuperContext
|
searchContext?: SearchSuperContext,
|
||||||
|
loadPromises?: Promise<any>[]
|
||||||
}): HTMLElement {
|
}): HTMLElement {
|
||||||
if(!fontWeight) fontWeight = 500;
|
if(!fontWeight) fontWeight = 500;
|
||||||
|
|
||||||
@ -394,7 +393,8 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
|||||||
message: null,
|
message: null,
|
||||||
container: icoDiv,
|
container: icoDiv,
|
||||||
boxWidth: 54,
|
boxWidth: 54,
|
||||||
boxHeight: 54
|
boxHeight: 54,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
icoDiv.style.width = icoDiv.style.height = '';
|
icoDiv.style.width = icoDiv.style.height = '';
|
||||||
}
|
}
|
||||||
@ -476,7 +476,11 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
|
|||||||
|
|
||||||
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject');
|
const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", 'foreignObject');
|
||||||
|
|
||||||
appPhotosManager.setAttachmentSize(photo, foreignObject, boxWidth, boxHeight/* , false, true */);
|
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo);
|
||||||
|
if(gotThumb) {
|
||||||
|
foreignObject.append(gotThumb.image);
|
||||||
|
}
|
||||||
|
appPhotosManager.setAttachmentSize(photo, foreignObject, boxWidth, boxHeight);
|
||||||
|
|
||||||
const width = +foreignObject.getAttributeNS(null, 'width');
|
const width = +foreignObject.getAttributeNS(null, 'width');
|
||||||
const height = +foreignObject.getAttributeNS(null, 'height');
|
const height = +foreignObject.getAttributeNS(null, 'height');
|
||||||
@ -525,7 +529,7 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
|
|||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size}: {
|
export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware, size, withoutPreloader, loadPromises}: {
|
||||||
photo: MyPhoto | MyDocument,
|
photo: MyPhoto | MyDocument,
|
||||||
message: any,
|
message: any,
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
@ -535,8 +539,23 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
isOut?: boolean,
|
isOut?: boolean,
|
||||||
lazyLoadQueue?: LazyLoadQueue,
|
lazyLoadQueue?: LazyLoadQueue,
|
||||||
middleware?: () => boolean,
|
middleware?: () => boolean,
|
||||||
size?: PhotoSize
|
size?: PhotoSize,
|
||||||
|
withoutPreloader?: boolean,
|
||||||
|
loadPromises?: Promise<any>[]
|
||||||
}) {
|
}) {
|
||||||
|
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
||||||
|
return {
|
||||||
|
loadPromises: {
|
||||||
|
thumb: Promise.resolve(),
|
||||||
|
full: Promise.resolve()
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
thumb: null,
|
||||||
|
full: null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if(boxWidth === undefined) {
|
if(boxWidth === undefined) {
|
||||||
boxWidth = mediaSizes.active.regular.width;
|
boxWidth = mediaSizes.active.regular.width;
|
||||||
}
|
}
|
||||||
@ -545,44 +564,50 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
boxHeight = mediaSizes.active.regular.height;
|
boxHeight = mediaSizes.active.regular.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loadThumbPromise: Promise<any>;
|
||||||
|
let thumbImage: HTMLImageElement;
|
||||||
let image: HTMLImageElement;
|
let image: HTMLImageElement;
|
||||||
if(withTail) {
|
if(withTail) {
|
||||||
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||||
} else {
|
} else {
|
||||||
|
image = new Image();
|
||||||
|
|
||||||
if(boxWidth && boxHeight) { // !album
|
if(boxWidth && boxHeight) { // !album
|
||||||
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight, false, true);
|
size = appPhotosManager.setAttachmentSize(photo, container, boxWidth, boxHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(photo._ == 'document' || !photo.downloaded) {
|
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo);
|
||||||
const thumbs = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
if(gotThumb) {
|
||||||
if(thumbs?.length && 'bytes' in thumbs[0]) {
|
loadThumbPromise = gotThumb.loadPromise;
|
||||||
appPhotosManager.setAttachmentPreview(thumbs[0].bytes, container, false);
|
thumbImage = gotThumb.image;
|
||||||
|
thumbImage.classList.add('media-photo');
|
||||||
|
container.append(thumbImage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
image = container.lastElementChild as HTMLImageElement;
|
image.classList.add('media-photo');
|
||||||
if(!image || image.tagName != 'IMG') {
|
|
||||||
container.append(image = new Image());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||||
|
|
||||||
const cacheContext = appPhotosManager.getCacheContext(photo);
|
const cacheContext = appPhotosManager.getCacheContext(photo);
|
||||||
|
|
||||||
|
const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled;
|
||||||
|
if(needFadeIn) {
|
||||||
|
image.classList.add('fade-in');
|
||||||
|
}
|
||||||
|
|
||||||
let preloader: ProgressivePreloader;
|
let preloader: ProgressivePreloader;
|
||||||
if(message?.media?.preloader) { // means upload
|
if(message?.media?.preloader) { // means upload
|
||||||
message.media.preloader.attach(container, false);
|
message.media.preloader.attach(container, false);
|
||||||
} else if(!cacheContext.downloaded) {
|
} else if(!cacheContext.downloaded && !withoutPreloader) {
|
||||||
preloader = new ProgressivePreloader(null, false, false, photo._ == 'document' ? 'prepend' : 'append');
|
preloader = new ProgressivePreloader(null, false, false, 'prepend');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loadPromise: Promise<any>;
|
||||||
const load = () => {
|
const load = () => {
|
||||||
const promise = photo._ == 'document' && photo.animated ?
|
if(loadPromise) return loadPromise;
|
||||||
|
|
||||||
|
const promise = photo._ === 'document' && photo.mime_type === 'image/gif' ?
|
||||||
appDocsManager.downloadDoc(photo, undefined, lazyLoadQueue?.queueId) :
|
appDocsManager.downloadDoc(photo, undefined, lazyLoadQueue?.queueId) :
|
||||||
appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId);
|
appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId);
|
||||||
|
|
||||||
@ -590,14 +615,56 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
|||||||
preloader.attach(container, true, promise);
|
preloader.attach(container, true, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise.then(() => {
|
return loadPromise = promise.then(() => {
|
||||||
if(middleware && !middleware()) return;
|
if(middleware && !middleware()) return;
|
||||||
|
|
||||||
renderImageFromUrl(image || container, cacheContext.url || photo.url);
|
return new Promise((resolve) => {
|
||||||
|
renderImageFromUrl(image, cacheContext.url || photo.url, () => {
|
||||||
|
container.append(image);
|
||||||
|
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
//resolve();
|
||||||
|
|
||||||
|
if(needFadeIn) {
|
||||||
|
setTimeout(() => {
|
||||||
|
image.classList.remove('fade-in');
|
||||||
|
|
||||||
|
if(thumbImage) {
|
||||||
|
thumbImage.remove();
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return cacheContext.downloaded || !lazyLoadQueue ? load() : (lazyLoadQueue.push({div: container, load/* : load, wasSeen: true */}), Promise.resolve());
|
if(cacheContext.downloaded) {
|
||||||
|
loadThumbPromise = load();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!lazyLoadQueue) {
|
||||||
|
loadPromise = load();
|
||||||
|
} else {
|
||||||
|
lazyLoadQueue.push({div: container, load});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(loadPromises) {
|
||||||
|
loadPromises.push(loadThumbPromise);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadPromises: {
|
||||||
|
thumb: loadThumbPromise,
|
||||||
|
full: loadPromise || Promise.resolve()
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
thumb: thumbImage,
|
||||||
|
full: image
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, onlyThumb, emoji, width, height, withThumb, loop}: {
|
||||||
@ -923,14 +990,15 @@ export function prepareAlbum(options: {
|
|||||||
} */
|
} */
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat}: {
|
export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises}: {
|
||||||
groupId: string,
|
groupId: string,
|
||||||
attachmentDiv: HTMLElement,
|
attachmentDiv: HTMLElement,
|
||||||
middleware?: () => boolean,
|
middleware?: () => boolean,
|
||||||
lazyLoadQueue?: LazyLoadQueue,
|
lazyLoadQueue?: LazyLoadQueue,
|
||||||
uploading?: boolean,
|
uploading?: boolean,
|
||||||
isOut: boolean,
|
isOut: boolean,
|
||||||
chat: Chat
|
chat: Chat,
|
||||||
|
loadPromises?: Promise<any>[]
|
||||||
}) {
|
}) {
|
||||||
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
|
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
|
||||||
|
|
||||||
@ -974,7 +1042,8 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
|
|||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue,
|
lazyLoadQueue,
|
||||||
middleware,
|
middleware,
|
||||||
size
|
size,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
wrapVideo({
|
wrapVideo({
|
||||||
@ -986,19 +1055,21 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
|
|||||||
withTail: false,
|
withTail: false,
|
||||||
isOut,
|
isOut,
|
||||||
lazyLoadQueue,
|
lazyLoadQueue,
|
||||||
middleware
|
middleware,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat}: {
|
export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises}: {
|
||||||
albumMustBeRenderedFull: boolean,
|
albumMustBeRenderedFull: boolean,
|
||||||
message: any,
|
message: any,
|
||||||
messageDiv: HTMLElement,
|
messageDiv: HTMLElement,
|
||||||
bubble: HTMLElement,
|
bubble: HTMLElement,
|
||||||
uploading?: boolean,
|
uploading?: boolean,
|
||||||
chat: Chat
|
chat: Chat,
|
||||||
|
loadPromises?: Promise<any>[]
|
||||||
}) {
|
}) {
|
||||||
let nameContainer: HTMLDivElement;
|
let nameContainer: HTMLDivElement;
|
||||||
const mids = albumMustBeRenderedFull ? chat.getMidsByMid(message.mid) : [message.mid];
|
const mids = albumMustBeRenderedFull ? chat.getMidsByMid(message.mid) : [message.mid];
|
||||||
@ -1011,7 +1082,8 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble,
|
|||||||
const message = chat.getMessage(mid);
|
const message = chat.getMessage(mid);
|
||||||
const doc = message.media.document;
|
const doc = message.media.document;
|
||||||
const div = wrapDocument({
|
const div = wrapDocument({
|
||||||
message
|
message,
|
||||||
|
loadPromises
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = document.createElement('div');
|
const container = document.createElement('div');
|
||||||
|
@ -14,3 +14,13 @@ export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue);
|
export const accumulate = (arr: number[], initialValue: number) => arr.reduce((acc, value) => acc + value, initialValue);
|
||||||
|
|
||||||
|
export function findAndSpliceAll<T>(array: Array<T>, verify: (value: T, index: number, arr: typeof array) => boolean) {
|
||||||
|
const out: typeof array = [];
|
||||||
|
let idx = -1;
|
||||||
|
while((idx = array.findIndex(verify)) !== -1) {
|
||||||
|
out.push(array.splice(idx, 1)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
34
src/helpers/blur.ts
Normal file
34
src/helpers/blur.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import fastBlur from '../vendor/fastBlur';
|
||||||
|
import { fastRaf } from './schedulers';
|
||||||
|
|
||||||
|
const RADIUS = 2;
|
||||||
|
const ITERATIONS = 2;
|
||||||
|
|
||||||
|
export default function blur(dataUri: string, delay?: number) {
|
||||||
|
return new Promise<string>((resolve) => {
|
||||||
|
fastRaf(() => {
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = img.width;
|
||||||
|
canvas.height = img.height;
|
||||||
|
|
||||||
|
const ctx = canvas.getContext('2d')!;
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0);
|
||||||
|
fastBlur(ctx, 0, 0, canvas.width, canvas.height, RADIUS, ITERATIONS);
|
||||||
|
|
||||||
|
resolve(canvas.toDataURL());
|
||||||
|
};
|
||||||
|
|
||||||
|
if(delay) {
|
||||||
|
setTimeout(() => {
|
||||||
|
img.src = dataUri;
|
||||||
|
}, delay);
|
||||||
|
} else {
|
||||||
|
img.src = dataUri;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -8,6 +8,7 @@ import { RichTextProcessor } from '../richtextprocessor';
|
|||||||
import webpWorkerController from '../webp/webpWorkerController';
|
import webpWorkerController from '../webp/webpWorkerController';
|
||||||
import appDownloadManager, { DownloadBlob } from './appDownloadManager';
|
import appDownloadManager, { DownloadBlob } from './appDownloadManager';
|
||||||
import appPhotosManager from './appPhotosManager';
|
import appPhotosManager from './appPhotosManager';
|
||||||
|
import blur from '../../helpers/blur';
|
||||||
|
|
||||||
export type MyDocument = Document.document;
|
export type MyDocument = Document.document;
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ export class AppDocsManager {
|
|||||||
if('bytes' in thumb) {
|
if('bytes' in thumb) {
|
||||||
// * exclude from state
|
// * exclude from state
|
||||||
defineNotNumerableProperties(thumb, ['url']);
|
defineNotNumerableProperties(thumb, ['url']);
|
||||||
thumb.url = appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker);
|
promise = blur(appPhotosManager.getPreviewURLFromBytes(thumb.bytes, !!doc.sticker)).then(url => thumb.url = url);
|
||||||
} else {
|
} else {
|
||||||
//return this.getFileURL(doc, false, thumb);
|
//return this.getFileURL(doc, false, thumb);
|
||||||
promise = this.downloadDoc(doc, thumb);
|
promise = this.downloadDoc(doc, thumb);
|
||||||
|
@ -19,7 +19,7 @@ import appUsersManager from "./appUsersManager";
|
|||||||
#ce671b 5 orange
|
#ce671b 5 orange
|
||||||
*/
|
*/
|
||||||
const DialogColorsFg = ['#c03d33', '#4fad2d', '#d09306', '#168acd', '#8544d6', '#cd4073', '#2996ad', '#ce671b'];
|
const DialogColorsFg = ['#c03d33', '#4fad2d', '#d09306', '#168acd', '#8544d6', '#cd4073', '#2996ad', '#ce671b'];
|
||||||
const DialogColors = ['#e17076', '#7bc862', '#e5ca77', '#65AADD', '#a695e7', '#ee7aae', '#6ec9cb', '#faa774'];
|
const DialogColors = ['red', 'green', 'yellow', 'blue', 'violet', 'pink', 'cyan', 'orange'];
|
||||||
const DialogColorsMap = [0, 7, 4, 1, 6, 3, 5];
|
const DialogColorsMap = [0, 7, 4, 1, 6, 3, 5];
|
||||||
|
|
||||||
export type PeerType = 'channel' | 'chat' | 'megagroup' | 'group' | 'saved';
|
export type PeerType = 'channel' | 'chat' | 'megagroup' | 'group' | 'saved';
|
||||||
|
@ -12,6 +12,8 @@ import { MyDocument } from "./appDocsManager";
|
|||||||
import appDownloadManager from "./appDownloadManager";
|
import appDownloadManager from "./appDownloadManager";
|
||||||
import appUsersManager from "./appUsersManager";
|
import appUsersManager from "./appUsersManager";
|
||||||
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
|
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
|
||||||
|
import blur from "../../helpers/blur";
|
||||||
|
import { renderImageFromUrl } from "../../components/misc";
|
||||||
|
|
||||||
export type MyPhoto = Photo.photo;
|
export type MyPhoto = Photo.photo;
|
||||||
|
|
||||||
@ -106,7 +108,7 @@ export class AppPhotosManager {
|
|||||||
bestPhotoSize = photoSize;
|
bestPhotoSize = photoSize;
|
||||||
|
|
||||||
const {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height);
|
const {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height);
|
||||||
if(w == width || h == height) {
|
if(w === width || h === height) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,47 +191,28 @@ export class AppPhotosManager {
|
|||||||
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
return thumb.url ?? (defineNotNumerableProperties(thumb, ['url']), thumb.url = this.getPreviewURLFromBytes(thumb.bytes, isSticker));
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAttachmentPreview(bytes: Uint8Array | number[], element: HTMLElement | SVGForeignObjectElement, isSticker = false, background = false) {
|
public getImageFromStrippedThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {
|
||||||
let url = this.getPreviewURLFromBytes(bytes, isSticker);
|
const url = this.getPreviewURLFromThumb(thumb, false);
|
||||||
|
|
||||||
if(background) {
|
const image = new Image();
|
||||||
let img = new Image();
|
image.classList.add('thumbnail');
|
||||||
img.src = url;
|
|
||||||
img.addEventListener('load', () => {
|
const loadPromise = blur(url).then(url => {
|
||||||
element.style.backgroundImage = 'url(' + url + ')';
|
return new Promise<any>((resolve) => {
|
||||||
|
renderImageFromUrl(image, url, resolve);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return element;
|
return {image, loadPromise};
|
||||||
} else {
|
|
||||||
if(element instanceof HTMLImageElement) {
|
|
||||||
element.src = url;
|
|
||||||
return element;
|
|
||||||
} else {
|
|
||||||
let img = new Image();
|
|
||||||
|
|
||||||
img.src = url;
|
|
||||||
element.append(img);
|
|
||||||
return img;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number, isSticker = false, dontRenderPreview = false) {
|
public setAttachmentSize(photo: MyPhoto | MyDocument, element: HTMLElement | SVGForeignObjectElement, boxWidth: number, boxHeight: number) {
|
||||||
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
|
const photoSize = this.choosePhotoSize(photo, boxWidth, boxHeight);
|
||||||
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
//console.log('setAttachmentSize', photo, photo.sizes[0].bytes, div);
|
||||||
|
|
||||||
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
|
||||||
const thumb = sizes?.length ? sizes[0] : null;
|
|
||||||
if(thumb && ('bytes' in thumb)) {
|
|
||||||
if((!photo.downloaded || (photo as MyDocument).type == 'video' || (photo as MyDocument).type == 'gif') && !isSticker && !dontRenderPreview) {
|
|
||||||
this.setAttachmentPreview(thumb.bytes, element, isSticker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let width: number;
|
let width: number;
|
||||||
let height: number;
|
let height: number;
|
||||||
if(photo._ == 'document') {
|
if(photo._ === 'document') {
|
||||||
width = photo.w || 512;
|
width = photo.w || 512;
|
||||||
height = photo.h || 512;
|
height = photo.h || 512;
|
||||||
} else {
|
} else {
|
||||||
@ -251,16 +234,35 @@ export class AppPhotosManager {
|
|||||||
return photoSize;
|
return photoSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number) {
|
public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {
|
||||||
const isMyDocument = photo._ == 'document';
|
if(!photo.downloaded || (photo as MyDocument).type === 'video' || (photo as MyDocument).type === 'gif') {
|
||||||
|
if(photo._ === 'document') {
|
||||||
|
const cacheContext = this.getCacheContext(photo);
|
||||||
|
if(cacheContext.downloaded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
|
const sizes = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
||||||
|
const thumb = sizes?.length ? sizes[0] : null;
|
||||||
|
if(thumb && ('bytes' in thumb)) {
|
||||||
|
return appPhotosManager.getImageFromStrippedThumb(thumb as any);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPhotoDownloadOptions(photo: MyPhoto | MyDocument, photoSize: PhotoSize, queueId?: number) {
|
||||||
|
const isMyDocument = photo._ === 'document';
|
||||||
|
|
||||||
|
if(!photoSize || photoSize._ === 'photoSizeEmpty') {
|
||||||
//console.error('no photoSize by photo:', photo);
|
//console.error('no photoSize by photo:', photo);
|
||||||
throw new Error('photoSizeEmpty!');
|
throw new Error('photoSizeEmpty!');
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe it's a thumb
|
// maybe it's a thumb
|
||||||
const isPhoto = (photoSize._ == 'photoSize' || photoSize._ == 'photoSizeProgressive') && photo.access_hash && photo.file_reference;
|
const isPhoto = (photoSize._ === 'photoSize' || photoSize._ === 'photoSizeProgressive') && photo.access_hash && photo.file_reference;
|
||||||
const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation | FileLocation = isPhoto ? {
|
const location: InputFileLocation.inputPhotoFileLocation | InputFileLocation.inputDocumentFileLocation | FileLocation = isPhoto ? {
|
||||||
_: isMyDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
_: isMyDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||||
id: photo.id,
|
id: photo.id,
|
||||||
@ -278,11 +280,25 @@ export class AppPhotosManager {
|
|||||||
return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};
|
return {url: getFileURL('photo', downloadOptions), location: downloadOptions.location};
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
public isDownloaded(media: any) {
|
||||||
|
const isPhoto = media._ === 'photo';
|
||||||
|
const photo = isPhoto ? this.getPhoto(media.id) : null;
|
||||||
|
let isDownloaded: boolean;
|
||||||
|
if(photo) {
|
||||||
|
isDownloaded = photo.downloaded > 0;
|
||||||
|
} else {
|
||||||
|
const cachedThumb = this.getDocumentCachedThumb(media.id);
|
||||||
|
isDownloaded = cachedThumb?.downloaded > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDownloaded;
|
||||||
|
}
|
||||||
|
|
||||||
public preloadPhoto(photoId: any, photoSize?: PhotoSize, queueId?: number): CancellablePromise<Blob> {
|
public preloadPhoto(photoId: any, photoSize?: PhotoSize, queueId?: number): CancellablePromise<Blob> {
|
||||||
const photo = this.getPhoto(photoId);
|
const photo = this.getPhoto(photoId);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if(!photo || photo._ == 'photoEmpty') {
|
if(!photo || photo._ === 'photoEmpty') {
|
||||||
throw new Error('preloadPhoto photoEmpty!');
|
throw new Error('preloadPhoto photoEmpty!');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +341,7 @@ export class AppPhotosManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getCacheContext(photo: any): DocumentCacheThumb {
|
public getCacheContext(photo: any): DocumentCacheThumb {
|
||||||
return photo._ == 'document' ? this.getDocumentCachedThumb(photo.id) : photo;
|
return photo._ === 'document' ? this.getDocumentCachedThumb(photo.id) : photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDocumentCachedThumb(docId: string) {
|
public getDocumentCachedThumb(docId: string) {
|
||||||
@ -351,7 +367,7 @@ export class AppPhotosManager {
|
|||||||
|
|
||||||
public savePhotoFile(photo: MyPhoto | MyDocument, queueId?: number) {
|
public savePhotoFile(photo: MyPhoto | MyDocument, queueId?: number) {
|
||||||
const fullPhotoSize = this.choosePhotoSize(photo, 0xFFFF, 0xFFFF);
|
const fullPhotoSize = this.choosePhotoSize(photo, 0xFFFF, 0xFFFF);
|
||||||
if(!(fullPhotoSize._ == 'photoSize' || fullPhotoSize._ == 'photoSizeProgressive')) {
|
if(!(fullPhotoSize._ === 'photoSize' || fullPhotoSize._ === 'photoSizeProgressive')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,51 @@
|
|||||||
avatar-element {
|
avatar-element {
|
||||||
--size: 54px;
|
--size: 54px;
|
||||||
--multiplier: 1;
|
--multiplier: 1;
|
||||||
|
--color-top: var(--peer-avatar-blue-top);
|
||||||
|
--color-bottom: var(--peer-avatar-blue-bottom);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
width: var(--size);
|
width: var(--size);
|
||||||
height: var(--size);
|
height: var(--size);
|
||||||
line-height: var(--size);
|
line-height: var(--size);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: $color-blue;
|
background: linear-gradient(var(--color-top), var(--color-bottom));
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: calc(1.25rem / var(--multiplier));
|
font-size: calc(1.25rem / var(--multiplier));
|
||||||
/* overflow: hidden; */
|
/* overflow: hidden; */
|
||||||
position: relative;
|
position: relative;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
&[data-color="red"] {
|
||||||
|
--color-top: var(--peer-avatar-red-top);
|
||||||
|
--color-bottom: var(--peer-avatar-red-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-color="orange"] {
|
||||||
|
--color-top: var(--peer-avatar-orange-top);
|
||||||
|
--color-bottom: var(--peer-avatar-orange-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-color="violet"] {
|
||||||
|
--color-top: var(--peer-avatar-violet-top);
|
||||||
|
--color-bottom: var(--peer-avatar-violet-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-color="green"] {
|
||||||
|
--color-top: var(--peer-avatar-green-top);
|
||||||
|
--color-bottom: var(--peer-avatar-green-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-color="cyan"] {
|
||||||
|
--color-top: var(--peer-avatar-cyan-top);
|
||||||
|
--color-bottom: var(--peer-avatar-cyan-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-color="pink"] {
|
||||||
|
--color-top: var(--peer-avatar-pink-top);
|
||||||
|
--color-bottom: var(--peer-avatar-pink-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
&.tgico-savedmessages:before {
|
&.tgico-savedmessages:before {
|
||||||
font-size: calc(25px / var(--multiplier));
|
font-size: calc(25px / var(--multiplier));
|
||||||
|
@ -1039,7 +1039,7 @@ $chat-helper-size: 39px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&-container .preloader-circular {
|
&-container .preloader-circular {
|
||||||
background-color: var(--message-time-background);
|
background-color: rgba(0, 0, 0, .3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,16 +597,10 @@ $bubble-margin: .25rem;
|
|||||||
display: flex; // lol
|
display: flex; // lol
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
img, video {
|
img, video {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
cursor: pointer;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .3s ease;
|
|
||||||
|
|
||||||
body.animation-level-0 & {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.download {
|
.download {
|
||||||
@ -633,11 +627,11 @@ $bubble-margin: .25rem;
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.preloader-container {
|
.preloader-container {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.sticker) {
|
&:not(.sticker) {
|
||||||
.attachment {
|
.attachment {
|
||||||
|
@ -28,10 +28,24 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 50px;
|
width: 54px;
|
||||||
height: 50px;
|
height: 54px;
|
||||||
display: flex;
|
display: flex;
|
||||||
/* cursor: pointer; */
|
/* cursor: pointer; */
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0);
|
||||||
|
|
||||||
|
body:not(.animation-level-0) & {
|
||||||
|
transition: opacity .2s ease-in-out, transform .2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-visible {
|
||||||
|
&:not(.backwards) {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +66,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preloader-path-new {
|
.preloader-path-new {
|
||||||
stroke-dasharray: 5, 200;
|
stroke-dasharray: 5, 149.82;
|
||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 0;
|
||||||
transition: stroke-dasharray 400ms ease-in-out;
|
transition: stroke-dasharray 400ms ease-in-out;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke: white;
|
stroke: white;
|
||||||
stroke-width: 1.5;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.preloader-swing {
|
&.preloader-swing {
|
||||||
@ -68,9 +82,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.preloader-path-new {
|
.preloader-path-new {
|
||||||
stroke-dasharray: 1, 200;
|
animation: dashNew 1.5s ease-in-out infinite;
|
||||||
stroke-dashoffset: 0;
|
|
||||||
animation: dashNew 1.5s ease-in-out infinite/* , color 6s ease-in-out infinite */;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +123,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate3d(-50%,-50%,0);
|
transform: translate3d(-50%, -50%, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,16 +151,16 @@
|
|||||||
|
|
||||||
@keyframes dashNew {
|
@keyframes dashNew {
|
||||||
0% {
|
0% {
|
||||||
stroke-dasharray: 1, 200;
|
stroke-dasharray: 1, 149.82; // 149.82 = getTotalLength
|
||||||
stroke-dashoffset: 0;
|
stroke-dashoffset: 0;
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
stroke-dasharray: 89, 200;
|
stroke-dasharray: 112.36, 149.82; // 112.36 = 149.82 * .75
|
||||||
stroke-dashoffset: -35px;
|
stroke-dashoffset: -38; // bruted
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
stroke-dasharray: 89, 200;
|
stroke-dasharray: 112.36, 149.82;
|
||||||
stroke-dashoffset: -286%;
|
stroke-dashoffset: -149.82; // totalLength
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,15 +361,6 @@
|
|||||||
|
|
||||||
.grid-item {
|
.grid-item {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&-media {
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity .2s ease;
|
|
||||||
|
|
||||||
html:not(.is-mac) &.thumbnail {
|
|
||||||
filter: blur(7px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* span.video-play {
|
/* span.video-play {
|
||||||
|
@ -96,6 +96,22 @@ $chat-padding-handhelds: .5rem;
|
|||||||
--messages-secondary-text-size: calc(var(--messages-text-size) - 1px);
|
--messages-secondary-text-size: calc(var(--messages-text-size) - 1px);
|
||||||
--esg-sticker-size: 80px;
|
--esg-sticker-size: 80px;
|
||||||
|
|
||||||
|
// https://github.com/overtake/TelegramSwift/blob/5cc7d2475fe4738a6aa0486c23eaf80a89d33b97/submodules/TGUIKit/TGUIKit/PresentationTheme.swift#L2054
|
||||||
|
--peer-avatar-red-top: #ff885e;
|
||||||
|
--peer-avatar-red-bottom: #ff516a;
|
||||||
|
--peer-avatar-orange-top: #ffcd6a;
|
||||||
|
--peer-avatar-orange-bottom: #ffa85c;
|
||||||
|
--peer-avatar-violet-top: #82b1ff;
|
||||||
|
--peer-avatar-violet-bottom: #665fff;
|
||||||
|
--peer-avatar-green-top: #a0de7e;
|
||||||
|
--peer-avatar-green-bottom: #54cb68;
|
||||||
|
--peer-avatar-cyan-top: #53edd6;
|
||||||
|
--peer-avatar-cyan-bottom: #28c9b7;
|
||||||
|
--peer-avatar-blue-top: #72d5fd;
|
||||||
|
--peer-avatar-blue-bottom: #2a9ef1;
|
||||||
|
--peer-avatar-pink-top: #e0a2f3;
|
||||||
|
--peer-avatar-pink-bottom: #d669ed;
|
||||||
|
|
||||||
@include respond-to(handhelds) {
|
@include respond-to(handhelds) {
|
||||||
--right-column-width: 100vw;
|
--right-column-width: 100vw;
|
||||||
--esg-sticker-size: 68px;
|
--esg-sticker-size: 68px;
|
||||||
@ -859,7 +875,6 @@ img.emoji {
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
-o-object-fit: cover;
|
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1161,3 +1176,19 @@ middle-ellipsis-element {
|
|||||||
background-color: var(--color-gray-hover);
|
background-color: var(--color-gray-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media-photo, .media-video {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
&.fade-in {
|
||||||
|
animation: fade-in-opacity .2s ease-in-out forwards;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-video {
|
||||||
|
z-index: 1; // * overflow media-photo
|
||||||
|
}
|
||||||
|
160
src/vendor/fastBlur.js
vendored
Normal file
160
src/vendor/fastBlur.js
vendored
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
Superfast Blur - a fast Box Blur For Canvas
|
||||||
|
|
||||||
|
Version: 0.5
|
||||||
|
Author: Mario Klingemann
|
||||||
|
Contact: mario@quasimondo.com
|
||||||
|
Website: http://www.quasimondo.com/BoxBlurForCanvas
|
||||||
|
Twitter: @quasimondo
|
||||||
|
|
||||||
|
In case you find this class useful - especially in commercial projects -
|
||||||
|
I am not totally unhappy for a small donation to my PayPal account
|
||||||
|
mario@quasimondo.de
|
||||||
|
|
||||||
|
Or support me on flattr:
|
||||||
|
https://flattr.com/thing/140066/Superfast-Blur-a-pretty-fast-Box-Blur-Effect-for-CanvasJavascript
|
||||||
|
|
||||||
|
Copyright (c) 2011 Mario Klingemann
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const mul_table = [1, 57, 41, 21, 203, 34, 97, 73, 227, 91, 149, 62, 105, 45, 39, 137, 241, 107, 3, 173, 39, 71, 65, 238, 219, 101, 187, 87, 81, 151, 141, 133, 249, 117, 221, 209, 197, 187, 177, 169, 5, 153, 73, 139, 133, 127, 243, 233, 223, 107, 103, 99, 191, 23, 177, 171, 165, 159, 77, 149, 9, 139, 135, 131, 253, 245, 119, 231, 224, 109, 211, 103, 25, 195, 189, 23, 45, 175, 171, 83, 81, 79, 155, 151, 147, 9, 141, 137, 67, 131, 129, 251, 123, 30, 235, 115, 113, 221, 217, 53, 13, 51, 50, 49, 193, 189, 185, 91, 179, 175, 43, 169, 83, 163, 5, 79, 155, 19, 75, 147, 145, 143, 35, 69, 17, 67, 33, 65, 255, 251, 247, 243, 239, 59, 29, 229, 113, 111, 219, 27, 213, 105, 207, 51, 201, 199, 49, 193, 191, 47, 93, 183, 181, 179, 11, 87, 43, 85, 167, 165, 163, 161, 159, 157, 155, 77, 19, 75, 37, 73, 145, 143, 141, 35, 138, 137, 135, 67, 33, 131, 129, 255, 63, 250, 247, 61, 121, 239, 237, 117, 29, 229, 227, 225, 111, 55, 109, 216, 213, 211, 209, 207, 205, 203, 201, 199, 197, 195, 193, 48, 190, 47, 93, 185, 183, 181, 179, 178, 176, 175, 173, 171, 85, 21, 167, 165, 41, 163, 161, 5, 79, 157, 78, 154, 153, 19, 75, 149, 74, 147, 73, 144, 143, 71, 141, 140, 139, 137, 17, 135, 134, 133, 66, 131, 65, 129, 1];
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
const shg_table = [0, 9, 10, 10, 14, 12, 14, 14, 16, 15, 16, 15, 16, 15, 15, 17, 18, 17, 12, 18, 16, 17, 17, 19, 19, 18, 19, 18, 18, 19, 19, 19, 20, 19, 20, 20, 20, 20, 20, 20, 15, 20, 19, 20, 20, 20, 21, 21, 21, 20, 20, 20, 21, 18, 21, 21, 21, 21, 20, 21, 17, 21, 21, 21, 22, 22, 21, 22, 22, 21, 22, 21, 19, 22, 22, 19, 20, 22, 22, 21, 21, 21, 22, 22, 22, 18, 22, 22, 21, 22, 22, 23, 22, 20, 23, 22, 22, 23, 23, 21, 19, 21, 21, 21, 23, 23, 23, 22, 23, 23, 21, 23, 22, 23, 18, 22, 23, 20, 22, 23, 23, 23, 21, 22, 20, 22, 21, 22, 24, 24, 24, 24, 24, 22, 21, 24, 23, 23, 24, 21, 24, 23, 24, 22, 24, 24, 22, 24, 24, 22, 23, 24, 24, 24, 20, 23, 22, 23, 24, 24, 24, 24, 24, 24, 24, 23, 21, 23, 22, 23, 24, 24, 24, 22, 24, 24, 24, 23, 22, 24, 24, 25, 23, 25, 25, 23, 24, 25, 25, 24, 22, 25, 25, 25, 24, 23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 23, 25, 23, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 22, 25, 25, 23, 25, 25, 20, 24, 25, 24, 25, 25, 22, 24, 25, 24, 25, 24, 25, 25, 24, 25, 25, 25, 25, 22, 25, 25, 25, 24, 25, 24, 25, 18];
|
||||||
|
|
||||||
|
export default function boxBlurCanvasRGB(context, top_x, top_y, width, height, radius, iterations) {
|
||||||
|
if (Number.isNaN(radius) || radius < 1) return;
|
||||||
|
|
||||||
|
radius |= 0;
|
||||||
|
|
||||||
|
if (Number.isNaN(iterations)) iterations = 1;
|
||||||
|
iterations |= 0;
|
||||||
|
if (iterations > 3) iterations = 3;
|
||||||
|
if (iterations < 1) iterations = 1;
|
||||||
|
|
||||||
|
const imageData = context.getImageData(top_x, top_y, width, height);
|
||||||
|
|
||||||
|
const pixels = imageData.data;
|
||||||
|
|
||||||
|
let rsum;
|
||||||
|
let gsum;
|
||||||
|
let bsum;
|
||||||
|
let x;
|
||||||
|
let y;
|
||||||
|
let i;
|
||||||
|
let p;
|
||||||
|
let p1;
|
||||||
|
let p2;
|
||||||
|
let yp;
|
||||||
|
let yi;
|
||||||
|
let yw;
|
||||||
|
let wm = width - 1;
|
||||||
|
let hm = height - 1;
|
||||||
|
let rad1 = radius + 1;
|
||||||
|
|
||||||
|
let r = [];
|
||||||
|
let g = [];
|
||||||
|
let b = [];
|
||||||
|
|
||||||
|
let mul_sum = mul_table[radius];
|
||||||
|
let shg_sum = shg_table[radius];
|
||||||
|
|
||||||
|
let vmin = [];
|
||||||
|
let vmax = [];
|
||||||
|
|
||||||
|
while (iterations-- > 0) {
|
||||||
|
yw = yi = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
rsum = pixels[yw] * rad1;
|
||||||
|
gsum = pixels[yw + 1] * rad1;
|
||||||
|
bsum = pixels[yw + 2] * rad1;
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; i++) {
|
||||||
|
p = yw + (((i > wm ? wm : i)) << 2);
|
||||||
|
rsum += pixels[p++];
|
||||||
|
gsum += pixels[p++];
|
||||||
|
bsum += pixels[p++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < width; x++) {
|
||||||
|
r[yi] = rsum;
|
||||||
|
g[yi] = gsum;
|
||||||
|
b[yi] = bsum;
|
||||||
|
|
||||||
|
if (y == 0) {
|
||||||
|
vmin[x] = ((p = x + rad1) < wm ? p : wm) << 2;
|
||||||
|
vmax[x] = ((p = x - radius) > 0 ? p << 2 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 = yw + vmin[x];
|
||||||
|
p2 = yw + vmax[x];
|
||||||
|
|
||||||
|
rsum += pixels[p1++] - pixels[p2++];
|
||||||
|
gsum += pixels[p1++] - pixels[p2++];
|
||||||
|
bsum += pixels[p1++] - pixels[p2++];
|
||||||
|
|
||||||
|
yi++;
|
||||||
|
}
|
||||||
|
yw += (width << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x = 0; x < width; x++) {
|
||||||
|
yp = x;
|
||||||
|
rsum = r[yp] * rad1;
|
||||||
|
gsum = g[yp] * rad1;
|
||||||
|
bsum = b[yp] * rad1;
|
||||||
|
|
||||||
|
for (i = 1; i <= radius; i++) {
|
||||||
|
yp += (i > hm ? 0 : width);
|
||||||
|
rsum += r[yp];
|
||||||
|
gsum += g[yp];
|
||||||
|
bsum += b[yp];
|
||||||
|
}
|
||||||
|
|
||||||
|
yi = x << 2;
|
||||||
|
for (y = 0; y < height; y++) {
|
||||||
|
pixels[yi] = (rsum * mul_sum) >>> shg_sum;
|
||||||
|
pixels[yi + 1] = (gsum * mul_sum) >>> shg_sum;
|
||||||
|
pixels[yi + 2] = (bsum * mul_sum) >>> shg_sum;
|
||||||
|
|
||||||
|
if (x == 0) {
|
||||||
|
vmin[y] = ((p = y + rad1) < hm ? p : hm) * width;
|
||||||
|
vmax[y] = ((p = y - radius) > 0 ? p * width : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
p1 = x + vmin[y];
|
||||||
|
p2 = x + vmax[y];
|
||||||
|
|
||||||
|
rsum += r[p1] - r[p2];
|
||||||
|
gsum += g[p1] - g[p2];
|
||||||
|
bsum += b[p1] - b[p2];
|
||||||
|
|
||||||
|
yi += width << 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.putImageData(imageData, top_x, top_y);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user