|
|
|
@ -4,27 +4,29 @@
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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 {
@@ -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}; |
|
|
|
|
} */ |
|
|
|
|
} |
|
|
|
|