@sarthak03dot/romantic-animations 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/gitignore +23 -0
- package/package.json +30 -0
- package/src/animations/floatingHearts.js +35 -0
- package/src/animations/heartBurst.js +56 -0
- package/src/animations/heartTrail.js +41 -0
- package/src/core/engine.js +13 -0
- package/src/index.js +19 -0
- package/vite.config.js +13 -0
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# romantic-animations 💖
|
|
2
|
+
|
|
3
|
+
A beautiful JavaScript library for romantic particle effects such as floating hearts, heart trails, love bursts, and more. Perfect for Valentine's Day, wedding websites, or any love-themed project.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
- ❤️ Floating hearts animation
|
|
7
|
+
- 💘 Easy to integrate
|
|
8
|
+
- 📦 Zero dependencies
|
|
9
|
+
- ⚙️ Fully customizable (soon)
|
|
10
|
+
|
|
11
|
+
## 📦 Installation
|
|
12
|
+
```bash
|
|
13
|
+
npm install romantic-animations
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 🚀 Usage
|
|
17
|
+
```html
|
|
18
|
+
<div id="animation-container"></div>
|
|
19
|
+
<script type="module">
|
|
20
|
+
import { startFloatingHearts } from 'romantic-animations';
|
|
21
|
+
startFloatingHearts("animation-container");
|
|
22
|
+
</script>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 📁 File Structure
|
|
26
|
+
- `src/` – core engine and animation modules
|
|
27
|
+
- `demo/` – live preview setup using Vite
|
|
28
|
+
- `package.json` – npm config
|
|
29
|
+
|
|
30
|
+
## 🔮 Upcoming
|
|
31
|
+
- 💫 Cursor heart trails
|
|
32
|
+
- 🎉 Love confetti bursts
|
|
33
|
+
- 🌈 Color customization
|
|
34
|
+
- 🎨 Preset themes
|
|
35
|
+
|
|
36
|
+
## 🧑💻 Author
|
|
37
|
+
Made with ❤️ by **Sarthak Singh**
|
|
38
|
+
|
|
39
|
+
## 📄 License
|
|
40
|
+
MIT
|
package/gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Ignore build output
|
|
2
|
+
dist/
|
|
3
|
+
node_modules/
|
|
4
|
+
demo
|
|
5
|
+
|
|
6
|
+
# Ignore Vite preview folder
|
|
7
|
+
.vite/
|
|
8
|
+
|
|
9
|
+
# Ignore logs
|
|
10
|
+
npm-debug.log*
|
|
11
|
+
yarn-debug.log*
|
|
12
|
+
yarn-error.log*
|
|
13
|
+
|
|
14
|
+
# Ignore environment files
|
|
15
|
+
.env
|
|
16
|
+
.env.*
|
|
17
|
+
|
|
18
|
+
# Ignore IDE/editor files
|
|
19
|
+
.vscode/
|
|
20
|
+
.idea/
|
|
21
|
+
|
|
22
|
+
# Ignore demo files (optional for npm publish)
|
|
23
|
+
demo/
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sarthak03dot/romantic-animations",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Romantic animations like hearts, sparkles, trails",
|
|
5
|
+
"homepage": "https://github.com/sarthak03dot/romantic-animations#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/sarthak03dot/romantic-animations/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/sarthak03dot/romantic-animations.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "ISC",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"romantic",
|
|
16
|
+
"heart",
|
|
17
|
+
"animation",
|
|
18
|
+
"canvas"
|
|
19
|
+
],
|
|
20
|
+
"author": "Sarthak Singh",
|
|
21
|
+
"type": "commonjs",
|
|
22
|
+
"main": "vite.config.js",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"start": "vite",
|
|
25
|
+
"build": "vite build"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"vite": "^7.0.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function floatingHearts(canvas) {
|
|
2
|
+
const ctx = canvas.getContext("2d");
|
|
3
|
+
const hearts = [];
|
|
4
|
+
|
|
5
|
+
function createHeart() {
|
|
6
|
+
return {
|
|
7
|
+
x: Math.random() * canvas.width,
|
|
8
|
+
y: canvas.height + 20,
|
|
9
|
+
size: 20 + Math.random() * 10,
|
|
10
|
+
speed: 1 + Math.random() * 2,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function drawHeart(h) {
|
|
15
|
+
ctx.fillStyle = "pink";
|
|
16
|
+
ctx.beginPath();
|
|
17
|
+
ctx.moveTo(h.x, h.y);
|
|
18
|
+
ctx.bezierCurveTo(h.x + h.size / 2, h.y - h.size, h.x + h.size, h.y + h.size / 3, h.x, h.y + h.size);
|
|
19
|
+
ctx.bezierCurveTo(h.x - h.size, h.y + h.size / 3, h.x - h.size / 2, h.y - h.size, h.x, h.y);
|
|
20
|
+
ctx.fill();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function animate() {
|
|
24
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
25
|
+
if (Math.random() < 0.1) hearts.push(createHeart());
|
|
26
|
+
for (let i = 0; i < hearts.length; i++) {
|
|
27
|
+
hearts[i].y -= hearts[i].speed;
|
|
28
|
+
drawHeart(hearts[i]);
|
|
29
|
+
}
|
|
30
|
+
requestAnimationFrame(animate);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
animate();
|
|
34
|
+
}
|
|
35
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export function heartBurst(canvas) {
|
|
2
|
+
const ctx = canvas.getContext("2d");
|
|
3
|
+
const bursts = [];
|
|
4
|
+
|
|
5
|
+
function createHeart(x, y) {
|
|
6
|
+
const angle = Math.random() * Math.PI * 2;
|
|
7
|
+
const speed = 2 + Math.random() * 3;
|
|
8
|
+
return {
|
|
9
|
+
x,
|
|
10
|
+
y,
|
|
11
|
+
size: 10 + Math.random() * 5,
|
|
12
|
+
vx: Math.cos(angle) * speed,
|
|
13
|
+
vy: Math.sin(angle) * speed,
|
|
14
|
+
alpha: 1,
|
|
15
|
+
decay: 0.02
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function drawHeart(h) {
|
|
20
|
+
ctx.fillStyle = `rgba(255, 20, 147, ${h.alpha})`;
|
|
21
|
+
ctx.beginPath();
|
|
22
|
+
ctx.moveTo(h.x, h.y);
|
|
23
|
+
ctx.bezierCurveTo(h.x + h.size / 2, h.y - h.size, h.x + h.size, h.y + h.size / 3, h.x, h.y + h.size);
|
|
24
|
+
ctx.bezierCurveTo(h.x - h.size, h.y + h.size / 3, h.x - h.size / 2, h.y - h.size, h.x, h.y);
|
|
25
|
+
ctx.fill();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
canvas.addEventListener("click", (e) => {
|
|
29
|
+
const rect = canvas.getBoundingClientRect();
|
|
30
|
+
const burst = [];
|
|
31
|
+
for (let i = 0; i < 20; i++) {
|
|
32
|
+
burst.push(createHeart(e.clientX - rect.left, e.clientY - rect.top));
|
|
33
|
+
}
|
|
34
|
+
bursts.push(burst);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
function animate() {
|
|
38
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
39
|
+
for (let b = bursts.length - 1; b >= 0; b--) {
|
|
40
|
+
const burst = bursts[b];
|
|
41
|
+
for (let i = burst.length - 1; i >= 0; i--) {
|
|
42
|
+
const h = burst[i];
|
|
43
|
+
h.x += h.vx;
|
|
44
|
+
h.y += h.vy;
|
|
45
|
+
h.alpha -= h.decay;
|
|
46
|
+
drawHeart(h);
|
|
47
|
+
if (h.alpha <= 0) burst.splice(i, 1);
|
|
48
|
+
}
|
|
49
|
+
if (burst.length === 0) bursts.splice(b, 1);
|
|
50
|
+
}
|
|
51
|
+
requestAnimationFrame(animate);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
animate();
|
|
55
|
+
}
|
|
56
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export function heartTrail(canvas) {
|
|
2
|
+
const ctx = canvas.getContext("2d");
|
|
3
|
+
const hearts = [];
|
|
4
|
+
|
|
5
|
+
function createHeart(x, y) {
|
|
6
|
+
return {
|
|
7
|
+
x,
|
|
8
|
+
y,
|
|
9
|
+
size: 8 + Math.random() * 5,
|
|
10
|
+
alpha: 1,
|
|
11
|
+
decay: 0.02
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function drawHeart(h) {
|
|
16
|
+
ctx.fillStyle = `rgba(255, 105, 180, ${h.alpha})`;
|
|
17
|
+
ctx.beginPath();
|
|
18
|
+
ctx.moveTo(h.x, h.y);
|
|
19
|
+
ctx.bezierCurveTo(h.x + h.size / 2, h.y - h.size, h.x + h.size, h.y + h.size / 3, h.x, h.y + h.size);
|
|
20
|
+
ctx.bezierCurveTo(h.x - h.size, h.y + h.size / 3, h.x - h.size / 2, h.y - h.size, h.x, h.y);
|
|
21
|
+
ctx.fill();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
canvas.addEventListener("mousemove", (e) => {
|
|
25
|
+
const rect = canvas.getBoundingClientRect();
|
|
26
|
+
hearts.push(createHeart(e.clientX - rect.left, e.clientY - rect.top));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
function animate() {
|
|
30
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
31
|
+
for (let i = hearts.length - 1; i >= 0; i--) {
|
|
32
|
+
drawHeart(hearts[i]);
|
|
33
|
+
hearts[i].alpha -= hearts[i].decay;
|
|
34
|
+
if (hearts[i].alpha <= 0) hearts.splice(i, 1);
|
|
35
|
+
}
|
|
36
|
+
requestAnimationFrame(animate);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
animate();
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function initCanvas(containerId) {
|
|
2
|
+
const container = document.getElementById(containerId);
|
|
3
|
+
const canvas = document.createElement('canvas');
|
|
4
|
+
canvas.width = container.offsetWidth;
|
|
5
|
+
canvas.height = container.offsetHeight;
|
|
6
|
+
canvas.style.position = 'absolute';
|
|
7
|
+
canvas.style.top = 0;
|
|
8
|
+
canvas.style.left = 0;
|
|
9
|
+
canvas.style.pointerEvents = 'none';
|
|
10
|
+
container.appendChild(canvas);
|
|
11
|
+
return canvas;
|
|
12
|
+
}
|
|
13
|
+
|
package/src/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { initCanvas } from "./core/engine.js";
|
|
2
|
+
import { floatingHearts } from "./animations/floatingHearts.js";
|
|
3
|
+
import { heartTrail } from "./animations/heartTrail.js";
|
|
4
|
+
import { heartBurst } from "./animations/heartBurst.js";
|
|
5
|
+
|
|
6
|
+
export function startFloatingHearts(containerId = 'body') {
|
|
7
|
+
const canvas = initCanvas(containerId);
|
|
8
|
+
floatingHearts(canvas);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function startHeartTrail(containerId = 'body') {
|
|
12
|
+
const canvas = initCanvas(containerId);
|
|
13
|
+
heartTrail(canvas);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function startHeartBurst(containerId = 'body') {
|
|
17
|
+
const canvas = initCanvas(containerId);
|
|
18
|
+
heartBurst(canvas);
|
|
19
|
+
}
|
package/vite.config.js
ADDED