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 = mediaSizes.isMobile ? this.pageEl.scrollWidth : this.pageEl.scrollWidth - 16;
|
||||
const maxHeight = appPhotosManager.windowH - 100;
|
||||
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(media);
|
||||
if(gotThumb) {
|
||||
container.append(gotThumb.image);
|
||||
}
|
||||
const size = appPhotosManager.setAttachmentSize(media, container, maxWidth, maxHeight);
|
||||
|
||||
// need after setAttachmentSize
|
||||
|
@ -20,7 +20,7 @@ import LazyLoadQueue from "./lazyLoadQueue";
|
||||
import { renderImageFromUrl, putPreloader, formatPhoneNumber } from "./misc";
|
||||
import { ripple } from "./ripple";
|
||||
import Scrollable, { ScrollableX } from "./scrollable";
|
||||
import { wrapDocument } from "./wrappers";
|
||||
import { wrapDocument, wrapPhoto, wrapVideo } from "./wrappers";
|
||||
|
||||
const testScroll = false;
|
||||
|
||||
@ -351,99 +351,36 @@ export default class AppSearchSuper {
|
||||
div.classList.add('grid-item');
|
||||
//this.log(message, photo);
|
||||
|
||||
const isPhoto = media._ === 'photo';
|
||||
|
||||
const photo = isPhoto ? appPhotosManager.getPhoto(media.id) : null;
|
||||
let isDownloaded: boolean;
|
||||
if(photo) {
|
||||
isDownloaded = photo.downloaded > 0;
|
||||
let wrapped: ReturnType<typeof wrapPhoto>;
|
||||
if(media._ !== 'photo') {
|
||||
wrapped = wrapVideo({
|
||||
doc: media,
|
||||
message,
|
||||
container: div,
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware,
|
||||
onlyPreview: true,
|
||||
withoutPreloader: true
|
||||
}).thumb;
|
||||
} else {
|
||||
const cachedThumb = appPhotosManager.getDocumentCachedThumb(media.id);
|
||||
isDownloaded = cachedThumb?.downloaded > 0;
|
||||
}
|
||||
|
||||
//this.log('inputMessagesFilterPhotoVideo', message, media);
|
||||
|
||||
if(!isPhoto) {
|
||||
const span = document.createElement('span');
|
||||
span.classList.add('video-time');
|
||||
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;
|
||||
const img = new Image();
|
||||
img.dataset.mid = '' + message.mid;
|
||||
img.classList.add('grid-item-media');
|
||||
if(needBlur) img.style.opacity = '0';
|
||||
div.append(img);
|
||||
|
||||
if(isDownloaded || willHaveThumb) {
|
||||
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);
|
||||
wrapped = wrapPhoto({
|
||||
photo: media,
|
||||
message,
|
||||
container: div,
|
||||
boxWidth: 0,
|
||||
boxHeight: 0,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware,
|
||||
withoutPreloader: true
|
||||
});
|
||||
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
if(sizes?.length) {
|
||||
if(isDownloaded) load();
|
||||
else this.lazyLoadQueue.push({div, load});
|
||||
}
|
||||
wrapped.images.thumb && wrapped.images.thumb.classList.add('grid-item-media');
|
||||
wrapped.images.full && wrapped.images.full.classList.add('grid-item-media');
|
||||
|
||||
promises.push(wrapped.loadPromises.thumb);
|
||||
|
||||
elemsToAppend.push({element: div, message});
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimatio
|
||||
import { fastRaf } from "../../helpers/schedulers";
|
||||
import { deferredPromise, CancellablePromise } from "../../helpers/cancellablePromise";
|
||||
|
||||
const USE_MEDIA_TAILS = false;
|
||||
const IGNORE_ACTIONS = ['messageActionHistoryClear'];
|
||||
|
||||
const TEST_SCROLL_TIMES: number = undefined;
|
||||
@ -1392,7 +1393,7 @@ export default class ChatBubbles {
|
||||
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);
|
||||
if(reverse) dateMessage.container.insertBefore(bubble, dateMessage.div.nextSibling);
|
||||
else dateMessage.container.append(bubble);
|
||||
@ -1400,47 +1401,6 @@ export default class ChatBubbles {
|
||||
|
||||
//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.setMessagesQueuePromise();
|
||||
@ -1624,6 +1584,8 @@ export default class ChatBubbles {
|
||||
bubble.dataset.mid = message.mid;
|
||||
bubble.dataset.timestamp = message.date;
|
||||
|
||||
const loadPromises: Promise<any>[] = [];
|
||||
|
||||
if(message._ === 'messageService') {
|
||||
let action = message.action;
|
||||
let _ = action._;
|
||||
@ -1636,7 +1598,7 @@ export default class ChatBubbles {
|
||||
bubbleContainer.innerHTML = `<div class="service-msg">${message.rReply}</div>`;
|
||||
|
||||
if(updatePosition) {
|
||||
this.renderMessagesQueue(message, bubble, reverse);
|
||||
this.renderMessagesQueue(message, bubble, reverse, loadPromises);
|
||||
}
|
||||
|
||||
return bubble;
|
||||
@ -1832,13 +1794,14 @@ export default class ChatBubbles {
|
||||
middleware: this.getMiddleware(),
|
||||
isOut: our,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
chat: this.chat
|
||||
chat: this.chat,
|
||||
loadPromises
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const withTail = !isAndroid && !message.message && !withReplies;
|
||||
const withTail = !isAndroid && !message.message && !withReplies && USE_MEDIA_TAILS;
|
||||
if(withTail) bubble.classList.add('with-media-tail');
|
||||
wrapPhoto({
|
||||
photo,
|
||||
@ -1847,7 +1810,8 @@ export default class ChatBubbles {
|
||||
withTail,
|
||||
isOut,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware: this.getMiddleware()
|
||||
middleware: this.getMiddleware(),
|
||||
loadPromises
|
||||
});
|
||||
|
||||
break;
|
||||
@ -1858,7 +1822,7 @@ export default class ChatBubbles {
|
||||
|
||||
let webpage = messageMedia.webpage;
|
||||
////////this.log('messageMediaWebPage', webpage);
|
||||
if(webpage._ == 'webPageEmpty') {
|
||||
if(webpage._ === 'webPageEmpty') {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1895,7 +1859,8 @@ export default class ChatBubbles {
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware: this.getMiddleware(),
|
||||
isOut,
|
||||
group: CHAT_ANIMATION_GROUP
|
||||
group: CHAT_ANIMATION_GROUP,
|
||||
loadPromises
|
||||
});
|
||||
//}
|
||||
} else {
|
||||
@ -1945,7 +1910,7 @@ export default class ChatBubbles {
|
||||
bubble.classList.add('photo');
|
||||
|
||||
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');
|
||||
} else if(size.h > size.w) {
|
||||
bubble.classList.add('is-vertical-photo');
|
||||
@ -1959,7 +1924,8 @@ export default class ChatBubbles {
|
||||
boxHeight: mediaSizes.active.webpage.height,
|
||||
isOut,
|
||||
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;
|
||||
this.appPhotosManager.setAttachmentSize(doc, attachmentDiv, size, size, true);
|
||||
this.appPhotosManager.setAttachmentSize(doc, attachmentDiv, size, size);
|
||||
//let preloader = new ProgressivePreloader(attachmentDiv, false);
|
||||
bubbleContainer.style.height = attachmentDiv.style.height;
|
||||
bubbleContainer.style.width = attachmentDiv.style.width;
|
||||
@ -2019,10 +1985,11 @@ export default class ChatBubbles {
|
||||
middleware: this.getMiddleware(),
|
||||
isOut: our,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
chat: this.chat
|
||||
chat: this.chat,
|
||||
loadPromises
|
||||
});
|
||||
} 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');
|
||||
wrapVideo({
|
||||
doc,
|
||||
@ -2034,7 +2001,8 @@ export default class ChatBubbles {
|
||||
isOut,
|
||||
lazyLoadQueue: this.lazyLoadQueue,
|
||||
middleware: this.getMiddleware(),
|
||||
group: CHAT_ANIMATION_GROUP
|
||||
group: CHAT_ANIMATION_GROUP,
|
||||
loadPromises
|
||||
});
|
||||
}
|
||||
|
||||
@ -2045,7 +2013,8 @@ export default class ChatBubbles {
|
||||
message,
|
||||
bubble,
|
||||
messageDiv,
|
||||
chat: this.chat
|
||||
chat: this.chat,
|
||||
loadPromises
|
||||
});
|
||||
|
||||
if(newNameContainer) {
|
||||
@ -2260,7 +2229,7 @@ export default class ChatBubbles {
|
||||
if(updatePosition) {
|
||||
this.bubbleGroups.addBubble(bubble, message, reverse);
|
||||
|
||||
this.renderMessagesQueue(message, bubble, reverse);
|
||||
this.renderMessagesQueue(message, bubble, reverse, loadPromises);
|
||||
} else {
|
||||
this.bubbleGroups.updateGroupByMessageId(message.mid);
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export default class GifsMasonry {
|
||||
const doc = appDocsManager.getDoc(docId);
|
||||
|
||||
const promise = this.scrollPromise.then(() => {
|
||||
const promise = wrapVideo({
|
||||
const res = wrapVideo({
|
||||
doc,
|
||||
container: div as HTMLDivElement,
|
||||
lazyLoadQueue: null,
|
||||
@ -73,6 +73,7 @@ export default class GifsMasonry {
|
||||
noInfo: true,
|
||||
});
|
||||
|
||||
const promise = res.loadPromise;
|
||||
promise.finally(() => {
|
||||
const video = div.querySelector('video');
|
||||
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { debounce } from "../helpers/schedulers";
|
||||
import { logger, LogLevels } from "../lib/logger";
|
||||
import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector";
|
||||
import { DEBUG } from "../lib/mtproto/mtproto_config";
|
||||
import { findAndSpliceAll } from "../helpers/array";
|
||||
|
||||
type LazyLoadElementBase = {
|
||||
load: () => Promise<any>
|
||||
@ -42,14 +44,16 @@ export class LazyLoadQueueBase {
|
||||
public lock() {
|
||||
if(this.lockPromise) return;
|
||||
|
||||
const perf = performance.now();
|
||||
//const perf = performance.now();
|
||||
this.lockPromise = new Promise((resolve, reject) => {
|
||||
this.unlockResolve = resolve;
|
||||
});
|
||||
|
||||
this.lockPromise.then(() => {
|
||||
this.log('was locked for:', performance.now() - perf);
|
||||
});
|
||||
/* if(DEBUG) {
|
||||
this.lockPromise.then(() => {
|
||||
this.log('was locked for:', performance.now() - perf);
|
||||
});
|
||||
} */
|
||||
}
|
||||
|
||||
public unlock() {
|
||||
@ -68,7 +72,9 @@ export class LazyLoadQueueBase {
|
||||
|
||||
this.inProcess.add(item);
|
||||
|
||||
this.log('will load media', this.lockPromise, item);
|
||||
/* if(DEBUG) {
|
||||
this.log('will load media', this.lockPromise, item);
|
||||
} */
|
||||
|
||||
try {
|
||||
//await new Promise((resolve) => setTimeout(resolve, 2e3));
|
||||
@ -81,7 +87,9 @@ export class LazyLoadQueueBase {
|
||||
|
||||
this.inProcess.delete(item);
|
||||
|
||||
this.log('loaded media', item);
|
||||
/* if(DEBUG) {
|
||||
this.log('loaded media', item);
|
||||
} */
|
||||
|
||||
this.processQueue();
|
||||
}
|
||||
@ -104,7 +112,7 @@ export class LazyLoadQueueBase {
|
||||
|
||||
do {
|
||||
if(item) {
|
||||
this.queue.findAndSplice(i => i == item);
|
||||
this.queue.findAndSplice(i => i === item);
|
||||
} else {
|
||||
item = this.getItem();
|
||||
}
|
||||
@ -168,12 +176,12 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
||||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
} else {
|
||||
for(const item of this.inProcess) {
|
||||
if(item.div == el.div) {
|
||||
if(item.div === el.div && item.load === el.load) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -201,7 +209,8 @@ export class LazyLoadQueueIntersector extends LazyLoadQueueBase {
|
||||
}
|
||||
|
||||
public unobserve(el: HTMLElement) {
|
||||
this.queue.findAndSplice(i => i.div === el);
|
||||
findAndSpliceAll(this.queue, (i) => i.div === el);
|
||||
|
||||
this.intersector.unobserve(el);
|
||||
}
|
||||
}
|
||||
@ -218,12 +227,11 @@ export default class LazyLoadQueue extends LazyLoadQueueIntersector {
|
||||
this.log('isIntersecting', target);
|
||||
|
||||
// need for set element first if scrolled
|
||||
const item = this.queue.findAndSplice(i => i.div == target);
|
||||
if(item) {
|
||||
findAndSpliceAll(this.queue, (i) => i.div === target).forEach(item => {
|
||||
item.wasSeen = true;
|
||||
this.queue.unshift(item);
|
||||
//this.processQueue(item);
|
||||
}
|
||||
});
|
||||
|
||||
this.setProcessQueueTimeout();
|
||||
}
|
||||
@ -261,11 +269,11 @@ export class LazyLoadQueueRepeat extends LazyLoadQueueIntersector {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||
if(visible) {
|
||||
const item = this.queue.findAndSplice(i => i.div == target);
|
||||
this.queue.unshift(item || this._queue.get(target));
|
||||
} else {
|
||||
this.queue.findAndSplice(i => i.div == target);
|
||||
spliced.forEach(item => {
|
||||
this.queue.unshift(item || this._queue.get(target));
|
||||
});
|
||||
}
|
||||
|
||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||
@ -298,9 +306,11 @@ export class LazyLoadQueueRepeat2 extends LazyLoadQueueIntersector {
|
||||
super(parallelLimit);
|
||||
|
||||
this.intersector = new VisibilityIntersector((target, visible) => {
|
||||
const item = this.queue.findAndSplice(i => i.div == target);
|
||||
if(visible && item) {
|
||||
this.queue.unshift(item);
|
||||
const spliced = findAndSpliceAll(this.queue, (i) => i.div === target);
|
||||
if(visible && spliced.length) {
|
||||
spliced.forEach(item => {
|
||||
this.queue.unshift(item);
|
||||
});
|
||||
}
|
||||
|
||||
this.onVisibilityChange && this.onVisibilityChange(target, visible);
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { isInDOM, cancelEvent, attachClickEvent } from "../helpers/dom";
|
||||
import { CancellablePromise } from "../helpers/cancellablePromise";
|
||||
import SetTransition from "./singleTransition";
|
||||
|
||||
const TRANSITION_TIME = 200;
|
||||
|
||||
export default class ProgressivePreloader {
|
||||
public preloader: HTMLDivElement;
|
||||
@ -20,8 +23,8 @@ export default class ProgressivePreloader {
|
||||
|
||||
this.preloader.innerHTML = `
|
||||
<div class="you-spin-me-round">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="preloader-circular" viewBox="25 25 50 50">
|
||||
<circle class="preloader-path-new" cx="50" cy="50" r="${streamable ? 19 : 23}" fill="none" stroke-miterlimit="10"/>
|
||||
<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="${streamable ? '50' : '54'}" cy="${streamable ? '50' : '54'}" r="${streamable ? 19 : 24}" fill="none" stroke-miterlimit="10"/>
|
||||
</svg>
|
||||
</div>`;
|
||||
|
||||
@ -95,16 +98,20 @@ export default class ProgressivePreloader {
|
||||
}
|
||||
|
||||
this.detached = false;
|
||||
window.requestAnimationFrame(() => {
|
||||
/* window.requestAnimationFrame(() => {
|
||||
if(this.detached) return;
|
||||
this.detached = false;
|
||||
this.detached = false; */
|
||||
|
||||
elem[this.attachMethod](this.preloader);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
SetTransition(this.preloader, 'is-visible', true, TRANSITION_TIME);
|
||||
});
|
||||
|
||||
if(this.cancelable && reset) {
|
||||
this.setProgress(0);
|
||||
}
|
||||
});
|
||||
//});
|
||||
}
|
||||
|
||||
public detach() {
|
||||
@ -113,14 +120,18 @@ export default class ProgressivePreloader {
|
||||
//return;
|
||||
|
||||
if(this.preloader.parentElement) {
|
||||
/* setTimeout(() => */window.requestAnimationFrame(() => {
|
||||
if(!this.detached) return;
|
||||
this.detached = true;
|
||||
/* setTimeout(() => *///window.requestAnimationFrame(() => {
|
||||
/* if(!this.detached) return;
|
||||
this.detached = true; */
|
||||
|
||||
if(this.preloader.parentElement) {
|
||||
this.preloader.remove();
|
||||
}
|
||||
})/* , 5e3) */;
|
||||
//if(this.preloader.parentElement) {
|
||||
window.requestAnimationFrame(() => {
|
||||
SetTransition(this.preloader, 'is-visible', false, TRANSITION_TIME, () => {
|
||||
this.preloader.remove();
|
||||
});
|
||||
});
|
||||
//}
|
||||
//})/* , 5e3) */;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +148,7 @@ export default class ProgressivePreloader {
|
||||
try {
|
||||
const totalLength = this.circle.getTotalLength();
|
||||
//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) {}
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,11 @@ import RichTextProcessor from '../lib/richtextprocessor';
|
||||
import appImManager from '../lib/appManagers/appImManager';
|
||||
import Chat from './chat/chat';
|
||||
import { SearchSuperContext } from './appSearchSuper.';
|
||||
import rootScope from '../lib/rootScope';
|
||||
|
||||
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,
|
||||
container?: HTMLElement,
|
||||
message?: any,
|
||||
@ -43,7 +44,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
middleware?: () => boolean,
|
||||
lazyLoadQueue?: LazyLoadQueue,
|
||||
noInfo?: true,
|
||||
group?: string
|
||||
group?: string,
|
||||
onlyPreview?: boolean,
|
||||
withoutPreloader?: boolean,
|
||||
loadPromises?: Promise<any>[]
|
||||
}) {
|
||||
const isAlbumItem = !(boxWidth && boxHeight);
|
||||
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') {
|
||||
return wrapPhoto({
|
||||
const photoRes = wrapPhoto({
|
||||
photo: doc,
|
||||
message,
|
||||
container,
|
||||
@ -81,8 +90,14 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
withTail,
|
||||
isOut,
|
||||
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');
|
||||
@ -91,6 +106,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
} */
|
||||
|
||||
const video = document.createElement('video');
|
||||
video.classList.add('media-video');
|
||||
video.muted = true;
|
||||
video.setAttribute('playsinline', 'true');
|
||||
if(doc.type == 'round') {
|
||||
@ -155,53 +171,37 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
} else {
|
||||
video.autoplay = true; // для safari
|
||||
}
|
||||
|
||||
let img: HTMLImageElement;
|
||||
|
||||
let photoRes: ReturnType<typeof wrapPhoto>;
|
||||
if(message) {
|
||||
if(!canAutoplay) {
|
||||
return wrapPhoto({
|
||||
photo: doc,
|
||||
message,
|
||||
container,
|
||||
boxWidth,
|
||||
boxHeight,
|
||||
withTail,
|
||||
isOut,
|
||||
lazyLoadQueue,
|
||||
middleware
|
||||
});
|
||||
photoRes = wrapPhoto({
|
||||
photo: doc,
|
||||
message,
|
||||
container,
|
||||
boxWidth,
|
||||
boxHeight,
|
||||
withTail,
|
||||
isOut,
|
||||
lazyLoadQueue,
|
||||
middleware,
|
||||
withoutPreloader: true,
|
||||
loadPromises
|
||||
});
|
||||
|
||||
res.thumb = photoRes;
|
||||
|
||||
if(!canAutoplay || onlyPreview) {
|
||||
res.loadPromise = photoRes.loadPromises.full;
|
||||
return res;
|
||||
}
|
||||
|
||||
if(withTail) {
|
||||
img = wrapMediaWithTail(doc, message, container, boxWidth, boxHeight, isOut);
|
||||
} 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;
|
||||
const foreignObject = (photoRes.images.thumb || photoRes.images.full).parentElement;
|
||||
video.width = +foreignObject.getAttributeNS(null, 'width');
|
||||
video.height = +foreignObject.getAttributeNS(null, 'height');
|
||||
foreignObject.append(video);
|
||||
}
|
||||
}
|
||||
|
||||
if(!img?.parentElement) {
|
||||
} else { // * gifs masonry
|
||||
const gotThumb = appDocsManager.getThumb(doc, false);
|
||||
if(gotThumb) {
|
||||
gotThumb.promise.then(() => {
|
||||
@ -253,14 +253,10 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
|
||||
//if(doc.type == 'gif'/* || true */) {
|
||||
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => {
|
||||
if(img?.parentElement) {
|
||||
img.remove();
|
||||
}
|
||||
|
||||
/* if(!video.paused) {
|
||||
video.pause();
|
||||
} */
|
||||
if(doc.type != 'round' && group) {
|
||||
if(doc.type !== 'round' && group) {
|
||||
animationIntersector.addAnimation(video, group);
|
||||
}
|
||||
|
||||
@ -327,7 +323,9 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
|
||||
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) => {
|
||||
@ -344,13 +342,14 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru
|
||||
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,
|
||||
withTime?: boolean,
|
||||
fontWeight?: number,
|
||||
voiceAsMusic?: boolean,
|
||||
showSender?: boolean,
|
||||
searchContext?: SearchSuperContext
|
||||
searchContext?: SearchSuperContext,
|
||||
loadPromises?: Promise<any>[]
|
||||
}): HTMLElement {
|
||||
if(!fontWeight) fontWeight = 500;
|
||||
|
||||
@ -394,7 +393,8 @@ export function wrapDocument({message, withTime, fontWeight, voiceAsMusic, showS
|
||||
message: null,
|
||||
container: icoDiv,
|
||||
boxWidth: 54,
|
||||
boxHeight: 54
|
||||
boxHeight: 54,
|
||||
loadPromises
|
||||
});
|
||||
icoDiv.style.width = icoDiv.style.height = '';
|
||||
}
|
||||
@ -475,8 +475,12 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
|
||||
svg.classList.add('bubble__media-container', isOut ? 'is-out' : 'is-in');
|
||||
|
||||
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 height = +foreignObject.getAttributeNS(null, 'height');
|
||||
@ -525,7 +529,7 @@ function wrapMediaWithTail(photo: MyPhoto | MyDocument, message: {mid: number, m
|
||||
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,
|
||||
message: any,
|
||||
container: HTMLElement,
|
||||
@ -535,8 +539,23 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
isOut?: boolean,
|
||||
lazyLoadQueue?: LazyLoadQueue,
|
||||
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) {
|
||||
boxWidth = mediaSizes.active.regular.width;
|
||||
}
|
||||
@ -545,44 +564,50 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
boxHeight = mediaSizes.active.regular.height;
|
||||
}
|
||||
|
||||
let loadThumbPromise: Promise<any>;
|
||||
let thumbImage: HTMLImageElement;
|
||||
let image: HTMLImageElement;
|
||||
if(withTail) {
|
||||
image = wrapMediaWithTail(photo, message, container, boxWidth, boxHeight, isOut);
|
||||
} else {
|
||||
image = new Image();
|
||||
|
||||
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 thumbs = (photo as MyPhoto).sizes || (photo as MyDocument).thumbs;
|
||||
if(thumbs?.length && 'bytes' in thumbs[0]) {
|
||||
appPhotosManager.setAttachmentPreview(thumbs[0].bytes, container, false);
|
||||
}
|
||||
}
|
||||
|
||||
image = container.lastElementChild as HTMLImageElement;
|
||||
if(!image || image.tagName != 'IMG') {
|
||||
container.append(image = new Image());
|
||||
const gotThumb = appPhotosManager.getStrippedThumbIfNeeded(photo);
|
||||
if(gotThumb) {
|
||||
loadThumbPromise = gotThumb.loadPromise;
|
||||
thumbImage = gotThumb.image;
|
||||
thumbImage.classList.add('media-photo');
|
||||
container.append(thumbImage);
|
||||
}
|
||||
}
|
||||
|
||||
if(!((photo as MyPhoto).sizes || (photo as MyDocument).thumbs)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
image.classList.add('media-photo');
|
||||
|
||||
//console.log('wrapPhoto downloaded:', photo, photo.downloaded, container);
|
||||
|
||||
|
||||
const cacheContext = appPhotosManager.getCacheContext(photo);
|
||||
|
||||
const needFadeIn = (thumbImage || !cacheContext.downloaded) && rootScope.settings.animationsEnabled;
|
||||
if(needFadeIn) {
|
||||
image.classList.add('fade-in');
|
||||
}
|
||||
|
||||
let preloader: ProgressivePreloader;
|
||||
if(message?.media?.preloader) { // means upload
|
||||
message.media.preloader.attach(container, false);
|
||||
} else if(!cacheContext.downloaded) {
|
||||
preloader = new ProgressivePreloader(null, false, false, photo._ == 'document' ? 'prepend' : 'append');
|
||||
} else if(!cacheContext.downloaded && !withoutPreloader) {
|
||||
preloader = new ProgressivePreloader(null, false, false, 'prepend');
|
||||
}
|
||||
|
||||
let loadPromise: Promise<any>;
|
||||
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) :
|
||||
appPhotosManager.preloadPhoto(photo, size, lazyLoadQueue?.queueId);
|
||||
|
||||
@ -590,14 +615,56 @@ export function wrapPhoto({photo, message, container, boxWidth, boxHeight, withT
|
||||
preloader.attach(container, true, promise);
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
return loadPromise = promise.then(() => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if(cacheContext.downloaded) {
|
||||
loadThumbPromise = load();
|
||||
}
|
||||
|
||||
return cacheContext.downloaded || !lazyLoadQueue ? load() : (lazyLoadQueue.push({div: container, load/* : load, wasSeen: true */}), Promise.resolve());
|
||||
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}: {
|
||||
@ -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,
|
||||
attachmentDiv: HTMLElement,
|
||||
middleware?: () => boolean,
|
||||
lazyLoadQueue?: LazyLoadQueue,
|
||||
uploading?: boolean,
|
||||
isOut: boolean,
|
||||
chat: Chat
|
||||
chat: Chat,
|
||||
loadPromises?: Promise<any>[]
|
||||
}) {
|
||||
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
|
||||
|
||||
@ -974,7 +1042,8 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
|
||||
isOut,
|
||||
lazyLoadQueue,
|
||||
middleware,
|
||||
size
|
||||
size,
|
||||
loadPromises
|
||||
});
|
||||
} else {
|
||||
wrapVideo({
|
||||
@ -986,19 +1055,21 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo
|
||||
withTail: false,
|
||||
isOut,
|
||||
lazyLoadQueue,
|
||||
middleware
|
||||
middleware,
|
||||
loadPromises
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat}: {
|
||||
export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat, loadPromises}: {
|
||||
albumMustBeRenderedFull: boolean,
|
||||
message: any,
|
||||
messageDiv: HTMLElement,
|
||||
bubble: HTMLElement,
|
||||
uploading?: boolean,
|
||||
chat: Chat
|
||||
chat: Chat,
|
||||
loadPromises?: Promise<any>[]
|
||||
}) {
|
||||
let nameContainer: HTMLDivElement;
|
||||
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 doc = message.media.document;
|
||||
const div = wrapDocument({
|
||||
message
|
||||
message,
|
||||
loadPromises
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
@ -13,4 +13,14 @@ export function listMergeSorted(list1: any[] = [], list2: any[] = []) {
|
||||
return result;
|
||||
}
|
||||
|
||||
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 appDownloadManager, { DownloadBlob } from './appDownloadManager';
|
||||
import appPhotosManager from './appPhotosManager';
|
||||
import blur from '../../helpers/blur';
|
||||
|
||||
export type MyDocument = Document.document;
|
||||
|
||||
@ -246,7 +247,7 @@ export class AppDocsManager {
|
||||
if('bytes' in thumb) {
|
||||
// * exclude from state
|
||||
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 {
|
||||
//return this.getFileURL(doc, false, thumb);
|
||||
promise = this.downloadDoc(doc, thumb);
|
||||
|
@ -19,7 +19,7 @@ import appUsersManager from "./appUsersManager";
|
||||
#ce671b 5 orange
|
||||
*/
|
||||
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];
|
||||
|
||||
export type PeerType = 'channel' | 'chat' | 'megagroup' | 'group' | 'saved';
|
||||
|
@ -12,6 +12,8 @@ import { MyDocument } from "./appDocsManager";
|
||||
import appDownloadManager from "./appDownloadManager";
|
||||
import appUsersManager from "./appUsersManager";
|
||||
import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config";
|
||||
import blur from "../../helpers/blur";
|
||||
import { renderImageFromUrl } from "../../components/misc";
|
||||
|
||||
export type MyPhoto = Photo.photo;
|
||||
|
||||
@ -106,7 +108,7 @@ export class AppPhotosManager {
|
||||
bestPhotoSize = photoSize;
|
||||
|
||||
const {w, h} = calcImageInBox(photoSize.w, photoSize.h, width, height);
|
||||
if(w == width || h == height) {
|
||||
if(w === width || h === height) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -189,47 +191,28 @@ export class AppPhotosManager {
|
||||
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) {
|
||||
let url = this.getPreviewURLFromBytes(bytes, isSticker);
|
||||
public getImageFromStrippedThumb(thumb: PhotoSize.photoCachedSize | PhotoSize.photoStrippedSize) {
|
||||
const url = this.getPreviewURLFromThumb(thumb, false);
|
||||
|
||||
if(background) {
|
||||
let img = new Image();
|
||||
img.src = url;
|
||||
img.addEventListener('load', () => {
|
||||
element.style.backgroundImage = 'url(' + url + ')';
|
||||
const image = new Image();
|
||||
image.classList.add('thumbnail');
|
||||
|
||||
const loadPromise = blur(url).then(url => {
|
||||
return new Promise<any>((resolve) => {
|
||||
renderImageFromUrl(image, url, resolve);
|
||||
});
|
||||
|
||||
return element;
|
||||
} else {
|
||||
if(element instanceof HTMLImageElement) {
|
||||
element.src = url;
|
||||
return element;
|
||||
} else {
|
||||
let img = new Image();
|
||||
|
||||
img.src = url;
|
||||
element.append(img);
|
||||
return img;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {image, loadPromise};
|
||||
}
|
||||
|
||||
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);
|
||||
//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 height: number;
|
||||
if(photo._ == 'document') {
|
||||
if(photo._ === 'document') {
|
||||
width = photo.w || 512;
|
||||
height = photo.h || 512;
|
||||
} else {
|
||||
@ -250,17 +233,36 @@ export class AppPhotosManager {
|
||||
|
||||
return photoSize;
|
||||
}
|
||||
|
||||
public getStrippedThumbIfNeeded(photo: MyPhoto | MyDocument): ReturnType<AppPhotosManager['getImageFromStrippedThumb']> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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';
|
||||
const isMyDocument = photo._ === 'document';
|
||||
|
||||
if(!photoSize || photoSize._ == 'photoSizeEmpty') {
|
||||
if(!photoSize || photoSize._ === 'photoSizeEmpty') {
|
||||
//console.error('no photoSize by photo:', photo);
|
||||
throw new Error('photoSizeEmpty!');
|
||||
}
|
||||
|
||||
// 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 ? {
|
||||
_: isMyDocument ? 'inputDocumentFileLocation' : 'inputPhotoFileLocation',
|
||||
id: photo.id,
|
||||
@ -277,12 +279,26 @@ export class AppPhotosManager {
|
||||
|
||||
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> {
|
||||
const photo = this.getPhoto(photoId);
|
||||
|
||||
// @ts-ignore
|
||||
if(!photo || photo._ == 'photoEmpty') {
|
||||
if(!photo || photo._ === 'photoEmpty') {
|
||||
throw new Error('preloadPhoto photoEmpty!');
|
||||
}
|
||||
|
||||
@ -325,7 +341,7 @@ export class AppPhotosManager {
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -351,7 +367,7 @@ export class AppPhotosManager {
|
||||
|
||||
public savePhotoFile(photo: MyPhoto | MyDocument, queueId?: number) {
|
||||
const fullPhotoSize = this.choosePhotoSize(photo, 0xFFFF, 0xFFFF);
|
||||
if(!(fullPhotoSize._ == 'photoSize' || fullPhotoSize._ == 'photoSizeProgressive')) {
|
||||
if(!(fullPhotoSize._ === 'photoSize' || fullPhotoSize._ === 'photoSizeProgressive')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,51 @@
|
||||
avatar-element {
|
||||
--size: 54px;
|
||||
--multiplier: 1;
|
||||
--color-top: var(--peer-avatar-blue-top);
|
||||
--color-bottom: var(--peer-avatar-blue-bottom);
|
||||
color: #fff;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
line-height: var(--size);
|
||||
border-radius: 50%;
|
||||
background-color: $color-blue;
|
||||
background: linear-gradient(var(--color-top), var(--color-bottom));
|
||||
text-align: center;
|
||||
font-size: calc(1.25rem / var(--multiplier));
|
||||
/* overflow: hidden; */
|
||||
position: relative;
|
||||
user-select: none;
|
||||
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 {
|
||||
font-size: calc(25px / var(--multiplier));
|
||||
|
@ -1039,7 +1039,7 @@ $chat-helper-size: 39px;
|
||||
}
|
||||
|
||||
&-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
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
img, video {
|
||||
max-width: 100%;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
transition: opacity .3s ease;
|
||||
|
||||
body.animation-level-0 & {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.download {
|
||||
@ -633,10 +627,10 @@ $bubble-margin: .25rem;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preloader-container {
|
||||
z-index: 1;
|
||||
}
|
||||
.preloader-container {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:not(.sticker) {
|
||||
|
@ -28,10 +28,24 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
width: 54px;
|
||||
height: 54px;
|
||||
display: flex;
|
||||
/* 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 {
|
||||
stroke-dasharray: 5, 200;
|
||||
stroke-dasharray: 5, 149.82;
|
||||
stroke-dashoffset: 0;
|
||||
transition: stroke-dasharray 400ms ease-in-out;
|
||||
stroke-linecap: round;
|
||||
stroke: white;
|
||||
stroke-width: 1.5;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
&.preloader-swing {
|
||||
@ -68,9 +82,7 @@
|
||||
}
|
||||
|
||||
.preloader-path-new {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dashoffset: 0;
|
||||
animation: dashNew 1.5s ease-in-out infinite/* , color 6s ease-in-out infinite */;
|
||||
animation: dashNew 1.5s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +123,7 @@
|
||||
background-color: #fff;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate3d(-50%,-50%,0);
|
||||
transform: translate3d(-50%, -50%, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,16 +151,16 @@
|
||||
|
||||
@keyframes dashNew {
|
||||
0% {
|
||||
stroke-dasharray: 1, 200;
|
||||
stroke-dasharray: 1, 149.82; // 149.82 = getTotalLength
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -35px;
|
||||
stroke-dasharray: 112.36, 149.82; // 112.36 = 149.82 * .75
|
||||
stroke-dashoffset: -38; // bruted
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 89, 200;
|
||||
stroke-dashoffset: -286%;
|
||||
stroke-dasharray: 112.36, 149.82;
|
||||
stroke-dashoffset: -149.82; // totalLength
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,15 +361,6 @@
|
||||
|
||||
.grid-item {
|
||||
overflow: hidden;
|
||||
|
||||
&-media {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease;
|
||||
|
||||
html:not(.is-mac) &.thumbnail {
|
||||
filter: blur(7px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* span.video-play {
|
||||
|
@ -96,6 +96,22 @@ $chat-padding-handhelds: .5rem;
|
||||
--messages-secondary-text-size: calc(var(--messages-text-size) - 1px);
|
||||
--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) {
|
||||
--right-column-width: 100vw;
|
||||
--esg-sticker-size: 68px;
|
||||
@ -859,7 +875,6 @@ img.emoji {
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
@ -1161,3 +1176,19 @@ middle-ellipsis-element {
|
||||
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