Files
a2f-service/examples/3d/expressionLibrary.js
2025-12-27 09:22:31 +08:00

278 lines
8.1 KiB
JavaScript

// 表情库系统
const ExpressionLibrary = {
expressions: {
// 原有表情
smile: {
name: '微笑',
blendShapes: {
mouthsmileleft: 0.4,
mouthsmileright: 0.4
},
duration: 2000
},
disgust: {
name: '嫌弃',
blendShapes: {
mouthupperupright: 0.43,
browdownleft: 1,
browdownright: 1,
nosesneerleft: 1,
nosesneerright: 1
},
duration: 2500
},
smirk: {
name: '冷笑',
blendShapes: {
eyesquintleft: 0.35,
eyesquintright: 0.35,
mouthsmileleft: 0.3
},
duration: 2000
},
grit: {
name: '咬牙切齿',
blendShapes: {
mouthrolllower: 0.3,
mouthrollupper: 0.44,
mouthshruglower: 0.42,
mouthshrugupper: 0.44,
browdownleft: 1,
browdownright: 1,
nosesneerleft: 1,
nosesneerright: 1
},
duration: 2500
},
// 新增表情
surprise: {
name: '惊讶',
blendShapes: {
eyewidenleft: 0.8,
eyewidenright: 0.8,
browouterupright: 0.7,
browouterupleft: 0.7,
jawopen: 0.4,
mouthfunnel: 0.3
},
duration: 2000
},
sad: {
name: '悲伤',
blendShapes: {
browinnerup: 0.8,
mouthfrownleft: 0.6,
mouthfrownright: 0.6,
mouthlowerdownleft: 0.4,
mouthlowerdownright: 0.4
},
duration: 3000
},
angry: {
name: '生气',
blendShapes: {
browdownleft: 1,
browdownright: 1,
eyesquintleft: 0.5,
eyesquintright: 0.5,
nosesneerleft: 0.7,
nosesneerright: 0.7,
mouthpressleft: 0.6,
mouthpressright: 0.6
},
duration: 2500
},
thinking: {
name: '思考',
blendShapes: {
eyesquintleft: 0.3,
eyesquintright: 0.3,
mouthpucker: 0.4
},
duration: 3000
},
happy: {
name: '开心',
blendShapes: {
mouthsmileleft: 0.8,
mouthsmileright: 0.8,
eyesquintleft: 0.4,
eyesquintright: 0.4,
cheeksquintleft: 0.6,
cheeksquintright: 0.6
},
duration: 2500
},
confused: {
name: '困惑',
blendShapes: {
browouterupleft: 0.6,
browdownright: 0.5,
mouthfrownleft: 0.3,
eyesquintright: 0.3
},
duration: 2500
}
},
// 随机表情播放器
randomPlayer: {
enabled: false,
timeout: null,
durationTimeout: null,
currentExpression: null,
lastExpressionKey: null,
pausedEyeLook: false,
intervalMin: 3000,
intervalMax: 8000,
start: function() {
this.enabled = true;
this.scheduleNext();
},
stop: function() {
this.enabled = false;
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
if (this.durationTimeout) {
clearTimeout(this.durationTimeout);
this.durationTimeout = null;
}
this.reset();
},
scheduleNext: function() {
if (!this.enabled) return;
// 清除之前的定时器(如果存在)
if (this.timeout) {
clearTimeout(this.timeout);
}
const delay = this.intervalMin + Math.random() * (this.intervalMax - this.intervalMin);
this.timeout = setTimeout(() => {
this.playRandom();
}, delay);
},
playRandom: function() {
if (!this.enabled) return;
// 获取启用的表情列表
const enabledKeys = window.enabledExpressions && window.enabledExpressions.size > 0
? Array.from(window.enabledExpressions)
: Object.keys(ExpressionLibrary.expressions);
if (enabledKeys.length === 0) {
this.scheduleNext();
return;
}
const availableKeys = enabledKeys.length > 1 && this.lastExpressionKey
? enabledKeys.filter(key => key !== this.lastExpressionKey)
: enabledKeys;
const randomKey = availableKeys[Math.floor(Math.random() * availableKeys.length)];
const expression = ExpressionLibrary.expressions[randomKey];
this.play(expression);
},
play: function(expression) {
this.currentExpression = expression;
this.lastExpressionKey = Object.keys(ExpressionLibrary.expressions).find(
key => ExpressionLibrary.expressions[key] === expression
);
// 暂停眼球移动(避免与表情冲突)
if (window.animator && window.animator.isEyeLookEnabled) {
window.animator._stopRandomEyeLook();
this.pausedEyeLook = true;
}
// 从主页面获取动画速度参数
const speed = window.expressionParams?.speed || 400;
const strength = window.expressionParams?.strength ?? 1;
// 应用表情
for (const [name, value] of Object.entries(expression.blendShapes)) {
if (window.setIdleAnimation) {
window.setIdleAnimation(name, value * strength, speed, 'easeInOutCubic');
}
}
// 使用用户设置的持续时间,如果没有则使用表情默认时间
const duration = (window.expressionDurations && window.expressionDurations[this.lastExpressionKey])
|| expression.duration;
// 清除之前的持续时间定时器(如果存在)
if (this.durationTimeout) {
clearTimeout(this.durationTimeout);
}
this.durationTimeout = setTimeout(() => {
this.durationTimeout = null;
this.reset();
if (!this.enabled) return;
this.scheduleNext();
}, duration);
},
reset: function() {
if (!this.currentExpression) return;
// 从主页面获取参数
const speed = window.expressionParams?.speed || 400;
// 重置表情
for (const name of Object.keys(this.currentExpression.blendShapes)) {
if (window.setIdleAnimation) {
window.setIdleAnimation(name, 0, speed + 100, 'easeInOutCubic');
}
}
this.currentExpression = null;
// 恢复眼球移动
if (this.pausedEyeLook && window.animator && window.animator.isEyeLookEnabled) {
window.animator._startRandomEyeLook();
this.pausedEyeLook = false;
}
}
},
// 手动播放指定表情
playExpression: function(expressionName) {
const expression = this.expressions[expressionName];
if (!expression) {
console.warn(`表情 "${expressionName}" 不存在`);
return;
}
this.randomPlayer.play(expression);
},
// 获取所有表情名称
getExpressionNames: function() {
return Object.keys(this.expressions).map(key => ({
key: key,
name: this.expressions[key].name
}));
}
};
// 导出到全局
if (typeof window !== 'undefined') {
window.ExpressionLibrary = ExpressionLibrary;
}