anouncer now has common anuncer, basicly favoritutes

This commit is contained in:
RezHackXYZ 2025-05-10 19:40:24 +05:30
parent d0f11472c9
commit 6bcde94422
No known key found for this signature in database
9 changed files with 541 additions and 64 deletions

316
src/Confetti.js Normal file
View file

@ -0,0 +1,316 @@
export function confettiAnimation() {
(() => {
"use strict";
// Utility functions grouped into a single object
const Utils = {
// Parse pixel values to numeric values
parsePx: (value) => parseFloat(value.replace(/px/, "")),
// Generate a random number between two values, optionally with a fixed precision
getRandomInRange: (min, max, precision = 0) => {
const multiplier = Math.pow(10, precision);
const randomValue = Math.random() * (max - min) + min;
return Math.floor(randomValue * multiplier) / multiplier;
},
// Pick a random item from an array
getRandomItem: (array) =>
array[Math.floor(Math.random() * array.length)],
// Scaling factor based on screen width
getScaleFactor: () => Math.log(window.innerWidth) / Math.log(1920),
// Debounce function to limit event firing frequency
debounce: (func, delay) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), delay);
};
},
};
// Precomputed constants
const DEG_TO_RAD = Math.PI / 180;
// Centralized configuration for default values
const defaultConfettiConfig = {
confettiesNumber: 250,
confettiRadius: 6,
confettiColors: [
"#fcf403",
"#62fc03",
"#f4fc03",
"#03e7fc",
"#03fca5",
"#a503fc",
"#fc03ad",
"#fc03c2",
],
emojies: [],
svgIcon: null, // Example SVG link
};
// Confetti class representing individual confetti pieces
class Confetti {
constructor({
initialPosition,
direction,
radius,
colors,
emojis,
svgIcon,
}) {
const speedFactor =
Utils.getRandomInRange(0.9, 1.7, 3) *
Utils.getScaleFactor();
this.speed = { x: speedFactor, y: speedFactor };
this.finalSpeedX = Utils.getRandomInRange(0.2, 0.6, 3);
this.rotationSpeed =
emojis.length || svgIcon
? 0.01
: Utils.getRandomInRange(0.03, 0.07, 3) *
Utils.getScaleFactor();
this.dragCoefficient = Utils.getRandomInRange(
0.0005,
0.0009,
6
);
this.radius = { x: radius, y: radius };
this.initialRadius = radius;
this.rotationAngle =
direction === "left"
? Utils.getRandomInRange(0, 0.2, 3)
: Utils.getRandomInRange(-0.2, 0, 3);
this.emojiRotationAngle = Utils.getRandomInRange(
0,
2 * Math.PI
);
this.radiusYDirection = "down";
const angle =
direction === "left"
? Utils.getRandomInRange(82, 15) * DEG_TO_RAD
: Utils.getRandomInRange(-15, -82) * DEG_TO_RAD;
this.absCos = Math.abs(Math.cos(angle));
this.absSin = Math.abs(Math.sin(angle));
const offset = Utils.getRandomInRange(-150, 0);
const position = {
x:
initialPosition.x +
(direction === "left" ? -offset : offset) * this.absCos,
y: initialPosition.y - offset * this.absSin,
};
this.position = { ...position };
this.initialPosition = { ...position };
this.color =
emojis.length || svgIcon
? null
: Utils.getRandomItem(colors);
this.emoji = emojis.length ? Utils.getRandomItem(emojis) : null;
this.svgIcon = null;
// Preload SVG if provided
if (svgIcon) {
this.svgImage = new Image();
this.svgImage.src = svgIcon;
this.svgImage.onload = () => {
this.svgIcon = this.svgImage; // Mark as ready once loaded
};
}
this.createdAt = Date.now();
this.direction = direction;
}
draw(context) {
const { x, y } = this.position;
const { x: radiusX, y: radiusY } = this.radius;
const scale = window.devicePixelRatio;
if (this.svgIcon) {
context.save();
context.translate(scale * x, scale * y);
context.rotate(this.emojiRotationAngle);
context.drawImage(
this.svgIcon,
-radiusX,
-radiusY,
radiusX * 2,
radiusY * 2
);
context.restore();
} else if (this.color) {
context.fillStyle = this.color;
context.beginPath();
context.ellipse(
x * scale,
y * scale,
radiusX * scale,
radiusY * scale,
this.rotationAngle,
0,
2 * Math.PI
);
context.fill();
} else if (this.emoji) {
context.font = `${radiusX * scale}px serif`;
context.save();
context.translate(scale * x, scale * y);
context.rotate(this.emojiRotationAngle);
context.textAlign = "center";
context.fillText(this.emoji, 0, radiusY / 2); // Adjust vertical alignment
context.restore();
}
}
updatePosition(deltaTime, currentTime) {
const elapsed = currentTime - this.createdAt;
if (this.speed.x > this.finalSpeedX) {
this.speed.x -= this.dragCoefficient * deltaTime;
}
this.position.x +=
this.speed.x *
(this.direction === "left" ? -this.absCos : this.absCos) *
deltaTime;
this.position.y =
this.initialPosition.y -
this.speed.y * this.absSin * elapsed +
(0.00125 * Math.pow(elapsed, 2)) / 2;
if (!this.emoji && !this.svgIcon) {
this.rotationSpeed -= 1e-5 * deltaTime;
this.rotationSpeed = Math.max(this.rotationSpeed, 0);
if (this.radiusYDirection === "down") {
this.radius.y -= deltaTime * this.rotationSpeed;
if (this.radius.y <= 0) {
this.radius.y = 0;
this.radiusYDirection = "up";
}
} else {
this.radius.y += deltaTime * this.rotationSpeed;
if (this.radius.y >= this.initialRadius) {
this.radius.y = this.initialRadius;
this.radiusYDirection = "down";
}
}
}
}
isVisible(canvasHeight) {
return this.position.y < canvasHeight + 100;
}
}
class ConfettiManager {
constructor() {
this.canvas = document.createElement("canvas");
// @ts-ignore
this.canvas.style =
"position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 1000; pointer-events: none;";
document.body.appendChild(this.canvas);
this.context = this.canvas.getContext("2d");
this.confetti = [];
this.lastUpdated = Date.now();
window.addEventListener(
"resize",
Utils.debounce(() => this.resizeCanvas(), 200)
);
this.resizeCanvas();
requestAnimationFrame(() => this.loop());
}
resizeCanvas() {
this.canvas.width = window.innerWidth * window.devicePixelRatio;
this.canvas.height =
window.innerHeight * window.devicePixelRatio;
}
addConfetti(config = {}) {
const {
confettiesNumber,
confettiRadius,
confettiColors,
emojies,
svgIcon,
} = {
...defaultConfettiConfig,
...config,
};
const baseY = (5 * window.innerHeight) / 7;
for (let i = 0; i < confettiesNumber / 2; i++) {
this.confetti.push(
new Confetti({
initialPosition: { x: 0, y: baseY },
direction: "right",
radius: confettiRadius,
colors: confettiColors,
emojis: emojies,
svgIcon,
})
);
this.confetti.push(
new Confetti({
initialPosition: { x: window.innerWidth, y: baseY },
direction: "left",
radius: confettiRadius,
colors: confettiColors,
emojis: emojies,
svgIcon,
})
);
}
}
resetAndStart(config = {}) {
// Clear existing confetti
this.confetti = [];
// Add new confetti
this.addConfetti(config);
}
loop() {
const currentTime = Date.now();
const deltaTime = currentTime - this.lastUpdated;
this.lastUpdated = currentTime;
this.context.clearRect(
0,
0,
this.canvas.width,
this.canvas.height
);
this.confetti = this.confetti.filter((item) => {
item.updatePosition(deltaTime, currentTime);
item.draw(this.context);
return item.isVisible(this.canvas.height);
});
requestAnimationFrame(() => this.loop());
}
}
const manager = new ConfettiManager();
manager.addConfetti();
const triggerButton = document.getElementById("show-again");
if (triggerButton) {
triggerButton.addEventListener("click", () =>
manager.addConfetti()
);
}
const resetInput = document.getElementById("reset");
if (resetInput) {
resetInput.addEventListener("input", () => manager.resetAndStart());
}
})();
}

