typography-toolkit 1.0.0

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 ADDED
@@ -0,0 +1,207 @@
1
+ # Typography Toolkit
2
+
3
+ Letter-by-letter text animations with proximity-based disintegration effects. Create animated text where each letter moves independently with base animations (falling, splitting, glitching, floating) and reacts to cursor proximity.
4
+
5
+ ## Features
6
+
7
+ - **Letter-by-Letter Control** - Each letter is a separate DOM element with independent animations
8
+ - **Base Animations** - Falling, splitting, glitching, and floating animations
9
+ - **Proximity-Based Disintegration** - Letters react to cursor without direct interaction
10
+ - **DOM-Based** - Uses CSS transforms (GPU-accelerated), accessible, and styleable
11
+ - **Modular & Extensible** - Easy to add new animation types and behaviors
12
+ - **Zero Dependencies** - Vanilla TypeScript/JavaScript
13
+
14
+ ## Installation
15
+
16
+ ### npm
17
+ ```bash
18
+ npm install typography-toolkit
19
+ ```
20
+
21
+ ### CDN (UMD)
22
+ ```html
23
+ <script src="https://unpkg.com/typography-toolkit/dist/typography-toolkit.umd.js"></script>
24
+ <script>
25
+ const { AnimatedText } = TypographyToolkit;
26
+ </script>
27
+ ```
28
+
29
+ ### ES Modules
30
+ ```javascript
31
+ import { AnimatedText } from 'typography-toolkit';
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### Basic Usage
37
+
38
+ ```javascript
39
+ import { AnimatedText } from 'typography-toolkit';
40
+
41
+ const text = new AnimatedText({
42
+ text: 'HELLO',
43
+ container: document.body,
44
+ animations: ['falling', 'glitching']
45
+ });
46
+
47
+ // Clean up when done
48
+ setTimeout(() => text.destroy(), 10000);
49
+ ```
50
+
51
+ ### With Disintegration
52
+
53
+ ```javascript
54
+ const text = new AnimatedText({
55
+ text: 'FEED ME',
56
+ container: document.body,
57
+ animations: ['falling', 'splitting'],
58
+ disintegration: {
59
+ enabled: true,
60
+ radius: 80,
61
+ behaviors: ['fall-away', 'explode'],
62
+ strength: 0.8
63
+ },
64
+ style: {
65
+ fontFamily: 'Arial',
66
+ fontSize: 24,
67
+ color: 'rgba(60, 60, 60, 0.8)'
68
+ }
69
+ });
70
+ ```
71
+
72
+ ### Advanced Usage
73
+
74
+ ```javascript
75
+ const text = new AnimatedText({
76
+ text: 'ANIMATED TEXT',
77
+ container: document.getElementById('container'),
78
+ animations: ['falling', 'splitting', 'glitching', 'floating'],
79
+ cycle: true, // Cycle through animation types per letter
80
+ speed: 1.5, // Animation speed multiplier
81
+ amplitude: 1.2, // Animation amplitude multiplier
82
+ disintegration: {
83
+ enabled: true,
84
+ radius: 100,
85
+ behaviors: ['fall-away', 'split-apart', 'explode'],
86
+ strength: 1.0
87
+ },
88
+ style: {
89
+ fontFamily: 'Georgia, serif',
90
+ fontSize: 32,
91
+ color: 'rgba(100, 50, 50, 0.9)',
92
+ fontWeight: 'bold'
93
+ },
94
+ position: {
95
+ x: 100,
96
+ y: 200
97
+ },
98
+ fadeOut: 8000 // Auto-destroy after 8 seconds
99
+ });
100
+ ```
101
+
102
+ ## API Reference
103
+
104
+ ### `AnimatedText`
105
+
106
+ Main class for creating animated text.
107
+
108
+ #### Constructor Options
109
+
110
+ ```typescript
111
+ interface AnimatedTextOptions {
112
+ text: string; // Text to animate
113
+ container: HTMLElement; // Container element
114
+ animations?: AnimationType[]; // Animation types (default: all)
115
+ cycle?: boolean; // Cycle through types per letter (default: true)
116
+ speed?: number; // Speed multiplier (default: 1.0)
117
+ amplitude?: number; // Amplitude multiplier (default: 1.0)
118
+ disintegration?: DisintegrationOptions; // Disintegration config
119
+ style?: StyleOptions; // CSS style options
120
+ position?: { x?: number; y?: number }; // Position (default: random)
121
+ fadeOut?: number; // Auto-destroy after ms (default: 0 = never)
122
+ }
123
+ ```
124
+
125
+ #### Methods
126
+
127
+ - `destroy()` - Clean up and remove the animated text
128
+ - `getElement()` - Get the text container element
129
+
130
+ ### Animation Types
131
+
132
+ - `'falling'` - Letters drift downward
133
+ - `'splitting'` - Letters drift outward horizontally
134
+ - `'glitching'` - Letters randomly shift position
135
+ - `'floating'` - Letters slowly rise
136
+
137
+ ### Disintegration Behaviors
138
+
139
+ - `'fall-away'` - Letters drop down when cursor approaches
140
+ - `'split-apart'` - Letters spread horizontally
141
+ - `'explode'` - Letters scatter in all directions
142
+
143
+ ### Style Options
144
+
145
+ ```typescript
146
+ interface StyleOptions {
147
+ fontFamily?: string;
148
+ fontSize?: number;
149
+ color?: string;
150
+ fontWeight?: string;
151
+ }
152
+ ```
153
+
154
+ ## Examples
155
+
156
+ ### Simple Falling Text
157
+
158
+ ```javascript
159
+ const text = new AnimatedText({
160
+ text: 'FALLING',
161
+ container: document.body,
162
+ animations: ['falling']
163
+ });
164
+ ```
165
+
166
+ ### Glitching Text with Disintegration
167
+
168
+ ```javascript
169
+ const text = new AnimatedText({
170
+ text: 'GLITCH',
171
+ container: document.body,
172
+ animations: ['glitching'],
173
+ disintegration: {
174
+ enabled: true,
175
+ radius: 60,
176
+ behaviors: ['explode']
177
+ }
178
+ });
179
+ ```
180
+
181
+ ### Multiple Animation Types
182
+
183
+ ```javascript
184
+ const text = new AnimatedText({
185
+ text: 'VARIED',
186
+ container: document.body,
187
+ animations: ['falling', 'splitting', 'glitching', 'floating'],
188
+ cycle: true // Each letter uses different animation
189
+ });
190
+ ```
191
+
192
+ ## Performance
193
+
194
+ - Uses `requestAnimationFrame` for smooth 60fps animations
195
+ - CSS transforms are GPU-accelerated
196
+ - Efficient proximity calculations
197
+ - Automatic cleanup on destroy
198
+
199
+ ## Browser Support
200
+
201
+ - Modern browsers with ES2020 support
202
+ - Requires `requestAnimationFrame` API
203
+ - CSS transforms support
204
+
205
+ ## License
206
+
207
+ MIT
@@ -0,0 +1,210 @@
1
+ class y {
2
+ update(t, o, s, i = {}) {
3
+ const e = i.speed || 1, a = i.amplitude || 20, l = o * 0.2, r = (s * 10 * e + l * 5) % a - a / 2;
4
+ return {
5
+ ...t,
6
+ y: r
7
+ };
8
+ }
9
+ }
10
+ class x {
11
+ update(t, o, s, i = {}) {
12
+ const e = i.speed || 1, a = i.amplitude || 4, l = o * 0.2, h = Math.sin(s * 0.5 * e + l) * a;
13
+ return {
14
+ ...t,
15
+ x: h
16
+ };
17
+ }
18
+ }
19
+ class C {
20
+ constructor() {
21
+ this.lastUpdate = 0, this.glitchX = 0, this.glitchY = 0, this.glitchRot = 0;
22
+ }
23
+ update(t, o, s, i = {}) {
24
+ const e = i.amplitude || 3;
25
+ return s - this.lastUpdate > 0.1 && (this.glitchX = (Math.random() - 0.5) * e, this.glitchY = (Math.random() - 0.5) * (e * 0.67), this.glitchRot = (Math.random() - 0.5) * e, this.lastUpdate = s), {
26
+ ...t,
27
+ x: this.glitchX,
28
+ y: this.glitchY,
29
+ rotation: this.glitchRot
30
+ };
31
+ }
32
+ }
33
+ class T {
34
+ update(t, o, s, i = {}) {
35
+ const e = i.speed || 1, a = i.amplitude || 15, l = o * 0.2, r = -((s * 8 * e + l * 4) % a - a / 2);
36
+ return {
37
+ ...t,
38
+ y: r
39
+ };
40
+ }
41
+ }
42
+ function b(c, t) {
43
+ const { x: o, y: s, rotation: i, scale: e, opacity: a } = t;
44
+ c.style.transform = `translate(${o}px, ${s}px) rotate(${i}deg) scale(${e})`, c.style.opacity = a.toString();
45
+ }
46
+ function v(c, t, o, s, i, e) {
47
+ if (!e.enabled)
48
+ return {
49
+ state: { x: 0, y: 0, rotation: 0, scale: 1, opacity: 1 },
50
+ applied: !1
51
+ };
52
+ const a = e.radius || 80, l = e.strength || 1, h = e.behaviors || ["fall-away", "split-apart", "explode"], r = o - c, d = s - t, m = Math.sqrt(r * r + d * d);
53
+ if (m >= a)
54
+ return {
55
+ state: { x: 0, y: 0, rotation: 0, scale: 1, opacity: 1 },
56
+ applied: !1
57
+ };
58
+ const n = (1 - m / a) * l, u = Math.atan2(d, r), M = h[i % h.length];
59
+ let p;
60
+ switch (M) {
61
+ case "fall-away":
62
+ const w = Math.cos(u) * n * 20, A = Math.sin(u) * n * 40 + n * 30;
63
+ p = {
64
+ x: w,
65
+ y: A,
66
+ rotation: n * 15,
67
+ scale: 1,
68
+ opacity: 1 - n * 0.6
69
+ };
70
+ break;
71
+ case "split-apart":
72
+ const f = i % 2 === 0 ? 1 : -1;
73
+ p = {
74
+ x: f * n * 50,
75
+ y: (Math.random() - 0.5) * n * 10,
76
+ rotation: n * 10 * f,
77
+ scale: 1,
78
+ opacity: 1 - n * 0.6
79
+ };
80
+ break;
81
+ case "explode":
82
+ const g = u + (Math.random() - 0.5) * 0.5;
83
+ p = {
84
+ x: Math.cos(g) * n * 40,
85
+ y: Math.sin(g) * n * 40,
86
+ rotation: n * 30,
87
+ scale: 1 + n * 0.4,
88
+ opacity: 1 - n * 0.6
89
+ };
90
+ break;
91
+ default:
92
+ p = { x: 0, y: 0, rotation: 0, scale: 1, opacity: 1 };
93
+ }
94
+ return {
95
+ state: p,
96
+ applied: !0
97
+ };
98
+ }
99
+ class F {
100
+ constructor(t) {
101
+ this.letters = [], this.animationFrame = null, this.mouseX = 0, this.mouseY = 0, this.mouseMoveHandler = null, this.startTime = Date.now(), this.isDestroyed = !1, this.container = t.container, this.animationTypes = t.animations || ["falling", "splitting", "glitching", "floating"], this.cycle = t.cycle !== !1, this.speed = t.speed || 1, this.amplitude = t.amplitude || 1, this.disintegration = t.disintegration || { enabled: !1 }, this.style = t.style || {}, this.fadeOut = t.fadeOut || 0, this.textContainer = document.createElement("div"), this.textContainer.style.position = "absolute", this.textContainer.style.pointerEvents = "none", this.textContainer.style.zIndex = "1", t.position && (t.position.x !== void 0 && (this.textContainer.style.left = `${t.position.x}px`), t.position.y !== void 0 && (this.textContainer.style.top = `${t.position.y}px`)), this.createLetters(t.text), this.setupMouseTracking(), this.startAnimation(), this.fadeOut > 0 && setTimeout(() => this.destroy(), this.fadeOut);
102
+ }
103
+ createLetters(t) {
104
+ t.toUpperCase().split("").forEach((s, i) => {
105
+ if (s === " ") {
106
+ const l = document.createTextNode(" ");
107
+ this.textContainer.appendChild(l);
108
+ return;
109
+ }
110
+ const e = document.createElement("span");
111
+ e.className = "animated-letter", e.textContent = s, e.dataset.index = i.toString(), e.dataset.char = s, this.applyStyle(e), e.style.display = "inline-block", e.style.position = "relative", e.style.transition = "transform 0.1s ease-out", this.textContainer.appendChild(e);
112
+ const a = e.getBoundingClientRect();
113
+ this.letters.push({
114
+ element: e,
115
+ index: i,
116
+ char: s,
117
+ baseX: a.left,
118
+ baseY: a.top
119
+ });
120
+ }), this.container.appendChild(this.textContainer);
121
+ }
122
+ applyStyle(t) {
123
+ this.style.fontFamily && (t.style.fontFamily = this.style.fontFamily), this.style.fontSize && (t.style.fontSize = `${this.style.fontSize}px`), this.style.color && (t.style.color = this.style.color), this.style.fontWeight && (t.style.fontWeight = this.style.fontWeight);
124
+ }
125
+ setupMouseTracking() {
126
+ this.disintegration.enabled && (this.mouseMoveHandler = (t) => {
127
+ this.mouseX = t.clientX, this.mouseY = t.clientY;
128
+ }, document.addEventListener("mousemove", this.mouseMoveHandler));
129
+ }
130
+ startAnimation() {
131
+ const t = () => {
132
+ if (this.isDestroyed) return;
133
+ const o = (Date.now() - this.startTime) * 1e-3;
134
+ this.letters.forEach((s, i) => {
135
+ const e = s.element.getBoundingClientRect(), a = e.left + e.width / 2, l = e.top + e.height / 2, h = v(
136
+ a,
137
+ l,
138
+ this.mouseX,
139
+ this.mouseY,
140
+ i,
141
+ this.disintegration
142
+ );
143
+ let r;
144
+ if (h.applied)
145
+ r = h.state;
146
+ else {
147
+ const d = this.getAnimationType(i);
148
+ r = this.getAnimation(d).update(
149
+ { x: 0, y: 0, rotation: 0, scale: 1, opacity: 1 },
150
+ i,
151
+ o,
152
+ {
153
+ speed: this.speed,
154
+ amplitude: this.amplitude * (d === "falling" || d === "floating" ? 20 : 4)
155
+ }
156
+ );
157
+ }
158
+ b(s.element, r);
159
+ }), this.animationFrame = requestAnimationFrame(t);
160
+ };
161
+ this.animationFrame = requestAnimationFrame(t);
162
+ }
163
+ getAnimationType(t) {
164
+ return this.cycle ? this.animationTypes[t % this.animationTypes.length] : this.animationTypes[0];
165
+ }
166
+ getAnimation(t) {
167
+ switch (t) {
168
+ case "falling":
169
+ return new y();
170
+ case "splitting":
171
+ return new x();
172
+ case "glitching":
173
+ return new C();
174
+ case "floating":
175
+ return new T();
176
+ default:
177
+ return new y();
178
+ }
179
+ }
180
+ /**
181
+ * Destroy the animated text instance
182
+ */
183
+ destroy() {
184
+ this.isDestroyed || (this.isDestroyed = !0, this.animationFrame !== null && cancelAnimationFrame(this.animationFrame), this.mouseMoveHandler && document.removeEventListener("mousemove", this.mouseMoveHandler), this.textContainer.style.transition = "opacity 3s ease", this.textContainer.style.opacity = "0", setTimeout(() => {
185
+ this.textContainer.parentNode && this.textContainer.parentNode.removeChild(this.textContainer);
186
+ }, 3e3));
187
+ }
188
+ /**
189
+ * Get the text container element
190
+ */
191
+ getElement() {
192
+ return this.textContainer;
193
+ }
194
+ }
195
+ typeof window < "u" && (window.TypographyToolkit = {
196
+ AnimatedText: F,
197
+ FallingAnimation: y,
198
+ SplittingAnimation: x,
199
+ GlitchingAnimation: C,
200
+ FloatingAnimation: T,
201
+ calculateDisintegration: v
202
+ });
203
+ export {
204
+ F as AnimatedText,
205
+ y as FallingAnimation,
206
+ T as FloatingAnimation,
207
+ C as GlitchingAnimation,
208
+ x as SplittingAnimation,
209
+ v as calculateDisintegration
210
+ };
@@ -0,0 +1 @@
1
+ var TypographyToolkit=function(d){"use strict";class u{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||20,l=o*.2,r=(n*10*e+l*5)%s-s/2;return{...t,y:r}}}class y{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||4,l=o*.2,h=Math.sin(n*.5*e+l)*s;return{...t,x:h}}}class f{constructor(){this.lastUpdate=0,this.glitchX=0,this.glitchY=0,this.glitchRot=0}update(t,o,n,i={}){const e=i.amplitude||3;return n-this.lastUpdate>.1&&(this.glitchX=(Math.random()-.5)*e,this.glitchY=(Math.random()-.5)*(e*.67),this.glitchRot=(Math.random()-.5)*e,this.lastUpdate=n),{...t,x:this.glitchX,y:this.glitchY,rotation:this.glitchRot}}}class g{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||15,l=o*.2,r=-((n*8*e+l*4)%s-s/2);return{...t,y:r}}}function w(c,t){const{x:o,y:n,rotation:i,scale:e,opacity:s}=t;c.style.transform=`translate(${o}px, ${n}px) rotate(${i}deg) scale(${e})`,c.style.opacity=s.toString()}function x(c,t,o,n,i,e){if(!e.enabled)return{state:{x:0,y:0,rotation:0,scale:1,opacity:1},applied:!1};const s=e.radius||80,l=e.strength||1,h=e.behaviors||["fall-away","split-apart","explode"],r=o-c,m=n-t,C=Math.sqrt(r*r+m*m);if(C>=s)return{state:{x:0,y:0,rotation:0,scale:1,opacity:1},applied:!1};const a=(1-C/s)*l,T=Math.atan2(m,r),b=h[i%h.length];let p;switch(b){case"fall-away":const F=Math.cos(T)*a*20,S=Math.sin(T)*a*40+a*30;p={x:F,y:S,rotation:a*15,scale:1,opacity:1-a*.6};break;case"split-apart":const A=i%2===0?1:-1;p={x:A*a*50,y:(Math.random()-.5)*a*10,rotation:a*10*A,scale:1,opacity:1-a*.6};break;case"explode":const M=T+(Math.random()-.5)*.5;p={x:Math.cos(M)*a*40,y:Math.sin(M)*a*40,rotation:a*30,scale:1+a*.4,opacity:1-a*.6};break;default:p={x:0,y:0,rotation:0,scale:1,opacity:1}}return{state:p,applied:!0}}class v{constructor(t){this.letters=[],this.animationFrame=null,this.mouseX=0,this.mouseY=0,this.mouseMoveHandler=null,this.startTime=Date.now(),this.isDestroyed=!1,this.container=t.container,this.animationTypes=t.animations||["falling","splitting","glitching","floating"],this.cycle=t.cycle!==!1,this.speed=t.speed||1,this.amplitude=t.amplitude||1,this.disintegration=t.disintegration||{enabled:!1},this.style=t.style||{},this.fadeOut=t.fadeOut||0,this.textContainer=document.createElement("div"),this.textContainer.style.position="absolute",this.textContainer.style.pointerEvents="none",this.textContainer.style.zIndex="1",t.position&&(t.position.x!==void 0&&(this.textContainer.style.left=`${t.position.x}px`),t.position.y!==void 0&&(this.textContainer.style.top=`${t.position.y}px`)),this.createLetters(t.text),this.setupMouseTracking(),this.startAnimation(),this.fadeOut>0&&setTimeout(()=>this.destroy(),this.fadeOut)}createLetters(t){t.toUpperCase().split("").forEach((n,i)=>{if(n===" "){const l=document.createTextNode(" ");this.textContainer.appendChild(l);return}const e=document.createElement("span");e.className="animated-letter",e.textContent=n,e.dataset.index=i.toString(),e.dataset.char=n,this.applyStyle(e),e.style.display="inline-block",e.style.position="relative",e.style.transition="transform 0.1s ease-out",this.textContainer.appendChild(e);const s=e.getBoundingClientRect();this.letters.push({element:e,index:i,char:n,baseX:s.left,baseY:s.top})}),this.container.appendChild(this.textContainer)}applyStyle(t){this.style.fontFamily&&(t.style.fontFamily=this.style.fontFamily),this.style.fontSize&&(t.style.fontSize=`${this.style.fontSize}px`),this.style.color&&(t.style.color=this.style.color),this.style.fontWeight&&(t.style.fontWeight=this.style.fontWeight)}setupMouseTracking(){this.disintegration.enabled&&(this.mouseMoveHandler=t=>{this.mouseX=t.clientX,this.mouseY=t.clientY},document.addEventListener("mousemove",this.mouseMoveHandler))}startAnimation(){const t=()=>{if(this.isDestroyed)return;const o=(Date.now()-this.startTime)*.001;this.letters.forEach((n,i)=>{const e=n.element.getBoundingClientRect(),s=e.left+e.width/2,l=e.top+e.height/2,h=x(s,l,this.mouseX,this.mouseY,i,this.disintegration);let r;if(h.applied)r=h.state;else{const m=this.getAnimationType(i);r=this.getAnimation(m).update({x:0,y:0,rotation:0,scale:1,opacity:1},i,o,{speed:this.speed,amplitude:this.amplitude*(m==="falling"||m==="floating"?20:4)})}w(n.element,r)}),this.animationFrame=requestAnimationFrame(t)};this.animationFrame=requestAnimationFrame(t)}getAnimationType(t){return this.cycle?this.animationTypes[t%this.animationTypes.length]:this.animationTypes[0]}getAnimation(t){switch(t){case"falling":return new u;case"splitting":return new y;case"glitching":return new f;case"floating":return new g;default:return new u}}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.animationFrame!==null&&cancelAnimationFrame(this.animationFrame),this.mouseMoveHandler&&document.removeEventListener("mousemove",this.mouseMoveHandler),this.textContainer.style.transition="opacity 3s ease",this.textContainer.style.opacity="0",setTimeout(()=>{this.textContainer.parentNode&&this.textContainer.parentNode.removeChild(this.textContainer)},3e3))}getElement(){return this.textContainer}}return typeof window<"u"&&(window.TypographyToolkit={AnimatedText:v,FallingAnimation:u,SplittingAnimation:y,GlitchingAnimation:f,FloatingAnimation:g,calculateDisintegration:x}),d.AnimatedText=v,d.FallingAnimation=u,d.FloatingAnimation=g,d.GlitchingAnimation=f,d.SplittingAnimation=y,d.calculateDisintegration=x,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"}),d}({});
@@ -0,0 +1 @@
1
+ (function(l,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(l=typeof globalThis<"u"?globalThis:l||self,p(l.TypographyToolkit={}))})(this,function(l){"use strict";class p{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||20,r=o*.2,c=(n*10*e+r*5)%s-s/2;return{...t,y:c}}}class y{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||4,r=o*.2,d=Math.sin(n*.5*e+r)*s;return{...t,x:d}}}class f{constructor(){this.lastUpdate=0,this.glitchX=0,this.glitchY=0,this.glitchRot=0}update(t,o,n,i={}){const e=i.amplitude||3;return n-this.lastUpdate>.1&&(this.glitchX=(Math.random()-.5)*e,this.glitchY=(Math.random()-.5)*(e*.67),this.glitchRot=(Math.random()-.5)*e,this.lastUpdate=n),{...t,x:this.glitchX,y:this.glitchY,rotation:this.glitchRot}}}class g{update(t,o,n,i={}){const e=i.speed||1,s=i.amplitude||15,r=o*.2,c=-((n*8*e+r*4)%s-s/2);return{...t,y:c}}}function b(h,t){const{x:o,y:n,rotation:i,scale:e,opacity:s}=t;h.style.transform=`translate(${o}px, ${n}px) rotate(${i}deg) scale(${e})`,h.style.opacity=s.toString()}function x(h,t,o,n,i,e){if(!e.enabled)return{state:{x:0,y:0,rotation:0,scale:1,opacity:1},applied:!1};const s=e.radius||80,r=e.strength||1,d=e.behaviors||["fall-away","split-apart","explode"],c=o-h,u=n-t,T=Math.sqrt(c*c+u*u);if(T>=s)return{state:{x:0,y:0,rotation:0,scale:1,opacity:1},applied:!1};const a=(1-T/s)*r,C=Math.atan2(u,c),w=d[i%d.length];let m;switch(w){case"fall-away":const F=Math.cos(C)*a*20,S=Math.sin(C)*a*40+a*30;m={x:F,y:S,rotation:a*15,scale:1,opacity:1-a*.6};break;case"split-apart":const v=i%2===0?1:-1;m={x:v*a*50,y:(Math.random()-.5)*a*10,rotation:a*10*v,scale:1,opacity:1-a*.6};break;case"explode":const M=C+(Math.random()-.5)*.5;m={x:Math.cos(M)*a*40,y:Math.sin(M)*a*40,rotation:a*30,scale:1+a*.4,opacity:1-a*.6};break;default:m={x:0,y:0,rotation:0,scale:1,opacity:1}}return{state:m,applied:!0}}class A{constructor(t){this.letters=[],this.animationFrame=null,this.mouseX=0,this.mouseY=0,this.mouseMoveHandler=null,this.startTime=Date.now(),this.isDestroyed=!1,this.container=t.container,this.animationTypes=t.animations||["falling","splitting","glitching","floating"],this.cycle=t.cycle!==!1,this.speed=t.speed||1,this.amplitude=t.amplitude||1,this.disintegration=t.disintegration||{enabled:!1},this.style=t.style||{},this.fadeOut=t.fadeOut||0,this.textContainer=document.createElement("div"),this.textContainer.style.position="absolute",this.textContainer.style.pointerEvents="none",this.textContainer.style.zIndex="1",t.position&&(t.position.x!==void 0&&(this.textContainer.style.left=`${t.position.x}px`),t.position.y!==void 0&&(this.textContainer.style.top=`${t.position.y}px`)),this.createLetters(t.text),this.setupMouseTracking(),this.startAnimation(),this.fadeOut>0&&setTimeout(()=>this.destroy(),this.fadeOut)}createLetters(t){t.toUpperCase().split("").forEach((n,i)=>{if(n===" "){const r=document.createTextNode(" ");this.textContainer.appendChild(r);return}const e=document.createElement("span");e.className="animated-letter",e.textContent=n,e.dataset.index=i.toString(),e.dataset.char=n,this.applyStyle(e),e.style.display="inline-block",e.style.position="relative",e.style.transition="transform 0.1s ease-out",this.textContainer.appendChild(e);const s=e.getBoundingClientRect();this.letters.push({element:e,index:i,char:n,baseX:s.left,baseY:s.top})}),this.container.appendChild(this.textContainer)}applyStyle(t){this.style.fontFamily&&(t.style.fontFamily=this.style.fontFamily),this.style.fontSize&&(t.style.fontSize=`${this.style.fontSize}px`),this.style.color&&(t.style.color=this.style.color),this.style.fontWeight&&(t.style.fontWeight=this.style.fontWeight)}setupMouseTracking(){this.disintegration.enabled&&(this.mouseMoveHandler=t=>{this.mouseX=t.clientX,this.mouseY=t.clientY},document.addEventListener("mousemove",this.mouseMoveHandler))}startAnimation(){const t=()=>{if(this.isDestroyed)return;const o=(Date.now()-this.startTime)*.001;this.letters.forEach((n,i)=>{const e=n.element.getBoundingClientRect(),s=e.left+e.width/2,r=e.top+e.height/2,d=x(s,r,this.mouseX,this.mouseY,i,this.disintegration);let c;if(d.applied)c=d.state;else{const u=this.getAnimationType(i);c=this.getAnimation(u).update({x:0,y:0,rotation:0,scale:1,opacity:1},i,o,{speed:this.speed,amplitude:this.amplitude*(u==="falling"||u==="floating"?20:4)})}b(n.element,c)}),this.animationFrame=requestAnimationFrame(t)};this.animationFrame=requestAnimationFrame(t)}getAnimationType(t){return this.cycle?this.animationTypes[t%this.animationTypes.length]:this.animationTypes[0]}getAnimation(t){switch(t){case"falling":return new p;case"splitting":return new y;case"glitching":return new f;case"floating":return new g;default:return new p}}destroy(){this.isDestroyed||(this.isDestroyed=!0,this.animationFrame!==null&&cancelAnimationFrame(this.animationFrame),this.mouseMoveHandler&&document.removeEventListener("mousemove",this.mouseMoveHandler),this.textContainer.style.transition="opacity 3s ease",this.textContainer.style.opacity="0",setTimeout(()=>{this.textContainer.parentNode&&this.textContainer.parentNode.removeChild(this.textContainer)},3e3))}getElement(){return this.textContainer}}typeof window<"u"&&(window.TypographyToolkit={AnimatedText:A,FallingAnimation:p,SplittingAnimation:y,GlitchingAnimation:f,FloatingAnimation:g,calculateDisintegration:x}),l.AnimatedText=A,l.FallingAnimation=p,l.FloatingAnimation=g,l.GlitchingAnimation=f,l.SplittingAnimation=y,l.calculateDisintegration=x,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "typography-toolkit",
3
+ "version": "1.0.0",
4
+ "description": "Letter-by-letter text animations with proximity-based disintegration effects",
5
+ "main": "dist/typography-toolkit.umd.js",
6
+ "module": "dist/typography-toolkit.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "unpkg": "dist/typography-toolkit.umd.js",
9
+ "jsdelivr": "dist/typography-toolkit.umd.js",
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && vite build",
16
+ "dev": "vite",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "typography",
21
+ "animation",
22
+ "text",
23
+ "letters",
24
+ "disintegration",
25
+ "interactive",
26
+ "dom"
27
+ ],
28
+ "author": "",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/mathonsunday/typography-toolkit.git"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.0.0",
36
+ "vite": "^5.0.0",
37
+ "@types/node": "^20.0.0"
38
+ }
39
+ }