@tombcato/smart-ticker 1.0.3 → 1.0.4
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 +39 -11
- package/dist/{TickerCore-DSrG8V7Z.cjs → TickerCore-BAsdPrD1.cjs} +2 -2
- package/dist/TickerCore-BAsdPrD1.cjs.map +1 -0
- package/dist/{TickerCore-DDdYkrsq.js → TickerCore-C7Ejc9kB.js} +2 -2
- package/dist/TickerCore-C7Ejc9kB.js.map +1 -0
- package/dist/index.cjs +10 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/style.css +4 -4
- package/dist/vue-demo.html +57 -13
- package/dist/vue.cjs +12 -5
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +13 -6
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
- package/dist/TickerCore-DDdYkrsq.js.map +0 -1
- package/dist/TickerCore-DSrG8V7Z.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
<h1 align="center">Smart Ticker</h1>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
9
|
-
|
|
9
|
+
高性能智能文本差异滚动组件,支持中英、数字、字母、符号、Emoji等多种字符集,基于 Levenshtein diff 算法,适用于React/Vue,<a href="https://tombcato.github.io/smart-ticker/">官网演示></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
<p align="center">
|
|
13
|
+
<strong>简体中文</strong> | <a href="./README_EN.md">English</a>
|
|
10
14
|
</p>
|
|
11
15
|
|
|
12
16
|
<p align="center">
|
|
13
17
|
<img src="./smartticker.gif" alt="Demo" width="600" />
|
|
14
18
|
</p>
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="./smartticker2.gif" alt="Demo" width="600" />
|
|
21
|
+
</p>
|
|
15
22
|
|
|
16
23
|
<p align="center">
|
|
17
24
|
<img src="https://img.shields.io/badge/React-18+-61DAFB?logo=react" alt="React" />
|
|
@@ -22,14 +29,11 @@
|
|
|
22
29
|
|
|
23
30
|
|
|
24
31
|
## ✨ 特性
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- **多字符集支持** - 支持数字、字母、符号等多种字符集,可混合使用
|
|
31
|
-
- **双框架支持** - 提供 React 组件和 Vue 组件
|
|
32
|
-
- **高性能** - 使用 `requestAnimationFrame` 和 `React.memo` 优化渲染
|
|
32
|
+
| | |
|
|
33
|
+
| :--- | :--- |
|
|
34
|
+
| **🌏 多字符集支持**<br>支持中英、数字、Emoji等混合滚动,基于 Unicode 宽度自动调整间距 | **🧠 智能差异动画**<br>Levenshtein 算法计算最小变更路径,相同的字符保持静止 |
|
|
35
|
+
| **⚡ 平滑中断**<br>动画过程中值突变时,从当前动态位置无缝流向新目标 | **📈 丰富动效**<br>内置 `linear`, `bounce`, `easeInOut` 等缓动,支持 `charWidth` 微调 |
|
|
36
|
+
| **🦄 双框架支持**<br>提供 React (Hooks) 和 Vue 3 (Composition) 组件,API 统一 | **🚀 极致性能**<br>基于 `RAF` 驱动,无多余 DOM 操作,适合高频数据流场景 |
|
|
33
37
|
|
|
34
38
|
## 📦 安装
|
|
35
39
|
|
|
@@ -151,8 +155,32 @@ const price = ref('73.18');
|
|
|
151
155
|
import { TickerUtils } from './components/Ticker';
|
|
152
156
|
|
|
153
157
|
TickerUtils.provideNumberList() // '0123456789'
|
|
154
|
-
TickerUtils.provideAlphabeticalList() // '
|
|
155
|
-
|
|
158
|
+
TickerUtils.provideAlphabeticalList() // 'abc...zABC...Z'
|
|
159
|
+
|
|
160
|
+
### 🧩 字符集配置详解
|
|
161
|
+
|
|
162
|
+
`characterLists` 是控制 Ticker 动画逻辑的核心配置。它接受一个字符串数组,数组的每一项代表一组**“可以互相滚动”**的字符。
|
|
163
|
+
|
|
164
|
+
**基本规则:**
|
|
165
|
+
1. **同组滚动**:如果旧字符和新字符在同一个字符串中(例如 `0` 变 `9` 在 `'0123456789'` 中),它们会产生滚动动画。
|
|
166
|
+
2. **跨组替换**:如果它们不在同一组(例如 `a` 变 `1`),或者任何一个字符不在配置列表中(例如汉字),它们会原地切换(Switch),不会产生滚动。
|
|
167
|
+
|
|
168
|
+
**配置技巧:**
|
|
169
|
+
|
|
170
|
+
* **默认全字母**:`TickerUtils.provideAlphabeticalList()` 默认包含 `a-z` 和 `A-Z`。如果你希望大小写之间可以滚动(如 `a` -> `A`),使用它即可。
|
|
171
|
+
* **由于物理隔离**:如果你不希望小写字母滚动变成大写字母(希望它们直接切换),请将它们配置为两个独立的字符串,例如 `['abc...', 'ABC...']`。
|
|
172
|
+
|
|
173
|
+
**示例:**
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
// 场景:数字、字母(大小写隔离)、符号
|
|
177
|
+
characterLists={[
|
|
178
|
+
'abcdefghijklmnopqrstuvwxyz', // 小写组
|
|
179
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ', // 大写组
|
|
180
|
+
'0123456789', // 数字组
|
|
181
|
+
'.,!@#$%^&*' // 符号组
|
|
182
|
+
]}
|
|
183
|
+
```
|
|
156
184
|
```
|
|
157
185
|
|
|
158
186
|
## 💻 运行演示
|
|
@@ -12,7 +12,7 @@ class TickerCharacterList {
|
|
|
12
12
|
__publicField(this, "numOriginalCharacters");
|
|
13
13
|
__publicField(this, "characterList");
|
|
14
14
|
__publicField(this, "characterIndicesMap");
|
|
15
|
-
const charsArray = characterList
|
|
15
|
+
const charsArray = [...characterList];
|
|
16
16
|
const length = charsArray.length;
|
|
17
17
|
this.numOriginalCharacters = length;
|
|
18
18
|
this.characterIndicesMap = /* @__PURE__ */ new Map();
|
|
@@ -240,4 +240,4 @@ exports.computeColumnActions = computeColumnActions;
|
|
|
240
240
|
exports.createColumn = createColumn;
|
|
241
241
|
exports.easingFunctions = easingFunctions;
|
|
242
242
|
exports.setTarget = setTarget;
|
|
243
|
-
//# sourceMappingURL=TickerCore-
|
|
243
|
+
//# sourceMappingURL=TickerCore-BAsdPrD1.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TickerCore-BAsdPrD1.cjs","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const TickerUtils = {\r\n provideNumberList: () => '0123456789',\r\n provideAlphabeticalList: () => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\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 const easingFunctions: Record<string, (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};\r\n"],"names":["r","c"],"mappings":"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"}
|
|
@@ -11,7 +11,7 @@ class TickerCharacterList {
|
|
|
11
11
|
__publicField(this, "numOriginalCharacters");
|
|
12
12
|
__publicField(this, "characterList");
|
|
13
13
|
__publicField(this, "characterIndicesMap");
|
|
14
|
-
const charsArray = characterList
|
|
14
|
+
const charsArray = [...characterList];
|
|
15
15
|
const length = charsArray.length;
|
|
16
16
|
this.numOriginalCharacters = length;
|
|
17
17
|
this.characterIndicesMap = /* @__PURE__ */ new Map();
|
|
@@ -241,4 +241,4 @@ export {
|
|
|
241
241
|
ACTION_DELETE as g,
|
|
242
242
|
setTarget as s
|
|
243
243
|
};
|
|
244
|
-
//# sourceMappingURL=TickerCore-
|
|
244
|
+
//# sourceMappingURL=TickerCore-C7Ejc9kB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TickerCore-C7Ejc9kB.js","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const TickerUtils = {\r\n provideNumberList: () => '0123456789',\r\n provideAlphabeticalList: () => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\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 const easingFunctions: Record<string, (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};\r\n"],"names":["r","c"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const React = require("react");
|
|
5
|
-
const TickerCore = require("./TickerCore-
|
|
5
|
+
const TickerCore = require("./TickerCore-BAsdPrD1.cjs");
|
|
6
6
|
const Ticker = ({
|
|
7
7
|
value,
|
|
8
8
|
characterLists: charListStrings = [TickerCore.TickerUtils.provideNumberList()],
|
|
@@ -50,7 +50,7 @@ const Ticker = ({
|
|
|
50
50
|
currentCols = currentCols.map((c) => TickerCore.applyProgress(c, progressRef.current, true).col);
|
|
51
51
|
colsRef.current = currentCols;
|
|
52
52
|
}
|
|
53
|
-
const targetChars = value
|
|
53
|
+
const targetChars = [...value];
|
|
54
54
|
const sourceChars = currentCols.map((c) => c.currentChar);
|
|
55
55
|
const actions = TickerCore.computeColumnActions(sourceChars, targetChars, supportedRef.current);
|
|
56
56
|
let ci = 0, ti = 0;
|
|
@@ -132,7 +132,14 @@ const Ticker = ({
|
|
|
132
132
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ticker-char", style: { transform: `translateY(${deltaEm + charHeight}em)` }, children: list[prevIdx] === TickerCore.EMPTY_CHAR ? " " : list[prevIdx] }, `p-${prevIdx}`)
|
|
133
133
|
);
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
const isFW = (c) => c && c.length > 0 && c.charCodeAt(0) > 255;
|
|
136
|
+
const getW = (c) => isFW(c) ? 1.25 : 0.8;
|
|
137
|
+
const startChar = list[col.startIndex];
|
|
138
|
+
const endChar = list[col.endIndex];
|
|
139
|
+
const w1 = getW(startChar);
|
|
140
|
+
const w2 = getW(endChar);
|
|
141
|
+
const baseW = w1 + (w2 - w1) * progress;
|
|
142
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ticker-column", style: { width: `${width * baseW * charWidth}em` }, children: chars }, i);
|
|
136
143
|
});
|
|
137
144
|
}, [columns, progress, charWidth]);
|
|
138
145
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `ticker ${className}`.trim(), children: rendered });
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/components/Ticker.tsx"],"sourcesContent":["/**\r\n * Ticker - 直接翻译自 Robinhood Android Ticker\r\n * https://github.com/robinhood/ticker\r\n */\r\nimport React, { useEffect, useRef, useState, useMemo } from 'react';\r\nimport './Ticker.css';\r\nimport {\r\n TickerUtils,\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../core/TickerCore';\r\n\r\n// 导出 Utils 供外部使用\r\nexport { TickerUtils };\r\n\r\n// ============================================================================\r\n// Ticker Component\r\n// ============================================================================\r\nexport interface TickerProps {\r\n value: string;\r\n characterLists?: string[];\r\n duration?: number;\r\n direction?: ScrollingDirection;\r\n easing?: string;\r\n className?: string;\r\n charWidth?: number;\r\n}\r\n\r\nexport const Ticker: React.FC<TickerProps> = ({\r\n value,\r\n characterLists: charListStrings = [TickerUtils.provideNumberList()],\r\n duration = 500,\r\n direction = 'ANY',\r\n easing = 'easeInOut', // Robinhood 默认: AccelerateDecelerateInterpolator\r\n className = '',\r\n charWidth = 1,\r\n}) => {\r\n const [columns, setColumns] = useState<ColumnState[]>([]);\r\n const [progress, setProgress] = useState(1);\r\n const animRef = useRef<number>();\r\n const colsRef = useRef<ColumnState[]>([]);\r\n const progressRef = useRef(1);\r\n const isFirstRef = useRef(true);\r\n const prevValueRef = useRef('');\r\n\r\n const lists = useMemo(() => charListStrings.map(s => new TickerCharacterList(s)), [charListStrings]);\r\n const supported = useMemo(() => {\r\n const set = new Set<string>();\r\n lists.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n }, [lists]);\r\n\r\n // 用 ref 存储依赖项,避免 useEffect 因为这些依赖重复触发\r\n const listsRef = useRef(lists);\r\n const supportedRef = useRef(supported);\r\n const directionRef = useRef(direction);\r\n const durationRef = useRef(duration);\r\n const easingRef = useRef(easing);\r\n listsRef.current = lists;\r\n supportedRef.current = supported;\r\n directionRef.current = direction;\r\n durationRef.current = duration;\r\n easingRef.current = easing;\r\n\r\n // 主要逻辑:value 变化时处理\r\n useEffect(() => {\r\n // 防止相同 value 重复触发(React StrictMode)\r\n if (value === prevValueRef.current) {\r\n return;\r\n }\r\n prevValueRef.current = value;\r\n\r\n // 取消正在进行的动画\r\n if (animRef.current) {\r\n cancelAnimationFrame(animRef.current);\r\n animRef.current = undefined;\r\n }\r\n\r\n // 如果动画进行中,先用当前进度更新列状态\r\n let currentCols = colsRef.current;\r\n if (progressRef.current < 1 && progressRef.current > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progressRef.current, true).col);\r\n colsRef.current = currentCols;\r\n }\r\n\r\n const targetChars = value.split('');\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supportedRef.current);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], listsRef.current, directionRef.current));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], listsRef.current, directionRef.current));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, listsRef.current, directionRef.current));\r\n }\r\n }\r\n\r\n colsRef.current = result;\r\n setColumns(result);\r\n\r\n // 动画\r\n if (!isFirstRef.current && result.length > 0) {\r\n progressRef.current = 0;\r\n setProgress(0);\r\n const start = performance.now();\r\n const dur = durationRef.current;\r\n const easeFn = easingFunctions[easingRef.current] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP); // 应用 easing 函数\r\n progressRef.current = p;\r\n\r\n // 节流:每 16ms 最多更新一次视图 (约 60fps)\r\n if (now - lastUpdate >= 16 || linearP >= 1) {\r\n lastUpdate = now;\r\n setProgress(p);\r\n }\r\n\r\n if (linearP < 1) {\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 动画结束\r\n const final = colsRef.current\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n animRef.current = undefined;\r\n }\r\n };\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 首次渲染,直接显示\r\n isFirstRef.current = false;\r\n progressRef.current = 1;\r\n setProgress(1);\r\n const final = result.map(c => applyProgress(c, 1).col).filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n }\r\n\r\n return () => {\r\n if (animRef.current) cancelAnimationFrame(animRef.current);\r\n };\r\n }, [value]); // 只依赖 value\r\n\r\n // 渲染\r\n const charHeight = 1.2; // 与 CSS line-height 匹配\r\n\r\n const rendered = useMemo(() => {\r\n return columns.map((col, i) => {\r\n const { charIdx, delta } = applyProgress(col, progress);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress;\r\n if (width <= 0) return null;\r\n\r\n const chars: React.ReactNode[] = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // 当前字符\r\n if (charIdx >= 0 && charIdx < list.length) {\r\n chars.push(\r\n <div key={`c-${charIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm}em)` }}>\r\n {list[charIdx] === EMPTY_CHAR ? '\\u00A0' : list[charIdx]}\r\n </div>\r\n );\r\n }\r\n // 下一个字符\r\n const nextIdx = charIdx + 1;\r\n if (nextIdx >= 0 && nextIdx < list.length) {\r\n chars.push(\r\n <div key={`n-${nextIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm - charHeight}em)` }}>\r\n {list[nextIdx] === EMPTY_CHAR ? '\\u00A0' : list[nextIdx]}\r\n </div>\r\n );\r\n }\r\n // 上一个字符(处理中断)\r\n const prevIdx = charIdx - 1;\r\n if (prevIdx >= 0 && prevIdx < list.length) {\r\n chars.push(\r\n <div key={`p-${prevIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm + charHeight}em)` }}>\r\n {list[prevIdx] === EMPTY_CHAR ? '\\u00A0' : list[prevIdx]}\r\n </div>\r\n );\r\n }\r\n\r\n // 基准宽度 0.8em * 倍率\r\n return (\r\n <div key={i} className=\"ticker-column\" style={{ width: `${width * 0.8 * charWidth}em` }}>\r\n {chars}\r\n </div>\r\n );\r\n });\r\n }, [columns, progress, charWidth]);\r\n\r\n return <div className={`ticker ${className}`.trim()}>{rendered}</div>;\r\n};\r\n\r\nexport default React.memo(Ticker);\r\n"],"names":["TickerUtils","useState","useRef","useMemo","TickerCharacterList","useEffect","applyProgress","computeColumnActions","ACTION_INSERT","setTarget","createColumn","ACTION_SAME","EMPTY_CHAR","easingFunctions","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/components/Ticker.tsx"],"sourcesContent":["/**\r\n * Ticker - 直接翻译自 Robinhood Android Ticker\r\n * https://github.com/robinhood/ticker\r\n */\r\nimport React, { useEffect, useRef, useState, useMemo } from 'react';\r\nimport './Ticker.css';\r\nimport {\r\n TickerUtils,\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../core/TickerCore';\r\n\r\n// 导出 Utils 供外部使用\r\nexport { TickerUtils };\r\n\r\n// ============================================================================\r\n// Ticker Component\r\n// ============================================================================\r\nexport interface TickerProps {\r\n value: string;\r\n characterLists?: string[];\r\n duration?: number;\r\n direction?: ScrollingDirection;\r\n easing?: string;\r\n className?: string;\r\n charWidth?: number;\r\n}\r\n\r\nexport const Ticker: React.FC<TickerProps> = ({\r\n value,\r\n characterLists: charListStrings = [TickerUtils.provideNumberList()],\r\n duration = 500,\r\n direction = 'ANY',\r\n easing = 'easeInOut', // Robinhood 默认: AccelerateDecelerateInterpolator\r\n className = '',\r\n charWidth = 1,\r\n}) => {\r\n const [columns, setColumns] = useState<ColumnState[]>([]);\r\n const [progress, setProgress] = useState(1);\r\n const animRef = useRef<number>();\r\n const colsRef = useRef<ColumnState[]>([]);\r\n const progressRef = useRef(1);\r\n const isFirstRef = useRef(true);\r\n const prevValueRef = useRef('');\r\n\r\n const lists = useMemo(() => charListStrings.map(s => new TickerCharacterList(s)), [charListStrings]);\r\n const supported = useMemo(() => {\r\n const set = new Set<string>();\r\n lists.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n }, [lists]);\r\n\r\n // 用 ref 存储依赖项,避免 useEffect 因为这些依赖重复触发\r\n const listsRef = useRef(lists);\r\n const supportedRef = useRef(supported);\r\n const directionRef = useRef(direction);\r\n const durationRef = useRef(duration);\r\n const easingRef = useRef(easing);\r\n listsRef.current = lists;\r\n supportedRef.current = supported;\r\n directionRef.current = direction;\r\n durationRef.current = duration;\r\n easingRef.current = easing;\r\n\r\n // 主要逻辑:value 变化时处理\r\n useEffect(() => {\r\n // 防止相同 value 重复触发(React StrictMode)\r\n if (value === prevValueRef.current) {\r\n return;\r\n }\r\n prevValueRef.current = value;\r\n\r\n // 取消正在进行的动画\r\n if (animRef.current) {\r\n cancelAnimationFrame(animRef.current);\r\n animRef.current = undefined;\r\n }\r\n\r\n // 如果动画进行中,先用当前进度更新列状态\r\n let currentCols = colsRef.current;\r\n if (progressRef.current < 1 && progressRef.current > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progressRef.current, true).col);\r\n colsRef.current = currentCols;\r\n }\r\n\r\n const targetChars = [...value];\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supportedRef.current);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], listsRef.current, directionRef.current));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], listsRef.current, directionRef.current));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, listsRef.current, directionRef.current));\r\n }\r\n }\r\n\r\n colsRef.current = result;\r\n setColumns(result);\r\n\r\n // 动画\r\n if (!isFirstRef.current && result.length > 0) {\r\n progressRef.current = 0;\r\n setProgress(0);\r\n const start = performance.now();\r\n const dur = durationRef.current;\r\n const easeFn = easingFunctions[easingRef.current] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP); // 应用 easing 函数\r\n progressRef.current = p;\r\n\r\n // 节流:每 16ms 最多更新一次视图 (约 60fps)\r\n if (now - lastUpdate >= 16 || linearP >= 1) {\r\n lastUpdate = now;\r\n setProgress(p);\r\n }\r\n\r\n if (linearP < 1) {\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 动画结束\r\n const final = colsRef.current\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n animRef.current = undefined;\r\n }\r\n };\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 首次渲染,直接显示\r\n isFirstRef.current = false;\r\n progressRef.current = 1;\r\n setProgress(1);\r\n const final = result.map(c => applyProgress(c, 1).col).filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n }\r\n\r\n return () => {\r\n if (animRef.current) cancelAnimationFrame(animRef.current);\r\n };\r\n }, [value]); // 只依赖 value\r\n\r\n // 渲染\r\n const charHeight = 1.2; // 与 CSS line-height 匹配\r\n\r\n const rendered = useMemo(() => {\r\n return columns.map((col, i) => {\r\n const { charIdx, delta } = applyProgress(col, progress);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress;\r\n if (width <= 0) return null;\r\n\r\n const chars: React.ReactNode[] = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // 当前字符\r\n if (charIdx >= 0 && charIdx < list.length) {\r\n chars.push(\r\n <div key={`c-${charIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm}em)` }}>\r\n {list[charIdx] === EMPTY_CHAR ? '\\u00A0' : list[charIdx]}\r\n </div>\r\n );\r\n }\r\n // 下一个字符\r\n const nextIdx = charIdx + 1;\r\n if (nextIdx >= 0 && nextIdx < list.length) {\r\n chars.push(\r\n <div key={`n-${nextIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm - charHeight}em)` }}>\r\n {list[nextIdx] === EMPTY_CHAR ? '\\u00A0' : list[nextIdx]}\r\n </div>\r\n );\r\n }\r\n // 上一个字符(处理中断)\r\n const prevIdx = charIdx - 1;\r\n if (prevIdx >= 0 && prevIdx < list.length) {\r\n chars.push(\r\n <div key={`p-${prevIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm + charHeight}em)` }}>\r\n {list[prevIdx] === EMPTY_CHAR ? '\\u00A0' : list[prevIdx]}\r\n </div>\r\n );\r\n }\r\n\r\n // 动态计算字符基础宽度:全角字符 1.1em,半角 0.8em\r\n // 使用线性插值 (Lerp) 避免突变跳动\r\n const isFW = (c: string) => c && c.length > 0 && c.charCodeAt(0) > 255;\r\n const getW = (c: string) => isFW(c) ? 1.25 : 0.8;\r\n\r\n // 安全获字符\r\n const startChar = list[col.startIndex];\r\n const endChar = list[col.endIndex];\r\n\r\n const w1 = getW(startChar);\r\n const w2 = getW(endChar);\r\n const baseW = w1 + (w2 - w1) * progress;\r\n\r\n return (\r\n <div key={i} className=\"ticker-column\" style={{ width: `${width * baseW * charWidth}em` }}>\r\n {chars}\r\n </div>\r\n );\r\n });\r\n }, [columns, progress, charWidth]);\r\n\r\n return <div className={`ticker ${className}`.trim()}>{rendered}</div>;\r\n};\r\n\r\nexport default React.memo(Ticker);\r\n"],"names":["TickerUtils","useState","useRef","useMemo","TickerCharacterList","useEffect","applyProgress","computeColumnActions","ACTION_INSERT","setTarget","createColumn","ACTION_SAME","EMPTY_CHAR","easingFunctions","jsx"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import React, { useState, useRef, useMemo, useEffect } from "react";
|
|
3
|
-
import { T as TickerUtils, a as TickerCharacterList, b as applyProgress, c as computeColumnActions, A as ACTION_INSERT, s as setTarget, d as createColumn, e as ACTION_SAME, E as EMPTY_CHAR, f as easingFunctions } from "./TickerCore-
|
|
4
|
-
import { g } from "./TickerCore-
|
|
3
|
+
import { T as TickerUtils, a as TickerCharacterList, b as applyProgress, c as computeColumnActions, A as ACTION_INSERT, s as setTarget, d as createColumn, e as ACTION_SAME, E as EMPTY_CHAR, f as easingFunctions } from "./TickerCore-C7Ejc9kB.js";
|
|
4
|
+
import { g } from "./TickerCore-C7Ejc9kB.js";
|
|
5
5
|
const Ticker = ({
|
|
6
6
|
value,
|
|
7
7
|
characterLists: charListStrings = [TickerUtils.provideNumberList()],
|
|
@@ -49,7 +49,7 @@ const Ticker = ({
|
|
|
49
49
|
currentCols = currentCols.map((c) => applyProgress(c, progressRef.current, true).col);
|
|
50
50
|
colsRef.current = currentCols;
|
|
51
51
|
}
|
|
52
|
-
const targetChars = value
|
|
52
|
+
const targetChars = [...value];
|
|
53
53
|
const sourceChars = currentCols.map((c) => c.currentChar);
|
|
54
54
|
const actions = computeColumnActions(sourceChars, targetChars, supportedRef.current);
|
|
55
55
|
let ci = 0, ti = 0;
|
|
@@ -131,7 +131,14 @@ const Ticker = ({
|
|
|
131
131
|
/* @__PURE__ */ jsx("div", { className: "ticker-char", style: { transform: `translateY(${deltaEm + charHeight}em)` }, children: list[prevIdx] === EMPTY_CHAR ? " " : list[prevIdx] }, `p-${prevIdx}`)
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
const isFW = (c) => c && c.length > 0 && c.charCodeAt(0) > 255;
|
|
135
|
+
const getW = (c) => isFW(c) ? 1.25 : 0.8;
|
|
136
|
+
const startChar = list[col.startIndex];
|
|
137
|
+
const endChar = list[col.endIndex];
|
|
138
|
+
const w1 = getW(startChar);
|
|
139
|
+
const w2 = getW(endChar);
|
|
140
|
+
const baseW = w1 + (w2 - w1) * progress;
|
|
141
|
+
return /* @__PURE__ */ jsx("div", { className: "ticker-column", style: { width: `${width * baseW * charWidth}em` }, children: chars }, i);
|
|
135
142
|
});
|
|
136
143
|
}, [columns, progress, charWidth]);
|
|
137
144
|
return /* @__PURE__ */ jsx("div", { className: `ticker ${className}`.trim(), children: rendered });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/components/Ticker.tsx"],"sourcesContent":["/**\r\n * Ticker - 直接翻译自 Robinhood Android Ticker\r\n * https://github.com/robinhood/ticker\r\n */\r\nimport React, { useEffect, useRef, useState, useMemo } from 'react';\r\nimport './Ticker.css';\r\nimport {\r\n TickerUtils,\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../core/TickerCore';\r\n\r\n// 导出 Utils 供外部使用\r\nexport { TickerUtils };\r\n\r\n// ============================================================================\r\n// Ticker Component\r\n// ============================================================================\r\nexport interface TickerProps {\r\n value: string;\r\n characterLists?: string[];\r\n duration?: number;\r\n direction?: ScrollingDirection;\r\n easing?: string;\r\n className?: string;\r\n charWidth?: number;\r\n}\r\n\r\nexport const Ticker: React.FC<TickerProps> = ({\r\n value,\r\n characterLists: charListStrings = [TickerUtils.provideNumberList()],\r\n duration = 500,\r\n direction = 'ANY',\r\n easing = 'easeInOut', // Robinhood 默认: AccelerateDecelerateInterpolator\r\n className = '',\r\n charWidth = 1,\r\n}) => {\r\n const [columns, setColumns] = useState<ColumnState[]>([]);\r\n const [progress, setProgress] = useState(1);\r\n const animRef = useRef<number>();\r\n const colsRef = useRef<ColumnState[]>([]);\r\n const progressRef = useRef(1);\r\n const isFirstRef = useRef(true);\r\n const prevValueRef = useRef('');\r\n\r\n const lists = useMemo(() => charListStrings.map(s => new TickerCharacterList(s)), [charListStrings]);\r\n const supported = useMemo(() => {\r\n const set = new Set<string>();\r\n lists.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n }, [lists]);\r\n\r\n // 用 ref 存储依赖项,避免 useEffect 因为这些依赖重复触发\r\n const listsRef = useRef(lists);\r\n const supportedRef = useRef(supported);\r\n const directionRef = useRef(direction);\r\n const durationRef = useRef(duration);\r\n const easingRef = useRef(easing);\r\n listsRef.current = lists;\r\n supportedRef.current = supported;\r\n directionRef.current = direction;\r\n durationRef.current = duration;\r\n easingRef.current = easing;\r\n\r\n // 主要逻辑:value 变化时处理\r\n useEffect(() => {\r\n // 防止相同 value 重复触发(React StrictMode)\r\n if (value === prevValueRef.current) {\r\n return;\r\n }\r\n prevValueRef.current = value;\r\n\r\n // 取消正在进行的动画\r\n if (animRef.current) {\r\n cancelAnimationFrame(animRef.current);\r\n animRef.current = undefined;\r\n }\r\n\r\n // 如果动画进行中,先用当前进度更新列状态\r\n let currentCols = colsRef.current;\r\n if (progressRef.current < 1 && progressRef.current > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progressRef.current, true).col);\r\n colsRef.current = currentCols;\r\n }\r\n\r\n const targetChars = value.split('');\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supportedRef.current);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], listsRef.current, directionRef.current));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], listsRef.current, directionRef.current));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, listsRef.current, directionRef.current));\r\n }\r\n }\r\n\r\n colsRef.current = result;\r\n setColumns(result);\r\n\r\n // 动画\r\n if (!isFirstRef.current && result.length > 0) {\r\n progressRef.current = 0;\r\n setProgress(0);\r\n const start = performance.now();\r\n const dur = durationRef.current;\r\n const easeFn = easingFunctions[easingRef.current] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP); // 应用 easing 函数\r\n progressRef.current = p;\r\n\r\n // 节流:每 16ms 最多更新一次视图 (约 60fps)\r\n if (now - lastUpdate >= 16 || linearP >= 1) {\r\n lastUpdate = now;\r\n setProgress(p);\r\n }\r\n\r\n if (linearP < 1) {\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 动画结束\r\n const final = colsRef.current\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n animRef.current = undefined;\r\n }\r\n };\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 首次渲染,直接显示\r\n isFirstRef.current = false;\r\n progressRef.current = 1;\r\n setProgress(1);\r\n const final = result.map(c => applyProgress(c, 1).col).filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n }\r\n\r\n return () => {\r\n if (animRef.current) cancelAnimationFrame(animRef.current);\r\n };\r\n }, [value]); // 只依赖 value\r\n\r\n // 渲染\r\n const charHeight = 1.2; // 与 CSS line-height 匹配\r\n\r\n const rendered = useMemo(() => {\r\n return columns.map((col, i) => {\r\n const { charIdx, delta } = applyProgress(col, progress);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress;\r\n if (width <= 0) return null;\r\n\r\n const chars: React.ReactNode[] = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // 当前字符\r\n if (charIdx >= 0 && charIdx < list.length) {\r\n chars.push(\r\n <div key={`c-${charIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm}em)` }}>\r\n {list[charIdx] === EMPTY_CHAR ? '\\u00A0' : list[charIdx]}\r\n </div>\r\n );\r\n }\r\n // 下一个字符\r\n const nextIdx = charIdx + 1;\r\n if (nextIdx >= 0 && nextIdx < list.length) {\r\n chars.push(\r\n <div key={`n-${nextIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm - charHeight}em)` }}>\r\n {list[nextIdx] === EMPTY_CHAR ? '\\u00A0' : list[nextIdx]}\r\n </div>\r\n );\r\n }\r\n // 上一个字符(处理中断)\r\n const prevIdx = charIdx - 1;\r\n if (prevIdx >= 0 && prevIdx < list.length) {\r\n chars.push(\r\n <div key={`p-${prevIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm + charHeight}em)` }}>\r\n {list[prevIdx] === EMPTY_CHAR ? '\\u00A0' : list[prevIdx]}\r\n </div>\r\n );\r\n }\r\n\r\n // 基准宽度 0.8em * 倍率\r\n return (\r\n <div key={i} className=\"ticker-column\" style={{ width: `${width * 0.8 * charWidth}em` }}>\r\n {chars}\r\n </div>\r\n );\r\n });\r\n }, [columns, progress, charWidth]);\r\n\r\n return <div className={`ticker ${className}`.trim()}>{rendered}</div>;\r\n};\r\n\r\nexport default React.memo(Ticker);\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/components/Ticker.tsx"],"sourcesContent":["/**\r\n * Ticker - 直接翻译自 Robinhood Android Ticker\r\n * https://github.com/robinhood/ticker\r\n */\r\nimport React, { useEffect, useRef, useState, useMemo } from 'react';\r\nimport './Ticker.css';\r\nimport {\r\n TickerUtils,\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../core/TickerCore';\r\n\r\n// 导出 Utils 供外部使用\r\nexport { TickerUtils };\r\n\r\n// ============================================================================\r\n// Ticker Component\r\n// ============================================================================\r\nexport interface TickerProps {\r\n value: string;\r\n characterLists?: string[];\r\n duration?: number;\r\n direction?: ScrollingDirection;\r\n easing?: string;\r\n className?: string;\r\n charWidth?: number;\r\n}\r\n\r\nexport const Ticker: React.FC<TickerProps> = ({\r\n value,\r\n characterLists: charListStrings = [TickerUtils.provideNumberList()],\r\n duration = 500,\r\n direction = 'ANY',\r\n easing = 'easeInOut', // Robinhood 默认: AccelerateDecelerateInterpolator\r\n className = '',\r\n charWidth = 1,\r\n}) => {\r\n const [columns, setColumns] = useState<ColumnState[]>([]);\r\n const [progress, setProgress] = useState(1);\r\n const animRef = useRef<number>();\r\n const colsRef = useRef<ColumnState[]>([]);\r\n const progressRef = useRef(1);\r\n const isFirstRef = useRef(true);\r\n const prevValueRef = useRef('');\r\n\r\n const lists = useMemo(() => charListStrings.map(s => new TickerCharacterList(s)), [charListStrings]);\r\n const supported = useMemo(() => {\r\n const set = new Set<string>();\r\n lists.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n }, [lists]);\r\n\r\n // 用 ref 存储依赖项,避免 useEffect 因为这些依赖重复触发\r\n const listsRef = useRef(lists);\r\n const supportedRef = useRef(supported);\r\n const directionRef = useRef(direction);\r\n const durationRef = useRef(duration);\r\n const easingRef = useRef(easing);\r\n listsRef.current = lists;\r\n supportedRef.current = supported;\r\n directionRef.current = direction;\r\n durationRef.current = duration;\r\n easingRef.current = easing;\r\n\r\n // 主要逻辑:value 变化时处理\r\n useEffect(() => {\r\n // 防止相同 value 重复触发(React StrictMode)\r\n if (value === prevValueRef.current) {\r\n return;\r\n }\r\n prevValueRef.current = value;\r\n\r\n // 取消正在进行的动画\r\n if (animRef.current) {\r\n cancelAnimationFrame(animRef.current);\r\n animRef.current = undefined;\r\n }\r\n\r\n // 如果动画进行中,先用当前进度更新列状态\r\n let currentCols = colsRef.current;\r\n if (progressRef.current < 1 && progressRef.current > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progressRef.current, true).col);\r\n colsRef.current = currentCols;\r\n }\r\n\r\n const targetChars = [...value];\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supportedRef.current);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], listsRef.current, directionRef.current));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], listsRef.current, directionRef.current));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, listsRef.current, directionRef.current));\r\n }\r\n }\r\n\r\n colsRef.current = result;\r\n setColumns(result);\r\n\r\n // 动画\r\n if (!isFirstRef.current && result.length > 0) {\r\n progressRef.current = 0;\r\n setProgress(0);\r\n const start = performance.now();\r\n const dur = durationRef.current;\r\n const easeFn = easingFunctions[easingRef.current] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP); // 应用 easing 函数\r\n progressRef.current = p;\r\n\r\n // 节流:每 16ms 最多更新一次视图 (约 60fps)\r\n if (now - lastUpdate >= 16 || linearP >= 1) {\r\n lastUpdate = now;\r\n setProgress(p);\r\n }\r\n\r\n if (linearP < 1) {\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 动画结束\r\n const final = colsRef.current\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n animRef.current = undefined;\r\n }\r\n };\r\n animRef.current = requestAnimationFrame(animate);\r\n } else {\r\n // 首次渲染,直接显示\r\n isFirstRef.current = false;\r\n progressRef.current = 1;\r\n setProgress(1);\r\n const final = result.map(c => applyProgress(c, 1).col).filter(c => c.currentWidth > 0);\r\n colsRef.current = final;\r\n setColumns(final);\r\n }\r\n\r\n return () => {\r\n if (animRef.current) cancelAnimationFrame(animRef.current);\r\n };\r\n }, [value]); // 只依赖 value\r\n\r\n // 渲染\r\n const charHeight = 1.2; // 与 CSS line-height 匹配\r\n\r\n const rendered = useMemo(() => {\r\n return columns.map((col, i) => {\r\n const { charIdx, delta } = applyProgress(col, progress);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress;\r\n if (width <= 0) return null;\r\n\r\n const chars: React.ReactNode[] = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // 当前字符\r\n if (charIdx >= 0 && charIdx < list.length) {\r\n chars.push(\r\n <div key={`c-${charIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm}em)` }}>\r\n {list[charIdx] === EMPTY_CHAR ? '\\u00A0' : list[charIdx]}\r\n </div>\r\n );\r\n }\r\n // 下一个字符\r\n const nextIdx = charIdx + 1;\r\n if (nextIdx >= 0 && nextIdx < list.length) {\r\n chars.push(\r\n <div key={`n-${nextIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm - charHeight}em)` }}>\r\n {list[nextIdx] === EMPTY_CHAR ? '\\u00A0' : list[nextIdx]}\r\n </div>\r\n );\r\n }\r\n // 上一个字符(处理中断)\r\n const prevIdx = charIdx - 1;\r\n if (prevIdx >= 0 && prevIdx < list.length) {\r\n chars.push(\r\n <div key={`p-${prevIdx}`} className=\"ticker-char\" style={{ transform: `translateY(${deltaEm + charHeight}em)` }}>\r\n {list[prevIdx] === EMPTY_CHAR ? '\\u00A0' : list[prevIdx]}\r\n </div>\r\n );\r\n }\r\n\r\n // 动态计算字符基础宽度:全角字符 1.1em,半角 0.8em\r\n // 使用线性插值 (Lerp) 避免突变跳动\r\n const isFW = (c: string) => c && c.length > 0 && c.charCodeAt(0) > 255;\r\n const getW = (c: string) => isFW(c) ? 1.25 : 0.8;\r\n\r\n // 安全获字符\r\n const startChar = list[col.startIndex];\r\n const endChar = list[col.endIndex];\r\n\r\n const w1 = getW(startChar);\r\n const w2 = getW(endChar);\r\n const baseW = w1 + (w2 - w1) * progress;\r\n\r\n return (\r\n <div key={i} className=\"ticker-column\" style={{ width: `${width * baseW * charWidth}em` }}>\r\n {chars}\r\n </div>\r\n );\r\n });\r\n }, [columns, progress, charWidth]);\r\n\r\n return <div className={`ticker ${className}`.trim()}>{rendered}</div>;\r\n};\r\n\r\nexport default React.memo(Ticker);\r\n"],"names":[],"mappings":"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"}
|
package/dist/style.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.ticker {
|
|
2
2
|
display: inline-flex;
|
|
3
3
|
overflow: hidden;
|
|
4
|
-
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
4
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
5
5
|
font-weight: 600;
|
|
6
6
|
line-height: 1.2;
|
|
7
7
|
}
|
|
@@ -26,21 +26,21 @@
|
|
|
26
26
|
justify-content: center;
|
|
27
27
|
white-space: pre;
|
|
28
28
|
}
|
|
29
|
-
.ticker[data-v-
|
|
29
|
+
.ticker[data-v-7e47ac69] {
|
|
30
30
|
display: inline-flex;
|
|
31
31
|
overflow: hidden;
|
|
32
32
|
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
33
33
|
font-weight: 600;
|
|
34
34
|
line-height: 1.2;
|
|
35
35
|
}
|
|
36
|
-
.ticker-column[data-v-
|
|
36
|
+
.ticker-column[data-v-7e47ac69] {
|
|
37
37
|
display: inline-block;
|
|
38
38
|
height: 1.2em;
|
|
39
39
|
overflow: hidden;
|
|
40
40
|
position: relative;
|
|
41
41
|
clip-path: inset(0);
|
|
42
42
|
}
|
|
43
|
-
.ticker-char[data-v-
|
|
43
|
+
.ticker-char[data-v-7e47ac69] {
|
|
44
44
|
position: absolute;
|
|
45
45
|
top: 0;
|
|
46
46
|
left: 0;
|
package/dist/vue-demo.html
CHANGED
|
@@ -162,11 +162,18 @@
|
|
|
162
162
|
<body>
|
|
163
163
|
<div id="app">
|
|
164
164
|
<div class="price-hero">
|
|
165
|
-
<div class="
|
|
165
|
+
<div class="control-buttons" style="justify-content: center; margin-bottom: 2rem;">
|
|
166
|
+
<button :class="{ active: mode === 'price' }" @click="mode = 'price'" style="padding: 0.5rem 1.5rem;">数字
|
|
167
|
+
/ Price</button>
|
|
168
|
+
<button :class="{ active: mode === 'text' }" @click="mode = 'text'" style="padding: 0.5rem 1.5rem;">文本 /
|
|
169
|
+
Text</button>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
166
172
|
<div class="price-value">
|
|
167
|
-
<span class="price-currency">$</span>
|
|
168
|
-
<ticker-component :value="
|
|
169
|
-
:character-lists="
|
|
173
|
+
<span v-if="mode === 'price'" class="price-currency">$</span>
|
|
174
|
+
<ticker-component :value="heroValue" :duration="animDuration" :easing="easing" :char-width="charWidth"
|
|
175
|
+
:character-lists="currentCharacterLists">
|
|
176
|
+
</ticker-component>
|
|
170
177
|
</div>
|
|
171
178
|
|
|
172
179
|
<div class="price-controls">
|
|
@@ -465,10 +472,23 @@
|
|
|
465
472
|
return columns.value.map(col => {
|
|
466
473
|
const { charIdx, delta } = applyProgress(col, progress.value);
|
|
467
474
|
const logicalWidth = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress.value;
|
|
468
|
-
|
|
475
|
+
|
|
476
|
+
// Full-width character interpolation logic
|
|
477
|
+
const list = col.charList || [];
|
|
478
|
+
const isFW = (c) => c && c.length > 0 && c.charCodeAt(0) > 255;
|
|
479
|
+
const getW = (c) => isFW(c) ? 1.1 : 0.8;
|
|
480
|
+
|
|
481
|
+
const startChar = list[col.startIndex] || '';
|
|
482
|
+
const endChar = list[col.endIndex] || '';
|
|
483
|
+
const w1 = getW(startChar);
|
|
484
|
+
const w2 = getW(endChar);
|
|
485
|
+
const baseW = w1 + (w2 - w1) * progress.value;
|
|
486
|
+
|
|
487
|
+
const width = logicalWidth * props.charWidth * baseW;
|
|
488
|
+
|
|
469
489
|
if (width <= 0) return { width, chars: [] };
|
|
470
490
|
const chars = [];
|
|
471
|
-
|
|
491
|
+
|
|
472
492
|
const deltaEm = delta * charHeight;
|
|
473
493
|
const add = (idx, offsetMod, keyPrefix) => {
|
|
474
494
|
if (idx >= 0 && idx < list.length) {
|
|
@@ -489,11 +509,21 @@
|
|
|
489
509
|
createApp({
|
|
490
510
|
components: { TickerComponent },
|
|
491
511
|
setup() {
|
|
512
|
+
const mode = ref('price');
|
|
492
513
|
const priceSequence = ['73.18', '76.58', '173.50', '9.1'];
|
|
493
|
-
const
|
|
514
|
+
const textSequence = [
|
|
515
|
+
'Smart Ticker',
|
|
516
|
+
'Small Diff',
|
|
517
|
+
'哈基米 Dif@#$',
|
|
518
|
+
'硅基生命 %@#$',
|
|
519
|
+
'宇宙生命 Smart',
|
|
520
|
+
];
|
|
521
|
+
|
|
522
|
+
const heroValue = ref(priceSequence[0]);
|
|
494
523
|
const animDuration = ref(800);
|
|
495
524
|
const easing = ref('easeInOut');
|
|
496
525
|
const charWidth = ref(1);
|
|
526
|
+
|
|
497
527
|
const durationOptions = [400, 800, 1200];
|
|
498
528
|
const widthOptions = [0.8, 1, 1.2];
|
|
499
529
|
const easingOptions = [
|
|
@@ -502,18 +532,30 @@
|
|
|
502
532
|
{ name: 'bounce', label: '回弹' },
|
|
503
533
|
];
|
|
504
534
|
|
|
535
|
+
const currentCharacterLists = computed(() => {
|
|
536
|
+
if (mode.value === 'price') return ['0123456789.,'];
|
|
537
|
+
// Provide alphabet, upcase, numbers, symbols
|
|
538
|
+
const alpha = 'abcdefghijklmnopqrstuvwxyz';
|
|
539
|
+
const symbols = ' .%v-@#$';
|
|
540
|
+
return [alpha, alpha.toUpperCase(), '0123456789', symbols];
|
|
541
|
+
});
|
|
542
|
+
|
|
505
543
|
let interval;
|
|
506
544
|
|
|
507
|
-
watch(animDuration, (
|
|
508
|
-
if (interval) clearInterval(interval);
|
|
545
|
+
watch([animDuration, mode], () => {
|
|
509
546
|
startInterval();
|
|
510
547
|
});
|
|
511
548
|
|
|
512
549
|
const startInterval = () => {
|
|
513
|
-
|
|
550
|
+
if (interval) clearInterval(interval);
|
|
551
|
+
let idx = 0;
|
|
552
|
+
// Reset to first value
|
|
553
|
+
heroValue.value = mode.value === 'price' ? priceSequence[0] : textSequence[0];
|
|
554
|
+
|
|
514
555
|
interval = setInterval(() => {
|
|
515
|
-
|
|
516
|
-
|
|
556
|
+
idx++;
|
|
557
|
+
const seq = mode.value === 'price' ? priceSequence : textSequence;
|
|
558
|
+
heroValue.value = seq[idx % seq.length];
|
|
517
559
|
}, animDuration.value + 1000);
|
|
518
560
|
};
|
|
519
561
|
|
|
@@ -526,7 +568,9 @@
|
|
|
526
568
|
});
|
|
527
569
|
|
|
528
570
|
return {
|
|
529
|
-
|
|
571
|
+
mode,
|
|
572
|
+
heroValue,
|
|
573
|
+
currentCharacterLists,
|
|
530
574
|
animDuration,
|
|
531
575
|
easing,
|
|
532
576
|
charWidth,
|
package/dist/vue.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const vue = require("vue");
|
|
4
|
-
const TickerCore = require("./TickerCore-
|
|
4
|
+
const TickerCore = require("./TickerCore-BAsdPrD1.cjs");
|
|
5
5
|
const charHeight = 1.2;
|
|
6
6
|
const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
7
7
|
__name: "Ticker",
|
|
@@ -35,7 +35,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
35
35
|
if (progress.value < 1 && progress.value > 0) {
|
|
36
36
|
currentCols = currentCols.map((c) => TickerCore.applyProgress(c, progress.value, true).col);
|
|
37
37
|
}
|
|
38
|
-
const targetChars = newValue
|
|
38
|
+
const targetChars = [...newValue];
|
|
39
39
|
const sourceChars = currentCols.map((c) => c.currentChar);
|
|
40
40
|
const actions = TickerCore.computeColumnActions(sourceChars, targetChars, supported.value);
|
|
41
41
|
let ci = 0, ti = 0;
|
|
@@ -100,7 +100,14 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
100
100
|
add(charIdx, 0, "c");
|
|
101
101
|
add(charIdx + 1, -charHeight, "n");
|
|
102
102
|
add(charIdx - 1, charHeight, "p");
|
|
103
|
-
|
|
103
|
+
const isFW = (c) => c && c.length > 0 && c.charCodeAt(0) > 255;
|
|
104
|
+
const getW = (c) => isFW(c) ? 1.25 : 0.8;
|
|
105
|
+
const startChar = list[col.startIndex] || "";
|
|
106
|
+
const endChar = list[col.endIndex] || "";
|
|
107
|
+
const w1 = getW(startChar);
|
|
108
|
+
const w2 = getW(endChar);
|
|
109
|
+
const baseW = w1 + (w2 - w1) * progress.value;
|
|
110
|
+
return { width, chars, baseW };
|
|
104
111
|
}).filter((c) => c.width > 0);
|
|
105
112
|
});
|
|
106
113
|
return (_ctx, _cache) => {
|
|
@@ -111,7 +118,7 @@ const _sfc_main = /* @__PURE__ */ vue.defineComponent({
|
|
|
111
118
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
112
119
|
key: i,
|
|
113
120
|
class: "ticker-column",
|
|
114
|
-
style: vue.normalizeStyle({ width: `${col.width * 0.8 * __props.charWidth}em` })
|
|
121
|
+
style: vue.normalizeStyle({ width: `${col.width * (col.baseW || 0.8) * __props.charWidth}em` })
|
|
115
122
|
}, [
|
|
116
123
|
(vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(col.chars, (charObj) => {
|
|
117
124
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
@@ -133,7 +140,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
133
140
|
}
|
|
134
141
|
return target;
|
|
135
142
|
};
|
|
136
|
-
const Ticker = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
143
|
+
const Ticker = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7e47ac69"]]);
|
|
137
144
|
exports.ACTION_DELETE = TickerCore.ACTION_DELETE;
|
|
138
145
|
exports.ACTION_INSERT = TickerCore.ACTION_INSERT;
|
|
139
146
|
exports.ACTION_SAME = TickerCore.ACTION_SAME;
|
package/dist/vue.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.cjs","sources":["../src/components/vue/Ticker.vue"],"sourcesContent":["<template>\r\n <div class=\"ticker\" :class=\"className\">\r\n <div\r\n v-for=\"(col, i) in renderedColumns\"\r\n :key=\"i\"\r\n class=\"ticker-column\"\r\n :style=\"{ width: `${col.width * 0.8 * charWidth}em` }\"\r\n >\r\n <div\r\n v-for=\"charObj in col.chars\"\r\n :key=\"charObj.key\"\r\n class=\"ticker-char\"\r\n :style=\"{ transform: `translateY(${charObj.offset}em)` }\"\r\n >\r\n {{ charObj.char === EMPTY_CHAR ? '\\u00A0' : charObj.char }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, onUnmounted } from 'vue';\r\nimport {\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../../core/TickerCore';\r\n\r\n// ============================================================================\r\n// Vue Component Setup\r\n// ============================================================================\r\n\r\nconst props = defineProps({\r\n value: { type: String, required: true },\r\n characterLists: { type: Array as () => string[], default: () => ['0123456789'] },\r\n duration: { type: Number, default: 500 },\r\n direction: { type: String as () => ScrollingDirection, default: 'ANY' },\r\n easing: { type: String, default: 'easeInOut' },\r\n className: { type: String, default: '' },\r\n charWidth: { type: Number, default: 1 },\r\n});\r\n\r\nconst columns = ref<ColumnState[]>([]);\r\nconst progress = ref(1);\r\nlet animId: number | undefined;\r\n\r\n// Initialization\r\nconst lists = computed(() => props.characterLists.map(s => new TickerCharacterList(s)));\r\nconst supported = computed(() => {\r\n const set = new Set<string>();\r\n lists.value.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n});\r\n\r\n// Watch for value changes\r\nwatch(() => props.value, (newValue, oldValue) => {\r\n if (newValue === oldValue) return;\r\n\r\n if (animId) {\r\n cancelAnimationFrame(animId);\r\n animId = undefined;\r\n }\r\n\r\n // Update current columns with current progress if mid-animation\r\n let currentCols = columns.value;\r\n if (progress.value < 1 && progress.value > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progress.value, true).col);\r\n }\r\n\r\n const targetChars = newValue.split('');\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supported.value);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], lists.value, props.direction));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], lists.value, props.direction));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, lists.value, props.direction));\r\n }\r\n }\r\n\r\n columns.value = result;\r\n\r\n // Start Animation\r\n progress.value = 0;\r\n const start = performance.now();\r\n const dur = props.duration;\r\n const easeFn = easingFunctions[props.easing] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP);\r\n \r\n // Throttled update\r\n const shouldUpdate = now - lastUpdate >= 16 || linearP >= 1;\r\n \r\n if (shouldUpdate) {\r\n lastUpdate = now;\r\n progress.value = p;\r\n }\r\n\r\n if (linearP < 1) {\r\n animId = requestAnimationFrame(animate);\r\n } else {\r\n const final = columns.value\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n columns.value = final;\r\n progress.value = 1;\r\n animId = undefined;\r\n }\r\n };\r\n animId = requestAnimationFrame(animate);\r\n\r\n}, { immediate: true }); \r\n\r\nonUnmounted(() => {\r\n if (animId) cancelAnimationFrame(animId);\r\n});\r\n\r\n// Derived View Data\r\nconst charHeight = 1.2;\r\n\r\nconst renderedColumns = computed(() => {\r\n return columns.value.map(col => {\r\n const { charIdx, delta } = applyProgress(col, progress.value);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress.value;\r\n if (width <= 0) return { width, chars: [] };\r\n\r\n const chars: Array<{ key: string, char: string, offset: number }> = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // Helper to add char\r\n const add = (idx: number, offsetMod: number, keyPrefix: string) => {\r\n if (idx >= 0 && idx < list.length) {\r\n chars.push({\r\n key: `${keyPrefix}-${idx}`,\r\n char: list[idx],\r\n offset: deltaEm + offsetMod\r\n });\r\n }\r\n };\r\n\r\n add(charIdx, 0, 'c');\r\n add(charIdx + 1, -charHeight, 'n');\r\n add(charIdx - 1, charHeight, 'p');\r\n\r\n return { width, chars };\r\n }).filter(c => c.width > 0);\r\n});\r\n\r\n</script>\r\n\r\n<style scoped>\r\n.ticker {\r\n display: inline-flex;\r\n overflow: hidden;\r\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\r\n font-weight: 600;\r\n line-height: 1.2;\r\n}\r\n\r\n.ticker-column {\r\n display: inline-block;\r\n height: 1.2em;\r\n overflow: hidden;\r\n position: relative;\r\n clip-path: inset(0);\r\n}\r\n\r\n.ticker-char {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n height: 1.2em;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n white-space: pre;\r\n}\r\n</style>\r\n"],"names":["ref","computed","TickerCharacterList","watch","applyProgress","computeColumnActions","ACTION_INSERT","setTarget","createColumn","ACTION_SAME","EMPTY_CHAR","easingFunctions","onUnmounted","_createElementBlock","_normalizeClass","_openBlock","_Fragment","_renderList","_normalizeStyle","_toDisplayString","_unref"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vue.cjs","sources":["../src/components/vue/Ticker.vue"],"sourcesContent":["<template>\r\n <div class=\"ticker\" :class=\"className\">\r\n <div\r\n v-for=\"(col, i) in renderedColumns\"\r\n :key=\"i\"\r\n class=\"ticker-column\"\r\n :style=\"{ width: `${col.width * (col.baseW || 0.8) * charWidth}em` }\"\r\n >\r\n <div\r\n v-for=\"charObj in col.chars\"\r\n :key=\"charObj.key\"\r\n class=\"ticker-char\"\r\n :style=\"{ transform: `translateY(${charObj.offset}em)` }\"\r\n >\r\n {{ charObj.char === EMPTY_CHAR ? '\\u00A0' : charObj.char }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, onUnmounted } from 'vue';\r\nimport {\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../../core/TickerCore';\r\n\r\n// ============================================================================\r\n// Vue Component Setup\r\n// ============================================================================\r\n\r\nconst props = defineProps({\r\n value: { type: String, required: true },\r\n characterLists: { type: Array as () => string[], default: () => ['0123456789'] },\r\n duration: { type: Number, default: 500 },\r\n direction: { type: String as () => ScrollingDirection, default: 'ANY' },\r\n easing: { type: String, default: 'easeInOut' },\r\n className: { type: String, default: '' },\r\n charWidth: { type: Number, default: 1 },\r\n});\r\n\r\nconst columns = ref<ColumnState[]>([]);\r\nconst progress = ref(1);\r\nlet animId: number | undefined;\r\n\r\n// Initialization\r\nconst lists = computed(() => props.characterLists.map(s => new TickerCharacterList(s)));\r\nconst supported = computed(() => {\r\n const set = new Set<string>();\r\n lists.value.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n});\r\n\r\n// Watch for value changes\r\nwatch(() => props.value, (newValue, oldValue) => {\r\n if (newValue === oldValue) return;\r\n\r\n if (animId) {\r\n cancelAnimationFrame(animId);\r\n animId = undefined;\r\n }\r\n\r\n // Update current columns with current progress if mid-animation\r\n let currentCols = columns.value;\r\n if (progress.value < 1 && progress.value > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progress.value, true).col);\r\n }\r\n\r\n const targetChars = [...newValue];\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supported.value);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], lists.value, props.direction));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], lists.value, props.direction));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, lists.value, props.direction));\r\n }\r\n }\r\n\r\n columns.value = result;\r\n\r\n // Start Animation\r\n progress.value = 0;\r\n const start = performance.now();\r\n const dur = props.duration;\r\n const easeFn = easingFunctions[props.easing] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP);\r\n \r\n // Throttled update\r\n const shouldUpdate = now - lastUpdate >= 16 || linearP >= 1;\r\n \r\n if (shouldUpdate) {\r\n lastUpdate = now;\r\n progress.value = p;\r\n }\r\n\r\n if (linearP < 1) {\r\n animId = requestAnimationFrame(animate);\r\n } else {\r\n const final = columns.value\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n columns.value = final;\r\n progress.value = 1;\r\n animId = undefined;\r\n }\r\n };\r\n animId = requestAnimationFrame(animate);\r\n\r\n}, { immediate: true }); \r\n\r\nonUnmounted(() => {\r\n if (animId) cancelAnimationFrame(animId);\r\n});\r\n\r\n// Derived View Data\r\nconst charHeight = 1.2;\r\n\r\nconst renderedColumns = computed(() => {\r\n return columns.value.map(col => {\r\n const { charIdx, delta } = applyProgress(col, progress.value);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress.value;\r\n if (width <= 0) return { width, chars: [] };\r\n\r\n const chars: Array<{ key: string, char: string, offset: number }> = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // Helper to add char\r\n const add = (idx: number, offsetMod: number, keyPrefix: string) => {\r\n if (idx >= 0 && idx < list.length) {\r\n chars.push({\r\n key: `${keyPrefix}-${idx}`,\r\n char: list[idx],\r\n offset: deltaEm + offsetMod\r\n });\r\n }\r\n };\r\n\r\n add(charIdx, 0, 'c');\r\n add(charIdx + 1, -charHeight, 'n');\r\n add(charIdx - 1, charHeight, 'p');\r\n\r\n // Check for full-width char\r\n const isFW = (c: string) => c && c.length > 0 && c.charCodeAt(0) > 255;\r\n const getW = (c: string) => isFW(c) ? 1.25 : 0.8;\r\n\r\n const startChar = list[col.startIndex] || '';\r\n const endChar = list[col.endIndex] || ''; // endIndex not targetIndex\r\n const w1 = getW(startChar);\r\n const w2 = getW(endChar);\r\n const baseW = w1 + (w2 - w1) * progress.value;\r\n\r\n return { width, chars, baseW };\r\n }).filter(c => c.width > 0);\r\n});\r\n\r\n</script>\r\n\r\n<style scoped>\r\n.ticker {\r\n display: inline-flex;\r\n overflow: hidden;\r\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\r\n font-weight: 600;\r\n line-height: 1.2;\r\n}\r\n\r\n.ticker-column {\r\n display: inline-block;\r\n height: 1.2em;\r\n overflow: hidden;\r\n position: relative;\r\n clip-path: inset(0);\r\n}\r\n\r\n.ticker-char {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n height: 1.2em;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n white-space: pre;\r\n}\r\n</style>\r\n"],"names":["ref","computed","TickerCharacterList","watch","applyProgress","computeColumnActions","ACTION_INSERT","setTarget","createColumn","ACTION_SAME","EMPTY_CHAR","easingFunctions","onUnmounted","_createElementBlock","_normalizeClass","_openBlock","_Fragment","_renderList","_normalizeStyle","_toDisplayString","_unref"],"mappings":"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"}
|
package/dist/vue.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineComponent, ref, computed, watch, onUnmounted, createElementBlock, openBlock, normalizeClass, Fragment, renderList, normalizeStyle, toDisplayString, unref } from "vue";
|
|
2
|
-
import { a as TickerCharacterList, b as applyProgress, c as computeColumnActions, A as ACTION_INSERT, s as setTarget, d as createColumn, e as ACTION_SAME, E as EMPTY_CHAR, f as easingFunctions } from "./TickerCore-
|
|
3
|
-
import { g, T } from "./TickerCore-
|
|
2
|
+
import { a as TickerCharacterList, b as applyProgress, c as computeColumnActions, A as ACTION_INSERT, s as setTarget, d as createColumn, e as ACTION_SAME, E as EMPTY_CHAR, f as easingFunctions } from "./TickerCore-C7Ejc9kB.js";
|
|
3
|
+
import { g, T } from "./TickerCore-C7Ejc9kB.js";
|
|
4
4
|
const charHeight = 1.2;
|
|
5
5
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
6
6
|
__name: "Ticker",
|
|
@@ -34,7 +34,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
34
34
|
if (progress.value < 1 && progress.value > 0) {
|
|
35
35
|
currentCols = currentCols.map((c) => applyProgress(c, progress.value, true).col);
|
|
36
36
|
}
|
|
37
|
-
const targetChars = newValue
|
|
37
|
+
const targetChars = [...newValue];
|
|
38
38
|
const sourceChars = currentCols.map((c) => c.currentChar);
|
|
39
39
|
const actions = computeColumnActions(sourceChars, targetChars, supported.value);
|
|
40
40
|
let ci = 0, ti = 0;
|
|
@@ -99,7 +99,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
99
99
|
add(charIdx, 0, "c");
|
|
100
100
|
add(charIdx + 1, -charHeight, "n");
|
|
101
101
|
add(charIdx - 1, charHeight, "p");
|
|
102
|
-
|
|
102
|
+
const isFW = (c) => c && c.length > 0 && c.charCodeAt(0) > 255;
|
|
103
|
+
const getW = (c) => isFW(c) ? 1.25 : 0.8;
|
|
104
|
+
const startChar = list[col.startIndex] || "";
|
|
105
|
+
const endChar = list[col.endIndex] || "";
|
|
106
|
+
const w1 = getW(startChar);
|
|
107
|
+
const w2 = getW(endChar);
|
|
108
|
+
const baseW = w1 + (w2 - w1) * progress.value;
|
|
109
|
+
return { width, chars, baseW };
|
|
103
110
|
}).filter((c) => c.width > 0);
|
|
104
111
|
});
|
|
105
112
|
return (_ctx, _cache) => {
|
|
@@ -110,7 +117,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
110
117
|
return openBlock(), createElementBlock("div", {
|
|
111
118
|
key: i,
|
|
112
119
|
class: "ticker-column",
|
|
113
|
-
style: normalizeStyle({ width: `${col.width * 0.8 * __props.charWidth}em` })
|
|
120
|
+
style: normalizeStyle({ width: `${col.width * (col.baseW || 0.8) * __props.charWidth}em` })
|
|
114
121
|
}, [
|
|
115
122
|
(openBlock(true), createElementBlock(Fragment, null, renderList(col.chars, (charObj) => {
|
|
116
123
|
return openBlock(), createElementBlock("div", {
|
|
@@ -132,7 +139,7 @@ const _export_sfc = (sfc, props) => {
|
|
|
132
139
|
}
|
|
133
140
|
return target;
|
|
134
141
|
};
|
|
135
|
-
const Ticker = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
142
|
+
const Ticker = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-7e47ac69"]]);
|
|
136
143
|
export {
|
|
137
144
|
g as ACTION_DELETE,
|
|
138
145
|
ACTION_INSERT,
|
package/dist/vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vue.js","sources":["../src/components/vue/Ticker.vue"],"sourcesContent":["<template>\r\n <div class=\"ticker\" :class=\"className\">\r\n <div\r\n v-for=\"(col, i) in renderedColumns\"\r\n :key=\"i\"\r\n class=\"ticker-column\"\r\n :style=\"{ width: `${col.width * 0.8 * charWidth}em` }\"\r\n >\r\n <div\r\n v-for=\"charObj in col.chars\"\r\n :key=\"charObj.key\"\r\n class=\"ticker-char\"\r\n :style=\"{ transform: `translateY(${charObj.offset}em)` }\"\r\n >\r\n {{ charObj.char === EMPTY_CHAR ? '\\u00A0' : charObj.char }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, onUnmounted } from 'vue';\r\nimport {\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../../core/TickerCore';\r\n\r\n// ============================================================================\r\n// Vue Component Setup\r\n// ============================================================================\r\n\r\nconst props = defineProps({\r\n value: { type: String, required: true },\r\n characterLists: { type: Array as () => string[], default: () => ['0123456789'] },\r\n duration: { type: Number, default: 500 },\r\n direction: { type: String as () => ScrollingDirection, default: 'ANY' },\r\n easing: { type: String, default: 'easeInOut' },\r\n className: { type: String, default: '' },\r\n charWidth: { type: Number, default: 1 },\r\n});\r\n\r\nconst columns = ref<ColumnState[]>([]);\r\nconst progress = ref(1);\r\nlet animId: number | undefined;\r\n\r\n// Initialization\r\nconst lists = computed(() => props.characterLists.map(s => new TickerCharacterList(s)));\r\nconst supported = computed(() => {\r\n const set = new Set<string>();\r\n lists.value.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n});\r\n\r\n// Watch for value changes\r\nwatch(() => props.value, (newValue, oldValue) => {\r\n if (newValue === oldValue) return;\r\n\r\n if (animId) {\r\n cancelAnimationFrame(animId);\r\n animId = undefined;\r\n }\r\n\r\n // Update current columns with current progress if mid-animation\r\n let currentCols = columns.value;\r\n if (progress.value < 1 && progress.value > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progress.value, true).col);\r\n }\r\n\r\n const targetChars = newValue.split('');\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supported.value);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], lists.value, props.direction));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], lists.value, props.direction));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, lists.value, props.direction));\r\n }\r\n }\r\n\r\n columns.value = result;\r\n\r\n // Start Animation\r\n progress.value = 0;\r\n const start = performance.now();\r\n const dur = props.duration;\r\n const easeFn = easingFunctions[props.easing] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP);\r\n \r\n // Throttled update\r\n const shouldUpdate = now - lastUpdate >= 16 || linearP >= 1;\r\n \r\n if (shouldUpdate) {\r\n lastUpdate = now;\r\n progress.value = p;\r\n }\r\n\r\n if (linearP < 1) {\r\n animId = requestAnimationFrame(animate);\r\n } else {\r\n const final = columns.value\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n columns.value = final;\r\n progress.value = 1;\r\n animId = undefined;\r\n }\r\n };\r\n animId = requestAnimationFrame(animate);\r\n\r\n}, { immediate: true }); \r\n\r\nonUnmounted(() => {\r\n if (animId) cancelAnimationFrame(animId);\r\n});\r\n\r\n// Derived View Data\r\nconst charHeight = 1.2;\r\n\r\nconst renderedColumns = computed(() => {\r\n return columns.value.map(col => {\r\n const { charIdx, delta } = applyProgress(col, progress.value);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress.value;\r\n if (width <= 0) return { width, chars: [] };\r\n\r\n const chars: Array<{ key: string, char: string, offset: number }> = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // Helper to add char\r\n const add = (idx: number, offsetMod: number, keyPrefix: string) => {\r\n if (idx >= 0 && idx < list.length) {\r\n chars.push({\r\n key: `${keyPrefix}-${idx}`,\r\n char: list[idx],\r\n offset: deltaEm + offsetMod\r\n });\r\n }\r\n };\r\n\r\n add(charIdx, 0, 'c');\r\n add(charIdx + 1, -charHeight, 'n');\r\n add(charIdx - 1, charHeight, 'p');\r\n\r\n return { width, chars };\r\n }).filter(c => c.width > 0);\r\n});\r\n\r\n</script>\r\n\r\n<style scoped>\r\n.ticker {\r\n display: inline-flex;\r\n overflow: hidden;\r\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\r\n font-weight: 600;\r\n line-height: 1.2;\r\n}\r\n\r\n.ticker-column {\r\n display: inline-block;\r\n height: 1.2em;\r\n overflow: hidden;\r\n position: relative;\r\n clip-path: inset(0);\r\n}\r\n\r\n.ticker-char {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n height: 1.2em;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n white-space: pre;\r\n}\r\n</style>\r\n"],"names":["_createElementBlock","_normalizeClass","_openBlock","_Fragment","_renderList","_normalizeStyle","_toDisplayString","_unref"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vue.js","sources":["../src/components/vue/Ticker.vue"],"sourcesContent":["<template>\r\n <div class=\"ticker\" :class=\"className\">\r\n <div\r\n v-for=\"(col, i) in renderedColumns\"\r\n :key=\"i\"\r\n class=\"ticker-column\"\r\n :style=\"{ width: `${col.width * (col.baseW || 0.8) * charWidth}em` }\"\r\n >\r\n <div\r\n v-for=\"charObj in col.chars\"\r\n :key=\"charObj.key\"\r\n class=\"ticker-char\"\r\n :style=\"{ transform: `translateY(${charObj.offset}em)` }\"\r\n >\r\n {{ charObj.char === EMPTY_CHAR ? '\\u00A0' : charObj.char }}\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, onUnmounted } from 'vue';\r\nimport {\r\n TickerCharacterList,\r\n ScrollingDirection,\r\n ColumnState,\r\n EMPTY_CHAR,\r\n computeColumnActions,\r\n createColumn,\r\n setTarget,\r\n applyProgress,\r\n easingFunctions,\r\n ACTION_INSERT,\r\n ACTION_SAME,\r\n} from '../../core/TickerCore';\r\n\r\n// ============================================================================\r\n// Vue Component Setup\r\n// ============================================================================\r\n\r\nconst props = defineProps({\r\n value: { type: String, required: true },\r\n characterLists: { type: Array as () => string[], default: () => ['0123456789'] },\r\n duration: { type: Number, default: 500 },\r\n direction: { type: String as () => ScrollingDirection, default: 'ANY' },\r\n easing: { type: String, default: 'easeInOut' },\r\n className: { type: String, default: '' },\r\n charWidth: { type: Number, default: 1 },\r\n});\r\n\r\nconst columns = ref<ColumnState[]>([]);\r\nconst progress = ref(1);\r\nlet animId: number | undefined;\r\n\r\n// Initialization\r\nconst lists = computed(() => props.characterLists.map(s => new TickerCharacterList(s)));\r\nconst supported = computed(() => {\r\n const set = new Set<string>();\r\n lists.value.forEach(l => l.getSupportedCharacters().forEach(c => set.add(c)));\r\n return set;\r\n});\r\n\r\n// Watch for value changes\r\nwatch(() => props.value, (newValue, oldValue) => {\r\n if (newValue === oldValue) return;\r\n\r\n if (animId) {\r\n cancelAnimationFrame(animId);\r\n animId = undefined;\r\n }\r\n\r\n // Update current columns with current progress if mid-animation\r\n let currentCols = columns.value;\r\n if (progress.value < 1 && progress.value > 0) {\r\n currentCols = currentCols.map(c => applyProgress(c, progress.value, true).col);\r\n }\r\n\r\n const targetChars = [...newValue];\r\n const sourceChars = currentCols.map(c => c.currentChar);\r\n const actions = computeColumnActions(sourceChars, targetChars, supported.value);\r\n\r\n let ci = 0, ti = 0;\r\n const result: ColumnState[] = [];\r\n const validCols = currentCols.filter(c => c.currentWidth > 0);\r\n\r\n for (const action of actions) {\r\n if (action === ACTION_INSERT) {\r\n result.push(setTarget(createColumn(), targetChars[ti++], lists.value, props.direction));\r\n } else if (action === ACTION_SAME) {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, targetChars[ti++], lists.value, props.direction));\r\n } else {\r\n const existing = validCols[ci++] || createColumn();\r\n result.push(setTarget(existing, EMPTY_CHAR, lists.value, props.direction));\r\n }\r\n }\r\n\r\n columns.value = result;\r\n\r\n // Start Animation\r\n progress.value = 0;\r\n const start = performance.now();\r\n const dur = props.duration;\r\n const easeFn = easingFunctions[props.easing] || easingFunctions.linear;\r\n let lastUpdate = 0;\r\n\r\n const animate = (now: number) => {\r\n const linearP = Math.min((now - start) / dur, 1);\r\n const p = easeFn(linearP);\r\n \r\n // Throttled update\r\n const shouldUpdate = now - lastUpdate >= 16 || linearP >= 1;\r\n \r\n if (shouldUpdate) {\r\n lastUpdate = now;\r\n progress.value = p;\r\n }\r\n\r\n if (linearP < 1) {\r\n animId = requestAnimationFrame(animate);\r\n } else {\r\n const final = columns.value\r\n .map(c => applyProgress(c, 1).col)\r\n .filter(c => c.currentWidth > 0);\r\n columns.value = final;\r\n progress.value = 1;\r\n animId = undefined;\r\n }\r\n };\r\n animId = requestAnimationFrame(animate);\r\n\r\n}, { immediate: true }); \r\n\r\nonUnmounted(() => {\r\n if (animId) cancelAnimationFrame(animId);\r\n});\r\n\r\n// Derived View Data\r\nconst charHeight = 1.2;\r\n\r\nconst renderedColumns = computed(() => {\r\n return columns.value.map(col => {\r\n const { charIdx, delta } = applyProgress(col, progress.value);\r\n const width = col.sourceWidth + (col.targetWidth - col.sourceWidth) * progress.value;\r\n if (width <= 0) return { width, chars: [] };\r\n\r\n const chars: Array<{ key: string, char: string, offset: number }> = [];\r\n const list = col.charList || [];\r\n const deltaEm = delta * charHeight;\r\n\r\n // Helper to add char\r\n const add = (idx: number, offsetMod: number, keyPrefix: string) => {\r\n if (idx >= 0 && idx < list.length) {\r\n chars.push({\r\n key: `${keyPrefix}-${idx}`,\r\n char: list[idx],\r\n offset: deltaEm + offsetMod\r\n });\r\n }\r\n };\r\n\r\n add(charIdx, 0, 'c');\r\n add(charIdx + 1, -charHeight, 'n');\r\n add(charIdx - 1, charHeight, 'p');\r\n\r\n // Check for full-width char\r\n const isFW = (c: string) => c && c.length > 0 && c.charCodeAt(0) > 255;\r\n const getW = (c: string) => isFW(c) ? 1.25 : 0.8;\r\n\r\n const startChar = list[col.startIndex] || '';\r\n const endChar = list[col.endIndex] || ''; // endIndex not targetIndex\r\n const w1 = getW(startChar);\r\n const w2 = getW(endChar);\r\n const baseW = w1 + (w2 - w1) * progress.value;\r\n\r\n return { width, chars, baseW };\r\n }).filter(c => c.width > 0);\r\n});\r\n\r\n</script>\r\n\r\n<style scoped>\r\n.ticker {\r\n display: inline-flex;\r\n overflow: hidden;\r\n font-family: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\r\n font-weight: 600;\r\n line-height: 1.2;\r\n}\r\n\r\n.ticker-column {\r\n display: inline-block;\r\n height: 1.2em;\r\n overflow: hidden;\r\n position: relative;\r\n clip-path: inset(0);\r\n}\r\n\r\n.ticker-char {\r\n position: absolute;\r\n top: 0;\r\n left: 0;\r\n right: 0;\r\n height: 1.2em;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n white-space: pre;\r\n}\r\n</style>\r\n"],"names":["_createElementBlock","_normalizeClass","_openBlock","_Fragment","_renderList","_normalizeStyle","_toDisplayString","_unref"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TickerCore-DDdYkrsq.js","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const TickerUtils = {\r\n provideNumberList: () => '0123456789',\r\n provideAlphabeticalList: () => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\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.split('');\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 const easingFunctions: Record<string, (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};\r\n"],"names":["r","c"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TickerCore-DSrG8V7Z.cjs","sources":["../src/core/TickerCore.ts"],"sourcesContent":["// ============================================================================\r\n// Constants\r\n// ============================================================================\r\nexport const EMPTY_CHAR = '\\0';\r\n\r\nexport const TickerUtils = {\r\n provideNumberList: () => '0123456789',\r\n provideAlphabeticalList: () => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',\r\n};\r\n\r\nexport type ScrollingDirection = 'ANY' | 'UP' | 'DOWN';\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.split('');\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 const easingFunctions: Record<string, (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};\r\n"],"names":["r","c"],"mappings":"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"}
|