View file

@ -45,7 +45,7 @@
DecideScrool(hour2, parseInt(hours[1]), 200, 10);
DecideScrool(min1, parseInt(minutes[0]), 200, 6);
DecideScrool(min2, parseInt(minutes[1]), 200, 10);
if (ShowSeconds.v) {
if (ShowSeconds) {
DecideScrool(sec1, parseInt(seconds[0]), 75, 6);
DecideScrool(sec2, parseInt(seconds[1]), 75, 10);
}

View file

@ -2,6 +2,7 @@
import Selector from "./selector.svelte";
import TopDisplay from "./TopDisplay.svelte";
import EditNameOfStudents from "./EditNameOfStudents.svelte";
import {ShowAlert} from "../app.svelte";
export let RandomNamesState = $state({
NotSelectedYet: [],
@ -24,7 +25,7 @@
];
RandomNamesState.NotSelectedYet.splice(randomIndex, 1);
} else {
alert("All students have been selected.");
ShowAlert("All students have been selected.", "warning");
}
}

View file

@ -1,27 +1,119 @@
<script>
let text = "";
let text = $state("");
function speak() {
window.speechSynthesis.cancel();
window.speechSynthesis.speak(new SpeechSynthesisUtterance(text));
window.speechSynthesis.speak(
Object.assign(new SpeechSynthesisUtterance(text), {
rate: 0.5,
})
);
}
let TempelateCommonAnounce = $state(["Please be quiet"]);
let CommonAnounce = $state([]);
export function newNames(Names) {
localStorage.setItem("CommonAnounce", JSON.stringify(Names));
}
let TempCommonAnounce = localStorage.getItem("CommonAnounce") || "";
if (TempCommonAnounce != "") {
CommonAnounce = JSON.parse(TempCommonAnounce);
} else {
CommonAnounce = $state.snapshot(TempelateCommonAnounce);
localStorage.setItem("CommonAnounce", JSON.stringify(CommonAnounce));
}
</script>
<div id="root">
<input
bind:value={text}
placeholder="Type in here what you want to announce"
/>
<button onclick={speak}>Play</button>
<div id="wrap">
<h1>Most Announced announcements</h1>
<div id="mostanouncedanounements">
{#each CommonAnounce as anouncement, i}
<div>
<button
class="anuncement"
onclick={() => {
text = anouncement;
speak();
}}
>
{anouncement}
</button>
<button
aria-label="Delete anouncement"
onclick={() => {
if (
confirm(
"Are you sure you want to delete this anouncement?"
)
) {
CommonAnounce.splice(i, 1);
localStorage.setItem(
"CommonAnounce",
JSON.stringify(CommonAnounce)
);
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="15px"
viewBox="0 -960 960 960"
width="15px"
fill="#FFFFFF"
><path
d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"
/></svg
></button
>
</div>
{/each}
</div>
<hr />
<h1>Or announce something else</h1>
<div>
<input
bind:value={text}
placeholder="Type in here what you want to announce"
/>
<button onclick={speak}>Play</button>
</div>
{#if text}
<button
id="new"
onclick={() => {
CommonAnounce.push(text);
text = "";
localStorage.setItem(
"CommonAnounce",
JSON.stringify(CommonAnounce)
);
}}>Add "{text}" to "Most Announced announcements"</button
>
{/if}
</div>
</div>
<style>
#root {
height: 100%;
display: grid;
place-items: center;
}
#wrap {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
flex-direction: column;
gap: 5px;
padding: 20px;
border-radius: 20px;
width: fit-content;
}
input {
@ -48,4 +140,41 @@
background-color: #414141;
transform: rotateZ(-15deg) scale(1.2);
}
hr {
width: 100%;
height: 2px;
background-color: #292929;
border: none;
margin: 0;
}
h1 {
text-align: center;
margin: 0;
font-size: 30px;
}
#mostanouncedanounements {
display: flex;
flex-direction: column;
justify-content: center;
gap: 10px;
}
.anuncement:hover {
transform: scale(1.1);
}
.anuncement {
width: calc(100% - 40px);
}
#new {
width: fit-content;
align-self: center;
font-size: 15px;
}
#new:hover {
transform: scale(1.1);
}
</style>

