前端可视化前端大屏适配方案

Posted 努力挣钱的小鑫

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端可视化前端大屏适配方案相关的知识,希望对你有一定的参考价值。


方案一:rem 单位+动态设置 html 的 font-size

动态设置 html 根字体的大小和 body 字体大小(使用 lib_flexible.js)

  • 将设计稿的宽(1920)平均分成 24 等份,每一份 80px;

  • html 根字体大小就设置为 80px,即 1rem = 80px,24rem = 1920px(移动端推荐分为 10 份);

  • 将 body 字体大小设置为 16px;

  • 最后需要使用插件或者其他方式将 px 转为 rem 单位:手动、less/scss 函数、cssrem 插件、webpack 插件、Vite 插件等。

lib_flexible.js 代码参考

(function flexible(window, document) 
  var docEl = document.documentElement;
  var dpr = window.devicePixelRatio || 1;

  // adjust body font size
  function setBodyFontSize() 
    if (document.body) 
      // body 字体大小默认为 16px
      document.body.style.fontSize = 16 * dpr + \'px\';
     else 
      document.addEventListener(\'DOMContentLoaded\', setBodyFontSize);
    
  
  setBodyFontSize();

  // 这里默认平均分成 10 等分(适用移动端)
  // set 1rem = viewWidth / 24 ;(使用pc端)
  function setRemUnit() 
    var rem = docEl.clientWidth / 24; // 1920 / 24 = 80
    docEl.style.fontSize = rem + \'px\'; // 设置 html字体的大小 80px
  

  setRemUnit();

  // reset rem unit on page resize
  window.addEventListener(\'resize\', setRemUnit);
  window.addEventListener(\'pageshow\', function (e) 
    if (e.persisted) 
      setRemUnit();
    
  );

  // detect 0.5px supports
  if (dpr >= 2) 
    var fakeBody = document.createElement(\'body\');
    var testElement = document.createElement(\'div\');
    testElement.style.border = \'.5px solid transparent\';
    fakeBody.appendChild(testElement);
    docEl.appendChild(fakeBody);
    if (testElement.offsetHeight === 1) 
      docEl.classList.add(\'hairlines\');
    
    docEl.removeChild(fakeBody);
  
)(window, document);

方案二:vw 单位

直接使用 vw 单位,屏幕的宽默认为 100vw,那么100vw = 1920px, 1vw = 19.2px 。

安装 cssrem 插件, body 的宽高(1920px * 1080px)直接把 px 单位转 vw 单位

其他 px 转 vw 方式:手动、less/scss 函数、cssrem 插件、webpack 插件、Vite 插件

方案三:scale 等比例缩放(推荐)

CSS 函数 scale() 用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素,同时可以在不同的方向设置不同的缩放值。

使用 CSS3 中的 scale 函数来缩放网页,我们可以使用两种方案来实现:

方案一:直接根据宽度的比率进行缩放。(宽度比率 = 网页当前宽 / 设计稿宽)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body,
      ul 
        margin: 0;
        padding: 0;
      
      body 
        width: 1920px;
        height: 1080px;
        box-sizing: border-box;
        border: 3px solid red;

        /* 指定缩放的原点在左上角 */
        transform-origin: left top;
      

      ul 
        width: 100%;
        height: 100%;
        list-style: none;

        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
      

      li 
        width: 33.333%;
        height: 50%;
        box-sizing: border-box;
        border: 2px solid green;
        font-size: 30px;
      
    </style>
  </head>
  <body>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>

    <script>
      // 设计稿:  1920 * 1080
      // 目标适配:  1920 * 1080   3840 * 2160 ( 2 * 2 ) ;  7680 * 2160( 4 * 2)

      // 1.设计稿的尺寸
      let targetX = 1920;
      // let targetY = 1080
      // let targetRatio = 16 / 9 // 宽高比率

      // 2.拿到当前设备(浏览器)的宽度
      let currentX = document.documentElement.clientWidth || document.body.clientWidth;
      //  1920 * 1080  -> 3840 * 2160

      // 3.计算缩放比例 (当前设备的宽度 / 设计稿的宽度)
      let scaleRatio = currentX / targetX; // 参照宽度进行缩放

      // 4.开始缩放网页 (缩放的原点在左上角)
      document.body.style = `transform: scale($scaleRatio)`;
    </script>
  </body>
</html>

方案二:动态计算网页宽高比,决定是是否按照宽度的比率进行缩放。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body,
      ul 
        margin: 0;
        padding: 0;
      
      body 
        position: relative;
        width: 1920px;
        height: 1080px;
        box-sizing: border-box;
        border: 3px solid red;

        /* 指定缩放的原点在左上角 */
        transform-origin: left top;
      

      ul 
        width: 100%;
        height: 100%;
        list-style: none;

        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
      

      li 
        width: 33.333%;
        height: 50%;
        box-sizing: border-box;
        border: 2px solid green;
        font-size: 30px;
      
    </style>
  </head>
  <body>
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>

    <script>
      // 设计稿:  1920 * 1080
      // 目标适配:  1920 * 1080   3840 * 2160 ( 2 * 2 ) ;  7680 * 2160( 4 * 2)

      // 1.设计稿的尺寸
      let targetX = 1920;
      let targetY = 1080;
      let targetRatio = 16 / 9; // 宽高比率

      // 2.拿到当前设备(浏览器)的宽度
      let currentX = document.documentElement.clientWidth || document.body.clientWidth;
      let currentY = document.documentElement.clientHeight || document.body.clientHeight;
      //  1920 * 1080  -> 3840 * 2160

      // 3.计算缩放比例
      let scaleRatio = currentX / targetX; // 参照宽度进行缩放 ( 默认情况 )
      let currentRatio = currentX / currentY; // 宽高比率

      // 超宽屏
      if (currentRatio > targetRatio) 
        // 如果当前设备的宽高比率大于设计稿的宽高比率,那么就以高度为参照进行缩放,并且居中显示
        scaleRatio = currentY / targetY;
        document.body.style = `width:$targetXpx; height:$targetYpx;transform: scale($scaleRatio) translateX(-50%); left: 50%`;
       else 
        // 4.开始缩放网页
        document.body.style = `width:$targetXpx; height:$targetYpx; transform: scale($scaleRatio)`;
      
    </script>
  </body>
</html>

三种适配放案对比

vw 相比 rem 的优势

优势一:不需要去计算html的font-size大小,不需要给html设置font-size,也不需要设置body的font-size,防止继承;

优势二:因为不依赖font-size的尺寸,所以不用担心某些原因html的font-size尺寸被篡改,页面尺寸混乱;

优势三:vw相比rem更加语义化。1vw是1/100的viewport大小(即将屏幕分成100份);并且具备rem之前所有的优点。

vw 和 rem 存在的问题

如果使用rem活vw单位时,在 js 中添加样式时,单位需要手动设置rem或vw。

第三方库(Element、Echarts等)的字体一般默认都是px单位,因此通常需要层叠第三方库的样式。

当大屏比例更大时,有些字体还需要相应的调整字号。

scale相比vw和rem的优势

优势一:相比于vw和rem,使用起来更简单,不需要对单位进行换算

优势二:因为不需要对单位进行换算,在使用第三方库时,不需要考虑单位转换问题

优势三:由于浏览器的字体默认最小是不能超过12px,导致rem或vw无法设置小于12px的字体,scale缩放没有这个问题。

大屏开发注意事项

1.字体大小设置问题(如上对比,scale不需要考虑这个问题)

2.图片模糊问题

  • 切图时切1倍图、2倍图、大屏用大图,小屏用小图;

  • 建议都使用 svg 矢量图,保证放大和缩小不会失真。

3.Echarts 渲染引擎的选择

推荐使用 SVG 渲染引擎,SVG 图扩展性更好,适合一些定制化操作。

4.动画卡顿优化

  • 创建新的渲染层:减少回流

    • 有明确的定位属性(relative、fixed、sticky、absolute)

    • 透明度(opacity 小于 1)

    • 有 CSS transform 属性(不为 none)

    • 当前有对于 opacity、transform、fliter、backdrop-filter 应用动画

    • backface-visibility 属性为 hidden

  • 启动GPU加速:创建合成层,合成层会开始GPU加速页面渲染,但不能滥用,过多渲染层会以消耗内存为代价

    • 对 opacity、transform、fliter、backdropfilter应用了animation或transition(需要是active的animation或者 transition)

    • 有 3D transform 函数:比如: translate3d、 translateZ、 scale3d 、 rotate3d ...

    • will-change 设置为 opacity、transform、top、left、bottom、right,比如:will-change: opacity , transform;其中 top、left等需要设置明确的定位属性,如 relative 等

  • 少用渐变和高斯模糊,当不需要动画时,及时关闭动画。

前端 vue 在可视化大屏领域的工作实践

导读:最近入职了一家互联网公司,主要是做物联网及互联网解决方案方向,我上来就接手了这个项目,是一个可视化管理地图,主要用于某国企物业的安全预警的职能,说来也比较倒霉,刚来这公司,公司做这个项目的前端和后端都跑路了,然后让我一个月给他整改完,说是重构吧,还不是,说不是重构吧,页面全让我改造了,变得更加有科技感,炫酷,性能提升了一部分。相当于页面重写,后端基本不用改什么东西,主要提供数据即可,新增的接口添加能展示数据即可。

前言

来这个公司的原因是因为我觉得这个公司属于偏初创公司,有一定的规模,有一定的挑战性,比大厂工作更加忙碌一些,我以前在一家国内互联网外包巨头行业工作,感觉比较闲,就是 965 那种模式,有可能是早点走,晚点到的那种状态,工作地点还不稳定,如果不跟项目的话。基本都是外派到保险公司(人保,人寿,国寿财及泰康等驻场开发),一两个月更新一点小功能就好。可以远程,可以驻场开发,中午休息两个半小时也比较舒服。福利待遇一般,工资一般。后来去了这家新公司,尝试去做一些有挑战性的事情,这段时间一直加班,晚上 8 点管饭,10 点之后打车报销。没有加班费,可以调休。然后一个人接手了这个项目,后来又来了一个哥们(java 工程师)添加一些接口及数据。

前端用到的一些技术点

做这个项目主要用到的是 vue+echarts+百度地图+高德地图+PDF.js+elementUI+vue-seamless-scroll 等。

为什么选 vue,不选 React 或者 Angular?

React 和 Vue 有许多相似之处,它们都有:

使用 Virtual DOM提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件。将注意力集中保持在核心库,而将其他功能如路由和全局状态管理交给相关的库。


由于有着众多的相似处,我们会用更多的时间在这一块进行比较。这里我们不只保证技术内容的准确性,同时也兼顾了平衡的考量。我们需要承认 React 比 Vue 更好的地方,比如更丰富的生态系统。

React 和 Vue 都是非常快的,所以速度并不是在它们之中做选择的决定性因素。

在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。

如要避免不必要的子组件的重渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现 shouldComponentUpdate 方法。同时你可能会需要使用不可变的数据结构来使得你的组件更容易被优化。

然而,使用 PureComponentshouldComponentUpdate 时,需要保证该组件的整个子树的渲染输出都是由该组件的 props 所决定的。如果不符合这个情况,那么此类优化就会导致难以察觉的渲染结果不一致。这使得 React 中的组件优化伴随着相当的心智负担。

在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。你可以理解为每一个组件都已经自动获得了 shouldComponentUpdate,并且没有上述的子树问题限制。

Vue 的这个特点使得开发者不再需要考虑此类优化,从而能够更好地专注于应用本身。

在 React 中,一切都是 JavaScript。不仅仅是 HTML 可以用 JSX 来表达,现在的潮流也越来越多地将 CSS 也纳入到 JavaScript 中来处理。这类方案有其优点,但也存在一些不是每个开发者都能接受的取舍。

Vue 的整体思想是拥抱经典的 Web 技术,并在其上进行扩展。

在 React 中,所有的组件的渲染功能都依靠 JSX。JSX 是使用 XML 语法编写 JavaScript 的一种语法糖。

使用 JSX 的渲染函数有下面这些优势

你可以使用完整的编程语言 JavaScript 功能来构建你的视图页面。比如你可以使用临时变量、JS 自带的流程控制、以及直接引用当前 JS 作用域中的值等等。开发工具对 JSX 的支持相比于现有可用的其他 Vue 模板还是比较先进的 (比如,linting、类型检查、编辑器的自动完成)。



事实上 Vue 也提供了渲染函数,甚至支持 JSX。然而,我们默认推荐的还是模板。任何合乎规范的 HTML 都是合法的 Vue 模板,这也带来了一些特有的优势:

对于很多习惯了 HTML 的开发者来说,模板比起 JSX 读写起来更自然。这里当然有主观偏好的成分,但如果这种区别会导致开发效率的提升,那么它就有客观的价值存在。基于 HTML 的模板使得将已有的应用逐步迁移到 Vue 更为容易。这也使得设计师和新人开发者更容易理解和参与到项目中。你甚至可以使用其他模板预处理器,比如 Pug 来书写 Vue 的模板。

有些开发者认为模板意味着需要学习额外的 DSL (Domain-Specific Language 领域特定语言) 才能进行开发——我们认为这种区别是比较肤浅的。首先,JSX 并不是没有学习成本的——它是基于 JS 之上的一套额外语法。同时,正如同熟悉 JS 的人学习 JSX 会很容易一样,熟悉 HTML 的人学习 Vue 的模板语法也是很容易的。最后,DSL 的存在使得我们可以让开发者用更少的代码做更多的事,比如 v-on 的各种修饰符,在 JSX 中实现对应的功能会需要多得多的代码。

更抽象一点来看,我们可以把组件区分为两类:一类是偏视图表现的 (presentational),一类则是偏逻辑的 (logical)。我们推荐在前者中使用模板,在后者中使用 JSX 或渲染函数。这两类组件的比例会根据应用类型的不同有所变化,但整体来说我们发现表现类的组件远远多于逻辑类组件。

Angular 事实上必须用 TypeScript 来开发,因为它的文档和学习资源几乎全部是面向 TS 的。TS 有很多好处——静态类型检查在大规模的应用中非常有用,同时对于 Java 和 C# 背景的开发者也是非常提升开发效率的。

然而,并不是所有人都想用 TS——在中小型规模的项目中,引入 TS 可能并不会带来太多明显的优势。在这些情况下,用 Vue 会是更好的选择,因为在不用 TS 的情况下使用 Angular 会很有挑战性。

最后,虽然 Vue 和 TS 的整合可能不如 Angular 那么深入,VUE 也提供了官方的类型声明和组件装饰器,并且知道有大量用户在生产环境中使用 Vue + TS 的组合。VUE 也和微软的 TS / VSCode 团队进行着积极的合作,目标是为 Vue + TS 用户提供更好的类型检查和 IDE 开发体验。

使用 vue 脚手架可以快速的进行开发,上手也很快。虽然我接触的技术栈也比较多,但是很难达到精通的级别,无疑,这是一个很好的选择。

使用 Echarts 的目的

使用这个 Echarts 主要是在大屏上做一些柱状图及饼状图,把数据以图表的形式进行展示。这个 echars 使用起来比较简单,一般找个简单的模板进行套用即可。

Echarts 的官网:http://echarts.apache.org/zh/index.html

这个是百度的一个产品,最近项目被孵化,有时候经常打不开,可以换个网络或者浏览器,实在不行可以使用 w3c 的 echarts 教程,属性也比较全。比菜鸟教程好很多。

这个如何使用我就不说了,想了解的可以参考官网教程:https://echarts.apache.org/zh/tutorial.html#5%20%E5%88%86%E9%92%9F%E4%B8%8A%E6%89%8B%20ECharts

使用百度地图的目的

这个项目原先是用的百度的卫星地图,卫星地图看起来比较模糊,不过功能都好使。

<!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />  <style type="text/css">  body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}</style>  <script type="text/javascript" src="//api.map.baidu.com/api?type=webgl&v=1.0&ak=您的密钥"></script>  <title>地球模式</title></head><body>  <div id="allmap"></div></body></html><script type="text/javascript">    // GL版命名空间为BMapGL  var map = new BMapGL.Map("allmap");    // 创建Map实例  map.centerAndZoom(new BMapGL.Point(118.5, 27.5), 5);  // 初始化地图,设置中心点坐标和地图级别  map.enableScrollWheelZoom(true);     //开启鼠标滚轮缩放  map.setMapType(BMAP_EARTH_MAP);      // 设置地图类型为地球模式</script>

后来在数据看板又加了一个灵魂地图,使用的是高德地图。

可能有人问,用一个地图不就行了?用两个不会引起性能方面的问题?说实话,确实会降低性能,不过客户要求好看,就只能鱼和熊不可兼得。

百度地图 API:https://lbsyun.baidu.com/index.php?title=jspopularGL

如果使用下钻功能,进行地理位置定位,在中国地图上进行打点,确定经纬度,使用其他地图都能取值,经纬度需要注意一下,别搞反了。

使用 vue-seamless-scroll 滚动插件的目的

这个可以使表格的数据自动滚动,看起来更酷,数据量比较多自动滚动,数据量比较少不让他滚动,鼠标悬浮数据暂停。

官方 gitHub:https://github.com/chenxuan0000/vue-seamless-scroll

文档:https://chenxuan0000.github.io/vue-seamless-scroll/guide/

主要配置项:

computed: {    classOption() {      return {        step: 0.5, // 数值越大速度滚动越快        limitMoveNum: 1, // 开始无缝滚动的数据量 this.dataList.length        hoverStop: true, // 是否开启鼠标悬停stop        direction: 1, // 0向下 1向上 2向左 3向右        openWatch: true, // 开启数据实时监控刷新dom        singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1        singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3        waitTime: 1000, // 单步运动停止的时间(默认值1000ms)        // isSingleRemUnit:true, //singleHeight and singleWidth是否开启rem度量  false/true        limitMoveNum:5,//开启无缝滚动的高度,源码设置为表格数据量小于5就不让她滚动。      };    }  }

使用 vue-seamless-scroll 自动滚动插件复制出来的数据点击事件无效的解决办法

问题分析:

当第一个 ul 中的数据滚动完时(真实数据),第二个 ul 部分的 click 事件不起作用(复制出来的数据),无法实现一些点击这行,弹窗详情信息业务需要功能。 

我需要这些数据添加一些点击事件,弹出二级页面及区域切换效果。

解决办法

采用事件委托的方式:

 事件委托又叫事件代理,就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。


一般来说,DOM需要有事件处理程序,我们都会直接给它设定事件处理程序就好了,但是如果有很多DOM需要添加处理事件,比如,一个ul下有很多个li,需要给每个li都添加相同的点击事件,这时候我们通常会用for循环的方法,遍历所有元素,然后给他们添加点击事件,虽然看似内心毫无波澜很合理的做法,背后实则存在着巨大的性能弊端。


在JS中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与DOM节点进行交互,所以访问DOM的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思路就是从减少DOM操作的角度去入手的原因。


这种情况,如果用时间委托,就会将所有的操作都放到JS程序里面,与DOM的操作就只需要交互一次,这样就能大大的减少与DOM的交互次数;并且,每一个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,性能自然就会越差,如果使用事件委托,我们就可以只对它的父级这一个对象进行操作,这样我们就值需要一个内存空间就够了,大量节省了内存空间,提高整体页面的性能了。


 

给外层 div 加点击事件,通过 event.target 获取到点击的 dom 元素

给点击的列的元素绑定 属性,这里我绑定了 id 和自定义属性 data-obj 对象,直接把改列的 item 添加进去,不用一个一个单独绑定。

点击方法,把原来的点击方法取消,直接在这个底部调用。

写了一个比较通用的方法其它几个页面按照这个方法也很好用,不用一个个绑定,那个类 row 纯属做了一个标志位,便于循环之后的判断,如果不清楚,直接 console.log,便于定位每个变量。

浏览器在线 PDF 预览取消下载按钮

需要在线 PDF 下载的功能,刚开始用的是第三方的 PDF.JS,按照网上的方法操作,排查,发现没有作用,经过和后端小伙伴沟通,原来这个地址是浏览器 PDF 预览。

浏览器 PDF 取消办法:在需要 URL 地址后边加上代码

'#toolbar=0'

加了代码:

 

或者

修改完看下效果:

上面的整行黑条都没了

顺便说一下

PDF.JS 取消下载的办法:

viewer.html 添加隐藏样式。

viewer.js 注释如下代码,版本不一样,行数一般不一样,建议用编译器 ctrl+f 进行检索 download。

还有一处,这个版本不一样,代码不一样,一样注释掉方法

总结

这个项目做的东西很多都是第一次接触,学到的东西还是挺多的,每天都加班到很晚才回家,主要原因是缺人,我自己再做,还有一个原因是我对这方便的技术不熟悉,导致工作效率有点低。再者就是甲方安排的需求多变,不断的加需求,这边都没人写需求文档,前端做了大量的循环及写了大量的假数据进行展示。导致整个系统性能稍微低下一点,已经采取了如下优化:

用了线上的服务器进行演示,发现甲方测试服务器和正式服务器配置都不是一个档次。好在我前后端都会搞。今天的内容就分享到这里,简单分享一下部分内容,谢谢大家。

以上是关于前端可视化前端大屏适配方案的主要内容,如果未能解决你的问题,请参考以下文章

我用Vue实现了--可视化大屏适配插件

前端 vue 在可视化大屏领域的工作实践

前端 vue 在可视化大屏领域的工作实践

大数据前端可视化大屏--前端开发之路

可视化大屏的几种适配方案

数据可视化大屏酷炫秘籍之前端开发者自己动手