背景
在HikCentral Professional 行业(智能巡检、食堂消费、月台管理)V1.1项目和HikCentral Professional ADDON V2.0项目中,月台的排队叫号业务以及教育的接送大屏业务都涉及到大屏页面,大屏都只做数据展示,无法进行触点击悬浮等交互,页面数据超长时,无法按寻常方式进行省略展示。以下均以教育接送大屏业务作为示例。
方案
页面文本一旦涉及超长,均采取横向轮播滚动的形式进行处理。
效果图

初始实现
- 滚动元素A设置
绝对定位
,容器元素B设置相对定位 - 获取元素A和元素B的长度,分别为widthA和widthB
- 若widthA小于等于widthB,元素A则正常展示不做滚动,步骤结束
- 若widthA大于widthB,使用定时器
setInterval
定时修改元素A的left定位,使A向左移动,此时left为负数 - 若元素A向左的定位位移大于等于widthB,则将元素A的left定位为+widthB,表现为元素A从右边重新出现
- 重复步骤4-5
js
// 横向滚动
getDomWidthAndMove(textClass, isChangeScreen) {
let textDom = document.getElementById(textClass);
if (!textDom) {
return;
}
let textLength = textDom.offsetWidth;
let boxWidth = textDom.parentNode.offsetWidth;
let leftStyle = parseInt(textDom.style.left.split('px')[0], 10);
if (isChangeScreen) {
textDom.style.left = '0px';
return;
}
if (textLength > boxWidth) {
if (!leftStyle && leftStyle !== 0) {
textDom.style.left = '0px';
} else if (Math.abs(leftStyle) >= textLength) {
textDom.style.left = `${boxWidth }px`;
} else {
textDom.style.left = `${leftStyle - 1 }px`;
}
} else {
if (leftStyle && leftStyle !== 0) {
textDom.style.left = '0px';
}
}
// 回收
textDom = null;
}
存在问题
- 大屏性能较低,长时间运行带有横向滚动效果的页面,会导致白屏
- 使用定时器时,动画效果不太流畅,存在卡顿问题
优化分析
- 通常,使用JavaScript的setInterval或递归setTimeout来不断修改元素的transform或left属性是比较常见的方法,但这些方法可能会导致重排(reflow)和重绘(repaint),尤其是在频繁操作DOM时。
- 如何优化JavaScript的实现呢?首先,减少重排和重绘是关键。使用transform的translateX属性会比修改left或marginLeft更高效,因为transform可以利用GPU加速,不会触发布局重排。另外,使用requestAnimationFrame代替setInterval或setTimeout可以确保动画与浏览器的刷新率同步,减少不必要的帧绘制,提高流畅度。
- 还需要考虑将滚动元素提升为合成层,这可以通过CSS的will-change属性或transform3d来实现,这样浏览器会将该元素单独处理,减少重新绘制的区域。此外,避免在滚动过程中进行复杂的DOM操作或样式计算,这些都会增加性能开销。
- 总结可能的优化点:使用requestAnimationFrame、transform进行位移、提升为合成层、避免强制同步布局、减少DOM操作等。
优化策略
-
使用CSS transform代替传统定位属性:transform的translateX操作会触发GPU加速,且不会引起布局重排
-
使用requestAnimationFrame替代setInterval:与浏览器刷新率同步,避免过度绘制
-
优化DOM结构:确保滚动元素形成独立的合成层,减少重绘区域
-
使用will-change属性预提示浏览器优化
-
避免强制同步布局:不要在动画循环中读取布局属性
优化实现
- 滚动元素A设置
transform动画
,容器元素B设置超长隐藏 - 获取元素A和元素B的长度,分别为widthA和widthB
- 若widthA小于等于widthB,元素A则正常展示不做滚动,步骤结束
- 若widthA大于widthB,使用请求帧
requestAnimationFrame
定时修改元素A的translateX,使A向左移动,此时translateX为负数 - 若元素A向左的定位位移大于等于widthB,则将元素A的translateX为widthB,表现为元素A从右边重新出现
- 重复步骤4-5
js
// 横向滚动
getDomWidthAndMove(textClass, isChangeScreen) {
let textDom = document.getElementById(textClass);
if (!textDom) {
return;
}
let textLength = textDom.offsetWidth;
let boxWidth = textDom.parentNode.offsetWidth;
let translateX = parseInt(textDom.style.transform?.replace(/[^\d-]/g, '') || 0, 10);
if (isChangeScreen) {
textDom.style.transform = 'translateX(0)';
return;
}
if (textLength > boxWidth) {
if (Math.abs(translateX) >= textLength) {
textDom.style.transform = `translateX(${boxWidth}px)`;
} else {
textDom.style.transform = `translateX(${translateX - 1}px)`;
}
} else {
textDom.style.transform = 'translateX(0)';
}
// 回收
textDom = null;
}