2025-09-30 16:52:04 +02:00

166 lines
3.5 KiB
TypeScript

import { memo } from '../../lib/teact/teact';
import buildClassName from '../../util/buildClassName';
import buildStyle from '../../util/buildStyle';
import styles from './Sparkles.module.scss';
type ButtonParameters = {
preset: 'button';
};
type ProgressParameters = {
preset: 'progress';
};
type PresetParameters = ButtonParameters | ProgressParameters;
type OwnProps = {
className?: string;
style?: string;
noAnimation?: boolean;
} & PresetParameters;
const SYMBOL = '✦';
const ANIMATION_DURATION = 5;
// Values are in percents
const BUTTON_POSITIONS = [{
x: 20,
y: 0,
size: 100,
durationShift: 10,
}, {
x: 15,
y: 15,
size: 75,
durationShift: 70,
}, {
x: 10,
y: 35,
size: 75,
durationShift: 90,
}, {
x: 20,
y: 70,
size: 125,
durationShift: 30,
}, {
x: 40,
y: 10,
size: 125,
durationShift: 0,
}, {
x: 45,
y: 60,
size: 75,
durationShift: 60,
}, {
x: 60,
y: -10,
size: 100,
durationShift: 20,
}, {
x: 55,
y: 40,
size: 75,
durationShift: 60,
}, {
x: 70,
y: 65,
size: 100,
durationShift: 90,
}, {
x: 80,
y: 10,
size: 75,
durationShift: 30,
}, {
x: 80,
y: 45,
size: 125,
durationShift: 0,
}];
const PROGRESS_POSITIONS = generateRandomProgressPositions(100);
const Sparkles = ({
className,
style,
noAnimation,
...presetSettings
}: OwnProps) => {
if (presetSettings.preset === 'button') {
return (
<div
className={buildClassName(styles.root, styles.button, className, noAnimation && styles.noAnimation)}
style={style}
>
{BUTTON_POSITIONS.map((position) => {
const shiftX = Math.cos(Math.atan2(-50 + position.y, -50 + position.x)) * 100;
const shiftY = Math.sin(Math.atan2(-50 + position.y, -50 + position.x)) * 100;
return (
<div
className={styles.symbol}
style={buildStyle(
`top: ${position.y}%`,
`left: ${position.x}%`,
`--_duration-shift: ${(-position.durationShift / 100) * ANIMATION_DURATION}s`,
`--_shift-x: ${shiftX}%`,
`--_shift-y: ${shiftY}%`,
`scale: ${position.size}%`,
)}
aria-hidden="true"
>
{SYMBOL}
</div>
);
})}
</div>
);
}
if (presetSettings.preset === 'progress') {
return (
<div className={buildClassName(styles.root, styles.progress, className)} style={style}>
{PROGRESS_POSITIONS.map((position) => {
return (
<div
className={styles.symbol}
style={buildStyle(
`top: ${position.y}%`,
`left: ${position.x}%`,
`--_shift-x: ${position.velocityX}%`,
`--_shift-y: ${position.velocityY}%`,
`scale: ${position.scale}%`,
`--_duration-shift: ${(-position.durationShift / 100) * ANIMATION_DURATION}s`,
)}
aria-hidden="true"
>
{SYMBOL}
</div>
);
})}
</div>
);
}
return undefined;
};
function generateRandomProgressPositions(count: number) {
const positions = [];
for (let i = 0; i < count; i++) {
positions.push({
x: Math.random() * 100,
y: Math.random() * 100,
velocityX: (Math.random() * 5 + 15) * 100,
velocityY: (Math.random() * 10 - 5) * 100,
scale: (Math.random() * 0.5 + 0.5) * 100,
durationShift: Math.random() * 100,
});
}
return positions;
}
export default memo(Sparkles);