爆肝十小时,为你总结出最全的数据大屏适配屏幕方案
前言
近期公司投放在展廳大屏中演示的大數據頁面,出現了文字、圖表、表格等多類組件顯示錯亂的情況,大部分原因還是適配問題。
我們做數據大屏時,因為顯示器尺寸不同,會導致展示的時候有留白區域,效果不好,所以得做一個適配方案,網上大致找了一圈,特此總結一下解決方案。
方案1:rem 方式
在vue項目中的index.html中加上
functiongetRem() {var html = document.getElementsByTagName("html")[0];var oWidth = document.body.clientWidth || document.documentElement.clientWidth;html.style.fontSize = 1 * (oWidth / 1920) + "px"; } window.onload = function() { * /\*初始化\*/ * getRem(); * /\*getRem綁定監聽\*/ * window.addEventListener("resize", getRem, false); };然后在大屏中相關的尺寸單位使用rem 即可,注意:1rem=根標簽(html)的fontSize,
通過上面的設置,如果設計圖寬是1920px,則大小按設計圖來就行,只是單位為rem,
比如設計圖中是20px,那編寫時改成20rem就行。
當然這種方法比較死板,每次寫還得手動換算px和rem的關系。
建議使用下面的輪子,在vue里面自動轉。寫代碼的時候不用自己算px轉rem。
自動px轉rem工具:postcss-pxtorem
移動端開發項目的時候單位為rem,一般常使用postcss-pxtorem 將px轉化為rem。
1.postcss-pxtorem
這是一個postcss的插件,可以不用自己計算rem的值,直接按照設計稿開發,然后自動編譯轉換成rem;
2. 安裝
npm install postcss-pxtorem -D
3. 配置
在webpack.config.js中配置
module.exports = {module: {rules: [{test: /\.css$/,exclude: /node_modules/,use: [{loader: 'style-loader',},{loader: 'css-loader',options: {importLoaders: 1,}},{loader: 'postcss-loader'}]}]} }在vue.config.js中配置:
const path = require("path"); const px2rem = require("postcss-px2rem"); const postcss = px2rem({remUnit: 75 //基準大小basesize,需要和rem.jst});const resolve = dir = > {return path.join(__dirname, dir);};module.exports - {pages: {…}.assetsDir: "static",configurewebpack: …,chainwebpack: config = > {…},css: {loaderOptions: {//這里配置postcsspostcss: {plugins: [postcss]}sass: data: `@import "styles/_variable.scss"`;}}},devServer: {compress: true,disableHostCheck: true,port: 8186}};4.處理窗口大小變化
src/libs/目錄下創建一個rem.js文件,配置如下圖所示
其中baseSize設置是自己定的,當設計稿為750px的時候,1rem=100px(物理像素)
5.css的值是不變的,但是font-size的值在隨著屏幕變化
這樣我們只關注375*667屏幕下的css設計就好。rem = css/font-size,這個時候需要使用postcss-pxtorem,將css的px自動轉化為對應的rem即可。
6.說到rem值就要提到邏輯像素、物理像素以及二倍屏的概念:
物理像素(px):就是屏幕上實際的像素點,它是ui設計時以及切圖時所使用的單位。
邏輯像素(pt):根據不同的設備存在著差異(下圖所示),也就是我們前端中css的像素。
#二倍屏:就是指物理像素/邏輯像素,就是一個邏輯像素單位中有多少個物理像素,一般都是二倍屏幕。
一般來說ui設計師根據物理像素設計設計稿,前端工程師根據邏輯像素(css)設計頁面。
參考文章:https://blog.csdn.net/weixin_43957184/article/details/103621350
下面還有一種是用JAVA轉文件的,需要裝JAVA環境,有一定JAVA基礎。
JAVA實現vue 代碼轉換工具
用代碼轉換工具將寫死的像素值乘以縮放比例。 gitee 代碼連接
1.讀取vue文件, 定義文件行鏈表 class的映射
fileReader = new FileReader(url);// 讀取文件bufferedReader = new BufferedReader(fileReader);// 結果文本StringBuilder resultText = new StringBuilder();// 行鏈表 用于查找 class樣式名稱LinkedList<String> lineList = new LinkedList<>();// class樣式映射Map<String, Map<String, String>> classMap = new HashMap<>();2.遍歷行, 定義樣式識別的正則表達式
// 每行插入鏈表頭lineList.addFirst(line);// class樣式 識別正則Matcher classMatcher = Pattern.compile(".*?-?.*?:.*?px.*?;").matcher(line);// id class 綁定樣式 識別正則Matcher classUseMatcher = Pattern.compile("(class|id)=\"([0-9a-z-])*?\"").matcher(line);3.處理style 有px的位置乘以 rate
if (line.contains("style=\"")) { // 處理style// 行文本頭部加入結果文本resultText.append(line, 0, line.indexOf("style=\""));// style 代碼正則Pattern pattern = Pattern.compile("style=\".*?\"");Matcher matcher = pattern.matcher(line);// 將 style="name:value;" 轉為 :style="[{name:value}]"resultText.append(":style=\"");while (matcher.find()) {String styleStr = matcher.group();styleStr = styleStr.replace("style=\"", "").replace("\"", "");resultText.append(parseStyleList(styleStr));}resultText.append("\"");String[] tailArr = pattern.split(line);// 行文本尾部 加入結果文本if (tailArr.length != 0 && tailArr.length > 1) {resultText.append(tailArr[1]);}}4.處理class樣式 class 樣式表轉為 hashMap 有px乘以 rate
if (classMatcher.find()) { // 處理class樣式// 遍歷查找 class 名稱for (String classNameLine : lineList) {// 查詢 .class-name #id-name 樣式定義 不支持 tag-nameif (classNameLine.contains("{") && (classNameLine.contains(".") || classNameLine.contains("#"))) {String className = classNameLine.trim().replace(".", "").replace("#", "").replace("{", "");// 橫線轉駝峰className = lineToHump(className);// 如果是多重定義的class 只保留一個if (className.contains(" ")) {className = className.split(" ")[0];}// 處理樣式鍵值對String styleStr = classMatcher.group().trim().replace(";", "");String[] styleArr = parseStyle(styleStr).replace(",", "").split(":");// class 鍵值對映射Map<String, String> innerClassMap = classMap.get(className);if (innerClassMap == null) {innerClassMap = new HashMap<>();}// class 鍵值對映射加入 class樣式映射innerClassMap.put(styleArr[0], styleArr[1]);classMap.put(className, innerClassMap);break;}} }5.使用 class="class-name" 的地方 加入 :class="className"
if (classUseMatcher.find()) {String classUseStr = classUseMatcher.group();String classUseHumpStr = lineToHump(classUseStr.replace("class=", "").replace("id=", "").replaceAll("\"", ""));// 行文本頭部加入結果文本resultText.append(line, 0, line.indexOf(classUseStr));resultText.append(classUseStr);resultText.append(" :class=\"");// class 轉 v-bind:class 橫線命名轉駝峰resultText.append(classUseHumpStr);resultText.append("\"");// 行文本尾部加入結果文本resultText.append(line, line.indexOf(classUseStr) + classUseStr.length(), line.length()); }6.vue data中加入 縮放比率 rate 組件中 有 rate 會自動縮放
StringBuffer dataBuffer = new StringBuffer();Matcher dataMatcher = Pattern.compile("data.*?\n.*?return.*?\\{", Pattern.MULTILINE).matcher(resultText);if (dataMatcher.find()) {dataMatcher.appendReplacement(dataBuffer, "data: function () {\n" +" return {\n" +" rate,\n");for (String key : classMap.keySet()) {Map<String, String> innerClassMap = classMap.get(key);dataBuffer.append(" ");dataBuffer.append(key);dataBuffer.append(": {");for (String innerKey : innerClassMap.keySet()) {dataBuffer.append(innerKey);dataBuffer.append(": ");dataBuffer.append(innerClassMap.get(innerKey));dataBuffer.append(",");}// stringBuffer.append(" ");dataBuffer.append("},\n");}}dataMatcher.appendTail(dataBuffer);resultText = new StringBuilder(dataBuffer);7.常量加入script中
String rateDefineStr = "\n" +" const scale = 16 / 9\n" +" const headerHeight = 47;\n" +" const tabHeight = 27;\n" +" const tabPadding = 5;\n" +" const designHeight=1080;\n" +" const marginTop = headerHeight + tabHeight + tabPadding;\n" +" const marginBottom = tabPadding;\n" +" const clientWidth = document.body.clientWidth\n" +" const windowHeight = document.body.clientHeight;\n" +" const clientHeight = windowHeight - marginTop - marginBottom;\n" +" const innerHeight = clientHeight;\n" +" const rate = innerHeight / designHeight\n" +" const centerWidth = clientHeight * scale;\n" +" const paddingWidth = (((clientWidth - 5 - 5) - (clientHeight * scale)) / 2);" +"\n ;\n";StringBuffer constBuffer = new StringBuffer();Matcher constMatcher = Pattern.compile("export default \\{", Pattern.MULTILINE).matcher(resultText);if (constMatcher.find()) {constMatcher.appendReplacement(constBuffer, rateDefineStr);constBuffer.append(" export default {");constMatcher.appendTail(constBuffer);System.out.println(constBuffer);}8.ecahrts 中的參數可以乘以 rate 常量
let option = {title: {subtextStyle: { lineHeight: 30 * rate , fontSize: 15 * rate }}, }}坑點
僅能滿足一些特定的小場景,大屏中的實施非常不友好:
方案2: 媒體查詢
媒體查詢是比較常見的屏幕適配方案了, 可以根據不同的屏幕大小提供不同的樣式方案, 媒體查詢可以很好的支持多數的pc端網頁布局需要了.
@media only screen and (max-width: 1000px) {.div-class {width: 720px;} }坑點
媒體查詢的問題也比較明顯:
方案3: viewpoint 視口
viewpoint 基本是目前多數移動端開發都會使用的適配方式, 可以設置對移動端設備的的界面進行整體的縮放, 這種方式的適配是最佳的方案.。
用mate標簽來得到理想的視口布局:
<meta name="viewport" content="target-densitydpi=high-dpi" />坑點
這種方案缺點很明顯,只能在移動端進行 viewpoint 適配, 我們目前的數據大屏項目就沒辦法用了。
方案4:flex + 百分比 + vh
最基本的大屏,純flex夠了。至于UI會不會挑毛病那就另一回事了。
1.百分比布局
百分比布局,也叫流式布局
百分比布局特點:寬度自適應,高度固定。
**流式布局簡單來說,不管在哪種設備中,頁面始終都是滿屏。**例如攜程移動網頁中,導航在iphone4中導航5等分,在iphoneX中也是5等分,下圖是ipnoneX中5等分的效果
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Lb0BluSr-1667791413664)(https://pics6.baidu.com/feed/a50f4bfbfbedab64a8026a813fa0c0c578311e9f.jpeg@f_auto?token=ab47e91071167b9057d58de374ee1404)]
代碼很簡單,就是用百分比寫width:
2.vh的使用
當然我們這里是數據大屏,高度如果寫固定的px會有問題,放全屏以后高度會不夠,所以這里推薦使用vh作為高度單位。
vh就是當前屏幕可見高度的1%,也就是說:
height:100vh == height:100%
區別:當元素沒有內容時候,設置height:100%該元素不會被撐開,但是設置height:100vh,該元素會被撐開和屏幕高度一致。
代碼中使用:
height:100vh;// 這里可以加也可以減 height:calc(100vh + 55px);3.flex布局
flex布局極大的提高了我們布局的效率,更簡單、靈活。
display: flex;一定要給父盒子加。
4.伸縮比——flex:1
把父盒子分為若干份數,每個子盒子各占幾份。
語法:
flex: 1;比如有一個父盒子里面有三個子盒子,每個子盒子寫 flex:1; 此時每個子盒子各占三分之一。
注意:中間flex: 1; 和 width 有沖突。 優先執行 flex:1;
5.圣杯布局
所謂的圣杯布局就是左右兩邊大小固定不變,中間寬度自適應。
一般這種布局方式適用于各種移動端頂部搜索部分,這是最常見的,如京東手機版主頁面頂部搜索
核心思路:
兩側盒子寫固定大小
中間盒子 flex: 1; 占滿剩余空間
.top {display: flex;justify-content: center; }.top div:first-child {width: 50px;height: 50px;background-color: red; }.top div:last-child {width: 50px;height: 50px;background-color: red; }.top div:nth-child(2) {flex: 1;height: 50px;background-color: pink; }坑點
方案5: scale 方式
我們整個大屏的尺寸設置和設計圖一樣,只是通過css的scale放大縮小屬性,來控制實際展示的大小。
通過監聽瀏覽器窗口的大小,來改變scale的比例,從而實現數據大屏適配。(百度、網易等大數據適配的解決方案均是這個)
封裝組件及使用
封裝一個組件,命名為ScreenAdapter
-
我們的設計稿寬高比是 1920 * 960
由于這個數據可視化的項目是適配寬屏的, 我可以先鋪滿高然后屏幕左右可能會有空白, 空白的部分用背景圖片填充就好了. 畫面的布局像素依然使用設計標注的像素值然后再乘屏幕縮放比。頁面適配樣例代碼(vue) :
<template><div class="outContainer" :style="{height:boxHeight,width:boxWidth}"><divclass="ScreenAdapter":style="style"><slot /></div></div></template> <script> export default {name: '',//參數注入props: {width: {type: String,default: '1920' },height: {type: String,default: '1080' }},data() {return {boxWidth:this.width,boxHeight:this.height,style: {width: this.width + 'px',height: this.height + 'px',transform: 'scale(1) translate(-50%, -50%)'}}},mounted() {this.setScale()window.onresize = this.Debounce(this.setScale, 200)// window.οnresize=this.setScale},destroyed(){window.removeEventListener("resize",this.Debounce(this.setScale, 200));},methods: {Debounce: (fn, t) => {const delay = t || 500let timerreturn function() {const args = argumentsif (timer) {clearTimeout(timer)}const context = thistimer = setTimeout(() => {timer = nullfn.apply(context, args)}, delay)}},// 獲取放大縮小比例getScale() {//容器高度this.boxWidth=(document.body.clientWidth)+'px'this.boxHeight=(document.body.clientHeight)+'px'let w;w = (document.body.clientWidth)/ this.widthreturn w},// 設置比例setScale() {this.style.transform = 'scale(' + this.getScale() + ') translate(0, 0)'//解決改變窗口大小時,意外出現滾動條問題this.$nextTick(()=>{this.style.transform = 'scale(' + this.getScale() + ') translate(0, 0)'})},} } </script> <style lang="less" scoped> .ScreenAdapter {overflow-y: scroll;scrollbar-width: none; /* firefox */-ms-overflow-style: none; /* IE 10+ */transform-origin: 0 0;position: absolute;left: 0;right: -17px;transition: 0.3s; } .ScreenAdapter::-webkit-scrollbar {display: none; /* Chrome Safari */ } .outContainer::-webkit-scrollbar {display: none; /* Chrome Safari */ } .outContainer{overflow-y: scroll;scrollbar-width: none; /* firefox */-ms-overflow-style: none; /* IE 10+ */position: relative; } </style>
將此組件作為外殼,包在我們搭建的頁面上,只包在最外層的父盒子即可:
<ScreenAdapter><div>大家好,我是大屏展示頁面<div><ScreenAdapter/>在ScreenAdapter組件內正常編寫即可,尺寸和設計圖一樣,設計圖是20px,就寫20px,不需要有其他的考慮(百分百尺寸等),懶人專用!
? [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-z2yef2aD-1667791413665)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5fe547d4549424db27a39944541e860~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.awebp)]
隨意更改展示尺寸或者按F11進行全屏展示,都會占滿寬,高度自適應。如果展示尺寸等于設置時的尺寸(比如1920*1080)那么會剛剛好占滿全屏。
如果是vue2的項目,直接復制代碼使用即可。
坑點
1.字體拉伸問題,在長寬比跟設計稿的長寬比不一致的情況下,字體會有拉伸的效果,要不變胖了,要不就是變矮了。也不是很完美。這種字體大小的適配就只能用rem處理。
2.放大的時候會出現滾動條,建議style中加個 overflow:hidden。
參考地址 :https://juejin.cn/post/6972416642600927246
方案總結
| rem | 支持各大屏幕尺寸適配,包含間距、字體樣式。 | 1.與element組件不兼容,如覆蓋樣式后的字體過大、文字重疊等錯亂問題。 2.如果項目中途改用rem,**工作量比較大,相當于之前寫的大半樣式白寫了。**3. 頁面中使用了 echarts 圖表, 里面的參數沒辦法應用 rem 的比例。 4.手動px轉rem很麻煩,不過使用輪子可以省略這一步。 | 推薦 |
| 媒體查詢 | 支持定制化更改 | 1.大量書寫媒體查詢代碼, 比較繁瑣,工期很趕的不建議用媒體查詢。2. 針對多種屏幕進行適配, 也無法保證完全兼容所有的屏幕。 3.無法支持 echarts 圖表中的參數進行適配。 | 不推薦 |
| viewpoint | 移動端開發都會使用的適配方式 | 只能在移動端進行 viewpoint 適配, 我們目前的數據大屏項目就沒辦法用了。 | 不推薦 |
| flex + 百分比 +vh | 簡單好用,適配較為優秀 | 1. flex存在瀏覽器兼容性問題,如果不考慮兼容性可以大量使用,具體可以看mdn,如果是移動端則不用考慮直接flex。 2.echarts沒法用,給vh等單位樣式不好調,只能給px。 3.僅能處理盒子的寬高適配,不能處理字體大小,字體大小的屏幕適配還得靠rem。 | 推薦 |
| scale方式 | 是通過css的scale放大縮小屬性,來控制實際展示的大小。百度、網易等大數據適配的解決方案均是這個。 | 1.字體拉伸問題,在長寬比跟設計稿的長寬比不一致的情況下,字體會有拉伸的效果,要不變胖了,要不就是變矮了。這種字體大小的適配就只能用rem處理。2.放大的時候會出現滾動條,建議style中加個 overflow:hidden。 | 推薦 |
總結
以上是生活随笔為你收集整理的爆肝十小时,为你总结出最全的数据大屏适配屏幕方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务了上万家企业做数字化转型后,我们写下
- 下一篇: DAY1主论坛回顾 | PGConf.A