View file

@ -97,26 +97,4 @@
text-decoration: none;
color: #444;
}
#UperLayer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backdrop-filter: blur(5px);
background-color: rgba(0, 0, 0, 0.5);
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
.close {
background-color: #2b2b2b;
color: #888;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
align-self: center;
}
</style>

View file

@ -1,33 +1,79 @@
<script>
import Router from "svelte-spa-router";
import Wordle from "./wordle/game.svelte";
import TypeSelector from "./SelectionMenue/TypeSelector.svelte";
import IdleScreen from "./IdleScreen/main.svelte";
import RandomName from "./RandomName/main.svelte";
import Announcer from "./announcer/main.svelte";
<script module>
import Router from "svelte-spa-router";
import Wordle from "./wordle/game.svelte";
import TypeSelector from "./SelectionMenue/TypeSelector.svelte";
import IdleScreen from "./IdleScreen/main.svelte";
import RandomName from "./RandomName/main.svelte";
import Announcer from "./announcer/main.svelte";
import { confettiAnimation } from "./Confetti.js";
let routes = {
"/": TypeSelector,
"/Wordle": Wordle,
"/IdleScreen": IdleScreen,
"/RandomName": RandomName,
"/announcer": Announcer,
};
let routes = {
"/": TypeSelector,
"/Wordle": Wordle,
"/IdleScreen": IdleScreen,
"/RandomName": RandomName,
"/announcer": Announcer,
};
let ShowAlertDiv = $state(false);
let ShowAlertText = $state("this Code is bad");
let ShowAlertType = $state("Error");
export function ShowAlert(text, type) {
ShowAlertDiv = true;
ShowAlertType = type;
ShowAlertText = text;
setTimeout(() => {
ShowAlertDiv = false;
}, 1500);
if (type == "success") {
confettiAnimation();
}
}
</script>
<div id="root">
<Router {routes} />
<Router {routes} />
</div>
{#if ShowAlertDiv == true}
<div id="alert">
<h1 class={ShowAlertType}>{ShowAlertText}</h1>
</div>
{/if}
<style>
:root {
background-color: #121212;
color: white;
}
:root {
background-color: #121212;
color: white;
}
#root {
height: 100%;
margin: 0;
font-family: "Sour Gummy", sans-serif;
}
#root {
height: 100%;
margin: 0;
font-family: "Sour Gummy", sans-serif;
}
#alert {
position: fixed;
top: 10px;
color: white;
left: 50%;
transform: translate(-50%, 0);
h1 {
margin: 0;
padding: 10px 20px;
border-radius: 20px;
font-family: "Sour Gummy", sans-serif;
}
.error {
background-color: #830000;
}
.warning {
background-color: #975b00;
}
.success {
background-color: #006b00;
}
}
</style>

View file

@ -55,7 +55,7 @@
function UpdateChart(val) {
dataPoints = data.value[LetersSelected];
if (chart) {
chart.data.labels = dataPoints.map((_, i) => `Point ${i + 1}`);
chart.data.labels = dataPoints.map((_, i) => `Game ${i + 1}`);
chart.data.datasets[0].data = $state.snapshot(dataPoints);
chart.update();
}

View file

@ -1,5 +1,6 @@
import wordExists from "word-exists";
import { generate } from "random-words";
import { generate } from "random-words"; import {ShowAlert} from "../app.svelte";
export let WordLegnth = $state({ v: 5 });
@ -85,7 +86,7 @@ export function newGame() {
}
function GameWin() {
alert("You win!");
ShowAlert("You win!", "success");
data.value[WordLegnth.v].push(words.v.length);
localStorage.setItem("WordleGamesData", JSON.stringify(data.value));
newGame();
@ -169,7 +170,7 @@ export function ButtonPressed(key) {
SendWord(CurrentWord.v);
CurrentWord.v = [];
} else {
alert("Not a valid word");
ShowAlert("Not a valid word", "error");
}
}
return;