@zeewain/3d-avatar-sdk 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +6 -5
  2. package/dist/assets/Build/webgl.data.unityweb +0 -0
  3. package/dist/assets/Build/webgl.framework.js.unityweb +0 -0
  4. package/dist/assets/Build/webgl.wasm.unityweb +0 -0
  5. package/dist/examples/test-umd/index.html +762 -0
  6. package/dist/examples/test-vue2/.eslintignore +45 -0
  7. package/dist/examples/test-vue2/.eslintrc.js +174 -0
  8. package/dist/examples/test-vue2/.stylelintignore +50 -0
  9. package/dist/examples/test-vue2/.stylelintrc.js +79 -0
  10. package/dist/examples/test-vue2/README.md +139 -0
  11. package/dist/examples/test-vue2/babel.config.js +14 -0
  12. package/dist/examples/test-vue2/package.json +53 -0
  13. package/dist/examples/test-vue2/pnpm-lock.yaml +8776 -0
  14. package/dist/examples/test-vue2/public/index.html +19 -0
  15. package/dist/examples/test-vue2/setup.js +170 -0
  16. package/dist/examples/test-vue2/src/App.vue +943 -0
  17. package/dist/examples/test-vue2/src/components/BroadcastAPI.vue +666 -0
  18. package/dist/examples/test-vue2/src/components/CameraAPI.vue +414 -0
  19. package/dist/examples/test-vue2/src/components/GlobalConfig.vue +200 -0
  20. package/dist/examples/test-vue2/src/components/InfoCards.vue +294 -0
  21. package/dist/examples/test-vue2/src/components/InitAPI.vue +334 -0
  22. package/dist/examples/test-vue2/src/components/LogPanel.vue +249 -0
  23. package/dist/examples/test-vue2/src/components/MotionControlAPI.vue +400 -0
  24. package/dist/examples/test-vue2/src/components/UnityPreview.vue +201 -0
  25. package/dist/examples/test-vue2/src/main.js +16 -0
  26. package/dist/examples/test-vue2/vue.config.js +41 -0
  27. package/dist/examples/test-vue3/.eslintrc +3 -0
  28. package/dist/examples/test-vue3/.stylelintignore +3 -0
  29. package/dist/examples/test-vue3/.stylelintrc +48 -0
  30. package/dist/examples/test-vue3/README.md +236 -0
  31. package/dist/examples/test-vue3/env.d.ts +8 -0
  32. package/dist/examples/test-vue3/index.html +95 -0
  33. package/dist/examples/test-vue3/package.json +55 -0
  34. package/dist/examples/test-vue3/pnpm-lock.yaml +4636 -0
  35. package/dist/examples/test-vue3/setup.js +167 -0
  36. package/dist/examples/test-vue3/src/App.vue +962 -0
  37. package/dist/examples/test-vue3/src/components/BroadcastAPI.vue +636 -0
  38. package/dist/examples/test-vue3/src/components/CameraAPI.vue +376 -0
  39. package/dist/examples/test-vue3/src/components/GlobalConfig.vue +213 -0
  40. package/dist/examples/test-vue3/src/components/InfoCards.vue +288 -0
  41. package/dist/examples/test-vue3/src/components/InitAPI.vue +339 -0
  42. package/dist/examples/test-vue3/src/components/LogPanel.vue +236 -0
  43. package/dist/examples/test-vue3/src/components/MotionControlAPI.vue +373 -0
  44. package/dist/examples/test-vue3/src/components/UnityPreview.vue +189 -0
  45. package/dist/examples/test-vue3/src/main.ts +12 -0
  46. package/dist/examples/test-vue3/src/types.ts +9 -0
  47. package/dist/examples/test-vue3/tsconfig.json +44 -0
  48. package/dist/examples/test-vue3/tsconfig.node.json +14 -0
  49. package/dist/examples/test-vue3/vite.config.ts +75 -0
  50. package/dist/index.d.ts +142 -132
  51. package/dist/index.es5.js +93 -41
  52. package/dist/index.es5.umd.js +93 -41
  53. package/dist/index.esm.js +101 -42
  54. package/dist/index.umd.cjs +101 -42
  55. package/package.json +4 -3
