Merge branch 'bg-fixes'

This commit is contained in:
Eduard Kuzmenko 2022-04-09 02:09:53 +03:00
commit ec7c400b11
5 changed files with 164 additions and 55 deletions

View File

@ -85,6 +85,7 @@ export default class Chat extends EventListenerBase<{
public patternCanvas: HTMLCanvasElement;
public backgroundTempId: number;
public setBackgroundPromise: Promise<void>;
// public renderDarkPattern: () => Promise<void>;
constructor(
public appImManager: AppImManager,
@ -159,6 +160,7 @@ export default class Chat extends EventListenerBase<{
this.patternRenderer =
this.gradientCanvas =
this.patternCanvas =
// this.renderDarkPattern =
undefined;
const intensity = theme.background.intensity && theme.background.intensity / 100;
@ -179,11 +181,28 @@ export default class Chat extends EventListenerBase<{
patternRenderer = this.patternRenderer = ChatBackgroundPatternRenderer.getInstance({
url,
width: rect.width,
height: rect.height
height: rect.height,
mask: isDarkPattern
});
patternCanvas = this.patternCanvas = patternRenderer.createCanvas();
patternCanvas.classList.add('chat-background-item-canvas', 'chat-background-item-pattern-canvas');
if(isDarkPattern) {
item.classList.add('is-dark');
}
// if(isDarkPattern) {
// this.renderDarkPattern = () => {
// return patternRenderer.exportCanvasPatternToImage(patternCanvas).then(url => {
// if(this.backgroundTempId !== tempId) {
// return;
// }
// gradientCanvas.style.webkitMaskImage = `url(${url})`;
// });
// };
// }
} else if(theme.background.slug) {
item.classList.add('is-image');
}
@ -236,7 +255,11 @@ export default class Chat extends EventListenerBase<{
return;
}
const append = [gradientCanvas, isDarkPattern ? undefined : patternCanvas].filter(Boolean);
const append = [
gradientCanvas,
// isDarkPattern && this.renderDarkPattern ? undefined : patternCanvas
patternCanvas
].filter(Boolean);
if(append.length) {
item.append(...append);
}
@ -261,18 +284,16 @@ export default class Chat extends EventListenerBase<{
if(patternRenderer) {
const renderPatternPromise = patternRenderer.renderToCanvas(patternCanvas);
renderPatternPromise.then(() => {
let promise: Promise<any>;
if(isDarkPattern) {
promise = patternRenderer.exportCanvasPatternToImage(patternCanvas).then(url => {
if(this.backgroundTempId !== tempId) {
return;
}
gradientCanvas.style.webkitMaskImage = `url(${url})`;
});
} else {
promise = Promise.resolve();
if(this.backgroundTempId !== tempId) {
return;
}
let promise: Promise<any>;
// if(isDarkPattern && this.renderDarkPattern) {
// promise = this.renderDarkPattern();
// } else {
promise = Promise.resolve();
// }
promise.then(cb);
});

View File

@ -4,27 +4,29 @@
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { IS_SAFARI } from "../../environment/userAgent";
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import deepEqual from "../../helpers/object/deepEqual";
import { renderImageFromUrlPromise } from "../../helpers/dom/renderImageFromUrl";
import mediaSizes, { ScreenSize } from "../../helpers/mediaSizes";
type ChatBackgroundPatternRendererInitOptions = {
url: string,
width: number,
height: number
height: number,
mask?: boolean
};
export default class ChatBackgroundPatternRenderer {
private static INSTANCES: ChatBackgroundPatternRenderer[] = [];
private pattern: CanvasPattern;
// private pattern: CanvasPattern;
private objectUrl: string;
private options: ChatBackgroundPatternRendererInitOptions;
private canvases: Set<HTMLCanvasElement>;
private createCanvasPatternPromise: Promise<void>;
private exportCanvasPatternToImagePromise: Promise<string>;
// private img: HTMLImageElement;
// private createCanvasPatternPromise: Promise<CanvasPattern>;
// private exportCanvasPatternToImagePromise: Promise<string>;
private renderImageFromUrlPromise: Promise<HTMLImageElement>;
private img: HTMLImageElement;
constructor() {
this.canvases = new Set();
@ -45,36 +47,54 @@ export default class ChatBackgroundPatternRenderer {
}
public init(options: ChatBackgroundPatternRendererInitOptions) {
// if(this.options) {
// if(this.options.width !== options.width || this.options.height !== options.height) {
// this.createCanvasPatternPromise =
// this.pattern =
// this.exportCanvasPatternToImagePromise =
// undefined;
// }
// }
this.options = options;
}
public renderToCanvas(canvas: HTMLCanvasElement) {
return this.createCanvasPattern(canvas).then(() => {
// return this.createCanvasPattern(canvas).then(() => {
// return this.fillCanvas(canvas);
// });
return this.renderImageFromUrl(this.options.url).then(() => {
return this.fillCanvas(canvas);
});
}
private createCanvasPattern(canvas: HTMLCanvasElement) {
private renderImageFromUrl(url: string) {
if(this.renderImageFromUrlPromise) return this.renderImageFromUrlPromise;
const img = this.img = document.createElement('img');
img.crossOrigin = 'anonymous';
return this.renderImageFromUrlPromise = renderImageFromUrlPromise(img, url, false).then(() => img);
}
/* private createCanvasPattern(canvas: HTMLCanvasElement) {
if(this.createCanvasPatternPromise) return this.createCanvasPatternPromise;
return this.createCanvasPatternPromise = new Promise((resolve) => {
const img = document.createElement('img');
img.crossOrigin = 'anonymous';
renderImageFromUrlPromise(img, this.options.url, false).then(() => {
let createPatternFrom: HTMLImageElement | HTMLCanvasElement;
if(IS_SAFARI) {
const canvas = createPatternFrom = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
} else {
createPatternFrom = img;
}
// this.img = img;
this.pattern = canvas.getContext('2d').createPattern(createPatternFrom, 'repeat-x');
resolve();
});
return this.createCanvasPatternPromise = this.renderImageFromUrl(this.options.url).then((img) => {
let createPatternFrom: HTMLImageElement | HTMLCanvasElement;
if(IS_SAFARI) {
const canvas = createPatternFrom = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
} else {
createPatternFrom = img;
}
const perf = performance.now();
this.pattern = canvas.getContext('2d').createPattern(createPatternFrom, 'repeat-x');
console.warn('creating pattern time:', performance.now() - perf);
return this.pattern;
});
}
@ -86,7 +106,7 @@ export default class ChatBackgroundPatternRenderer {
resolve(newUrl);
}, 'image/png');
});
}
} */
public cleanup(canvas: HTMLCanvasElement) {
this.canvases.delete(canvas);
@ -102,14 +122,42 @@ export default class ChatBackgroundPatternRenderer {
public fillCanvas(canvas: HTMLCanvasElement) {
const context = canvas.getContext('2d');
context.fillStyle = this.pattern;
context.fillRect(0, 0, canvas.width, canvas.height);
// context.drawImage(this.img, 0, 0, canvas.width, canvas.height);
if(context.fillStyle instanceof CanvasPattern) {
context.clearRect(0, 0, canvas.width, canvas.height);
}
// const perf = performance.now();
const img = this.img;
let imageWidth = img.width, imageHeight = img.height;
// if(imageHeight < canvas.height) {
const ratio = canvas.height / imageHeight;
imageWidth *= ratio;
imageHeight = canvas.height;
// }
if(this.options.mask) {
context.fillStyle = '#000';
context.fillRect(0, 0, canvas.width, canvas.height);
context.globalCompositeOperation = 'destination-out';
} else {
context.globalCompositeOperation = 'source-over';
}
for(let x = 0; x < canvas.width; x += imageWidth) {
for(let y = 0; y < canvas.height; y += imageHeight) {
context.drawImage(img, x, y, imageWidth, imageHeight);
}
}
// context.fillStyle = this.pattern;
// context.fillRect(0, 0, canvas.width, canvas.height);
// console.warn('fill canvas time', performance.now() - perf);
}
public setCanvasDimensions(canvas: HTMLCanvasElement) {
canvas.width = this.options.width * window.devicePixelRatio;
canvas.height = this.options.height * window.devicePixelRatio * 1.5;
const devicePixelRatio = Math.min(2, window.devicePixelRatio);
canvas.width = this.options.width * devicePixelRatio;
canvas.height = this.options.height * devicePixelRatio * (mediaSizes.activeScreen === ScreenSize.large ? 1.5 : 1);
}
public createCanvas() {
@ -118,4 +166,34 @@ export default class ChatBackgroundPatternRenderer {
this.setCanvasDimensions(canvas);
return canvas;
}
public resize(width: number, height: number) {
this.init({
...this.options,
width,
height
});
const promises: Promise<any>[] = [];
for(const canvas of this.canvases) {
this.setCanvasDimensions(canvas);
promises.push(this.renderToCanvas(canvas));
}
return Promise.all(promises);
}
public static resizeInstances(width: number, height: number) {
return Promise.all(this.INSTANCES.map(instance => instance.resize(width, height)));
}
/* public setResizeMode(resizing: boolean) {
const canvases = Array.from(this.canvases);
const canvas = canvases[canvases.length - 1];
canvas.style.display = resizing ? 'none' : '';
const img = this.img;
img.style.display = resizing ? '' : 'none';
return {img, canvas};
} */
}

View File

@ -61,7 +61,7 @@ export default function renderImageFromUrl(
}
export function renderImageFromUrlPromise(elem: Parameters<typeof renderImageFromUrl>[0], url: string, useCache?: boolean) {
return new Promise((resolve) => {
return new Promise<Event>((resolve) => {
renderImageFromUrl(elem, url, resolve, useCache);
});
}

View File

@ -89,6 +89,7 @@ import getObjectKeysAndSort from '../../helpers/object/getObjectKeysAndSort';
import type GroupCallInstance from '../calls/groupCallInstance';
import type CallInstance from '../calls/callInstance';
import numberThousandSplitter from '../../helpers/number/numberThousandSplitter';
import ChatBackgroundPatternRenderer from '../../components/chat/patternRenderer';
//console.log('appImManager included33!');
@ -234,11 +235,17 @@ export class AppImManager {
this.appendEmojiAnimationContainer(to);
});
const resizeBackgroundDebounced = debounce(() => {
this.setBackground(this.lastBackgroundUrl, false);
}, 200, false, true);
mediaSizes.addEventListener('resize', () => {
resizeBackgroundDebounced();
// const perf = performance.now();
const rect = this.chatsContainer.getBoundingClientRect();
ChatBackgroundPatternRenderer.resizeInstances(rect.width, rect.height).then(() => {
// this.log.warn('resize bg time:', performance.now() - perf);
// for(const chat of this.chats) {
// if(chat.renderDarkPattern) {
// chat.renderDarkPattern();
// }
// }
});
});
rootScope.addEventListener('history_focus', (e) => {

View File

@ -691,8 +691,11 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
align-items: center;
justify-content: center;
// mix-blend-mode: overlay;
height: 150%;
top: -25%;
@include respond-to(medium-screens) {
height: 150%;
top: -25%;
}
}
@include animation-level(2) {
@ -727,7 +730,7 @@ $background-transition-total-time: #{$input-transition-time - $background-transi
width: 100%;
}
&-pattern-canvas {
&:not(.is-dark) &-pattern-canvas {
mix-blend-mode: overlay;
// height: 100%;
}