200 lines
6.2 KiB
TypeScript
200 lines
6.2 KiB
TypeScript
/*
|
||
* https://github.com/morethanwords/tweb
|
||
* Copyright (C) 2019-2021 Eduard Kuzmenko
|
||
* https://github.com/morethanwords/tweb/blob/master/LICENSE
|
||
*/
|
||
|
||
import findUpClassName from "../helpers/dom/findUpClassName";
|
||
import {isTouchSupported} from "../helpers/touchSupport";
|
||
import rootScope from "../lib/rootScope";
|
||
|
||
let rippleClickId = 0;
|
||
export function ripple(elem: HTMLElement, callback: (id: number) => Promise<boolean | void> = () => Promise.resolve(), onEnd: (id: number) => void = null, prepend = false) {
|
||
//return;
|
||
if(elem.querySelector('.c-ripple')) return;
|
||
elem.classList.add('rp');
|
||
|
||
let r = document.createElement('div');
|
||
r.classList.add('c-ripple');
|
||
|
||
const isSquare = elem.classList.contains('rp-square');
|
||
if(isSquare) {
|
||
r.classList.add('is-square');
|
||
}
|
||
|
||
elem[prepend ? 'prepend' : 'append'](r);
|
||
|
||
let handler: () => void;
|
||
//let animationEndPromise: Promise<number>;
|
||
const drawRipple = (clientX: number, clientY: number) => {
|
||
const startTime = Date.now();
|
||
const elem = document.createElement('div');
|
||
|
||
const clickId = rippleClickId++;
|
||
|
||
//console.log('ripple drawRipple');
|
||
|
||
const duration = +window.getComputedStyle(r).getPropertyValue('--ripple-duration').replace('s', '') * 1000;
|
||
//console.log('ripple duration', duration);
|
||
|
||
handler = () => {
|
||
//handler = () => animationEndPromise.then((duration) => {
|
||
//console.log('ripple animation was:', duration);
|
||
|
||
//const duration = isSquare || mediaSizes.isMobile ? 200 : 700;
|
||
//return;
|
||
let elapsedTime = Date.now() - startTime;
|
||
if(elapsedTime < duration) {
|
||
let delay = Math.max(duration - elapsedTime, duration / 2);
|
||
setTimeout(() => elem.classList.add('hiding'), Math.max(delay - duration / 2, 0));
|
||
|
||
setTimeout(() => {
|
||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||
elem.remove();
|
||
if(onEnd) onEnd(clickId);
|
||
}, delay);
|
||
} else {
|
||
elem.classList.add('hiding');
|
||
setTimeout(() => {
|
||
//console.log('ripple elapsedTime total pre-remove:', Date.now() - startTime);
|
||
elem.remove();
|
||
if(onEnd) onEnd(clickId);
|
||
}, duration / 2);
|
||
}
|
||
|
||
if(!isTouchSupported) {
|
||
window.removeEventListener('contextmenu', handler);
|
||
}
|
||
|
||
handler = null;
|
||
touchStartFired = false;
|
||
};
|
||
//});
|
||
|
||
callback && callback(clickId);
|
||
|
||
/* callback().then((bad) => {
|
||
if(bad) {
|
||
span.remove();
|
||
return;
|
||
} */
|
||
|
||
//console.log('ripple after promise', Date.now() - startTime);
|
||
//console.log('ripple tooSlow:', tooSlow);
|
||
/* if(tooSlow) {
|
||
span.remove();
|
||
return;
|
||
} */
|
||
|
||
window.requestAnimationFrame(() => {
|
||
let rect = r.getBoundingClientRect();
|
||
elem.classList.add('c-ripple__circle');
|
||
|
||
let clickX = clientX - rect.left;
|
||
let clickY = clientY - rect.top;
|
||
|
||
let size: number, clickPos: number;
|
||
if(rect.width > rect.height) {
|
||
size = rect.width;
|
||
clickPos = clickX;
|
||
} else {
|
||
size = rect.height;
|
||
clickPos = clickY;
|
||
}
|
||
|
||
let offsetFromCenter = clickPos > (size / 2) ? size - clickPos : clickPos;
|
||
size = size - offsetFromCenter;
|
||
size *= 1.1;
|
||
|
||
// center of circle
|
||
let x = clickX - size / 2;
|
||
let y = clickY - size / 2;
|
||
|
||
//console.log('ripple click', offsetFromCenter, size, clickX, clickY);
|
||
|
||
elem.style.width = elem.style.height = size + 'px';
|
||
elem.style.left = x + 'px';
|
||
elem.style.top = y + 'px';
|
||
|
||
// нижний код выполняется с задержкой
|
||
/* animationEndPromise = new Promise((resolve) => {
|
||
span.addEventListener('animationend', () => {
|
||
// 713 -> 700
|
||
resolve(((Date.now() - startTime) / 100 | 0) * 100);
|
||
}, {once: true});
|
||
}); */
|
||
|
||
// нижний код не всегда включает анимацию ПРИ КЛИКЕ НА ТАЧПАД БЕЗ ТАПТИК ЭНЖИНА
|
||
/* span.style.display = 'none';
|
||
r.append(span);
|
||
duration = +window.getComputedStyle(span).getPropertyValue('animation-duration').replace('s', '') * 1000;
|
||
span.style.display = ''; */
|
||
|
||
r.append(elem);
|
||
|
||
//r.classList.add('active');
|
||
//handler();
|
||
});
|
||
//});
|
||
};
|
||
|
||
// TODO: rename this variable
|
||
let touchStartFired = false;
|
||
if(isTouchSupported) {
|
||
let touchEnd = () => {
|
||
handler && handler();
|
||
};
|
||
|
||
elem.addEventListener('touchstart', (e) => {
|
||
if(!rootScope.settings.animationsEnabled) {
|
||
return;
|
||
}
|
||
|
||
//console.log('ripple touchstart', e);
|
||
if(e.touches.length > 1
|
||
|| touchStartFired
|
||
|| (['BUTTON', 'A'].includes((e.target as HTMLElement).tagName) && e.target !== elem)
|
||
|| findUpClassName(e.target as HTMLElement, 'c-ripple') !== r) {
|
||
return;
|
||
}
|
||
|
||
//console.log('touchstart', e);
|
||
touchStartFired = true;
|
||
|
||
let {clientX, clientY} = e.touches[0];
|
||
drawRipple(clientX, clientY);
|
||
elem.addEventListener('touchend', touchEnd, {once: true});
|
||
|
||
window.addEventListener('touchmove', (e) => {
|
||
e.cancelBubble = true;
|
||
e.stopPropagation();
|
||
touchEnd();
|
||
elem.removeEventListener('touchend', touchEnd);
|
||
}, {once: true});
|
||
}, {passive: true});
|
||
} else {
|
||
elem.addEventListener('mousedown', (e) => {
|
||
if(![0, 2].includes(e.button)) { // only left and right buttons
|
||
return;
|
||
}
|
||
|
||
if(!rootScope.settings.animationsEnabled) {
|
||
return;
|
||
}
|
||
//console.log('ripple mousedown', e, e.target, findUpClassName(e.target as HTMLElement, 'c-ripple') === r);
|
||
|
||
if(elem.dataset.ripple === '0' || findUpClassName(e.target as HTMLElement, 'c-ripple') !== r || (e.target as HTMLElement).tagName === 'A') {
|
||
return false;
|
||
} else if(touchStartFired) {
|
||
touchStartFired = false;
|
||
return false;
|
||
}
|
||
|
||
let {clientX, clientY} = e;
|
||
drawRipple(clientX, clientY);
|
||
window.addEventListener('mouseup', handler, {once: true});
|
||
window.addEventListener('contextmenu', handler, {once: true});
|
||
});
|
||
}
|
||
}
|