generated from partypages/party-template
This commit is contained in:
parent
7a36dce160
commit
a4515d4e3e
16742
package-lock.json
generated
16742
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
126
src/AnnoyerHandler.tsx
Normal file
126
src/AnnoyerHandler.tsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
|
||||||
|
export type Annoyer = {
|
||||||
|
image: string,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
angle: number,
|
||||||
|
xSpeed: number,
|
||||||
|
ySpeed: number,
|
||||||
|
angleSpeed: number,
|
||||||
|
isVisible: boolean,
|
||||||
|
wasVisible: boolean,
|
||||||
|
age: number,
|
||||||
|
uuid: string
|
||||||
|
};
|
||||||
|
|
||||||
|
const randomGaussian = () => {
|
||||||
|
return Math.sqrt(-2 * Math.log(1 - Math.random())) * Math.cos(2 * Math.PI * Math.random());
|
||||||
|
}
|
||||||
|
export class AnnoyerHandler {
|
||||||
|
public annoyers: Set<Annoyer>;
|
||||||
|
private images: string[];
|
||||||
|
private max_annoyers: number;
|
||||||
|
|
||||||
|
constructor(images: string[], start_limit?: number) {
|
||||||
|
this.annoyers = new Set<Annoyer>();
|
||||||
|
this.images = images;
|
||||||
|
this.max_annoyers = start_limit ?? 0;
|
||||||
|
while (this.annoyers.size < this.max_annoyers) {
|
||||||
|
this.annoyers.add(this.generateAnnoyer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateAnnoyer = (): Annoyer => {
|
||||||
|
const usedImages = new Set(Array.from(this.annoyers).map(annoyer => annoyer.image));
|
||||||
|
const possibleImages = this.images.filter(image => !usedImages.has(image));
|
||||||
|
let image: string;
|
||||||
|
if (possibleImages.length !== 0) {
|
||||||
|
//console.log("there are unused images, taking one of them");
|
||||||
|
const ims = Array.from(possibleImages);
|
||||||
|
image = ims[Math.floor(Math.random() * ims.length)];
|
||||||
|
} else {
|
||||||
|
//console.log("all images are in use, taking one at random");
|
||||||
|
image = this.images[Math.floor(Math.random() * this.images.length)];
|
||||||
|
}
|
||||||
|
const maxWidth = document.documentElement.clientWidth;
|
||||||
|
const maxHeight = document.documentElement.clientHeight;
|
||||||
|
const outerMargin = 150;
|
||||||
|
|
||||||
|
const startingPosition = (): [number, number] => {
|
||||||
|
const r1 = Math.random() * 2 - 1;
|
||||||
|
const r2 = Math.random() * 2 - 1;
|
||||||
|
const marginX = r1 * outerMargin;
|
||||||
|
const marginY = r2 * outerMargin;
|
||||||
|
const x = r1 < 0 ? marginX : maxWidth + marginX;
|
||||||
|
const y = r2 < 0 ? marginY : maxHeight + marginY;
|
||||||
|
return [x,y];
|
||||||
|
};
|
||||||
|
const [startX, startY] = startingPosition();
|
||||||
|
|
||||||
|
const speed = (startingPosition: [number, number]): [number, number] => {
|
||||||
|
const r1 = Math.random();
|
||||||
|
const r2 = Math.random();
|
||||||
|
const targetX = r1 * maxWidth;
|
||||||
|
const targetY = r2 * maxHeight;
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
(targetX - startingPosition[0]) * (targetX - startingPosition[0]) +
|
||||||
|
(targetY - startingPosition[1]) * (targetY - startingPosition[1])
|
||||||
|
)
|
||||||
|
const speed = ( Math.abs(randomGaussian()) + 0.5) * 2;
|
||||||
|
const normalizedSpeed = speed / distance;
|
||||||
|
return [(targetX - startingPosition[0]) * normalizedSpeed, (targetY - startingPosition[1]) * normalizedSpeed];
|
||||||
|
};
|
||||||
|
const [speedX, speedY] = speed([startX, startY]);
|
||||||
|
const newAnnoyer = {
|
||||||
|
image: image,
|
||||||
|
x: startX,
|
||||||
|
y: startY,
|
||||||
|
angle: 0,
|
||||||
|
angleSpeed: randomGaussian(),
|
||||||
|
xSpeed: speedX ,
|
||||||
|
ySpeed: speedY ,
|
||||||
|
age: Math.random() * -1000,
|
||||||
|
isVisible: false,
|
||||||
|
wasVisible: false,
|
||||||
|
uuid: "" + Math.random()
|
||||||
|
};
|
||||||
|
return newAnnoyer;
|
||||||
|
};
|
||||||
|
|
||||||
|
die = (annoyer: Annoyer) => {
|
||||||
|
this.annoyers.delete(annoyer);
|
||||||
|
this.spawnUntilLimit();
|
||||||
|
};
|
||||||
|
|
||||||
|
changeLimit = (newValue: number) => {
|
||||||
|
this.max_annoyers = newValue;
|
||||||
|
this.spawnUntilLimit();
|
||||||
|
};
|
||||||
|
|
||||||
|
spawnUntilLimit = () => {
|
||||||
|
while (this.annoyers.size < this.max_annoyers)
|
||||||
|
this.annoyers.add(this.generateAnnoyer());
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
tick = () => {
|
||||||
|
this.annoyers.forEach(annoyer => {
|
||||||
|
//if (annoyer.age > 10000) return;
|
||||||
|
annoyer.x += annoyer.xSpeed;
|
||||||
|
annoyer.y += annoyer.ySpeed;
|
||||||
|
annoyer.angle += annoyer.angleSpeed;
|
||||||
|
annoyer.age++;
|
||||||
|
|
||||||
|
if (annoyer.isVisible && !annoyer.wasVisible) {
|
||||||
|
//console.log("annoyer became visible");
|
||||||
|
} else if (annoyer.wasVisible && !annoyer.isVisible) {
|
||||||
|
//console.log("annoyer became invisible");
|
||||||
|
//console.log("annoyer died at the age of ",annoyer.age);
|
||||||
|
this.die(annoyer);
|
||||||
|
} else if (annoyer.age > 10000) this.die(annoyer);
|
||||||
|
annoyer.wasVisible = annoyer.isVisible;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
45
src/KrassesOverlay.tsx
Normal file
45
src/KrassesOverlay.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Annoyer, AnnoyerHandler } from './AnnoyerHandler';
|
||||||
|
import './Krassesoverlay.css';
|
||||||
|
|
||||||
|
const emojis = Array.from("🥐🥯🥞🍦🧁🍰🎂🍮🍭🍫🍬🍩🍪");
|
||||||
|
|
||||||
|
|
||||||
|
export const KrassesOverlay: React.FC = () => {
|
||||||
|
|
||||||
|
const [annoyerHandler, ] = useState(() => new AnnoyerHandler(emojis, 2));
|
||||||
|
const [x, setX] = useState(0);
|
||||||
|
const update = () => setX(x+1);
|
||||||
|
|
||||||
|
const handler = () => {
|
||||||
|
annoyerHandler.tick();
|
||||||
|
update();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(handler, 10);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
});
|
||||||
|
|
||||||
|
const ref = (annoyer: Annoyer) => (element: HTMLImageElement) => {
|
||||||
|
if (!element) return;
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const visible = rect.bottom >= 0 &&
|
||||||
|
rect.right >= 0 &&
|
||||||
|
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||||
|
rect.left <= (window.innerWidth || document.documentElement.clientWidth);
|
||||||
|
annoyer.isVisible = visible;
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div className="overlay">
|
||||||
|
{Array.from(annoyerHandler.annoyers).map((annoyer, index) => (
|
||||||
|
<span ref={ref(annoyer)} key={index} className="annoyer" style={
|
||||||
|
{
|
||||||
|
fontSize: 40,
|
||||||
|
top: annoyer.y, left: annoyer.x,
|
||||||
|
transform: `rotate(${annoyer.angle}deg)`
|
||||||
|
}
|
||||||
|
}>{annoyer.image}</span>
|
||||||
|
))}
|
||||||
|
</div>;
|
||||||
|
}
|
12
src/Krassesoverlay.css
Normal file
12
src/Krassesoverlay.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.overlay {
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annoyer {
|
||||||
|
position: absolute;
|
||||||
|
max-block-size: 70px;
|
||||||
|
}
|
@ -69,3 +69,22 @@ input[type="radio"]:checked+label {
|
|||||||
#coming-no+label {
|
#coming-no+label {
|
||||||
--selected-color: #dc2626;
|
--selected-color: #dc2626;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
background-image: url("https://www.inshape.de/fileadmin/user_upload/user_upload/Fotolia_216856994_Subscription_Monthly_M.jpg");
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: absolute;
|
||||||
|
filter: blur(7px);
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bgwrap {
|
||||||
|
position: sticky;
|
||||||
|
top: 0px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page :is(p, label, h1, h2, h3, h4, h5, h6) {
|
||||||
|
filter: drop-shadow(1px 1px 3px white);
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { APIEndPoint, PartyContext } from './PartyContext';
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faAngleDown, faCalendarDays, faLocationDot } from '@fortawesome/free-solid-svg-icons';
|
import { faAngleDown, faCalendarDays, faLocationDot } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { modifySelfRequest, parseURI } from './partyApi';
|
import { modifySelfRequest, parseURI } from './partyApi';
|
||||||
|
import { KrassesOverlay } from './KrassesOverlay';
|
||||||
|
|
||||||
export const PartyPage: React.FC = () => {
|
export const PartyPage: React.FC = () => {
|
||||||
const partyContext = useContext(PartyContext);
|
const partyContext = useContext(PartyContext);
|
||||||
@ -58,6 +59,10 @@ export const PartyPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div className="App">
|
return <div className="App">
|
||||||
|
<div className="bgwrap">
|
||||||
|
<div className="bg" />
|
||||||
|
<KrassesOverlay />
|
||||||
|
</div>
|
||||||
<div className='page'>
|
<div className='page'>
|
||||||
<h1>Hallo {dear} {name},</h1>
|
<h1>Hallo {dear} {name},</h1>
|
||||||
<div className='page-content'>
|
<div className='page-content'>
|
||||||
|
Loading…
Reference in New Issue
Block a user