Telegram Web K with changes to work inside I2P https://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

183 lines
4.3 KiB

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import {IS_MOBILE} from '../environment/userAgent';
import {animate} from '../helpers/animation';
import liteMode from '../helpers/liteMode';
import {Middleware} from '../helpers/middleware';
import clamp from '../helpers/number/clamp';
import animationIntersector, {AnimationItemGroup, AnimationItemWrapper} from './animationIntersector';
type DotRendererDot = {
x: number,
y: number,
opacity: number,
radius: number
mOpacity: number,
adding: boolean,
counter: number,
path: Path2D
};
export default class DotRenderer implements AnimationItemWrapper {
public canvas: HTMLCanvasElement;
private context: CanvasRenderingContext2D;
private dots: DotRendererDot[];
public paused: boolean;
public autoplay: boolean;
public tempId: number;
private dpr: number;
public loop: boolean = true;
constructor(
private width: number,
private height: number,
private multiply?: number
) {
const canvas = this.canvas = document.createElement('canvas');
const dpr = this.dpr = window.devicePixelRatio;
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.classList.add('canvas-thumbnail', 'canvas-dots');
this.paused = true;
this.autoplay = true;
this.tempId = 0;
this.context = canvas.getContext('2d');
}
private prepare() {
let count = Math.round(this.width * this.height / (35 * (IS_MOBILE ? 2 : 1)));
count *= this.multiply || 1;
count = Math.min(!liteMode.isAvailable('chat_spoilers') ? 400 : IS_MOBILE ? 1000 : 2200, count);
count = Math.round(count);
const dots: DotRendererDot[] = this.dots = new Array(count);
for(let i = 0; i < count; ++i) {
dots[i] = this.generateDot();
}
}
private generateDot(adding?: boolean): DotRendererDot {
const x = Math.floor(Math.random() * this.canvas.width);
const y = Math.floor(Math.random() * this.canvas.height);
const opacity = adding ? 0 : Math.random();
const radius = (Math.random() >= .8 ? 1 : 0.5) * this.dpr;
const path = new Path2D();
path.arc(x, y, radius, 0, 2 * Math.PI, false);
return {
x,
y,
opacity,
radius,
mOpacity: opacity,
adding: adding ?? Math.random() >= .5,
counter: 0,
path
};
}
private draw() {
const {context, canvas, dots} = this;
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = '#fff';
const add = 0.02;
for(let i = 0, length = dots.length; i < length; ++i) {
const dot = dots[i];
const addOpacity = dot.adding ? add : -add;
dot.mOpacity += addOpacity;
// if(dot.mOpacity <= 0) dot.mOpacity = dot.opacity;
// const easedOpacity = easing(dot.mOpacity);
const easedOpacity = clamp(dot.mOpacity, 0, 1);
context.globalAlpha = easedOpacity;
context.fill(dot.path);
if(dot.mOpacity <= 0) {
dot.adding = true;
if(++dot.counter >= 1) {
dots[i] = this.generateDot(dot.adding);
}
} else if(dot.mOpacity >= 1) {
dot.adding = false;
}
}
}
public remove() {
this.pause();
}
public pause() {
if(this.paused) {
return;
}
this.paused = true;
++this.tempId;
}
public renderFirstFrame() {
if(!this.dots) {
this.prepare();
}
this.draw();
}
public play() {
if(!this.paused) {
return;
}
this.paused = false;
const tempId = ++this.tempId;
if(!this.dots) {
this.prepare();
}
animate(() => {
if(this.tempId !== tempId || this.paused) {
return false;
}
this.draw();
return true;
});
}
public static create({
width,
height,
middleware,
animationGroup,
multiply
}: {
width: number,
height: number,
middleware: Middleware,
animationGroup: AnimationItemGroup,
multiply?: number
}) {
const dotRenderer = new DotRenderer(width, height, multiply);
dotRenderer.renderFirstFrame();
animationIntersector.addAnimation({
animation: dotRenderer,
group: animationGroup,
observeElement: dotRenderer.canvas,
controlled: middleware
});
return dotRenderer;
}
}