Readme

Blooming Button

Add a blooming emoji effect when a button is clicked. Import this val as a module and add the bloom-button class to your button. Demo here: https://www.val.town/v/maxm/bloomingButtonDemo

/** @jsxImportSource https://esm.sh/react */ import { renderToString } from "npm:react-dom/server"; export default async function(req: Request): Promise<Response> { return new Response( renderToString( <> <link rel="stylesheet" href="https://jenil.github.io/bulmaswatch/simplex/bulmaswatch.min.css" /> <div style={{ textAlign: "center", marginTop: "100px" }}> <button className="is-success button bloom-button" id="treeButton">Click Me</button> </div> <script type="module" src="https://esm.town/v/maxm/bloomingButton" /> </>, ), { headers: { "content-type": "text/html" } }, ); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
let active = false;
function getRandomEmoji() {
const emojis = ["🌳", "🌳", "🌲", "🌿", "🌱", "🌾", "🍁", "πŸ‚", "πŸƒ", "🌷", "πŸͺ»", "πŸ„"];
const randomIndex = Math.floor(Math.random() * emojis.length);
return emojis[randomIndex];
}
function injectCSS(css: string) {
const style = document.createElement("style");
style.type = "text/css";
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
injectCSS(`
.bloom-background div {
position: absolute; font-size: 24px;
transform: translate(-50%, -50%);
animation: radiateOutward 8s linear forwards;
}
@keyframes radiateOutward {
0% {transform: translate(-50%, -50%) scale(1); opacity: 0;}
50% {opacity: 1;}
100% {
transform: translate(calc(-50% + var(--translateX) * 1.5), calc(-50% + var(--translateY) * 1.5)) scale(1);
opacity: 0;
}
}`);
// Map to store the relationship between buttons and their background divs
const buttonBackgroundMap = new Map();
// Function to create or get the background div
function getBackgroundDiv(button) {
if (!buttonBackgroundMap.has(button)) {
const newDiv = document.createElement("div");
newDiv.classList.add("bloom-background");
newDiv.style.position = "fixed";
newDiv.style.width = "300px";
newDiv.style.height = "300px";
newDiv.style.zIndex = "-1";
newDiv.style.backgroundColor = "transparent"; // Change as needed
document.body.appendChild(newDiv);
buttonBackgroundMap.set(button, newDiv);
}
return buttonBackgroundMap.get(button);
}
// Function to position the background div behind the button
function positionDivBehindButton(button) {
const rect = button.getBoundingClientRect();
const newDiv = getBackgroundDiv(button);
newDiv.style.left = `${rect.left + rect.width / 2 - 150}px`;
newDiv.style.top = `${rect.top + rect.height / 2 - 150}px`;
return newDiv;
}
for (const button of document.querySelectorAll(".bloom-button")) {
const bloomContainer = positionDivBehindButton(button);
window.addEventListener("resize", () => positionDivBehindButton(button));
window.addEventListener("scroll", () => positionDivBehindButton(button));
button.addEventListener("click", function() {
if (active) return;
active = true;
const numberOfTrees = 20;
const angleStep = (2 * Math.PI) / numberOfTrees;
const interval = 100; // Interval in milliseconds to create new trees
let count = 0;
let prevAngle = 0;
setInterval(() => {
count++;
const i = count % numberOfTrees;
const tree = document.createElement("div");
tree.innerHTML = getRandomEmoji();
let angle = Math.random() * numberOfTrees * angleStep;
if (angle < prevAngle + 5 && angle > prevAngle - 5) {
angle = angle - 10;
}
const x = 100 * Math.cos(angle);
const y = 100 * Math.sin(angle);
prevAngle = angle;
tree.style.left = "50%";
tree.style.top = "50%";
tree.style.setProperty("--translateX", `${x}px`);
tree.style.setProperty("--translateY", `${y}px`);
bloomContainer.appendChild(tree);
setTimeout(() => {
tree.remove();
}, 8000); // Remove tree after animation duration
}, interval);
});
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
2
stevekrouse avatar

Nice!

  1. What are the button and bloom-button classes doing in the example?
  2. Can you have the bulmaswatch css file get added dynamically so the user has to import one less thing?
  3. In theory this could be a good use case for custom html elements, but those are funky
maxm avatar

@stevekrouse

.bloom-button is the class you need in order for the affect to be applied.

.button and the bulmaswatch stuff is just for the demo, you could style your button however you like.

#3 yes, I wondered how I'd package that up. I know @pomdtr had some examples like that but I'm not sure how I'd inject the javascript reliably. maybe just inject a script tag for every element?

v30
May 29, 2024