@tombcato/smart-ticker 1.1.0 → 1.2.1
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 +105 -93
- package/dist/{TickerCore-BAsdPrD1.cjs → TickerCore-D-qtiXMY.cjs} +39 -5
- package/dist/TickerCore-D-qtiXMY.cjs.map +1 -0
- package/dist/{TickerCore-C7Ejc9kB.js → TickerCore-D5-49bZ3.js} +42 -8
- package/dist/TickerCore-D5-49bZ3.js.map +1 -0
- package/dist/index.cjs +135 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +136 -41
- package/dist/index.js.map +1 -1
- package/dist/style.css +26 -25
- package/dist/vue.cjs +172 -99
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.ts +91 -105
- package/dist/vue.js +173 -100
- package/dist/vue.js.map +1 -1
- package/package.json +4 -1
- package/dist/TickerCore-BAsdPrD1.cjs.map +0 -1
- package/dist/TickerCore-C7Ejc9kB.js.map +0 -1
- package/dist/vue-demo.html +0 -632
package/README.md
CHANGED
|
@@ -6,20 +6,22 @@
|
|
|
6
6
|
<h1 align="center">Smart Ticker</h1>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
|
|
9
|
+
<a href="./README.md">🇨🇳 简体中文</a> | <strong>🇬🇧 English</strong>
|
|
10
10
|
</p>
|
|
11
|
-
|
|
11
|
+
High-Performance Text Diff Motion Component based on Levenshtein diff algorithm. Make your text flow like water. <a href="https://tombcato.github.io/smart-ticker/?lang=en">Live Demo ></a> <br />
|
|
12
12
|
<p align="center">
|
|
13
|
-
|
|
13
|
+
Supports any characters<br />
|
|
14
|
+
<img src="./smartticker2.gif" alt="Demo" width="600" />
|
|
14
15
|
</p>
|
|
15
|
-
|
|
16
16
|
<p align="center">
|
|
17
|
-
|
|
17
|
+
Supports Prefix/Suffix, Intl Formatting, Auto-Scale, Fading Edges<br />
|
|
18
|
+
<img src="./smartticker3.gif" alt="Demo" width="600" />
|
|
18
19
|
</p>
|
|
19
20
|
<p align="center">
|
|
20
|
-
<img src="./
|
|
21
|
+
<img src="./smartticker.gif" alt="Demo" width="600" />
|
|
21
22
|
</p>
|
|
22
23
|
|
|
24
|
+
|
|
23
25
|
<p align="center">
|
|
24
26
|
<img src="https://img.shields.io/badge/React-18+-61DAFB?logo=react" alt="React" />
|
|
25
27
|
<img src="https://img.shields.io/badge/Vue-3+-4FC08D?logo=vuedotjs" alt="Vue" />
|
|
@@ -28,55 +30,54 @@
|
|
|
28
30
|
</p>
|
|
29
31
|
|
|
30
32
|
|
|
31
|
-
## ✨ 特性
|
|
32
33
|
| | |
|
|
33
34
|
| :--- | :--- |
|
|
34
|
-
| **🌏
|
|
35
|
-
| **⚡
|
|
36
|
-
| **🦄
|
|
35
|
+
| **🌏 Multi-Charset Support**<br>Supports CJK, Numbers, Emojis, and mixed text rolling. Auto-adjusts spacing based on Unicode width. | **🧠 Smart Diff Animation**<br>Uses Levenshtein algorithm to find the shortest change path; identical characters remain static. |
|
|
36
|
+
| **⚡ Smooth Interruption**<br>Seamlessly transitions to new targets if the value changes dynamically during animation. | **📈 Rich Motion**<br>Built-in variety of easings.Supports custom easing function. Supports `charWidth` fine-tuning. |
|
|
37
|
+
| **🦄 Dual Framework**<br>Provides both React (Hooks) and Vue 3 (Composition) components with a unified API. | **🚀 High Performance**<br>Powered by `RAF`, supporting **Auto-scale**, **Fading Edge**, and **Disable Animation**. |
|
|
37
38
|
|
|
38
|
-
## 📦
|
|
39
|
+
## 📦 Installation
|
|
39
40
|
|
|
40
|
-
### NPM
|
|
41
|
+
### NPM (Recommended)
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
44
|
npm install @tombcato/smart-ticker
|
|
44
45
|
```
|
|
45
46
|
|
|
46
|
-
###
|
|
47
|
+
### From Source
|
|
47
48
|
|
|
48
49
|
```bash
|
|
49
|
-
#
|
|
50
|
+
# Clone repository
|
|
50
51
|
git clone https://github.com/tombcato/smart-ticker.git
|
|
51
52
|
|
|
52
|
-
#
|
|
53
|
+
# Install dependencies
|
|
53
54
|
cd smart-ticker
|
|
54
55
|
npm install
|
|
55
56
|
|
|
56
|
-
#
|
|
57
|
+
# Start dev server
|
|
57
58
|
npm run dev
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
## 🚀
|
|
61
|
+
## 🚀 Usage
|
|
61
62
|
|
|
62
|
-
### 📦
|
|
63
|
+
### 📦 Import Styles
|
|
63
64
|
|
|
64
|
-
**NPM
|
|
65
|
+
When using **NPM**, you **MUST** explicitly import the style file for the component to work.
|
|
65
66
|
|
|
66
67
|
```javascript
|
|
67
68
|
import '@tombcato/smart-ticker/style.css'
|
|
68
69
|
```
|
|
69
70
|
|
|
70
|
-
>
|
|
71
|
+
> **Source Integration**: If copying source code, ensure React version imports `Ticker.css` and Vue version uses the built-in styles.
|
|
71
72
|
|
|
72
73
|
### React
|
|
73
74
|
|
|
74
75
|
```tsx
|
|
75
|
-
// NPM
|
|
76
|
+
// NPM Usage
|
|
76
77
|
import { Ticker } from '@tombcato/smart-ticker';
|
|
77
78
|
import '@tombcato/smart-ticker/style.css';
|
|
78
79
|
|
|
79
|
-
//
|
|
80
|
+
// Source Usage
|
|
80
81
|
// import { Ticker } from './components/Ticker';
|
|
81
82
|
|
|
82
83
|
function App() {
|
|
@@ -98,11 +99,11 @@ function App() {
|
|
|
98
99
|
|
|
99
100
|
```vue
|
|
100
101
|
<script setup>
|
|
101
|
-
// NPM
|
|
102
|
+
// NPM Usage
|
|
102
103
|
import { Ticker } from '@tombcato/smart-ticker/vue';
|
|
103
104
|
import '@tombcato/smart-ticker/style.css';
|
|
104
105
|
|
|
105
|
-
//
|
|
106
|
+
// Source Usage
|
|
106
107
|
// import Ticker from './components/vue/Ticker.vue';
|
|
107
108
|
|
|
108
109
|
import { ref } from 'vue';
|
|
@@ -120,133 +121,144 @@ const price = ref('73.18');
|
|
|
120
121
|
</template>
|
|
121
122
|
```
|
|
122
123
|
|
|
123
|
-
### 💅
|
|
124
|
+
### 💅 Customization
|
|
124
125
|
|
|
125
|
-
####
|
|
126
|
+
#### Custom Fonts
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
The component uses the system monospace stack by default. To use a custom font (e.g., `JetBrains Mono`), ensure it is **monospace** and override via CSS:
|
|
128
129
|
|
|
129
130
|
```css
|
|
130
|
-
/*
|
|
131
|
+
/* In global styles or component styles */
|
|
131
132
|
.ticker {
|
|
132
133
|
font-family: 'JetBrains Mono', monospace !important;
|
|
133
134
|
}
|
|
134
135
|
```
|
|
135
136
|
|
|
136
|
-
>
|
|
137
|
-
|
|
137
|
+
> **Note**: Must be a **monospace font**, otherwise alignment issues may occur during scrolling animations.
|
|
138
138
|
|
|
139
139
|
## ⚙️ API
|
|
140
|
+
|
|
140
141
|
### Props
|
|
141
142
|
|
|
142
|
-
|
|
|
143
|
+
| Prop | Type | Default | Description |
|
|
143
144
|
|------|------|--------|------|
|
|
144
|
-
| `value` | `string` | - |
|
|
145
|
-
| `duration` | `number` | `500` |
|
|
146
|
-
| `easing` | `EasingName \| function` | `'easeInOut'` |
|
|
147
|
-
| `direction` | `string` | `'ANY'` |
|
|
148
|
-
| `charWidth` | `number` | `1` |
|
|
149
|
-
| `characterLists` | `string[]` | `['0123456789']` |
|
|
150
|
-
| `className` | `string` | `''` |
|
|
151
|
-
| `animateOnMount` | `boolean` | `false` |
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
| `value` | `string`\|`number` | - | The text value to display (Required) |
|
|
146
|
+
| `duration` | `number` | `500` | Animation duration (ms) |
|
|
147
|
+
| `easing` | `EasingName \| function` | `'easeInOut'` | Easing: `linear`, `easeIn`, `easeOut`, `easeInOut`, `bounce`, or custom `(t: number) => number` |
|
|
148
|
+
| `direction` | `string` | `'ANY'` | Scroll direction: `UP`, `DOWN`, `ANY` (shortest path) |
|
|
149
|
+
| `charWidth` | `number` | `1` | Character width multiplier (base 0.8em) |
|
|
150
|
+
| `characterLists` | `string[]` | `['0123456789']` | Allowed character sets |
|
|
151
|
+
| `className` | `string` | `''` | Custom CSS class name |
|
|
152
|
+
| `animateOnMount` | `boolean` | `false` | Animate on initial render |
|
|
153
|
+
| `disableAnimation` | `boolean` | `false` | Disable animation, show final value immediately |
|
|
154
|
+
| `autoScale` | `boolean` | `false` | Enable auto-scaling to fit container width |
|
|
155
|
+
| `fadingEdge` | `boolean` | `false` | Enable top/bottom fading edge effect |
|
|
156
|
+
| `prefix` | `string` | - | Static prefix (not animated) |
|
|
157
|
+
| `suffix` | `string` | - | Static suffix (not animated) |
|
|
158
|
+
| `numberFormat` | `Intl.NumberFormat` | - | Intl formatter number `value` |
|
|
159
|
+
| `onAnimationEnd` | `() => void` | - | Callback when animation ends (Vue: `@animation-end`) |
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### 🧩 Character Configuration (characterLists)
|
|
163
|
+
|
|
164
|
+
`characterLists` controls the core animation logic. It accepts an array of strings, where each string represents a group of characters that can **scroll into each other**.
|
|
165
|
+
|
|
166
|
+
#### Presets (Common Character Lists)
|
|
167
|
+
For convenience, we provide built-in constants for common character sets:
|
|
158
168
|
|
|
159
169
|
```ts
|
|
160
|
-
import {
|
|
170
|
+
import { Presets } from '@tombcato/smart-ticker';
|
|
161
171
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
`characterLists` 是控制 Ticker 动画逻辑的核心配置。它接受一个字符串数组,数组的每一项代表一组**“可以互相滚动”**的字符。
|
|
168
|
-
|
|
169
|
-
**基本规则:**
|
|
170
|
-
1. **同组滚动**:如果旧字符和新字符在同一个字符串中(例如 `0` 变 `9` 在 `'0123456789'` 中),它们会产生滚动动画。
|
|
171
|
-
2. **跨组替换**:如果它们不在同一组(例如 `a` 变 `1`),或者任何一个字符不在配置列表中(例如汉字),它们会原地切换(Switch),不会产生滚动。
|
|
172
|
+
Presets.NUMBER // '0123456789'
|
|
173
|
+
Presets.ALPHABET // 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
174
|
+
Presets.ALPHANUMERIC // '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
175
|
+
Presets.CURRENCY // '0123456789.,'
|
|
176
|
+
```
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
#### Animation Rules
|
|
179
|
+
1. **Scroll**: If both the old and new characters belong to the same group string (e.g., `0` to `9` in `Presets.NUMBER`), they will scroll.
|
|
180
|
+
2. **Switch**: If they are in different groups, or if a character is not in any list (e.g., Chinese characters), they will switch instantly (fade/flip) without scrolling.
|
|
174
181
|
|
|
175
|
-
|
|
176
|
-
*
|
|
182
|
+
#### Configuration Tips
|
|
183
|
+
* **Common Use Case**: Simply use `Presets.ALPHANUMERIC` to support most alphanumeric scrolling.
|
|
184
|
+
* **Case Isolation**: To prevent scrolling between cases (e.g., `a` -> `A`), list them as separate groups: `[Presets.NUMBER, 'abc...', 'ABC...']`.
|
|
177
185
|
|
|
178
|
-
|
|
186
|
+
**Code Example:**
|
|
179
187
|
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
188
|
+
```tsx
|
|
189
|
+
<Ticker
|
|
190
|
+
value={val}
|
|
191
|
+
characterLists={[
|
|
192
|
+
Presets.NUMBER, // Numbers
|
|
193
|
+
'abcdefghijklmnopqrstuvwxyz', // Lowercase Group
|
|
194
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', // Uppercase Group
|
|
195
|
+
'.,!@#$%^&*' // Symbols
|
|
196
|
+
]}
|
|
197
|
+
/>
|
|
189
198
|
```
|
|
190
199
|
|
|
191
|
-
## 💻
|
|
200
|
+
## 💻 Running Demos
|
|
192
201
|
|
|
193
|
-
|
|
202
|
+
This project includes complete NPM-based user examples for React and Vue in the `examples` directory.
|
|
194
203
|
|
|
195
|
-
###
|
|
204
|
+
### Start React Demo
|
|
196
205
|
|
|
197
206
|
```bash
|
|
198
207
|
cd examples/react-demo
|
|
199
208
|
npm install
|
|
200
209
|
npm run dev
|
|
210
|
+
# Demo runs at http://localhost:5179
|
|
201
211
|
```
|
|
202
212
|
|
|
203
|
-
###
|
|
213
|
+
### Start Vue Demo
|
|
204
214
|
|
|
205
215
|
```bash
|
|
206
216
|
cd examples/vue-demo
|
|
207
217
|
npm install
|
|
208
218
|
npm run dev
|
|
219
|
+
# Demo runs at http://localhost:5180
|
|
209
220
|
```
|
|
210
221
|
|
|
211
|
-
## 📁
|
|
222
|
+
## 📁 Project Structure
|
|
212
223
|
|
|
213
224
|
```
|
|
214
225
|
smart-ticker/
|
|
215
226
|
├── src/
|
|
216
227
|
│ ├── components/
|
|
217
|
-
│ │ ├── Ticker.tsx # React
|
|
218
|
-
│ │ ├── Ticker.css #
|
|
228
|
+
│ │ ├── Ticker.tsx # React Component Source
|
|
229
|
+
│ │ ├── Ticker.css # Component Core Styles
|
|
219
230
|
│ │ └── vue/
|
|
220
|
-
│ │ └── Ticker.vue # Vue
|
|
231
|
+
│ │ └── Ticker.vue # Vue Component Source
|
|
221
232
|
│ ├── core/
|
|
222
|
-
│ │ └── TickerCore.ts #
|
|
233
|
+
│ │ └── TickerCore.ts # Core Logic (Levenshtein diff algo)
|
|
223
234
|
│ └── ...
|
|
224
|
-
├── examples/ #
|
|
235
|
+
├── examples/ # Standalone Example Projects
|
|
225
236
|
│ ├── react-demo/ # React Demo (Vite + React + TS)
|
|
226
237
|
│ └── vue-demo/ # Vue Demo (Vite + Vue + TS)
|
|
227
238
|
├── public/
|
|
228
|
-
│ └── vue-demo.html #
|
|
239
|
+
│ └── vue-demo.html # Single File CDN Demo
|
|
229
240
|
└── package.json
|
|
230
241
|
```
|
|
231
242
|
|
|
232
|
-
## 🎨
|
|
243
|
+
## 🎨 Example Scenarios
|
|
233
244
|
|
|
234
|
-
-
|
|
235
|
-
-
|
|
236
|
-
-
|
|
237
|
-
-
|
|
238
|
-
-
|
|
245
|
+
- **Financial Data** - Stock prices, crypto rates
|
|
246
|
+
- **Counters** - Page views, likes
|
|
247
|
+
- **Scoreboards** - Real-time sports scores
|
|
248
|
+
- **Airport Info** - Flight numbers, gates
|
|
249
|
+
- **Privacy Mode** - Balance hide/show toggle
|
|
239
250
|
|
|
240
|
-
## 🔧
|
|
251
|
+
## 🔧 Tech Stack
|
|
241
252
|
|
|
242
|
-
-
|
|
243
|
-
-
|
|
244
|
-
-
|
|
245
|
-
-
|
|
253
|
+
- **Build Tool**: Vite
|
|
254
|
+
- **Language**: TypeScript
|
|
255
|
+
- **Frameworks**: React 18 / Vue 3
|
|
256
|
+
- **Styling**: CSS Variables + Responsive Design
|
|
246
257
|
|
|
247
|
-
## 📝
|
|
258
|
+
## 📝 Changelog
|
|
248
259
|
|
|
249
|
-
|
|
260
|
+
See [CHANGELOG_EN.md](./CHANGELOG_EN.md) for version history.
|
|
250
261
|
|
|
251
262
|
## 📄 License
|
|
263
|
+
|
|
252
264
|
MIT
|
|
@@ -3,10 +3,35 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
const EMPTY_CHAR = "\0";
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const Presets = {
|
|
7
|
+
NUMBER: "0123456789",
|
|
8
|
+
ALPHABET: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
9
|
+
ALPHANUMERIC: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
10
|
+
// 常用货币字符:数字 + 标点 货币符号不穿滚动不加
|
|
11
|
+
CURRENCY: "0123456789.,"
|
|
9
12
|
};
|
|
13
|
+
const TickerConstants = {
|
|
14
|
+
CHAR_HEIGHT: 1.2,
|
|
15
|
+
// em
|
|
16
|
+
DEFAULT_DURATION: 500,
|
|
17
|
+
// ms
|
|
18
|
+
DEFAULT_MIN_SCALE: 0.1,
|
|
19
|
+
FW_RATIO: 1.25,
|
|
20
|
+
// 中文字符宽度1.25em
|
|
21
|
+
HW_RATIO: 0.75
|
|
22
|
+
// 英文字符宽度0.75em
|
|
23
|
+
};
|
|
24
|
+
const isFW = (c) => {
|
|
25
|
+
if (!c || c.length === 0) return false;
|
|
26
|
+
const code = c.codePointAt(0) || 0;
|
|
27
|
+
return code >= 12288 && code <= 40959 || // CJK 标点 + 汉字
|
|
28
|
+
code >= 44032 && code <= 55215 || // 韩文
|
|
29
|
+
code >= 65280 && code <= 65519 || // 全角 ASCII
|
|
30
|
+
code >= 127744 && code <= 129791 || // Emoji 范围
|
|
31
|
+
code >= 9728 && code <= 10175 || // Misc Symbols (e.g. ☀, ☂, ☁) & Dingbats
|
|
32
|
+
code >= 11008 && code <= 11263;
|
|
33
|
+
};
|
|
34
|
+
const getW = (c) => isFW(c) ? TickerConstants.FW_RATIO : TickerConstants.HW_RATIO;
|
|
10
35
|
class TickerCharacterList {
|
|
11
36
|
constructor(characterList) {
|
|
12
37
|
__publicField(this, "numOriginalCharacters");
|
|
@@ -227,17 +252,26 @@ const easingFunctions = {
|
|
|
227
252
|
} else {
|
|
228
253
|
return n1 * (t -= 2.625 / d1) * t + 0.984375;
|
|
229
254
|
}
|
|
255
|
+
},
|
|
256
|
+
easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
|
|
257
|
+
easeOutExpo: (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
|
|
258
|
+
backOut: (t) => {
|
|
259
|
+
const c1 = 1.70158;
|
|
260
|
+
const c3 = c1 + 1;
|
|
261
|
+
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
|
|
230
262
|
}
|
|
231
263
|
};
|
|
232
264
|
exports.ACTION_DELETE = ACTION_DELETE;
|
|
233
265
|
exports.ACTION_INSERT = ACTION_INSERT;
|
|
234
266
|
exports.ACTION_SAME = ACTION_SAME;
|
|
235
267
|
exports.EMPTY_CHAR = EMPTY_CHAR;
|
|
268
|
+
exports.Presets = Presets;
|
|
236
269
|
exports.TickerCharacterList = TickerCharacterList;
|
|
237
|
-
exports.
|
|
270
|
+
exports.TickerConstants = TickerConstants;
|
|
238
271
|
exports.applyProgress = applyProgress;
|
|
239
272
|
exports.computeColumnActions = computeColumnActions;
|
|
240
273
|
exports.createColumn = createColumn;
|
|
241
274
|
exports.easingFunctions = easingFunctions;
|
|
275
|
+
exports.getW = getW;
|
|
242
276
|
exports.setTarget = setTarget;
|
|
243
|
-
//# sourceMappingURL=TickerCore-
|
|
277
|
+
//# sourceMappingURL=TickerCore-D-qtiXMY.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TickerCore-D-qtiXMY.cjs","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const Presets = {\r\n NUMBER: '0123456789',\r\n ALPHABET: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n ALPHANUMERIC: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n // 常用货币字符:数字 + 标点 货币符号不穿滚动不加\r\n CURRENCY: '0123456789.,',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\r\n\r\n// ============================================================================\r\n// Defaults & Helpers\r\n// ============================================================================\r\nexport const TickerConstants = {\r\n CHAR_HEIGHT: 1.2, // em\r\n DEFAULT_DURATION: 500, // ms\r\n DEFAULT_MIN_SCALE: 0.1,\r\n FW_RATIO: 1.25, // 中文字符宽度1.25em\r\n HW_RATIO: 0.75, // 英文字符宽度0.75em\r\n};\r\n\r\n// Check if character is full-width (CJK, Emoji, etc.)\r\nexport const isFW = (c: string): boolean => {\r\n if (!c || c.length === 0) return false;\r\n const code = c.codePointAt(0) || 0;\r\n return (\r\n (code >= 0x3000 && code <= 0x9FFF) || // CJK 标点 + 汉字\r\n (code >= 0xAC00 && code <= 0xD7AF) || // 韩文\r\n (code >= 0xFF00 && code <= 0xFFEF) || // 全角 ASCII\r\n (code >= 0x1F300 && code <= 0x1FAFF) || // Emoji 范围\r\n (code >= 0x2600 && code <= 0x27BF) || // Misc Symbols (e.g. ☀, ☂, ☁) & Dingbats\r\n (code >= 0x2B00 && code <= 0x2BFF) // Misc Symbols and Arrows (e.g. ⭐ 0x2B50)\r\n );\r\n};\r\n\r\nexport const getW = (c: string): number => isFW(c) ? TickerConstants.FW_RATIO : TickerConstants.HW_RATIO;\r\n\r\n// ============================================================================\r\n// TickerCharacterList\r\n// ============================================================================\r\nexport class TickerCharacterList {\r\n private numOriginalCharacters: number;\r\n private characterList: string[];\r\n private characterIndicesMap: Map<string, number>;\r\n\r\n constructor(characterList: string) {\r\n const charsArray = [...characterList];\r\n const length = charsArray.length;\r\n this.numOriginalCharacters = length;\r\n this.characterIndicesMap = new Map();\r\n\r\n for (let i = 0; i < length; i++) {\r\n this.characterIndicesMap.set(charsArray[i], i);\r\n }\r\n\r\n this.characterList = new Array(length * 2 + 1);\r\n this.characterList[0] = EMPTY_CHAR;\r\n for (let i = 0; i < length; i++) {\r\n this.characterList[1 + i] = charsArray[i];\r\n this.characterList[1 + length + i] = charsArray[i];\r\n }\r\n }\r\n\r\n getCharacterIndices(\r\n start: string,\r\n end: string,\r\n direction: ScrollingDirection\r\n ): { startIndex: number; endIndex: number } | null {\r\n let startIndex = this.getIndexOfChar(start);\r\n let endIndex = this.getIndexOfChar(end);\r\n\r\n if (startIndex < 0 || endIndex < 0) return null;\r\n\r\n switch (direction) {\r\n case 'DOWN':\r\n if (end === EMPTY_CHAR) {\r\n endIndex = this.characterList.length;\r\n } else if (endIndex < startIndex) {\r\n endIndex += this.numOriginalCharacters;\r\n }\r\n break;\r\n case 'UP':\r\n if (startIndex < endIndex) {\r\n startIndex += this.numOriginalCharacters;\r\n }\r\n break;\r\n case 'ANY':\r\n if (start !== EMPTY_CHAR && end !== EMPTY_CHAR) {\r\n if (endIndex < startIndex) {\r\n const nonWrap = startIndex - endIndex;\r\n const wrap = this.numOriginalCharacters - startIndex + endIndex;\r\n if (wrap < nonWrap) endIndex += this.numOriginalCharacters;\r\n } else if (startIndex < endIndex) {\r\n const nonWrap = endIndex - startIndex;\r\n const wrap = this.numOriginalCharacters - endIndex + startIndex;\r\n if (wrap < nonWrap) startIndex += this.numOriginalCharacters;\r\n }\r\n }\r\n break;\r\n }\r\n return { startIndex, endIndex };\r\n }\r\n\r\n getSupportedCharacters(): Set<string> {\r\n return new Set(this.characterIndicesMap.keys());\r\n }\r\n\r\n getCharacterList(): string[] {\r\n return this.characterList;\r\n }\r\n\r\n private getIndexOfChar(c: string): number {\r\n if (c === EMPTY_CHAR) return 0;\r\n if (this.characterIndicesMap.has(c)) return this.characterIndicesMap.get(c)! + 1;\r\n return -1;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Levenshtein\r\n// ============================================================================\r\nexport const ACTION_SAME = 0;\r\nexport const ACTION_INSERT = 1;\r\nexport const ACTION_DELETE = 2;\r\n\r\nexport function computeColumnActions(source: string[], target: string[], supported: Set<string>): number[] {\r\n let si = 0, ti = 0;\r\n const actions: number[] = [];\r\n\r\n while (true) {\r\n const endS = si === source.length;\r\n const endT = ti === target.length;\r\n if (endS && endT) break;\r\n if (endS) { for (; ti < target.length; ti++) actions.push(ACTION_INSERT); break; }\r\n if (endT) { for (; si < source.length; si++) actions.push(ACTION_DELETE); break; }\r\n\r\n const sSupp = supported.has(source[si]);\r\n const tSupp = supported.has(target[ti]);\r\n\r\n if (sSupp && tSupp) {\r\n let se = si + 1, te = ti + 1;\r\n while (se < source.length && supported.has(source[se])) se++;\r\n while (te < target.length && supported.has(target[te])) te++;\r\n\r\n const sLen = se - si, tLen = te - ti;\r\n if (sLen === tLen) {\r\n for (let i = 0; i < sLen; i++) actions.push(ACTION_SAME);\r\n } else {\r\n const matrix: number[][] = Array(sLen + 1).fill(null).map(() => Array(tLen + 1).fill(0));\r\n for (let i = 0; i <= sLen; i++) matrix[i][0] = i;\r\n for (let j = 0; j <= tLen; j++) matrix[0][j] = j;\r\n for (let r = 1; r <= sLen; r++) {\r\n for (let c = 1; c <= tLen; c++) {\r\n const cost = source[si + r - 1] === target[ti + c - 1] ? 0 : 1;\r\n matrix[r][c] = Math.min(matrix[r - 1][c] + 1, matrix[r][c - 1] + 1, matrix[r - 1][c - 1] + cost);\r\n }\r\n }\r\n const result: number[] = [];\r\n let r = sLen, c = tLen;\r\n while (r > 0 || c > 0) {\r\n if (r === 0) { result.push(ACTION_INSERT); c--; }\r\n else if (c === 0) { result.push(ACTION_DELETE); r--; }\r\n else {\r\n const ins = matrix[r][c - 1], del = matrix[r - 1][c], rep = matrix[r - 1][c - 1];\r\n if (ins < del && ins < rep) { result.push(ACTION_INSERT); c--; }\r\n else if (del < rep) { result.push(ACTION_DELETE); r--; }\r\n else { result.push(ACTION_SAME); r--; c--; }\r\n }\r\n }\r\n for (let i = result.length - 1; i >= 0; i--) actions.push(result[i]);\r\n }\r\n si = se; ti = te;\r\n } else if (sSupp) { actions.push(ACTION_INSERT); ti++; }\r\n else if (tSupp) { actions.push(ACTION_DELETE); si++; }\r\n else { actions.push(ACTION_SAME); si++; ti++; }\r\n }\r\n return actions;\r\n}\r\n\r\n// ============================================================================\r\n// Column State\r\n// ============================================================================\r\nexport interface ColumnState {\r\n currentChar: string;\r\n targetChar: string;\r\n charList: string[] | null;\r\n startIndex: number;\r\n endIndex: number;\r\n sourceWidth: number;\r\n currentWidth: number;\r\n targetWidth: number;\r\n directionAdj: number;\r\n prevDelta: number;\r\n currDelta: number;\r\n}\r\n\r\nexport function createColumn(): ColumnState {\r\n return {\r\n currentChar: EMPTY_CHAR, targetChar: EMPTY_CHAR, charList: null,\r\n startIndex: 0, endIndex: 0, sourceWidth: 0, currentWidth: 0, targetWidth: 0,\r\n directionAdj: 1, prevDelta: 0, currDelta: 0,\r\n };\r\n}\r\n\r\nexport function setTarget(col: ColumnState, target: string, lists: TickerCharacterList[], dir: ScrollingDirection): ColumnState {\r\n const c = { ...col };\r\n c.targetChar = target;\r\n c.sourceWidth = c.currentWidth;\r\n c.targetWidth = target === EMPTY_CHAR ? 0 : 1;\r\n\r\n let found = false;\r\n for (const list of lists) {\r\n const indices = list.getCharacterIndices(c.currentChar, target, dir);\r\n if (indices) {\r\n c.charList = list.getCharacterList();\r\n c.startIndex = indices.startIndex;\r\n c.endIndex = indices.endIndex;\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) {\r\n c.charList = c.currentChar === target ? [c.currentChar] : [c.currentChar, target];\r\n c.startIndex = 0;\r\n c.endIndex = c.currentChar === target ? 0 : 1;\r\n }\r\n\r\n c.directionAdj = c.endIndex >= c.startIndex ? 1 : -1;\r\n c.prevDelta = c.currDelta;\r\n c.currDelta = 0;\r\n return c;\r\n}\r\n\r\nexport function applyProgress(col: ColumnState, progress: number, forceUpdate = false): { col: ColumnState; charIdx: number; delta: number } {\r\n const c = { ...col };\r\n const total = Math.abs(c.endIndex - c.startIndex);\r\n const pos = progress * total;\r\n const offset = pos - Math.floor(pos);\r\n const additional = c.prevDelta * (1 - progress);\r\n const delta = offset * c.directionAdj + additional;\r\n const charIdx = c.startIndex + Math.floor(pos) * c.directionAdj;\r\n\r\n if (progress >= 1) {\r\n c.currentChar = c.targetChar;\r\n c.currDelta = 0;\r\n c.prevDelta = 0;\r\n } else if (forceUpdate && c.charList && charIdx >= 0 && charIdx < c.charList.length) {\r\n c.currentChar = c.charList[charIdx];\r\n c.currDelta = delta;\r\n }\r\n\r\n c.currentWidth = c.sourceWidth + (c.targetWidth - c.sourceWidth) * progress;\r\n return { col: c, charIdx, delta };\r\n}\r\n\r\n// ============================================================================\r\n// Easing Functions\r\n// ============================================================================\r\nexport type EasingName = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'bounce' | 'easeOutCubic' | 'easeOutExpo' | 'backOut';\r\n\r\nexport const easingFunctions: Record<EasingName, (t: number) => number> = {\r\n linear: (t) => t,\r\n easeIn: (t) => t * t,\r\n easeOut: (t) => 1 - (1 - t) * (1 - t),\r\n easeInOut: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,\r\n bounce: (t) => {\r\n const n1 = 7.5625;\r\n const d1 = 2.75;\r\n if (t < 1 / d1) {\r\n return n1 * t * t;\r\n } else if (t < 2 / d1) {\r\n return n1 * (t -= 1.5 / d1) * t + 0.75;\r\n } else if (t < 2.5 / d1) {\r\n return n1 * (t -= 2.25 / d1) * t + 0.9375;\r\n } else {\r\n return n1 * (t -= 2.625 / d1) * t + 0.984375;\r\n }\r\n },\r\n easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),\r\n easeOutExpo: (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),\r\n backOut: (t) => {\r\n const c1 = 1.70158;\r\n const c3 = c1 + 1;\r\n return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);\r\n },\r\n};\r\n"],"names":["r","c"],"mappings":"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"}
|
|
@@ -2,10 +2,35 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
const EMPTY_CHAR = "\0";
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const Presets = {
|
|
6
|
+
NUMBER: "0123456789",
|
|
7
|
+
ALPHABET: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
8
|
+
ALPHANUMERIC: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
9
|
+
// 常用货币字符:数字 + 标点 货币符号不穿滚动不加
|
|
10
|
+
CURRENCY: "0123456789.,"
|
|
8
11
|
};
|
|
12
|
+
const TickerConstants = {
|
|
13
|
+
CHAR_HEIGHT: 1.2,
|
|
14
|
+
// em
|
|
15
|
+
DEFAULT_DURATION: 500,
|
|
16
|
+
// ms
|
|
17
|
+
DEFAULT_MIN_SCALE: 0.1,
|
|
18
|
+
FW_RATIO: 1.25,
|
|
19
|
+
// 中文字符宽度1.25em
|
|
20
|
+
HW_RATIO: 0.75
|
|
21
|
+
// 英文字符宽度0.75em
|
|
22
|
+
};
|
|
23
|
+
const isFW = (c) => {
|
|
24
|
+
if (!c || c.length === 0) return false;
|
|
25
|
+
const code = c.codePointAt(0) || 0;
|
|
26
|
+
return code >= 12288 && code <= 40959 || // CJK 标点 + 汉字
|
|
27
|
+
code >= 44032 && code <= 55215 || // 韩文
|
|
28
|
+
code >= 65280 && code <= 65519 || // 全角 ASCII
|
|
29
|
+
code >= 127744 && code <= 129791 || // Emoji 范围
|
|
30
|
+
code >= 9728 && code <= 10175 || // Misc Symbols (e.g. ☀, ☂, ☁) & Dingbats
|
|
31
|
+
code >= 11008 && code <= 11263;
|
|
32
|
+
};
|
|
33
|
+
const getW = (c) => isFW(c) ? TickerConstants.FW_RATIO : TickerConstants.HW_RATIO;
|
|
9
34
|
class TickerCharacterList {
|
|
10
35
|
constructor(characterList) {
|
|
11
36
|
__publicField(this, "numOriginalCharacters");
|
|
@@ -226,19 +251,28 @@ const easingFunctions = {
|
|
|
226
251
|
} else {
|
|
227
252
|
return n1 * (t -= 2.625 / d1) * t + 0.984375;
|
|
228
253
|
}
|
|
254
|
+
},
|
|
255
|
+
easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
|
|
256
|
+
easeOutExpo: (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),
|
|
257
|
+
backOut: (t) => {
|
|
258
|
+
const c1 = 1.70158;
|
|
259
|
+
const c3 = c1 + 1;
|
|
260
|
+
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);
|
|
229
261
|
}
|
|
230
262
|
};
|
|
231
263
|
export {
|
|
232
264
|
ACTION_INSERT as A,
|
|
233
265
|
EMPTY_CHAR as E,
|
|
234
|
-
|
|
235
|
-
TickerCharacterList as
|
|
236
|
-
applyProgress as
|
|
266
|
+
Presets as P,
|
|
267
|
+
TickerCharacterList as T,
|
|
268
|
+
applyProgress as a,
|
|
269
|
+
TickerConstants as b,
|
|
237
270
|
computeColumnActions as c,
|
|
238
271
|
createColumn as d,
|
|
239
272
|
ACTION_SAME as e,
|
|
240
273
|
easingFunctions as f,
|
|
241
|
-
|
|
274
|
+
getW as g,
|
|
275
|
+
ACTION_DELETE as h,
|
|
242
276
|
setTarget as s
|
|
243
277
|
};
|
|
244
|
-
//# sourceMappingURL=TickerCore-
|
|
278
|
+
//# sourceMappingURL=TickerCore-D5-49bZ3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TickerCore-D5-49bZ3.js","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const Presets = {\r\n NUMBER: '0123456789',\r\n ALPHABET: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n ALPHANUMERIC: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n // 常用货币字符:数字 + 标点 货币符号不穿滚动不加\r\n CURRENCY: '0123456789.,',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\r\n\r\n// ============================================================================\r\n// Defaults & Helpers\r\n// ============================================================================\r\nexport const TickerConstants = {\r\n CHAR_HEIGHT: 1.2, // em\r\n DEFAULT_DURATION: 500, // ms\r\n DEFAULT_MIN_SCALE: 0.1,\r\n FW_RATIO: 1.25, // 中文字符宽度1.25em\r\n HW_RATIO: 0.75, // 英文字符宽度0.75em\r\n};\r\n\r\n// Check if character is full-width (CJK, Emoji, etc.)\r\nexport const isFW = (c: string): boolean => {\r\n if (!c || c.length === 0) return false;\r\n const code = c.codePointAt(0) || 0;\r\n return (\r\n (code >= 0x3000 && code <= 0x9FFF) || // CJK 标点 + 汉字\r\n (code >= 0xAC00 && code <= 0xD7AF) || // 韩文\r\n (code >= 0xFF00 && code <= 0xFFEF) || // 全角 ASCII\r\n (code >= 0x1F300 && code <= 0x1FAFF) || // Emoji 范围\r\n (code >= 0x2600 && code <= 0x27BF) || // Misc Symbols (e.g. ☀, ☂, ☁) & Dingbats\r\n (code >= 0x2B00 && code <= 0x2BFF) // Misc Symbols and Arrows (e.g. ⭐ 0x2B50)\r\n );\r\n};\r\n\r\nexport const getW = (c: string): number => isFW(c) ? TickerConstants.FW_RATIO : TickerConstants.HW_RATIO;\r\n\r\n// ============================================================================\r\n// TickerCharacterList\r\n// ============================================================================\r\nexport class TickerCharacterList {\r\n private numOriginalCharacters: number;\r\n private characterList: string[];\r\n private characterIndicesMap: Map<string, number>;\r\n\r\n constructor(characterList: string) {\r\n const charsArray = [...characterList];\r\n const length = charsArray.length;\r\n this.numOriginalCharacters = length;\r\n this.characterIndicesMap = new Map();\r\n\r\n for (let i = 0; i < length; i++) {\r\n this.characterIndicesMap.set(charsArray[i], i);\r\n }\r\n\r\n this.characterList = new Array(length * 2 + 1);\r\n this.characterList[0] = EMPTY_CHAR;\r\n for (let i = 0; i < length; i++) {\r\n this.characterList[1 + i] = charsArray[i];\r\n this.characterList[1 + length + i] = charsArray[i];\r\n }\r\n }\r\n\r\n getCharacterIndices(\r\n start: string,\r\n end: string,\r\n direction: ScrollingDirection\r\n ): { startIndex: number; endIndex: number } | null {\r\n let startIndex = this.getIndexOfChar(start);\r\n let endIndex = this.getIndexOfChar(end);\r\n\r\n if (startIndex < 0 || endIndex < 0) return null;\r\n\r\n switch (direction) {\r\n case 'DOWN':\r\n if (end === EMPTY_CHAR) {\r\n endIndex = this.characterList.length;\r\n } else if (endIndex < startIndex) {\r\n endIndex += this.numOriginalCharacters;\r\n }\r\n break;\r\n case 'UP':\r\n if (startIndex < endIndex) {\r\n startIndex += this.numOriginalCharacters;\r\n }\r\n break;\r\n case 'ANY':\r\n if (start !== EMPTY_CHAR && end !== EMPTY_CHAR) {\r\n if (endIndex < startIndex) {\r\n const nonWrap = startIndex - endIndex;\r\n const wrap = this.numOriginalCharacters - startIndex + endIndex;\r\n if (wrap < nonWrap) endIndex += this.numOriginalCharacters;\r\n } else if (startIndex < endIndex) {\r\n const nonWrap = endIndex - startIndex;\r\n const wrap = this.numOriginalCharacters - endIndex + startIndex;\r\n if (wrap < nonWrap) startIndex += this.numOriginalCharacters;\r\n }\r\n }\r\n break;\r\n }\r\n return { startIndex, endIndex };\r\n }\r\n\r\n getSupportedCharacters(): Set<string> {\r\n return new Set(this.characterIndicesMap.keys());\r\n }\r\n\r\n getCharacterList(): string[] {\r\n return this.characterList;\r\n }\r\n\r\n private getIndexOfChar(c: string): number {\r\n if (c === EMPTY_CHAR) return 0;\r\n if (this.characterIndicesMap.has(c)) return this.characterIndicesMap.get(c)! + 1;\r\n return -1;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Levenshtein\r\n// ============================================================================\r\nexport const ACTION_SAME = 0;\r\nexport const ACTION_INSERT = 1;\r\nexport const ACTION_DELETE = 2;\r\n\r\nexport function computeColumnActions(source: string[], target: string[], supported: Set<string>): number[] {\r\n let si = 0, ti = 0;\r\n const actions: number[] = [];\r\n\r\n while (true) {\r\n const endS = si === source.length;\r\n const endT = ti === target.length;\r\n if (endS && endT) break;\r\n if (endS) { for (; ti < target.length; ti++) actions.push(ACTION_INSERT); break; }\r\n if (endT) { for (; si < source.length; si++) actions.push(ACTION_DELETE); break; }\r\n\r\n const sSupp = supported.has(source[si]);\r\n const tSupp = supported.has(target[ti]);\r\n\r\n if (sSupp && tSupp) {\r\n let se = si + 1, te = ti + 1;\r\n while (se < source.length && supported.has(source[se])) se++;\r\n while (te < target.length && supported.has(target[te])) te++;\r\n\r\n const sLen = se - si, tLen = te - ti;\r\n if (sLen === tLen) {\r\n for (let i = 0; i < sLen; i++) actions.push(ACTION_SAME);\r\n } else {\r\n const matrix: number[][] = Array(sLen + 1).fill(null).map(() => Array(tLen + 1).fill(0));\r\n for (let i = 0; i <= sLen; i++) matrix[i][0] = i;\r\n for (let j = 0; j <= tLen; j++) matrix[0][j] = j;\r\n for (let r = 1; r <= sLen; r++) {\r\n for (let c = 1; c <= tLen; c++) {\r\n const cost = source[si + r - 1] === target[ti + c - 1] ? 0 : 1;\r\n matrix[r][c] = Math.min(matrix[r - 1][c] + 1, matrix[r][c - 1] + 1, matrix[r - 1][c - 1] + cost);\r\n }\r\n }\r\n const result: number[] = [];\r\n let r = sLen, c = tLen;\r\n while (r > 0 || c > 0) {\r\n if (r === 0) { result.push(ACTION_INSERT); c--; }\r\n else if (c === 0) { result.push(ACTION_DELETE); r--; }\r\n else {\r\n const ins = matrix[r][c - 1], del = matrix[r - 1][c], rep = matrix[r - 1][c - 1];\r\n if (ins < del && ins < rep) { result.push(ACTION_INSERT); c--; }\r\n else if (del < rep) { result.push(ACTION_DELETE); r--; }\r\n else { result.push(ACTION_SAME); r--; c--; }\r\n }\r\n }\r\n for (let i = result.length - 1; i >= 0; i--) actions.push(result[i]);\r\n }\r\n si = se; ti = te;\r\n } else if (sSupp) { actions.push(ACTION_INSERT); ti++; }\r\n else if (tSupp) { actions.push(ACTION_DELETE); si++; }\r\n else { actions.push(ACTION_SAME); si++; ti++; }\r\n }\r\n return actions;\r\n}\r\n\r\n// ============================================================================\r\n// Column State\r\n// ============================================================================\r\nexport interface ColumnState {\r\n currentChar: string;\r\n targetChar: string;\r\n charList: string[] | null;\r\n startIndex: number;\r\n endIndex: number;\r\n sourceWidth: number;\r\n currentWidth: number;\r\n targetWidth: number;\r\n directionAdj: number;\r\n prevDelta: number;\r\n currDelta: number;\r\n}\r\n\r\nexport function createColumn(): ColumnState {\r\n return {\r\n currentChar: EMPTY_CHAR, targetChar: EMPTY_CHAR, charList: null,\r\n startIndex: 0, endIndex: 0, sourceWidth: 0, currentWidth: 0, targetWidth: 0,\r\n directionAdj: 1, prevDelta: 0, currDelta: 0,\r\n };\r\n}\r\n\r\nexport function setTarget(col: ColumnState, target: string, lists: TickerCharacterList[], dir: ScrollingDirection): ColumnState {\r\n const c = { ...col };\r\n c.targetChar = target;\r\n c.sourceWidth = c.currentWidth;\r\n c.targetWidth = target === EMPTY_CHAR ? 0 : 1;\r\n\r\n let found = false;\r\n for (const list of lists) {\r\n const indices = list.getCharacterIndices(c.currentChar, target, dir);\r\n if (indices) {\r\n c.charList = list.getCharacterList();\r\n c.startIndex = indices.startIndex;\r\n c.endIndex = indices.endIndex;\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) {\r\n c.charList = c.currentChar === target ? [c.currentChar] : [c.currentChar, target];\r\n c.startIndex = 0;\r\n c.endIndex = c.currentChar === target ? 0 : 1;\r\n }\r\n\r\n c.directionAdj = c.endIndex >= c.startIndex ? 1 : -1;\r\n c.prevDelta = c.currDelta;\r\n c.currDelta = 0;\r\n return c;\r\n}\r\n\r\nexport function applyProgress(col: ColumnState, progress: number, forceUpdate = false): { col: ColumnState; charIdx: number; delta: number } {\r\n const c = { ...col };\r\n const total = Math.abs(c.endIndex - c.startIndex);\r\n const pos = progress * total;\r\n const offset = pos - Math.floor(pos);\r\n const additional = c.prevDelta * (1 - progress);\r\n const delta = offset * c.directionAdj + additional;\r\n const charIdx = c.startIndex + Math.floor(pos) * c.directionAdj;\r\n\r\n if (progress >= 1) {\r\n c.currentChar = c.targetChar;\r\n c.currDelta = 0;\r\n c.prevDelta = 0;\r\n } else if (forceUpdate && c.charList && charIdx >= 0 && charIdx < c.charList.length) {\r\n c.currentChar = c.charList[charIdx];\r\n c.currDelta = delta;\r\n }\r\n\r\n c.currentWidth = c.sourceWidth + (c.targetWidth - c.sourceWidth) * progress;\r\n return { col: c, charIdx, delta };\r\n}\r\n\r\n// ============================================================================\r\n// Easing Functions\r\n// ============================================================================\r\nexport type EasingName = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | 'bounce' | 'easeOutCubic' | 'easeOutExpo' | 'backOut';\r\n\r\nexport const easingFunctions: Record<EasingName, (t: number) => number> = {\r\n linear: (t) => t,\r\n easeIn: (t) => t * t,\r\n easeOut: (t) => 1 - (1 - t) * (1 - t),\r\n easeInOut: (t) => t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2,\r\n bounce: (t) => {\r\n const n1 = 7.5625;\r\n const d1 = 2.75;\r\n if (t < 1 / d1) {\r\n return n1 * t * t;\r\n } else if (t < 2 / d1) {\r\n return n1 * (t -= 1.5 / d1) * t + 0.75;\r\n } else if (t < 2.5 / d1) {\r\n return n1 * (t -= 2.25 / d1) * t + 0.9375;\r\n } else {\r\n return n1 * (t -= 2.625 / d1) * t + 0.984375;\r\n }\r\n },\r\n easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),\r\n easeOutExpo: (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t),\r\n backOut: (t) => {\r\n const c1 = 1.70158;\r\n const c3 = c1 + 1;\r\n return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2);\r\n },\r\n};\r\n"],"names":["r","c"],"mappings":"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"}
|