share-image
ESC

Stats Overlay 可视化功能的实现原理解析

在博客主题的开发中,为了提升首页的视觉冲击力和互动性,我们在 Hero Splash(首屏动画)中加入了一个 “Stats Overlay”(数据统计浮层)。这个浮层用于实时显示马拉松轨迹动画过程中的关键数据(如时间、距离、配速、心率等)。

本文将深入解析这个功能的实现原理,涵盖 HTML 结构、CSS 样式设计以及 JavaScript 的交互逻辑。

1. 核心功能概述

Stats Overlay 的主要功能是在地图轨迹动画播放时,同步显示当前的运动数据。它具备以下特点:

  • 动态显隐:配合轨迹动画的进度自动浮现。
  • 实时更新:随着动画帧的渲染,实时计算并显示当前位置对应的数据。
  • 交互性:支持折叠/展开,适配移动端展示。

2. HTML 结构设计

该功能嵌入在 layout.ejs 中,作为 #hero-splash 的一部分。结构上主要包含两部分:摘要数据区(Always Visible)和详细图表区(Collapsible)。

<!-- Stats Overlay -->
<div class="stats-overlay" id="stats-overlay">
<div class="stats-main-card">
<!-- 头部:标题与折叠按钮 -->
<div class="stats-header">
<span class="stats-title">2024 桐庐半程马拉松</span>
<button class="stats-toggle-btn" id="stats-toggle">
<i class="fas fa-chevron-up"></i>
</button>
</div>

<div class="stats-content-wrapper" id="stats-content">
<!-- 核心数据区:时间、距离、配速、步频 -->
<div class="stats-summary-section">
<!-- ... 数据项 DOM 结构 ... -->
</div>

<!-- 心率展示区 -->
<div class="chart-block hr-block-card">
<!-- ... 心率可视化 DOM ... -->
</div>

<!-- 详细图表区:折叠部分 -->
<div class="stats-charts-section">
<canvas id="hr-chart"></canvas>
<canvas id="pace-chart"></canvas>
<canvas id="cadence-chart"></canvas>
</div>
</div>
</div>
</div>

3. CSS 样式与过渡动画

样式定义在 style.styl 中。为了实现平滑的显示效果,我们使用了 CSS Transition。

关键点在于 .stats-overlay 的初始状态和 .visible 状态的切换:

/* Stats Overlay 基础样式 */
.stats-overlay
position absolute
top 40px
right 40px
/* 玻璃拟态背景 */
background rgba(10, 10, 10, 0.95)
backdrop-filter blur(20px)
/* 过渡效果 */
transition opacity 0.5s ease
opacity 1 /* 默认状态,配合 JS 控制 */

/* 显隐状态控制类 */
&.visible
opacity 1

注:在实际应用中,通常会将初始 opacity 设置为 0,然后通过 JS 添加 .visible 类将其设置为 1,从而实现淡入效果。

4. JavaScript 逻辑实现

逻辑主要集中在 layout.ejs<script> 标签中,核心在于 animate 函数和 DOM 的操作。

4.1 触发显示逻辑

在马拉松轨迹动画的渲染循环 animate(timestamp) 中,有一段代码专门用于控制 Overlay 的显示:

// 动画循环函数
function animate(timestamp) {
// ... 计算进度和绘制轨迹 ...

// 强制显示 Stats Overlay
const statsOverlay = document.getElementById('stats-overlay');
// 当动画开始且 Overlay 尚未显示时,添加 visible 类
if (statsOverlay && !statsOverlay.classList.contains('visible')) {
statsOverlay.classList.add('visible');
}

// ... 更新数据 ...

if (progress < 1) {
requestAnimationFrame(animate);
}
}

这段逻辑确保了只有当轨迹动画真正开始播放时,数据浮层才会展示给用户,避免了页面加载初期的突兀感。

4.2 数据实时更新

在每一帧动画中,JS 会根据当前的动画进度(ease 变量),计算出当前应该展示的数据点:

// 根据进度计算当前数据索引
const currentIdx = Math.floor(ease * pathArr.length);

if (currentIdx < stats.length) {
const stat = stats[currentIdx];

// 更新 DOM 文本
document.getElementById('stats-time').innerText = formatTime(stat.time);
document.getElementById('stats-dist').innerText = (ease * totalDist).toFixed(2);
// ... 更新其他数据 ...

// 更新心率颜色和区间指示器
const zone = getHeartRateZone(stat.hr);
const elHR = document.getElementById('stats-hr');
elHR.style.color = zone.color;
}

4.3 整体显隐控制 (SessionStorage)

为了不打扰用户的后续访问,我们利用 sessionStorage 记录用户是否已经看过开屏动画:

const heroSplash = document.getElementById('hero-splash');

// 检查是否已访问过
if (sessionStorage.getItem('splashVisited')) {
if (heroSplash) heroSplash.style.display = 'none'; // 直接隐藏整个 Splash
return;
}

当用户滚动页面或触发交互时,会调用 hideSplash() 函数,将 splashVisited 标记存入 SessionStorage,并隐藏包含 Stats Overlay 在内的整个 Splash 层。

5. 总结

Stats Overlay 的实现是一个典型的前端可视化案例,它融合了:

  1. DOM 操作:通过 Class 切换实现样式的动态变更。
  2. CSS3 动画:利用 transition 实现流畅的视觉过渡。
  3. 数据驱动视图:基于时间轴(RequestAnimationFrame)实时更新 UI 数据。
  4. 用户体验优化:通过 SessionStorage 避免重复展示,尊重用户的使用习惯。

这种实现方式既保证了视觉上的酷炫感,又兼顾了性能和用户体验,非常适合用于个人博客的个性化展示。

文章作者:阿文
文章链接: https://www.awen.me/post/5926b06.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 阿文的博客