WebSocket实现实时投票统计动画的核心是数据实时推送与前端平滑渲染:服务端主动推送票数,前端用requestAnimationFrame实现数字滚动、柱状图高度及百分比的缓动动画,并通过防抖避免高频更新导致的动画冲突。

WebSocket 实现实时投票统计动画,核心是“数据实时推送 + 前端平滑渲染”。服务端通过 WebSocket 主动把最新票数推给所有客户端,前端收到后不直接跳变数字,而是用缓动动画(如 `requestAnimationFrame`)让数字从旧值滚动到新值,视觉更自然。
1. 建立 WebSocket 连接并监听投票数据
页面加载后连接服务端 WebSocket,并约定好消息格式(例如 `{type: 'vote-update', data: {optionA: 12, optionB: 8}}`):
const socket = new WebSocket('ws://localhost:8080');
<p>socket.onopen = () => console.log('已连接到投票服务器');
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'vote-update') {
updateVoteBars(msg.data); // 触发动画更新
}
};
2. 用 requestAnimationFrame 实现数字滚动动画
避免 `setTimeout` 或 `setInterval` 的不精确性,用 `requestAnimationFrame` 做逐帧过渡。关键点:记录起始值、目标值、当前帧进度,线性插值计算中间值:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
function animateNumber(el, start, end, duration = 600) {
const startTime = performance.now();
const ease = t => t; // 线性缓动;可换为 t => t * t(加速)等
<p>function step(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const eased = ease(progress);
const value = Math.floor(start + (end - start) * eased);</p><pre class="brush:php;toolbar:false;">el.textContent = value;
if (progress < 1) {
requestAnimationFrame(step);
}} requestAnimationFrame(step); }
3. 更新柱状图高度与百分比(带过渡)
除了数字滚动,柱状图高度和百分比也应平滑变化。CSS 配合 JS 控制:
// HTML 中每个选项有对应元素
// <div class="bar" data-option="A"><span class="count">0</span><span class="percent">0%</span></div>
<p>function updateVoteBars(data) {
const total = Object.values(data).reduce((a, b) => a + b, 0);</p><p>Object.entries(data).forEach(([key, count]) => {
const bar = document.querySelector(<code>[data-option="${key}"]</code>);
const countEl = bar.querySelector('.count');
const percentEl = bar.querySelector('.percent');
const heightEl = bar.querySelector('.fill');</p><pre class="brush:php;toolbar:false;">const oldCount = parseInt(countEl.textContent) || 0;
animateNumber(countEl, oldCount, count);
const percent = total ? Math.round((count / total) * 100) : 0;
animateNumber(percentEl, parseInt(percentEl.textContent) || 0, percent);
// 同时更新高度(CSS transition 已设 height 0.3s ease)
if (heightEl) {
heightEl.style.height = `${percent}%`;
}}); }
注意:`.fill` 元素需设置 `transition: height 0.3s ease`,且父容器 `overflow: hidden`,确保高度变化可视。
4. 防抖与状态同步(避免动画打架)
若短时间内收到多条更新(如高并发投票),连续触发动画会导致数值错乱或卡顿。加一层简单防抖:
- 用 `clearTimeout(pendingTimer)` 取消未完成的更新
- 只保留最后一次收到的数据,延迟 30ms 再执行动画(给服务端聚合留余地)
- 或在服务端做简单合并(如每 100ms 推一次全量快照)
这样既保证实时性,又不让前端被高频消息拖垮。

















