功能-情绪雷达图展示
字数
2285 字
阅读时间
11 分钟
版本:v1.0.0 | 更新日期:2025-10-08 关联页面:心语精灵对话界面 | 关联组件:EmotionRadarChart
功能概述
情绪雷达图展示功能将AI分析的情绪数据以直观的雷达图形式可视化呈现,帮助用户更好地理解自己的情绪状态和心理特征,为情绪管理和心理健康提供数据支撑。
设计理念
- 直观可视化:将抽象的心理维度转化为直观的图形展示
- 动态交互:支持动态展示和历史对比,增强用户参与感
- 专业标准:基于心理学理论设计的五维评估体系
- 个性化反馈:根据雷达图特征提供个性化的情绪管理建议
雷达图维度设计
五维心理特征
信任度 (trust)
▲
│
开放度 ───┼─── 抵抗度
(openness) │ (resistance)
│
压力度 (stress)
▼
掌控度 (control)1. 信任度 (trust)
定义:用户对他人的信任程度和社交开放性 高分特征:愿意信任他人,乐于社交,寻求支持 低分特征:防备心理强,难以信任他人,倾向自我保护
2. 开放度 (openness)
定义:用户表达情感和接纳新观念的开放程度 高分特征:情感表达丰富,乐于尝试新事物,接纳度高 低分特征:情感内敛,习惯固守原有认知,变化适应性差
3. 抵抗度 (resistance)
定义:用户对建议、改变或外界影响的抵抗程度 高分特征:坚持己见,不易被说服,独立性强 低分特征:易受影响,顺从性强,缺乏主见
4. 压力度 (stress)
定义:用户当前感受到的压力水平和紧张程度 高分特征:感到压力山大,紧张焦虑,难以放松 低分特征:心态平和,压力感知低,放松状态
5. 掌控度 (control)
定义:用户对生活和情绪的掌控感程度 高分特征:感到生活可控,能管理情绪,积极主动 低分特征:感到失控,被动应对,缺乏自主性
界面设计
雷达图样式
基础样式
css
.radar-chart {
width: 200px;
height: 200px;
position: relative;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
.radar-grid {
stroke: rgba(255, 255, 255, 0.3);
stroke-width: 1;
fill: none;
}
.radar-axis {
stroke: rgba(255, 255, 255, 0.5);
stroke-width: 1;
}
.radar-data {
fill: rgba(74, 144, 226, 0.3);
stroke: #4a90e2;
stroke-width: 2;
}
.radar-point {
fill: #4a90e2;
stroke: white;
stroke-width: 2;
}动画效果
css
@keyframes radarDraw {
0% {
opacity: 0;
transform: scale(0.8);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.radar-data {
animation: radarDraw 0.6s ease-out;
transform-origin: center;
}组件结构
EmotionRadarChart
├── RadarCanvas (雷达图画布)
├── DimensionLabels (维度标签)
├── DataPoints (数据点)
├── TrendOverlay (趋势对比层)
├── Tooltip (悬停提示)
└── ControlPanel (控制面板)
├── HistoryToggle (历史对比开关)
├── AnimationSpeed (动画速度)
└── ExportButton (导出功能)技术实现
Canvas绘制实现
javascript
class EmotionRadarChart {
constructor(canvas, data) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.data = data;
this.dimensions = ['trust', 'openness', 'resistance', 'stress', 'control'];
this.center = { x: canvas.width / 2, y: canvas.height / 2 };
this.radius = Math.min(canvas.width, canvas.height) / 2 - 20;
}
drawGrid() {
const levels = 5;
for (let i = 1; i <= levels; i++) {
const levelRadius = (this.radius / levels) * i;
this.drawPolygon(levelRadius, `rgba(255, 255, 255, ${0.1 * i})`);
}
}
drawAxes() {
const angleStep = (Math.PI * 2) / this.dimensions.length;
this.dimensions.forEach((dimension, index) => {
const angle = angleStep * index - Math.PI / 2;
const x = this.center.x + Math.cos(angle) * this.radius;
const y = this.center.y + Math.sin(angle) * this.radius;
this.ctx.beginPath();
this.ctx.moveTo(this.center.x, this.center.y);
this.ctx.lineTo(x, y);
this.ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)';
this.ctx.stroke();
this.drawLabel(dimension, x, y, angle);
});
}
drawData(data, color = '#4a90e2') {
const angleStep = (Math.PI * 2) / this.dimensions.length;
const points = [];
this.dimensions.forEach((dimension, index) => {
const angle = angleStep * index - Math.PI / 2;
const value = data[dimension] || 0;
const distance = (value / 100) * this.radius;
const x = this.center.x + Math.cos(angle) * distance;
const y = this.center.y + Math.sin(angle) * distance;
points.push({ x, y });
});
this.drawPolygonFromPoints(points, color);
this.drawDataPoints(points);
}
animateTransition(fromData, toData, duration = 600) {
const startTime = Date.now();
const animate = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = this.easeInOutCubic(progress);
const currentData = this.interpolateData(fromData, toData, easeProgress);
this.clear();
this.drawGrid();
this.drawAxes();
this.drawData(currentData);
if (progress < 1) {
requestAnimationFrame(animate);
}
};
animate();
}
}数据处理
javascript
// 数据归一化处理
function normalizeEmotionData(rawData) {
return {
trust: Math.max(0, Math.min(100, rawData.trust * 100)),
openness: Math.max(0, Math.min(100, rawData.openness * 100)),
resistance: Math.max(0, Math.min(100, rawData.resistance * 100)),
stress: Math.max(0, Math.min(100, rawData.stress * 100)),
control: Math.max(0, Math.min(100, rawData.control * 100))
};
}
// 趋势分析
function analyzeEmotionTrend(historicalData) {
if (historicalData.length < 2) return null;
const trend = {};
Object.keys(historicalData[0]).forEach(dimension => {
const values = historicalData.map(data => data[dimension]);
const change = values[values.length - 1] - values[0];
trend[dimension] = {
change: change,
direction: change > 0 ? 'up' : change < 0 ? 'down' : 'stable',
significance: Math.abs(change) > 10 ? 'high' : 'low'
};
});
return trend;
}交互功能
基础交互
悬停提示:
- 鼠标悬停在数据点上显示具体数值
- 显示维度名称和当前值
- 提供维度的简要说明
点击详情:
- 点击数据点展开详细信息
- 显示该维度的历史趋势
- 提供相关建议和资源
缩放功能:
- 支持双指缩放查看细节
- 保持中心点不变
- 自动适配显示区域
高级交互
历史对比:
- 选择历史时间点进行对比
- 使用不同颜色区分不同时间
- 显示变化趋势和差异
动画控制:
- 播放/暂停动画
- 调整动画速度
- 循环播放历史变化
数据导出:
- 导出当前雷达图为图片
- 导出数据为CSV格式
- 分享到社交媒体
响应式设计
尺寸适配
css
/* 移动端适配 */
@media (max-width: 768px) {
.radar-chart {
width: 150px;
height: 150px;
}
.dimension-label {
font-size: 12px;
}
}
/* 桌面端适配 */
@media (min-width: 769px) {
.radar-chart {
width: 250px;
height: 250px;
}
.dimension-label {
font-size: 14px;
}
}触摸优化
javascript
class TouchRadarChart extends EmotionRadarChart {
constructor(canvas, data) {
super(canvas, data);
this.setupTouchEvents();
}
setupTouchEvents() {
let lastTouch = null;
this.canvas.addEventListener('touchstart', (e) => {
lastTouch = e.touches[0];
});
this.canvas.addEventListener('touchmove', (e) => {
if (lastTouch) {
const deltaX = e.touches[0].clientX - lastTouch.clientX;
const deltaY = e.touches[0].clientY - lastTouch.clientY;
// 处理触摸手势
this.handleTouchGesture(deltaX, deltaY);
lastTouch = e.touches[0];
}
});
}
}数据分析功能
情绪模式识别
javascript
function analyzeEmotionPattern(data) {
const patterns = [];
// 高压力模式
if (data.stress > 70 && data.control < 30) {
patterns.push({
type: 'high_stress',
description: '高压力低掌控模式',
suggestion: '建议寻求专业支持,学习压力管理技巧'
});
}
// 社交开放模式
if (data.trust > 70 && data.openness > 70) {
patterns.push({
type: 'social_open',
description: '社交开放模式',
suggestion: '良好的社交状态,继续保持开放心态'
});
}
// 抗拒改变模式
if (data.resistance > 70 && data.openness < 30) {
patterns.push({
type: 'resistance_to_change',
description: '抗拒改变模式',
suggestion: '尝试逐步接纳新观念,保持开放心态'
});
}
return patterns;
}健康状态评估
javascript
function assessEmotionalHealth(data) {
let score = 0;
let factors = [];
// 理想状态:高信任、高开放、中低抵抗、低压力、高掌控
if (data.trust > 60) score += 20;
else factors.push('提升人际信任');
if (data.openness > 60) score += 20;
else factors.push('增强开放心态');
if (data.resistance < 60) score += 20;
else factors.push('降低抗拒心理');
if (data.stress < 40) score += 20;
else factors.push('管理压力水平');
if (data.control > 60) score += 20;
else factors.push('增强生活掌控感');
return {
score: score,
level: score >= 80 ? 'excellent' : score >= 60 ? 'good' : 'needs_improvement',
factors: factors
};
}性能优化
渲染优化
Canvas优化:
- 使用
requestAnimationFrame优化动画 - 避免频繁的重绘操作
- 合并绘制指令减少调用
- 使用
数据缓存:
- 缓存计算结果避免重复计算
- 使用 Web Workers 处理复杂计算
- 本地存储历史数据
内存管理:
- 及时清理不需要的图形对象
- 限制历史数据存储数量
- 定期垃圾回收
加载优化
javascript
// 懒加载雷达图组件
class LazyRadarChart {
constructor(container) {
this.container = container;
this.observer = new IntersectionObserver(this.handleIntersection.bind(this));
this.observer.observe(container);
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.loadChart();
this.observer.unobserve(entry.target);
}
});
}
async loadChart() {
const { EmotionRadarChart } = await import('./components/EmotionRadarChart.js');
this.chart = new EmotionRadarChart(this.container);
}
}测试策略
单元测试
javascript
describe('EmotionRadarChart', () => {
test('should normalize data correctly', () => {
const rawData = { trust: 0.8, openness: 0.6, resistance: 0.3, stress: 0.2, control: 0.9 };
const normalized = normalizeEmotionData(rawData);
expect(normalized.trust).toBe(80);
expect(normalized.openness).toBe(60);
expect(normalized.resistance).toBe(30);
expect(normalized.stress).toBe(20);
expect(normalized.control).toBe(90);
});
test('should identify emotion patterns', () => {
const highStressData = { trust: 50, openness: 40, resistance: 30, stress: 80, control: 20 };
const patterns = analyzeEmotionPattern(highStressData);
expect(patterns).toContainEqual({
type: 'high_stress',
description: '高压力低掌控模式',
suggestion: '建议寻求专业支持,学习压力管理技巧'
});
});
});集成测试
javascript
describe('Radar Chart Integration', () => {
test('should render with real data', async () => {
const mockData = {
primary_emotion: '焦虑',
radar_dimensions: {
trust: 0.3,
openness: 0.4,
resistance: 0.7,
stress: 0.8,
control: 0.2
}
};
const component = mount(EmotionRadarChart, { data: mockData });
await component.update();
expect(component.canvas).toBeDefined();
expect(component.dataPoints).toHaveLength(5);
});
});相关文档
版本记录
| 版本 | 日期 | 变更内容 | 作者 |
|---|---|---|---|
| v1.0.0 | 2025-10-08 | 初始版本,完成雷达图核心功能设计 | 哈雷酱 |