近期公司投放在展厅大屏中演示的大数据页面,出现了文字、图表、表格等多类组件显示错乱的情况,大部分原因还是适配问题。
我们做数据大屏时,因为显示器尺寸不同,会导致展示的时候有留白区域,效果不好,所以得做一个适配方案,网上大致找了一圈,特此总结一下解决方案。
在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。
移动端开发项目的时候单位为rem,一般常使用postcss-pxtorem 将px转化为rem。
这是一个postcss的插件,可以不用自己计算rem的值,直接按照设计稿开发,然后自动编译转换成rem;
npm install postcss-pxtorem -D
在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: {
//这里配置postcss
postcss: {
plugins: [postcss]
}
sass: data: `@import "styles/_variable.scss"`;
}
}
},
devServer: {
compress: true,
disableHostCheck: true,
port: 8186
}};
src/libs/目录下创建一个rem.js文件,配置如下图所示
其中baseSize设置是自己定的,当设计稿为750px的时候,1rem=100px(物理像素)
//基准大小
const basesize = 75;
//设置rem函数
function setRem() {
/当前页面宽度相对于750宽的缩放比例,可根据自己需要修改。
let scale = document.documentElement.clientwidth /375;
if (window.orientation == 180 || window.orientation == 0) {
scale = document.documentElement.clientwidth / 375;
}
if (window.orientation = -90 || window.orientation == -90) {
scale = document.documentElement.clientwidth / 667;
}
// 设置页面根节点字体大小
document.documentElement.style.fontsize = baseSize * Math.min(scale, 2) + “px";
}
//初始化
setRem();
//改变窗口大小时重新设置rem
window.onresize = function(){setRem();}
这样我们只关注375*667屏幕下的css设计就好。rem = css/font-size,这个时候需要使用postcss-pxtorem,将css的px自动转化为对应的rem即可。
物理像素(px):就是屏幕上实际的像素点,它是ui设计时以及切图时所使用的单位。
逻辑像素(pt):根据不同的设备存在着差异(下图所示),也就是我们前端中css的像素。
#二倍屏:就是指物理像素/逻辑像素,就是一个逻辑像素单位中有多少个物理像素,一般都是二倍屏幕。
一般来说ui设计师根据物理像素设计设计稿,前端工程师根据逻辑像素(css)设计页面。
参考文章:https://blog.csdn.net/weixin_43957184/article/details/103621350
下面还有一种是用JAVA转文件的,需要装JAVA环境,有一定JAVA基础。
用代码转换工具将写死的像素值乘以缩放比例。 gitee 代码连接
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<>();
// 每行插入链表头
lineList.addFirst(line);
// class样式 识别正则
Matcher classMatcher = Pattern.compile(".*?-?.*?:.*?px.*?;").matcher(line);
// id class 绑定样式 识别正则
Matcher classUseMatcher = Pattern.compile("(class|id)=\"([0-9a-z-])*?\"").matcher(line);
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]);
}
}
class
样式表转为 hashMap
有px乘以 rate
if (classMatcher.find()) { // 处理class样式
// 遍历查找 class 名称
for (String classNameLine : lineList) {
// 查询 .class-name #id-name 样式定义 不支持 tag-name
if (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;
}
}
}
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());
}
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);
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);
}
rate
常量let option = {
title: {
subtextStyle: { lineHeight: 30 * rate , fontSize: 15 * rate }
},
}}
仅能满足一些特定的小场景,大屏中的实施非常不友好:
font-size:0.5rem
。此时投放到大屏,字体是自适应的,但其组件的多种默认属性用px为单位。如line-height
的设置为22px,此时覆盖样式后的字体过大,并不能适应行高,就会出现文字重叠等错乱问题。fontSize
时会影响到页面中已经写好的内容,工作量比较大,相当于之前写的大半样式白写了。echarts
图表, 里面的参数没办法应用 rem
的比例。媒体查询是比较常见的屏幕适配方案了, 可以根据不同的屏幕大小提供不同的样式方案, 媒体查询可以很好的支持多数的pc端网页布局需要了.
@media only screen and (max-width: 1000px) {
.div-class {
width: 720px;
}
}
媒体查询的问题也比较明显:
echarts
图表中的参数进行适配。viewpoint
基本是目前多数移动端开发都会使用的适配方式, 可以设置对移动端设备的的界面进行整体的缩放, 这种方式的适配是最佳的方案.。
用mate标签来得到理想的视口布局:
<meta name="viewport" content="target-densitydpi=high-dpi" />
这种方案缺点很明显,只能在移动端进行 viewpoint
适配, 我们目前的数据大屏项目就没办法用了。
最基本的大屏,纯flex够了。至于UI会不会挑毛病那就另一回事了。
百分比布局,也叫流式布局
百分比布局特点:宽度自适应,高度固定。
**流式布局简单来说,不管在哪种设备中,页面始终都是满屏。**例如携程移动网页中,导航在iphone4中导航5等分,在iphoneX中也是5等分,下图是ipnoneX中5等分的效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lb0BluSr-1667791413664)(https://pics6.baidu.com/feed/a50f4bfbfbedab64a8026a813fa0c0c578311e9f.jpeg@f_auto?token=ab47e91071167b9057d58de374ee1404)]
代码很简单,就是用百分比写width:
当然我们这里是数据大屏,高度如果写固定的px会有问题,放全屏以后高度会不够,所以这里推荐使用vh作为高度单位。
vh就是当前屏幕可见高度的1%,也就是说:
height:100vh == height:100%
区别:当元素没有内容时候,设置height:100%该元素不会被撑开,但是设置height:100vh,该元素会被撑开和屏幕高度一致。
代码中使用:
height:100vh;
// 这里可以加也可以减
height:calc(100vh + 55px);
flex布局极大的提高了我们布局的效率,更简单、灵活。
display: flex;
一定要给父盒子加。
把父盒子分为若干份数,每个子盒子各占几份。
语法:
flex: 1;
比如有一个父盒子里面有三个子盒子,每个子盒子写 flex:1; 此时每个子盒子各占三分之一。
注意:中间flex: 1; 和 width 有冲突。 优先执行 flex:1;
所谓的圣杯布局就是左右两边大小固定不变,中间宽度自适应。
一般这种布局方式适用于各种移动端顶部搜索部分,这是最常见的,如京东手机版主页面顶部搜索
核心思路:
两侧盒子写固定大小
中间盒子 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;
}
我们整个大屏的尺寸设置和设计图一样,只是通过css的scale放大缩小属性,来控制实际展示的大小。
通过监听浏览器窗口的大小,来改变scale的比例,从而实现数据大屏适配。(百度、网易等大数据适配的解决方案均是这个)
封装一个组件,命名为ScreenAdapter
我们的设计稿宽高比是 1920 * 960
由于这个数据可视化的项目是适配宽屏的, 我可以先铺满高然后屏幕左右可能会有空白, 空白的部分用背景图片填充就好了. 画面的布局像素依然使用设计标注的像素值然后再乘屏幕缩放比。
页面适配样例代码(vue) :
<template>
<div class="outContainer" :style="{height:boxHeight,width:boxWidth}">
<div
class="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 || 500
let timer
return function() {
const args = arguments
if (timer) {
clearTimeout(timer)
}
const context = this
timer = setTimeout(() => {
timer = null
fn.apply(context, args)
}, delay)
}
},
// 获取放大缩小比例
getScale() {
//容器高度
this.boxWidth=(document.body.clientWidth)+'px'
this.boxHeight=(document.body.clientHeight)+'px'
let w;
w = (document.body.clientWidth)/ this.width
return 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 。 |
推荐 |