大屏适配解决方案——vw和vh适配方案
= 类名需要与项目公共类名作区分,不然会相互影响 ==// index.vue 页面。
·
整体思路
- 整体宽高布局100vw,100vh,每个模块的宽高:使用vw,vh
- 字体大小适配:px转化为vw、vh
- 处理公共样式:新建一个rem.scss文件
- 图表根据屏幕尺寸变化自动resize():自定义指令
- 图表里的文字,例如图例、横坐标等字体大小适配:公共方法
- 子组件监听数据变化实时刷新图表
- websocket:递增间隔重连
- 订单统计列表数据轮播切换
1、按照设计稿的尺寸,将px按比例计算转为vw和vh
util.scss(仅需修改设计稿的默认宽高)
//假设设计稿尺寸为1920x*1080px的屏幕分辨率
//1920px = 100vw
//1080px = 100vh
@use "sass:math";
//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;
//px转为vw的函数
@function vw($px) {
@return math.div($px , $designWidth) * 100vw;
}
//px转为vh的函数
@function vh($px) {
@return math.div($px , $designHeight) * 100vh;
}
vue.config.js
// 配置一下utils.scss的路径,就可以全局使用了
module.exports={
publicPath: '',
configureWebpack: {
name: "app name",
resolve: {
alias: {
'@': resolve('src')
}
}
},
css:{ //全局配置utils.scss
loaderOptions:{
sass:{
prependData:`@import "@/styles/utils.scss";`
}
}
}
}
在.vue文件中使用
<style lang="scss" scoped="scoped">
/*
直接使用vw和vh函数,将像素值传进去,得到的就是具体的vw vh单位
*/
.box{
width: vw(300);
height: vh(100);
font-size: vh(16);
background-color: black;
margin-left: vw(10);
margin-top: vh(10);
border: vh(2) solid red;
}
</style>
2、 处理公共样式:新建一个rem.scss文件
== 类名需要与项目公共类名作区分,不然会相互影响 ==
//默认设计稿的宽度
$designWidth:1920;
//默认设计稿的高度
$designHeight:1080;
@for $value from 1 through 100 {
.pdRem-#{$value},
.ptbRem-#{$value},
.ptRem-#{$value} {
padding-top: $value/$designHeight * 1vh;
}
.pdRem-#{$value},
.ptbRem-#{$value},
.pbRem-#{$value} {
padding-bottom: $value/$designHeight * 1vh;
}
.pdRem-#{$value},
.plrRem-#{$value},
.plRem-#{$value} {
padding-left: $value/$designWidth * 1vw;
}
.pdRem-#{$value},
.plrRem-#{$value},
.prRem-#{$value} {
padding-right: $value/$designWidth * 1vw;
}
.mgRem-#{$value},
.mtbRem-#{$value},
.mtRem-#{$value} {
margin-top: $value/$designHeight * 1vh;
}
.mgRem-#{$value},
.mtbRem-#{$value},
.mbRem-#{$value} {
margin-bottom: $value/$designHeight * 1vh;
}
.mgRem-#{$value},
.mlrRem-#{$value},
.mlRem-#{$value} {
margin-left: $value/$designWidth * 1vw;
}
.mgRem-#{$value},
.mlrRem-#{$value},
.mrRem-#{$value} {
margin-right: $value/$designWidth * 1vw;
}
}
@for $value from 10 through 40 {
.sizeRem-#{$value} {
font-size: $value/$designHeight * 1vh;
}
}
*{
box-sizing: border-box;
}
.s-yellow{
color: #FFC300;
}
.s-red{
color: #CB272C;
}
.s-blue{
color: #00CDCF;
}
.s-gray{
color: #979797;
}
.line-1 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 引入即可使用
<style lang="scss" scoped>
@import "./components/css/rem.scss";
</style>
3、Echarts图表适配自动resize()
封装指令 directive.js
npm install element-resize-detector --save
// directive.js
import * as ECharts from "echarts";
import elementResizeDetectorMaker from "element-resize-detector";
import Vue from "vue";
const HANDLER = "_vue_resize_handler";
function bind(el, binding) {
el[HANDLER] = binding.value
? binding.value
: () => {
let chart = ECharts.getInstanceByDom(el);
if (!chart) {
return;
}
chart.resize();
};
// 监听绑定的div大小变化,更新 echarts 大小
elementResizeDetectorMaker().listenTo(el, el[HANDLER]);
}
function unbind(el) {
// window.removeEventListener("resize", el[HANDLER]);
elementResizeDetectorMaker().removeListener(el, el[HANDLER]);
delete el[HANDLER];
}
// 自定义指令:v-chart-resize 示例:v-chart-resize="fn"
Vue.directive("resize", { bind, unbind });
main.js 中引入即可使用
import '@/directive/directive';
<template>
<div class="linechart">
<div ref="chart" v-resize class="chart"></div>
</div>
</template>
4、图表里的文字,例如图例、横坐标等字体大小适配:公共方法
dataUtil.js
export const fitChartSize = (size,defalteWidth = 1920) => {
let clientWidth = window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;
if (!clientWidth) return size;
let scale = (clientWidth / defalteWidth);
return Number((size*scale).toFixed(3));
}
函数挂载到原型上即可使用
import {fitChartSize} from '@src/utils/dataUtil.js'
Vue.prototype.fitSize = fitChartSize;
textStyle: {
color: "green",
fontSize: this.fitSize(18) //字体自适应
},
5、子组件监听数据变化实时刷新图表:深度监听+setOption
props: ["list"],
watch: {
list: {
handler(val) {
// 深度监听没有旧值
this.month = val.map((item) => {
return item.month + "月";
});
this.orderNum = val.map((item) => {
return item.orderCount;
});
this.oldNum = val.map((item) => {
return item.servedCount;
});
this.goodRate = val.map((item) => {
return item.favorableRate;
});
let option = {
xAxis: [
{
data: this.month,
},
],
series: [
{
data: this.orderNum, // 订单量
},
{
data: this.oldNum, // 服务人数
},
{
data: this.goodRate, //折线图数据
},
],
};
this.chart.setOption(option);
},
// 这里是关键,代表递归监听的变化
deep: true,
},
},
6、websocket
// index.vue 页面
本地运行时,websocket和接口都用的一个端口号,会导致端口号被占用而运行报错,测试环境没事
<script>
export default {
data() {
return {
lockReconnect: false, //禁止重连
time: 1, // 断开重连的时间间隔
websock: null,
// 对象分析 30秒切换tab,最新订单10条,30秒滚动,更新天气
timerId: null,
// websocket
open: false, //是否真正建立连接
res: {}, // 所有数据
],
};
},
created() {
this.initWebSocket();
},
mounted() {
this.timerId = setInterval(() => {
this.timeOperated();
}, 30000);
},
// 销毁定时器
beforeDestroy() {
clearInterval(this.timerId); // 在Vue实例销毁前,清除时间定时器
this.timerId = null;
},
destroyed() {
this.websock.close(); //离开路由之后断开websocket连接
},
methods: {
// 定时30s操作
timeOperated() {
if (this.open) {
this.websock.send("ping");
}
// 切换tab
this.$refs.chart5.tab =
this.$refs.chart5.tab == 3 ? 1 : this.$refs.chart5.tab + 1;
// 切换最近订单
this.$refs.chart7.tab = this.$refs.chart7.tab == 1 ? 2 : 1;
// TODO更新天气
this.$refs.header.getWeather();
},
//初始化weosocket
initWebSocket() {
const url=window.location.host
this.websock = new WebSocket(
`ws://${url}${process.env.VUE_APP_BASE_API}/report/webSocket/screen/2`
);
// 连接建立时触发
this.websock.onopen = () => {
console.log("连接成功");
this.open = true;
this.time = 1;
};
// 客户端接收服务端数据时触发
this.websock.onmessage = (e) => {
if (e.data != "ping") {
console.log(JSON.parse(e.data));
this.res = JSON.parse(e.data);
} else {
console.log("接收数据:", e.data);
}
};
// 通信发生错误时触发
this.websock.onerror = () => {
console.log("出现错误");
this.open = false;
this.reconnect();
};
// 连接关闭时触发
this.websock.onclose = () => {
console.log("断开连接");
this.open = false;
this.reconnect();
};
},
// 断开重连
reconnect(url) {
// 防止错误和断开重复重连
if (this.lockReconnect) return;
this.lockReconnect = true;
setTimeout(() => {
//没连接上会一直重连,设置延迟避免请求过多
this.initWebSocket();
this.lockReconnect = false;
this.time++;
}, this.time * 1000);
},
},
};
</script>
7、订单统计列表数据轮播切换:轮播图10s切换+双层数组(每页4个)
<template>
<div class="w100 sizeRem-14 white">
<el-carousel indicator-position="none" height="100%" autoplay interval="10000">
<el-carousel-item v-for="(items, index) in newList" :key="index">
<div style="height: 20%" class="flex1 w100" v-for="(item, i) in items" :key="i">
<div class="flex-2 line-1">{{ item.providerName }}</div>
<div class="flex-1 text-right">{{ item.totalOrderCount }}</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
export default {
props: ["list"],
data() {
return {
newList: []
};
},
watch: {
list: {
handler(val) {
let arr = []
for (var i = 0; i < val.length; i += 4) {
arr.push(val.slice(i, i + 4));
}
this.newList = arr
console.log(111, this.newList)
},
// 这里是关键,代表递归监听的变化
deep: true,
},
},
};
</script>
更多推荐
已为社区贡献27条内容
所有评论(0)