@@ -0,0 +1,249 @@
1
+ <template>
2
+ <div class="log-panel">
3
+ <div class="log-container">
4
+ <div class="log-list" ref="logContainer">
5
+ <el-timeline v-if="logs.length > 0">
6
+ <el-timeline-item
7
+ v-for="(log, index) in logs"
8
+ :key="index"
9
+ :timestamp="log.time"
10
+ :type="getLogType(log.type)"
11
+ :icon="getLogIcon(log.type)"
12
+ placement="top"
13
+ size="small"
14
+ >
15
+ <div class="log-item">
16
+ <el-tag
17
+ :type="getTagType(log.type)"
18
+ size="mini"
19
+ class="log-type-tag"
20
+ >
21
+ {{ getTypeLabel(log.type) }}
22
+ </el-tag>
23
+ <span class="log-message">{{ log.message }}</span>
24
+ </div>
25
+ </el-timeline-item>
26
+ </el-timeline>
27
+
28
+ <div v-else class="empty-logs">
29
+ <el-empty description="暂无日志" image-size="80">
30
+ <template #description>
31
+ <span>暂无操作日志</span>
32
+ </template>
33
+ </el-empty>
34
+ </div>
35
+ </div>
36
+
37
+ <div class="log-actions">
38
+ <el-button
39
+ type="primary"
40
+ size="mini"
41
+ icon="el-icon-bottom"
42
+ @click="scrollToBottom"
43
+ >
44
+ 滚动到底部
45
+ </el-button>
46
+ <el-button
47
+ type="danger"
48
+ size="mini"
49
+ icon="el-icon-delete"
50
+ @click="clearLogs"
51
+ >
52
+ 清空日志
53
+ </el-button>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </template>
58
+
59
+ <script>
60
+ export default {
61
+ name: 'LogPanel',
62
+ props: {
63
+ logs: {
64
+ type: Array,
65
+ default: () => []
66
+ }
67
+ },
68
+ watch: {
69
+ logs: {
70
+ handler () {
71
+ this.$nextTick(() => {
72
+ this.scrollToBottom()
73
+ })
74
+ },
75
+ deep: true
76
+ }
77
+ },
78
+ methods: {
79
+ scrollToBottom () {
80
+ const logContainer = this.$refs.logContainer
81
+ if (logContainer) {
82
+ logContainer.scrollTop = logContainer.scrollHeight
83
+ }
84
+ },
85
+
86
+ clearLogs () {
87
+ this.$emit('clear-logs')
88
+ },
89
+
90
+ getLogType (type) {
91
+ const typeMap = {
92
+ info: 'primary',
93
+ success: 'success',
94
+ warn: 'warning',
95
+ error: 'danger'
96
+ }
97
+ return typeMap[type] || 'primary'
98
+ },
99
+
100
+ getLogIcon (type) {
101
+ const iconMap = {
102
+ info: 'el-icon-info',
103
+ success: 'el-icon-success',
104
+ warn: 'el-icon-warning',
105
+ error: 'el-icon-error'
106
+ }
107
+ return iconMap[type] || 'el-icon-info'
108
+ },
109
+
110
+ getTagType (type) {
111
+ const tagMap = {
112
+ info: 'info',
113
+ success: 'success',
114
+ warn: 'warning',
115
+ error: 'danger'
116
+ }
117
+ return tagMap[type] || 'info'
118
+ },
119
+
120
+ getTypeLabel (type) {
121
+ const labelMap = {
122
+ info: '信息',
123
+ success: '成功',
124
+ warn: '警告',
125
+ error: '错误'
126
+ }
127
+ return labelMap[type] || '信息'
128
+ }
129
+ }
130
+ }
131
+ </script>
132
+
133
+ <style lang="scss" scoped>
134
+ .log-panel {
135
+ height: 100%;
136
+ max-height: 650px;
137
+ display: flex;
138
+ flex-direction: column;
139
+
140
+ .log-container {
141
+ height: 100%;
142
+ display: flex;
143
+ flex-direction: column;
144
+ overflow: auto;
145
+
146
+ .log-list {
147
+ flex: 1;
148
+ overflow-y: auto;
149
+ min-height: 300px;
150
+ padding: 10px;
151
+ background-color: #fafafa;
152
+ border-radius: 4px;
153
+ border: 1px solid #e4e7ed;
154
+
155
+ .el-timeline {
156
+ .el-timeline-item {
157
+ .el-timeline-item__wrapper {
158
+ .el-timeline-item__timestamp {
159
+ font-size: 11px;
160
+ color: #909399;
161
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
162
+ }
163
+
164
+ .el-timeline-item__content {
165
+ .log-item {
166
+ display: flex;
167
+ align-items: center;
168
+ gap: 8px;
169
+ margin-bottom: 5px;
170
+
171
+ .log-type-tag {
172
+ flex-shrink: 0;
173
+ }
174
+
175
+ .log-message {
176
+ font-size: 13px;
177
+ line-height: 1.4;
178
+ color: #2c3e50;
179
+ word-break: break-word;
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ .empty-logs {
188
+ height: 200px;
189
+ display: flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ }
193
+ }
194
+
195
+ .log-actions {
196
+ margin-top: 10px;
197
+ display: flex;
198
+ gap: 8px;
199
+ justify-content: center;
200
+
201
+ .el-button {
202
+ flex: 1;
203
+ max-width: 120px;
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ // 移动端优化
210
+ @media screen and (max-width: 768px) {
211
+ .log-panel {
212
+ .log-container {
213
+ .log-list {
214
+ min-height: 250px;
215
+ padding: 8px;
216
+
217
+ .el-timeline {
218
+ .el-timeline-item {
219
+ .el-timeline-item__wrapper {
220
+ .el-timeline-item__timestamp {
221
+ font-size: 10px;
222
+ }
223
+
224
+ .el-timeline-item__content {
225
+ .log-item {
226
+ flex-direction: column;
227
+ align-items: flex-start;
228
+ gap: 4px;
229
+
230
+ .log-message {
231
+ font-size: 12px;
232
+ }
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ .log-actions {
241
+ .el-button {
242
+ font-size: 12px;
243
+ padding: 5px 8px;
244
+ }
245
+ }
246
+ }
247
+ }
248
+ }
249
+ </style>
@@ -0,0 +1,400 @@
1
+ <template>
2
+ <div class="motion-control-api">
3
+ <!-- API说明 -->
4
+ <div class="api-description">
5
+ <el-alert
6
+ title="动作管理API"
7
+ type="info"
8
+ :closable="false"
9
+ show-icon
10
+ >
11
+ <template slot="default">
12
+ <div class="api-signatures">
13
+ <div class="api-signature">
14
+ <code>playMotion(clipCode: string): Promise&lt;IAvatarCallbackResponse&gt;</code>
15
+ </div>
16
+ <div class="api-signature">
17
+ <code>getCurrentMotion(getRemainingTime?: boolean): Promise&lt;any&gt;</code>
18
+ </div>
19
+ </div>
20
+ <p>完整的数字人动作管理功能,支持动作播放和状态查询</p>
21
+ <p class="api-features">
22
+ ✅ 动作播放控制 &nbsp;&nbsp;
23
+ ✅ 当前动作查询 &nbsp;&nbsp;
24
+ ✅ 剩余时间获取 &nbsp;&nbsp;
25
+ ✅ 统一状态管理
26
+ </p>
27
+ </template>
28
+ </el-alert>
29
+ </div>
30
+
31
+ <!-- 动作播放控制 -->
32
+ <div class="api-controls">
33
+ <el-card shadow="hover" class="control-card">
34
+ <div slot="header" class="card-header">
35
+ <span>🎭 动作播放</span>
36
+ <el-tag v-if="sdkStatus.canPlayMotion" type="success" size="mini">可用</el-tag>
37
+ <el-tag v-else type="info" size="mini">不可用</el-tag>
38
+ </div>
39
+
40
+ <div class="control-content">
41
+ <el-form :model="motionData" label-width="80px" size="small">
42
+ <el-form-item label="动作编码" required>
43
+ <el-input
44
+ v-model="motionData.clipCode"
45
+ placeholder="请输入动作编码"
46
+ prefix-icon="el-icon-magic-stick"
47
+ :disabled="!sdkStatus.canPlayMotion"
48
+ @keydown.enter.native="handlePlayMotion"
49
+ />
50
+ <div class="input-help">
51
+ <span class="help-text">输入动作编码</span>
52
+ </div>
53
+ </el-form-item>
54
+
55
+ <el-form-item class="action-buttons">
56
+ <el-button
57
+ type="primary"
58
+ size="medium"
59
+ icon="el-icon-video-play"
60
+ :loading="playLoading"
61
+ :disabled="!sdkStatus.canPlayMotion || !motionData.clipCode || !globalConfig.avatarCode"
62
+ @click="handlePlayMotion"
63
+ >
64
+ {{ playLoading ? '播放中...' : '播放动作' }}
65
+ </el-button>
66
+ </el-form-item>
67
+ </el-form>
68
+
69
+ <div class="status-tips">
70
+ <el-alert
71
+ v-if="!sdkStatus.canPlayMotion"
72
+ title="请先初始化Avatar后再播放动作"
73
+ type="warning"
74
+ :closable="false"
75
+ show-icon
76
+ />
77
+ <el-alert
78
+ v-else-if="!globalConfig.avatarCode"
79
+ title="请先在全局配置中设置Avatar Code"
80
+ type="warning"
81
+ :closable="false"
82
+ show-icon
83
+ />
84
+ </div>
85
+ </div>
86
+ </el-card>
87
+ </div>
88
+
89
+ <!-- 动作查询 -->
90
+ <div class="api-controls" style="margin-top: 20px;">
91
+ <el-card shadow="hover" class="control-card">
92
+ <div slot="header" class="card-header">
93
+ <span>🔍 动作查询</span>
94
+ <el-tag v-if="sdkStatus.canGetMotion" type="success" size="mini">可用</el-tag>
95
+ <el-tag v-else type="info" size="mini">不可用</el-tag>
96
+ </div>
97
+
98
+ <div class="control-content">
99
+ <el-form size="small">
100
+ <el-form-item>
101
+ <el-checkbox
102
+ v-model="queryData.getRemainingTime"
103
+ :disabled="!sdkStatus.canGetMotion"
104
+ >
105
+ 获取剩余播放时间
106
+ </el-checkbox>
107
+ </el-form-item>
108
+
109
+ <el-form-item class="action-buttons">
110
+ <el-button
111
+ type="primary"
112
+ size="medium"
113
+ icon="el-icon-search"
114
+ :loading="queryLoading"
115
+ :disabled="!sdkStatus.canGetMotion"
116
+ @click="handleGetCurrentMotion"
117
+ >
118
+ {{ queryLoading ? '查询中...' : '获取当前动作' }}
119
+ </el-button>
120
+ </el-form-item>
121
+ </el-form>
122
+
123
+ <div class="status-tips">
124
+ <el-alert
125
+ v-if="!sdkStatus.canGetMotion"
126
+ title="请先初始化Avatar后再查询动作"
127
+ type="warning"
128
+ :closable="false"
129
+ show-icon
130
+ />
131
+ </div>
132
+ </div>
133
+ </el-card>
134
+ </div>
135
+
136
+ <!-- 执行结果 -->
137
+ <div class="api-result" style="margin-top: 20px;">
138
+ <el-card shadow="hover" class="result-card">
139
+ <div slot="header" class="card-header">
140
+ <span>执行结果</span>
141
+ <el-button
142
+ type="text"
143
+ size="mini"
144
+ icon="el-icon-refresh"
145
+ @click="clearResult"
146
+ >
147
+ 清空
148
+ </el-button>
149
+ </div>
150
+
151
+ <div class="result-content">
152
+ <pre class="result-text">{{ result }}</pre>
153
+ </div>
154
+ </el-card>
155
+ </div>
156
+ </div>
157
+ </template>
158
+
159
+ <script>
160
+ export default {
161
+ name: 'MotionControlAPI',
162
+ props: {
163
+ sdkStatus: {
164
+ type: Object,
165
+ required: true
166
+ },
167
+ globalConfig: {
168
+ type: Object,
169
+ required: true
170
+ }
171
+ },
172
+ data () {
173
+ return {
174
+ motionData: {
175
+ clipCode: 'DH_ACTION_MODEL103_000023'
176
+ },
177
+ queryData: {
178
+ getRemainingTime: true
179
+ },
180
+ result: '等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n',
181
+ playLoading: false,
182
+ queryLoading: false
183
+ }
184
+ },
185
+ methods: {
186
+ // 播放动作
187
+ async handlePlayMotion () {
188
+ const { avatarCode } = this.globalConfig
189
+ const { clipCode } = this.motionData
190
+
191
+ if (!avatarCode) {
192
+ this.result = '❌ 请先在全局配置中设置Avatar Code'
193
+ this.$message.warning('请先设置Avatar Code')
194
+ return
195
+ }
196
+
197
+ if (!clipCode) {
198
+ this.result = '❌ 请输入动作编码'
199
+ this.$message.warning('请输入动作编码')
200
+ return
201
+ }
202
+
203
+ this.playLoading = true
204
+ this.result = `🔄 正在播放动作...\n🎭 动作编码: ${clipCode}\n🤖 数字人: ${avatarCode}\n\n📡 发送动作播放指令...`
205
+
206
+ try {
207
+ this.$emit('play-motion', {
208
+ clipCode
209
+ })
210
+
211
+ // 模拟加载时间
212
+ setTimeout(() => {
213
+ this.playLoading = false
214
+ this.result = '✅ 动作播放请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 操作说明:\n- 动作播放成功会返回动作ID\n- 不同动作有不同的播放时长\n- 播放过程中可以播放新动作覆盖\n- 可以使用查询功能获取当前动作状态'
215
+ }, 800)
216
+ } catch (error) {
217
+ this.playLoading = false
218
+ this.result = `❌ 动作播放失败: ${error.message}\n\n🔍 可能的原因:\n- 动作编码不存在\n- Avatar未正确初始化\n- Unity通信异常\n- 动作文件损坏`
219
+ this.$message.error(`动作播放失败: ${error.message}`)
220
+ }
221
+ },
222
+
223
+ // 获取当前动作
224
+ async handleGetCurrentMotion () {
225
+ if (!this.sdkStatus.canGetMotion) {
226
+ this.$message.warning('请先初始化Avatar')
227
+ return
228
+ }
229
+
230
+ this.queryLoading = true
231
+ this.result = `🔄 正在获取当前动作信息...\n📊 查询参数: ${this.queryData.getRemainingTime ? '包含剩余时间' : '仅获取动作编码'}\n\n📡 发送查询请求...`
232
+
233
+ try {
234
+ this.$emit('get-current-motion', this.queryData)
235
+
236
+ // 模拟加载时间
237
+ setTimeout(() => {
238
+ this.queryLoading = false
239
+ this.result = '✅ 动作查询请求已发送\n📋 请查看右侧日志面板获取详细信息\n\n📝 查询结果说明:\n- 如果没有播放动作,返回值为空\n- 动作ID格式通常为 MOTION_XXX\n- 剩余时间以毫秒为单位\n- 查询不会影响当前播放状态'
240
+ }, 500)
241
+ } catch (error) {
242
+ this.queryLoading = false
243
+ this.result = `❌ 动作查询失败: ${error.message}\n\n🔍 可能的原因:\n- Avatar未正确初始化\n- Unity通信异常\n- 内部状态错误`
244
+ this.$message.error(`动作查询失败: ${error.message}`)
245
+ }
246
+ },
247
+
248
+ // 清空结果
249
+ clearResult () {
250
+ this.result = '等待动作操作...\n\n📋 功能说明:\n- 播放动作:输入动作编码来播放指定动作\n- 查询动作:获取当前正在播放的动作信息\n- 剩余时间:可选择查询动作剩余播放时间\n\n🎭 '
251
+ }
252
+ }
253
+ }
254
+ </script>
255
+
256
+ <style lang="scss" scoped>
257
+ .motion-control-api {
258
+ .api-description {
259
+ margin-bottom: 20px;
260
+
261
+ .api-signatures {
262
+ .api-signature {
263
+ background-color: #f5f5f5;
264
+ padding: 8px 12px;
265
+ border-radius: 4px;
266
+ margin-bottom: 8px;
267
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
268
+ font-size: 12px;
269
+ border-left: 4px solid #f56c6c;
270
+
271
+ &:last-child {
272
+ margin-bottom: 10px;
273
+ }
274
+
275
+ code {
276
+ color: #2c3e50;
277
+ font-weight: 600;
278
+ }
279
+ }
280
+ }
281
+
282
+ p {
283
+ margin: 5px 0;
284
+ color: #606266;
285
+ font-size: 14px;
286
+
287
+ &.api-features {
288
+ color: #f56c6c;
289
+ font-weight: 500;
290
+ margin-top: 8px;
291
+ }
292
+ }
293
+ }
294
+
295
+ .api-controls {
296
+ .control-card {
297
+ .card-header {
298
+ display: flex;
299
+ justify-content: space-between;
300
+ align-items: center;
301
+ font-weight: 600;
302
+ }
303
+
304
+ .control-content {
305
+ .el-form-item {
306
+ margin-bottom: 18px;
307
+
308
+ :deep(.el-form-item__label) {
309
+ font-weight: 500;
310
+ color: #303133;
311
+ }
312
+
313
+ &.action-buttons {
314
+ margin-bottom: 0;
315
+ margin-top: 25px;
316
+
317
+ .el-button {
318
+ width: 100%;
319
+ padding: 10px 0;
320
+ font-weight: 500;
321
+ }
322
+ }
323
+
324
+ .input-help {
325
+ margin-top: 5px;
326
+
327
+ .help-text {
328
+ font-size: 12px;
329
+ color: #909399;
330
+ line-height: 1.4;
331
+ }
332
+ }
333
+ }
334
+
335
+ .status-tips {
336
+ margin-top: 15px;
337
+
338
+ .el-alert {
339
+ margin-bottom: 10px;
340
+
341
+ &:last-child {
342
+ margin-bottom: 0;
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
348
+ }
349
+
350
+ .api-result {
351
+ .result-card {
352
+ .card-header {
353
+ display: flex;
354
+ justify-content: space-between;
355
+ align-items: center;
356
+ font-weight: 600;
357
+ }
358
+
359
+ .result-content {
360
+ max-height: 300px;
361
+ overflow-y: auto;
362
+
363
+ .result-text {
364
+ margin: 0;
365
+ padding: 15px;
366
+ background-color: #f8f9fa;
367
+ border-radius: 4px;
368
+ border: 1px solid #e9ecef;
369
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
370
+ font-size: 13px;
371
+ line-height: 1.6;
372
+ color: #2c3e50;
373
+ white-space: pre-wrap;
374
+ word-wrap: break-word;
375
+ }
376
+ }
377
+ }
378
+ }
379
+ }
380
+
381
+ // 移动端优化
382
+ @media screen and (width <= 768px) {
383
+ .motion-control-api {
384
+ .api-controls .control-card .control-content {
385
+ .el-form-item {
386
+ .input-help {
387
+ .help-text {
388
+ font-size: 11px;
389
+ }
390
+ }
391
+
392
+ &.action-buttons .el-button {
393
+ padding: 8px 0;
394
+ font-size: 13px;
395
+ }
396
+ }
397
+ }
398
+ }
399
+ }
400
+ </style>