@sarthak03dot/romantic-animations 1.2.0 → 1.2.3

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.
@@ -1,89 +1,89 @@
1
- import { mergeOptions } from '../core/engine.js';
2
-
3
- const DEFAULTS = {
4
- count: 0.12, // hearts spawned per frame (probability)
5
- minSize: 14,
6
- maxSize: 32,
7
- minSpeed: 0.8,
8
- maxSpeed: 2.4,
9
- colors: ['#ff6b8a', '#ff4d6d', '#ff85a1', '#ffc2d1', '#ff0a54', '#ff477e'],
10
- wobble: true, // horizontal sine drift
11
- glow: true,
12
- };
13
-
14
- /**
15
- * Draw a proper heart shape centred at (cx, cy) with given radius.
16
- */
17
- function drawHeartShape(ctx, cx, cy, r, color, alpha = 1, glow = false) {
18
- ctx.save();
19
- ctx.globalAlpha = alpha;
20
- if (glow) {
21
- ctx.shadowColor = color;
22
- ctx.shadowBlur = r * 1.2;
23
- }
24
- ctx.fillStyle = color;
25
- ctx.beginPath();
26
- ctx.moveTo(cx, cy + r * 0.3);
27
- // left lobe
28
- ctx.bezierCurveTo(cx - r * 1.1, cy - r * 0.5, cx - r * 1.6, cy + r * 0.5, cx, cy + r * 1.4);
29
- // right lobe
30
- ctx.bezierCurveTo(cx + r * 1.6, cy + r * 0.5, cx + r * 1.1, cy - r * 0.5, cx, cy + r * 0.3);
31
- ctx.fill();
32
- ctx.restore();
33
- }
34
-
35
- export function floatingHearts(canvas, userOptions = {}) {
36
- const opts = mergeOptions(DEFAULTS, userOptions);
37
- const ctx = canvas.getContext('2d');
38
- const hearts = [];
39
- let running = true;
40
- let frame = 0;
41
-
42
- function createHeart() {
43
- const size = opts.minSize + Math.random() * (opts.maxSize - opts.minSize);
44
- return {
45
- x: Math.random() * canvas.width,
46
- y: canvas.height + size * 2,
47
- size,
48
- speed: opts.minSpeed + Math.random() * (opts.maxSpeed - opts.minSpeed),
49
- color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
50
- alpha: 0.7 + Math.random() * 0.3,
51
- wobbleOffset: Math.random() * Math.PI * 2,
52
- wobbleSpeed: 0.02 + Math.random() * 0.03,
53
- wobbleAmount: 0.5 + Math.random() * 1.5,
54
- };
55
- }
56
-
57
- function animate() {
58
- if (!running) return;
59
- frame++;
60
- ctx.clearRect(0, 0, canvas.width, canvas.height);
61
-
62
- if (Math.random() < opts.count) hearts.push(createHeart());
63
-
64
- for (let i = hearts.length - 1; i >= 0; i--) {
65
- const h = hearts[i];
66
- h.y -= h.speed;
67
- h.wobbleOffset += h.wobbleSpeed;
68
- const xOffset = opts.wobble ? Math.sin(h.wobbleOffset) * h.wobbleAmount * h.size * 0.5 : 0;
69
-
70
- // Fade out near top
71
- const fadeAlpha = Math.min(h.alpha, h.y / (canvas.height * 0.2));
72
- if (fadeAlpha <= 0 || h.y < -h.size * 3) {
73
- hearts.splice(i, 1);
74
- continue;
75
- }
76
-
77
- drawHeartShape(ctx, h.x + xOffset, h.y, h.size, h.color, Math.max(0, fadeAlpha), opts.glow);
78
- }
79
-
80
- requestAnimationFrame(animate);
81
- }
82
-
83
- animate();
84
-
85
- return function stop() {
86
- running = false;
87
- ctx.clearRect(0, 0, canvas.width, canvas.height);
88
- };
89
- }
1
+ import { mergeOptions } from '../core/engine.js';
2
+
3
+ const DEFAULTS = {
4
+ count: 0.12, // hearts spawned per frame (probability)
5
+ minSize: 14,
6
+ maxSize: 32,
7
+ minSpeed: 0.8,
8
+ maxSpeed: 2.4,
9
+ colors: ['#ff6b8a', '#ff4d6d', '#ff85a1', '#ffc2d1', '#ff0a54', '#ff477e'],
10
+ wobble: true, // horizontal sine drift
11
+ glow: true,
12
+ };
13
+
14
+ /**
15
+ * Draw a proper heart shape centred at (cx, cy) with given radius.
16
+ */
17
+ function drawHeartShape(ctx, cx, cy, r, color, alpha = 1, glow = false) {
18
+ ctx.save();
19
+ ctx.globalAlpha = alpha;
20
+ if (glow) {
21
+ ctx.shadowColor = color;
22
+ ctx.shadowBlur = r * 1.2;
23
+ }
24
+ ctx.fillStyle = color;
25
+ ctx.beginPath();
26
+ ctx.moveTo(cx, cy + r * 0.3);
27
+ // left lobe
28
+ ctx.bezierCurveTo(cx - r * 1.1, cy - r * 0.5, cx - r * 1.6, cy + r * 0.5, cx, cy + r * 1.4);
29
+ // right lobe
30
+ ctx.bezierCurveTo(cx + r * 1.6, cy + r * 0.5, cx + r * 1.1, cy - r * 0.5, cx, cy + r * 0.3);
31
+ ctx.fill();
32
+ ctx.restore();
33
+ }
34
+
35
+ export function floatingHearts(canvas, userOptions = {}) {
36
+ const opts = mergeOptions(DEFAULTS, userOptions);
37
+ const ctx = canvas.getContext('2d');
38
+ const hearts = [];
39
+ let running = true;
40
+ let frame = 0;
41
+
42
+ function createHeart() {
43
+ const size = opts.minSize + Math.random() * (opts.maxSize - opts.minSize);
44
+ return {
45
+ x: Math.random() * canvas.width,
46
+ y: canvas.height + size * 2,
47
+ size,
48
+ speed: opts.minSpeed + Math.random() * (opts.maxSpeed - opts.minSpeed),
49
+ color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
50
+ alpha: 0.7 + Math.random() * 0.3,
51
+ wobbleOffset: Math.random() * Math.PI * 2,
52
+ wobbleSpeed: 0.02 + Math.random() * 0.03,
53
+ wobbleAmount: 0.5 + Math.random() * 1.5,
54
+ };
55
+ }
56
+
57
+ function animate() {
58
+ if (!running) return;
59
+ frame++;
60
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
61
+
62
+ if (Math.random() < opts.count) hearts.push(createHeart());
63
+
64
+ for (let i = hearts.length - 1; i >= 0; i--) {
65
+ const h = hearts[i];
66
+ h.y -= h.speed;
67
+ h.wobbleOffset += h.wobbleSpeed;
68
+ const xOffset = opts.wobble ? Math.sin(h.wobbleOffset) * h.wobbleAmount * h.size * 0.5 : 0;
69
+
70
+ // Fade out near top
71
+ const fadeAlpha = Math.min(h.alpha, h.y / (canvas.height * 0.2));
72
+ if (fadeAlpha <= 0 || h.y < -h.size * 3) {
73
+ hearts.splice(i, 1);
74
+ continue;
75
+ }
76
+
77
+ drawHeartShape(ctx, h.x + xOffset, h.y, h.size, h.color, Math.max(0, fadeAlpha), opts.glow);
78
+ }
79
+
80
+ requestAnimationFrame(animate);
81
+ }
82
+
83
+ animate();
84
+
85
+ return function stop() {
86
+ running = false;
87
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
88
+ };
89
+ }
@@ -1,76 +1,76 @@
1
- import { mergeOptions } from '../core/engine.js';
2
-
3
- const DEFAULTS = {
4
- orbCount: 15,
5
- minSize: 50,
6
- maxSize: 150,
7
- colors: ['#ff4d6d', '#c77dff', '#48cae4', '#ffe66d'],
8
- speed: 0.5,
9
- glow: true,
10
- };
11
-
12
- export function floatingOrbs(canvas, userOptions = {}) {
13
- const opts = mergeOptions(DEFAULTS, userOptions);
14
- const ctx = canvas.getContext('2d');
15
- const orbs = [];
16
- let running = true;
17
-
18
- function createOrb() {
19
- const size = opts.minSize + Math.random() * (opts.maxSize - opts.minSize);
20
- return {
21
- x: Math.random() * canvas.width,
22
- y: Math.random() * canvas.height,
23
- size,
24
- vx: (Math.random() - 0.5) * opts.speed,
25
- vy: (Math.random() - 0.5) * opts.speed,
26
- color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
27
- alpha: 0,
28
- };
29
- }
30
-
31
- for (let i = 0; i < opts.orbCount; i++) {
32
- const orb = createOrb();
33
- orb.alpha = Math.random() * 0.5 + 0.1;
34
- orbs.push(orb);
35
- }
36
-
37
- function animate() {
38
- if (!running) return;
39
- ctx.clearRect(0, 0, canvas.width, canvas.height);
40
-
41
- for (let i = 0; i < orbs.length; i++) {
42
- const b = orbs[i];
43
- b.x += b.vx;
44
- b.y += b.vy;
45
-
46
- // Bounce off walls
47
- if (b.x < -b.size) b.x = canvas.width + b.size;
48
- if (b.x > canvas.width + b.size) b.x = -b.size;
49
- if (b.y < -b.size) b.y = canvas.height + b.size;
50
- if (b.y > canvas.height + b.size) b.y = -b.size;
51
-
52
- ctx.save();
53
- ctx.globalAlpha = b.alpha;
54
- ctx.globalCompositeOperation = 'screen';
55
-
56
- const gradient = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.size);
57
- gradient.addColorStop(0, b.color);
58
- gradient.addColorStop(1, 'rgba(0,0,0,0)');
59
-
60
- ctx.fillStyle = gradient;
61
- ctx.beginPath();
62
- ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
63
- ctx.fill();
64
- ctx.restore();
65
- }
66
-
67
- requestAnimationFrame(animate);
68
- }
69
-
70
- animate();
71
-
72
- return function stop() {
73
- running = false;
74
- ctx.clearRect(0, 0, canvas.width, canvas.height);
75
- };
76
- }
1
+ import { mergeOptions } from '../core/engine.js';
2
+
3
+ const DEFAULTS = {
4
+ orbCount: 15,
5
+ minSize: 50,
6
+ maxSize: 150,
7
+ colors: ['#ff4d6d', '#c77dff', '#48cae4', '#ffe66d'],
8
+ speed: 0.5,
9
+ glow: true,
10
+ };
11
+
12
+ export function floatingOrbs(canvas, userOptions = {}) {
13
+ const opts = mergeOptions(DEFAULTS, userOptions);
14
+ const ctx = canvas.getContext('2d');
15
+ const orbs = [];
16
+ let running = true;
17
+
18
+ function createOrb() {
19
+ const size = opts.minSize + Math.random() * (opts.maxSize - opts.minSize);
20
+ return {
21
+ x: Math.random() * canvas.width,
22
+ y: Math.random() * canvas.height,
23
+ size,
24
+ vx: (Math.random() - 0.5) * opts.speed,
25
+ vy: (Math.random() - 0.5) * opts.speed,
26
+ color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
27
+ alpha: 0,
28
+ };
29
+ }
30
+
31
+ for (let i = 0; i < opts.orbCount; i++) {
32
+ const orb = createOrb();
33
+ orb.alpha = Math.random() * 0.5 + 0.1;
34
+ orbs.push(orb);
35
+ }
36
+
37
+ function animate() {
38
+ if (!running) return;
39
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
40
+
41
+ for (let i = 0; i < orbs.length; i++) {
42
+ const b = orbs[i];
43
+ b.x += b.vx;
44
+ b.y += b.vy;
45
+
46
+ // Bounce off walls
47
+ if (b.x < -b.size) b.x = canvas.width + b.size;
48
+ if (b.x > canvas.width + b.size) b.x = -b.size;
49
+ if (b.y < -b.size) b.y = canvas.height + b.size;
50
+ if (b.y > canvas.height + b.size) b.y = -b.size;
51
+
52
+ ctx.save();
53
+ ctx.globalAlpha = b.alpha;
54
+ ctx.globalCompositeOperation = 'screen';
55
+
56
+ const gradient = ctx.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.size);
57
+ gradient.addColorStop(0, b.color);
58
+ gradient.addColorStop(1, 'rgba(0,0,0,0)');
59
+
60
+ ctx.fillStyle = gradient;
61
+ ctx.beginPath();
62
+ ctx.arc(b.x, b.y, b.size, 0, Math.PI * 2);
63
+ ctx.fill();
64
+ ctx.restore();
65
+ }
66
+
67
+ requestAnimationFrame(animate);
68
+ }
69
+
70
+ animate();
71
+
72
+ return function stop() {
73
+ running = false;
74
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
75
+ };
76
+ }
@@ -1,113 +1,113 @@
1
- import { mergeOptions } from '../core/engine.js';
2
-
3
- const DEFAULTS = {
4
- count: 20, // hearts per burst
5
- minSize: 8,
6
- maxSize: 20,
7
- minSpeed: 2,
8
- maxSpeed: 7,
9
- gravity: 0.08,
10
- decay: 0.018,
11
- colors: ['#ff0a54', '#ff477e', '#ff7096', '#ff85a1', '#fbb1bd', '#ff4d6d'],
12
- glow: true,
13
- symbols: ['heart'], // 'heart' | 'star' | 'sparkle'
14
- };
15
-
16
- function drawSymbol(ctx, type, cx, cy, r, color, alpha, glow) {
17
- ctx.save();
18
- ctx.globalAlpha = Math.max(0, alpha);
19
- if (glow) { ctx.shadowColor = color; ctx.shadowBlur = r * 2; }
20
- ctx.fillStyle = color;
21
-
22
- if (type === 'star') {
23
- // 5-point star
24
- ctx.beginPath();
25
- for (let i = 0; i < 5; i++) {
26
- const outer = (Math.PI / 2) + (i * 2 * Math.PI) / 5;
27
- const inner = outer + Math.PI / 5;
28
- if (i === 0) ctx.moveTo(cx + r * Math.cos(outer), cy - r * Math.sin(outer));
29
- else ctx.lineTo(cx + r * Math.cos(outer), cy - r * Math.sin(outer));
30
- ctx.lineTo(cx + (r * 0.4) * Math.cos(inner), cy - (r * 0.4) * Math.sin(inner));
31
- }
32
- ctx.closePath();
33
- ctx.fill();
34
- } else if (type === 'sparkle') {
35
- // 4-point sparkle
36
- for (let i = 0; i < 4; i++) {
37
- const a = (i * Math.PI) / 2;
38
- ctx.beginPath();
39
- ctx.ellipse(cx + Math.cos(a) * r * 0.5, cy + Math.sin(a) * r * 0.5, r * 0.18, r * 0.7, a, 0, Math.PI * 2);
40
- ctx.fill();
41
- }
42
- } else {
43
- // heart
44
- ctx.beginPath();
45
- ctx.moveTo(cx, cy + r * 0.3);
46
- ctx.bezierCurveTo(cx - r * 1.1, cy - r * 0.5, cx - r * 1.6, cy + r * 0.5, cx, cy + r * 1.4);
47
- ctx.bezierCurveTo(cx + r * 1.6, cy + r * 0.5, cx + r * 1.1, cy - r * 0.5, cx, cy + r * 0.3);
48
- ctx.fill();
49
- }
50
- ctx.restore();
51
- }
52
-
53
- export function heartBurst(canvas, userOptions = {}) {
54
- const opts = mergeOptions(DEFAULTS, userOptions);
55
- const ctx = canvas.getContext('2d');
56
- const particles = [];
57
- let running = true;
58
-
59
- function spawnBurst(x, y) {
60
- for (let i = 0; i < opts.count; i++) {
61
- const angle = Math.random() * Math.PI * 2;
62
- const speed = opts.minSpeed + Math.random() * (opts.maxSpeed - opts.minSpeed);
63
- particles.push({
64
- x, y,
65
- size: opts.minSize + Math.random() * (opts.maxSize - opts.minSize),
66
- vx: Math.cos(angle) * speed,
67
- vy: Math.sin(angle) * speed,
68
- alpha: 1,
69
- decay: opts.decay * (0.8 + Math.random() * 0.4),
70
- color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
71
- symbol: opts.symbols[Math.floor(Math.random() * opts.symbols.length)],
72
- });
73
- }
74
- }
75
-
76
- const onClick = (e) => {
77
- const rect = canvas.getBoundingClientRect();
78
- spawnBurst(e.clientX - rect.left, e.clientY - rect.top);
79
- };
80
- const onTouch = (e) => {
81
- const rect = canvas.getBoundingClientRect();
82
- Array.from(e.changedTouches).forEach((t) => spawnBurst(t.clientX - rect.left, t.clientY - rect.top));
83
- };
84
-
85
- window.addEventListener('click', onClick);
86
- window.addEventListener('touchend', onTouch, { passive: true });
87
-
88
- function animate() {
89
- if (!running) return;
90
- ctx.clearRect(0, 0, canvas.width, canvas.height);
91
-
92
- for (let i = particles.length - 1; i >= 0; i--) {
93
- const p = particles[i];
94
- p.x += p.vx;
95
- p.y += p.vy;
96
- p.vy += opts.gravity; // gravity
97
- p.alpha -= p.decay;
98
- drawSymbol(ctx, p.symbol, p.x, p.y, p.size, p.color, p.alpha, opts.glow);
99
- if (p.alpha <= 0) particles.splice(i, 1);
100
- }
101
-
102
- requestAnimationFrame(animate);
103
- }
104
-
105
- animate();
106
-
107
- return function stop() {
108
- running = false;
109
- window.removeEventListener('click', onClick);
110
- window.removeEventListener('touchend', onTouch);
111
- ctx.clearRect(0, 0, canvas.width, canvas.height);
112
- };
113
- }
1
+ import { mergeOptions } from '../core/engine.js';
2
+
3
+ const DEFAULTS = {
4
+ count: 20, // hearts per burst
5
+ minSize: 8,
6
+ maxSize: 20,
7
+ minSpeed: 2,
8
+ maxSpeed: 7,
9
+ gravity: 0.08,
10
+ decay: 0.018,
11
+ colors: ['#ff0a54', '#ff477e', '#ff7096', '#ff85a1', '#fbb1bd', '#ff4d6d'],
12
+ glow: true,
13
+ symbols: ['heart'], // 'heart' | 'star' | 'sparkle'
14
+ };
15
+
16
+ function drawSymbol(ctx, type, cx, cy, r, color, alpha, glow) {
17
+ ctx.save();
18
+ ctx.globalAlpha = Math.max(0, alpha);
19
+ if (glow) { ctx.shadowColor = color; ctx.shadowBlur = r * 2; }
20
+ ctx.fillStyle = color;
21
+
22
+ if (type === 'star') {
23
+ // 5-point star
24
+ ctx.beginPath();
25
+ for (let i = 0; i < 5; i++) {
26
+ const outer = (Math.PI / 2) + (i * 2 * Math.PI) / 5;
27
+ const inner = outer + Math.PI / 5;
28
+ if (i === 0) ctx.moveTo(cx + r * Math.cos(outer), cy - r * Math.sin(outer));
29
+ else ctx.lineTo(cx + r * Math.cos(outer), cy - r * Math.sin(outer));
30
+ ctx.lineTo(cx + (r * 0.4) * Math.cos(inner), cy - (r * 0.4) * Math.sin(inner));
31
+ }
32
+ ctx.closePath();
33
+ ctx.fill();
34
+ } else if (type === 'sparkle') {
35
+ // 4-point sparkle
36
+ for (let i = 0; i < 4; i++) {
37
+ const a = (i * Math.PI) / 2;
38
+ ctx.beginPath();
39
+ ctx.ellipse(cx + Math.cos(a) * r * 0.5, cy + Math.sin(a) * r * 0.5, r * 0.18, r * 0.7, a, 0, Math.PI * 2);
40
+ ctx.fill();
41
+ }
42
+ } else {
43
+ // heart
44
+ ctx.beginPath();
45
+ ctx.moveTo(cx, cy + r * 0.3);
46
+ ctx.bezierCurveTo(cx - r * 1.1, cy - r * 0.5, cx - r * 1.6, cy + r * 0.5, cx, cy + r * 1.4);
47
+ ctx.bezierCurveTo(cx + r * 1.6, cy + r * 0.5, cx + r * 1.1, cy - r * 0.5, cx, cy + r * 0.3);
48
+ ctx.fill();
49
+ }
50
+ ctx.restore();
51
+ }
52
+
53
+ export function heartBurst(canvas, userOptions = {}) {
54
+ const opts = mergeOptions(DEFAULTS, userOptions);
55
+ const ctx = canvas.getContext('2d');
56
+ const particles = [];
57
+ let running = true;
58
+
59
+ function spawnBurst(x, y) {
60
+ for (let i = 0; i < opts.count; i++) {
61
+ const angle = Math.random() * Math.PI * 2;
62
+ const speed = opts.minSpeed + Math.random() * (opts.maxSpeed - opts.minSpeed);
63
+ particles.push({
64
+ x, y,
65
+ size: opts.minSize + Math.random() * (opts.maxSize - opts.minSize),
66
+ vx: Math.cos(angle) * speed,
67
+ vy: Math.sin(angle) * speed,
68
+ alpha: 1,
69
+ decay: opts.decay * (0.8 + Math.random() * 0.4),
70
+ color: opts.colors[Math.floor(Math.random() * opts.colors.length)],
71
+ symbol: opts.symbols[Math.floor(Math.random() * opts.symbols.length)],
72
+ });
73
+ }
74
+ }
75
+
76
+ const onClick = (e) => {
77
+ const rect = canvas.getBoundingClientRect();
78
+ spawnBurst(e.clientX - rect.left, e.clientY - rect.top);
79
+ };
80
+ const onTouch = (e) => {
81
+ const rect = canvas.getBoundingClientRect();
82
+ Array.from(e.changedTouches).forEach((t) => spawnBurst(t.clientX - rect.left, t.clientY - rect.top));
83
+ };
84
+
85
+ window.addEventListener('click', onClick);
86
+ window.addEventListener('touchend', onTouch, { passive: true });
87
+
88
+ function animate() {
89
+ if (!running) return;
90
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
91
+
92
+ for (let i = particles.length - 1; i >= 0; i--) {
93
+ const p = particles[i];
94
+ p.x += p.vx;
95
+ p.y += p.vy;
96
+ p.vy += opts.gravity; // gravity
97
+ p.alpha -= p.decay;
98
+ drawSymbol(ctx, p.symbol, p.x, p.y, p.size, p.color, p.alpha, opts.glow);
99
+ if (p.alpha <= 0) particles.splice(i, 1);
100
+ }
101
+
102
+ requestAnimationFrame(animate);
103
+ }
104
+
105
+ animate();
106
+
107
+ return function stop() {
108
+ running = false;
109
+ window.removeEventListener('click', onClick);
110
+ window.removeEventListener('touchend', onTouch);
111
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
112
+ };
113
+ }