最近,在逛github时,看到了Geeker-Admin开源框架,其中实现了一个数据大屏的功能,于是,也想跟着模仿一下,但实践起来却没那么简单,因为数据大屏最主要的是要先解决其适配问题。
看了网上的各种方案,目前大家采用的大概有 3 种:
这 3 种方案中,最简单的方式当属 scale 方案了。
不过它的主要缺点是当大屏跟 ui 稿的比例不一样时,会出现周边留白情况。不过如果想简单,并且客户能同意留白,选用 scale 即可。
Geeker-Admin开源框架就是利用scale来解决数据大屏适配问题,接下来开始分析其代码。
Geeker-Admin是如何利用scale来解决大屏适配问题的呢?
先来看看Geeker-Admin中数据大屏的实现效果:
经过分析,解决适配问题的关键代码集中在如下两部分:
const dataScreenRef = ref<HTMLElement | null>(null);
onMounted(() => {
// 初始化时为外层盒子加上缩放属性,防止刷新界面时就已经缩放
if (dataScreenRef.value) {
dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`;
dataScreenRef.value.style.width = `1920px`;
dataScreenRef.value.style.height = `1080px`;
}
// 初始化 echarts
initCharts();
// 为浏览器绑定事件
window.addEventListener("resize", resize);
});
// 根据浏览器大小推断缩放比例
const getScale = (width = 1920, height = 1080) => {
let ww = window.innerWidth / width;
let wh = window.innerHeight / height;
return ww < wh ? ww : wh;
};
// 监听浏览器 resize 事件
const resize = () => {
if (dataScreenRef.value) {
dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`;
}
// 使用了 scale 的echarts其实不需要需要重新计算缩放比例
Object.values(dataScreen).forEach(chart => {
chart && chart.resize();
});
};
.dataScreen-container {
width: 100%;
height: 100%;
background: url("./images/bg.png") no-repeat;
background-repeat: no-repeat;
background-attachment: fixed;
background-position: center;
background-size: 100% 100%;
background-size: cover;
.screen {
position: fixed;
top: 50%;
left: 50%;
z-index: 999;
display: flex;
flex-direction: column;
overflow: hidden;
transition: transform 0.3s;
transform-origin: left top;
}
}
先准备一个容器dataScreen-container,设置width = 100%,height = 100%,并设置背景图片,作为整个大屏展示的背景。
js最关键的是如下代码:
// 根据浏览器大小推断缩放比例
const getScale = (width = 1920, height = 1080) => {
let ww = window.innerWidth / width;
let wh = window.innerHeight / height;
return ww < wh ? ww : wh;
};
这段代码的意思是,根据浏览器大小计算缩放比例:
css部分最关键的是如下代码:
.screen {
position: fixed;
top: 50%;
left: 50%;
transform-origin: left top;
}
position: fixed;top: 50%;left: 50%;这几行容易理解,就是把当前数据大屏的左上角固定在整个屏幕的中间位置。
在CSS3变形中,任何元素都有一个中心原点。默认情况下,元素的中心原点位于x轴和y轴的50%处,如下图所示:
默认情况下,CSS3的各种变形(平移、缩放、倾斜等)都是以元素的中心原点进行变形的。
在CSS3中,可以使用transform-origin属性来改变元素的中心原点,那么transform-origin: left top的意思就是设置元素的缩放中心点为左上。
上述css部分代码的意思就是把当前数据大屏的左上角固定在整个屏幕的中间位置,再把缩放中心设置为元素的左上角,那么缩放之后数据大屏的左上角依然是在屏幕中间位置,便于后续的居中。
在初始化和每次窗口大小发生变化时都需要根据计算得到的缩放比例进行缩放,translate(-50%,-50%)会移动自身长宽的 50%,使当前数据大屏居中。
dataScreenRef.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`;
整个实现思路可以总结为:通过css的transform的scale 属性,根据屏幕大小,对图表进行整体的等比缩放,从而达到自适应效果。
分析完上述代码,就开始自己动手尝试一下。
<template>
<div class="container">
<div class="screen" ref="screen">
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue'
let screen = ref()
onMounted(() => {
screen.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`
})
//监听视口变化
window.onresize = () => {
screen.value.style.transform = `scale(${getScale()}) translate(-50%, -50%)`
}
//定义大屏缩放比例,假设当前设计稿是 1920 * 1080(宽高比16:9)
const getScale = (w = 1920, h = 1080) => {
const ww = window.innerWidth / w //Window.innerWidth获取窗口的视口宽度,宽度缩放比
const wh = window.innerHeight / h //window.innerHeight获取窗口的视口高度,高度缩放比
//如果当前视口宽度变小或者视口高度变大,就按照宽度的缩放比例缩放,上下会有留白
//如果当前视口宽度变大或者视口高度变小,就按照高度的缩放比例缩放,左右会有留白
return ww < wh ? ww : wh
}
</script>
<style scoped lang="scss">
.container {
width: 100vw;
height: 100vh;
background: url('./images/bg.png') no-repeat;
background-size: cover;
//让当前大屏左上角固定在屏幕中间,进行缩放,缩放完成再平移回去
.screen {
position: fixed;
width: 1920px;
height: 1080px;
left: 50%;
top: 50%;
//改变元素的中心原点为左上,默认在元素中心
transform-origin: top left;
background-color: red;
}
}
</style>
红色部分就相当于数据大屏内容部分。当屏幕宽高比刚好是 16:9 时,页面能刚好全屏展示。
当屏幕宽高比大于 16:9 时,页面左右留白。