关键词:
假设我们正在开发一个可视化拖拽的搭建平台,可以拖拽生成工作台或可视化大屏,或者直接就是开发一个大屏,首先必须要考虑的一个问题就是页面如何适应屏幕,因为我们在搭建或开发时一般都会基于一个固定的宽高,但是实际的屏幕可能大小不一,接下来我们就尝试几种简单且常见的方案,并简单分析一下利弊。
demo
首先写个基础的demo
给后续使用:
<script setup>
import ref from "vue";
import Widget from "./components/Widget.vue";
import LineChart from "./components/LineChart.vue";
import BarChart from "./components/BarChart.vue";
import PieChart from "./components/PieChart.vue";
import FunnelChart from "./components/FunnelChart.vue";
// 画布宽高
const canvasWidth = ref(1920);
const canvasHeight = ref(1080);
// 组件宽高
const widgetWidth = ref(960);
const widgetHeight = ref(540);
</script>
<template>
<div class="canvasBox">
<div
class="canvas"
:style=" width: canvasWidth + 'px', height: canvasHeight + 'px' "
>
<Widget :width="widgetWidth" :height="widgetHeight" :left="0" :top="0">
<LineChart></LineChart>
</Widget>
<Widget :width="widgetWidth" :height="widgetHeight" :left="widgetWidth" :top="0">
<BarChart></BarChart>
</Widget>
<Widget :width="widgetWidth" :height="widgetHeight" :left="0" :top="widgetHeight">
<PieChart></PieChart>
</Widget>
<Widget :width="widgetWidth" :height="widgetHeight" :left="widgetWidth" :top="widgetHeight">
<FunnelChart></FunnelChart>
</Widget>
</div>
</div>
</template>
<style scoped>
.canvasBox
width: 100vw;
height: 100vh;
.canvas
position: relative;
</style>
每个图表组件的宽高都设为100%
,然后都被Widget
组件包裹,所以实际宽高是依赖Widget
组件的,Widget
组件为绝对定位,并且宽高、位置通过props
传入,模拟我们的拖拽操作,简单起见,所有图表的宽高我们都设为了相同的。
Widget
组件:
<script setup>
const props = defineProps(
width:
type: Number,
default: 0,
,
height:
type: Number,
default: 0,
,
left:
type: Number,
default: 0,
,
top:
type: Number,
default: 0,
,
);
</script>
<template>
<div
class="widgetBox"
:style="
width: width + 'px',
height: height + 'px',
left: left + 'px',
top: top + 'px',
"
>
<slot></slot>
</div>
</template>
<style scoped>
.widgetBox
position: absolute;
</style>
组件整体的容器为类名为canvas
的元素,相对定位,宽高也是动态设置的,canvas
元素的父级canvasBox
元素宽高设为和屏幕宽高一致。
固定尺寸
即宽度、高度固定,如果宽高小于屏幕宽高则在屏幕居中。
这个是最简单的方案了,相当于不适配屏幕,画布配置了多大实际就是多大,不随屏幕的变化而变化,所以各个组件的宽高也是在配置后不会改变,一般用于尺寸固定且后期不会改变的可视化大屏。
我们前面的demo
初始就是这种方式:
当然,如果宽高小于屏幕的话居中的逻辑需要加一下,居中的方法有很多,通过css
、js
都可,根据自己的喜好来就行:
// 画布的位置
const canvasLeft = ref(0);
const canvasTop = ref(0);
// 如果屏幕的宽或高比画布的大,那么居中显示
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
if (windowWidth > canvasWidth.value)
canvasLeft.value = (windowWidth - canvasWidth.value) / 2;
if (windowHeight > canvasHeight.value)
canvasTop.value = (windowHeight - canvasHeight.value) / 2;
<div
class="canvas"
:style="
width: canvasWidth + 'px',
height: canvasHeight + 'px',
left: canvasLeft + 'px',
top: canvasTop + 'px',
"
>
</div>
判断窗口宽度和高度是否大于画布的宽高,是的话通过left
或top
来调整:
自适应宽度
即宽度适应屏幕,高度不变,这种方案的缺点是垂直方向上会出现滚动条。
比如画布设置的宽度为1920
,但是实际上屏幕的宽度为1280
,那么缩小了1.5
倍,那么画布和每个组件的宽度也需要同步缩小1.5
倍,并且每个组件的left
值也需要进行动态调整。
首先实现一下容器元素canvas
的尺寸调整:
// 保存原始画布的宽度
const originCanvasWidth = ref(canvasWidth.value);
// 宽度缩放比例
const ratioWidth = ref(1);
// 当前窗口的宽度
let windowWidth = window.innerWidth;
// 将画布宽度设置为当前窗口的宽度
canvasWidth.value = windowWidth;
// 计算当前宽度和原始宽度的比例
ratioWidth.value = windowWidth / originCanvasWidth.value;
然后再把这个比例传给Widget
组件进行调整:
<Widget :ratioWidth="ratioWidth">
<LineChart></LineChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
<BarChart></BarChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
<PieChart></PieChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
<FunnelChart></FunnelChart>
</Widget>
在Widget
组件里我们只要把宽度和left
都乘以这个比例即可,为什么是乘,很简单:
newWidth / width = ratioWidth = windowWidth / originCanvasWidth
newWidth = width * ratioWidth
// left同样看做是一个距左侧的宽度即可
<div
class="widgetBox"
:style="
width: width * ratioWidth + 'px',
height: height + 'px',
left: left * ratioWidth + 'px',
top: top + 'px',
"
>
<slot></slot>
</div>
自适应屏幕
即宽高都自适应,和上一种方案相比,这种横竖都不会出现滚动条,且能完全铺满屏幕。
实现也很简单,在上一个【自适应宽度】的基础上加上高度自适应即可。
// 画布原始宽高
const originCanvasWidth = ref(canvasWidth.value);
const originCanvasHeight = ref(canvasHeight.value);
// 缩放比例
const ratioWidth = ref(1);
const ratioHeight = ref(1);
// 当前窗口的宽高
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
// 将画布宽高设置为当前窗口的宽高
canvasWidth.value = windowWidth;
canvasHeight.value = windowHeight;
// 计算当前宽高和原始宽高的比例
ratioWidth.value = windowWidth / originCanvasWidth.value;
ratioHeight.value = windowHeight / originCanvasHeight.value;
同样再把比例传给Widget
组件进行调整:
<Widget :ratioWidth="ratioWidth" :ratioHeight="ratioHeight">
<LineChart></LineChart>
</Widget>
<Widget :ratioWidth="ratioWidth" :ratioHeight="ratioHeight">
<BarChart></BarChart>
</Widget>
<Widget :ratioWidth="ratioWidth" :ratioHeight="ratioHeight">
<PieChart></PieChart>
</Widget>
<Widget :ratioWidth="ratioWidth" :ratioHeight="ratioHeight">
<FunnelChart></FunnelChart>
</Widget>
<div
class="widgetBox"
:style="
width: width * ratioWidth + 'px',
height: height * ratioHeight + 'px',
left: left * ratioWidth + 'px',
top: top * ratioHeight + 'px',
"
>
<slot></slot>
</div>
整体等比例缩放
即通过css
的transform
属性来对组件容器canvas
进行整体的缩放,保持原比例,在屏幕居中显示,当然你可以选择只缩放宽度或高度,但是这样会变形。
前面的两种方案,我们的组件开发时都必须要考虑容器的宽高,即需要进行适配,但是宽高比太极限了说实话很难处理,显示效果肯定是比较差的,但是这种整体等比例适配就无需考虑这种情况。
实际项目中如果有大屏需要适应屏幕,我一般都通过这种方法,优点是简单,缺点是水平或垂直空间上可能会留白,但是背景是全屏的,所以效果也不会很差。
实现也很简单,计算一下画布原始比例,再计算一下屏幕的比例,然后再判断是宽度和屏幕一致,高度自适应,还是高度和屏幕一致,宽度自适应:
// 当前窗口宽高比例
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
let windowRatio = windowWidth / windowHeight;
// 画布原始宽高比例
const canvasRatio = canvasWidth.value / canvasHeight.value;
// 计算画布适应后的新宽高
let newCanvasWidth = 0;
let newCanvasHeight = 0;
if (canvasRatio > windowRatio) // 画布的宽高比大于屏幕的宽高比
// 画布的宽度调整为屏幕的宽度
newCanvasWidth = windowWidth;
// 画布的高度根据画布原比例进行缩放
newCanvasHeight = windowWidth / canvasRatio;
else // 画布的宽高比小于屏幕的宽高比
// 画布的高度调整为屏幕的高度
newCanvasHeight = windowHeight;
// 画布的宽度根据画布原比例进行缩放
newCanvasWidth = windowHeight * canvasRatio;
// ...
假设屏幕的宽高相同,那么比例为1
。
第一种情况,假设画布的宽是高的两倍,那么比例为2
,要保持原比例2
适应屏幕,显然只能宽度和屏幕一致,高度自适应,因为如果高度和屏幕一致,那么宽度需要是高度的两倍,屏幕显然显示不下:
第二种情况,假设画布的高是宽的两倍,那么比例为0.5
,要保持比例为0.5
适应屏幕,需要高度和屏幕一致,宽度自适应:
计算完了画布适应屏幕后的新宽高,接下来就可以计算它相对于画布原始宽高的缩放比例:
// ...
// 相对于画布原始宽高的缩放比例
const canvasStyle = reactive(
transform: "",
);
const scaleX = newCanvasWidth / canvasWidth.value;
const scaleY = newCanvasHeight / canvasHeight.value;
canvasStyle.transform = `scale($scaleX, $scaleY)`
把样式添加到容器元素canvas
上即可:
<div
class="canvas"
:style="
width: canvasWidth + 'px',
height: canvasHeight + 'px',
...canvasStyle
"
>
</div>
显示的位置似乎有点问题,这其实是因为默认情况下元素的变换都是以自身的中心点为原点进行变换的:
我们只要改成以左上角为原点即可:
const canvasStyle = reactive(
transform: "",
transformOrigin: `left top`// 改成以左上角为变换原点
);
最后再来让它居中:
// 居中
const translateX = (windowWidth - newCanvasWidth) / 2 / scaleX;
const translateY = (windowHeight - newCanvasHeight) / 2 / scaleY;
canvasStyle.transform = `scale($scaleX, $scaleY) translate($translateXpx, $translateYpx)`;
窗口的宽高减去画布适应后的新宽高,即剩余的空间,再除以2
进行居中显示,为什么还要除以缩放值呢,因为translate
的值也会随scale
进行缩放,比如translateX
计算出来为100
,scaleX
为0.5
,那么实际上最终的偏移量为100*0.5=50
,这显然不对,所以我们除一个缩放值进行抵消。
这个方案似乎很完美,那么还有没有问题呢,显然是有的,一个小问题是缩放后文字可能会模糊,这个问题不大,笔者遇到的另一个问题是如果使用了getBoundingClientRect
方法获取元素信息,本意是获取元素原始的尺寸数据,但是缩放后返回的就是缩放后的数据,那么可能会和我们的原始意图出现偏差,比如有一个如下的div
:
<div ref="el1" style="width: 200px; height: 200px; background: red; position: absolute; left: 50px; top: 50px;"></div>
我们想要动态根据这个div
大小和位置复制一个div
:
<div ref="el2" style="background: green; position: absolute"></div>
const width, height = el1.value.getBoundingClientRect();
const left, top = window.getComputedStyle(el1.value);
el2.value.style.width = `$widthpx`;
el2.value.style.height = `$heightpx`;
el2.value.style.left = left;
el2.value.style.top = top;
可以看到获取到的宽高比实际的小了一点,这显然不是我们需要的,解决方法是要么不要使用getBoundingClientRect
方法,使用offsetWdith
等不会被缩放影响的方法或属性获取元素尺寸,要么把获取到的数据除以缩放值。
当然可能还会存在其他一些属性或方法也会存在这个问题,这就需要各位在实际的开发时进行测试了。
总结
本文简单总结了一下大屏适配的几种方法,没有哪一种是最好的,也没有哪一种是非常完美的,没办法,很多时候都是需要进行一定妥协的。
demo
地址:https://wanglin2.github.io/visual-drag-platform-fit-demo/
demo
仓库地址:https://github.com/wanglin2/visual-drag-platform-fit-demo
前端大屏常用的几种适配方案(代码片段)
...片等大小均能自动适配1.因为是根据ui稿等比缩放,当大屏跟ui稿的比例不一样时,会出现周边留白情况2.当缩放比例过大时候,字体会有一点点模糊,就一点点3.当缩放比例过大时候,事件热区会偏移。插件v-sc... 查看详情
使用动态rem方案适配不同屏幕尺寸
参考技术A大屏可视化项目在还原设计图时,需要考虑不同的屏幕尺寸,所以需要使用动态适配的方案来尽可能还原设计图。设计图比例为16/9,需要在不同屏幕尺寸(浏览器视口尺寸)下,大屏页面都能保证比例不变,元素和字体... 查看详情
前端可视化前端大屏适配方案(代码片段)
方案一:rem单位+动态设置html的font-size动态设置html根字体的大小和body字体大小(使用lib_flexible.js)将设计稿的宽(1920)平均分成24等份,每一份80px;html根字体大小就设置为80px,即1rem=80px,24rem=1920px(移动端推荐分为10份);将... 查看详情
数据可视化大屏屏幕适配
效果显示侧边栏时隐藏侧边栏时全屏时原理transform根据父盒子尺寸与原图尺寸的比例缩放代码伸缩盒子组件ScaleBox<template><divclass="scale_wrap"ref="scaleBoxRef"><divclass="ScreenAdapter":style="width:can... 查看详情
适配方案
目前来看,适配从设备来分大致分两种,一种是手机端适配,另一种是PC端适配。本次教程主要讲PC端适配。一,为什么讲述本次教程? 昨天不是很忙,在闲下来的时候看京东,淘宝,天猫的前端代码,当我改变浏... 查看详情
移动端主流适配方案
...优点:简单方便,只需要固定高度,宽度自适应;缺点:大屏幕手机实际显示的不协调。2、响应式布局优点:可以节约成本,不用再做专门的webapp网站(外包公司、小公司、博客);缺点:工作量大、维护很难;国内大型企业在... 查看详情
这些天来,为appsrc设置上限的几种方法中哪一种是好的和合适的?
】这些天来,为appsrc设置上限的几种方法中哪一种是好的和合适的?【英文标题】:Whichofseveralwaystosetthecapsforanappsrcisgoodandproperthesedays?【发布时间】:2018-01-2323:15:45【问题描述】:我正在Linux上用C++编写实验性gstreamer应用程序。... 查看详情
python实用小工具了解一下,总有一款是你需要的
1.实现简单探测使用socket模块,connect()方法建立与指定IP和端口的网络连接;revc(1024)方法将读取套接字中接下来的1024B数据通过函数实现通过def()关键字定义,示例中定义扫描FTPbanner信息的函数:迭代实现OS模块os.pa... 查看详情
100+大屏模板免费领!葡萄城bi行业应用方案重磅发布!
...作为基础战略资源和关键生产要素的地位日益凸显。数据可视化大屏作为一种用于数据分析的热门应用,能够帮助企业有效挖掘海量数据资产、实现差异化竞争。但我们发现在实际应用场景中,设计出完美适配行业典型... 查看详情
腾讯优测优分享|谈谈移动端屏幕适配的几种方法
腾讯优测是专业的移动云测试平台,自动化测试提供全面兼容性适配测试,云真机提供远程真机租用服务,优分享不定时提供大量移动研发及测试相关干货!移动端web开发相对于PC端web开发,我们可以庆幸不用... 查看详情
理解水平居中的几种表现(代码片段)
CSS居中算是一个比较基础的问题,在实际运用中,需要考虑到的一般是两种情况,一种是主要是表现为文字,图片等行内元素的居中,一种是指div等块级标签元素的居中。1.水平居中 text-align【场景一】:在父元素中设置text-a... 查看详情
开发测试调试问题的几种解决方案
...试,这无疑是非常麻烦的,这里记录了我尽可能简化操作的几种方案。 第一种:直接在webpack中设置将文件打包到Tomcat服务器下 通常来说,通过vue-cli构建的项目,会把dist 查看详情
移动适配请使用比rem等更好的布局方案
...幕都能比例协调的显示。下面我们先来看下大家耳熟能详的几种方案是如何去解决屏幕适配的。rem/em:rem根元 查看详情
100+大屏模板免费领!葡萄城bi行业应用方案重磅发布!
...作为基础战略资源和关键生产要素的地位日益凸显。数据可视化大屏作为一种用于数据分析的热门应用,能够帮助企业有效挖掘海量数据资产、实现差异化竞争。但我们发现在实际应用场景中,设计出完美适配行业典型... 查看详情
vue大屏自适应
...际业务中,我们常用图表来做数据统计,数据展示,数据可视化等比较直观的方式来达到一目了然的数据查看,但在大屏开发过程中,常会因为适配不同屏幕而感到困扰,下面我们来解决一下这个不算难题的难题废话不多说,先... 查看详情
微服务之间的几种调用方式哪种最佳?
推荐阅读:字节一面:单核CPU支持Java多线程吗?什么?在微服务架构中,需要调用很多服务才能完成一项功能。服务之间如何互相调用就变成微服务架构中的一个关键问题。服务调用有两种方式,一种是RP... 查看详情
两个大屏可视化案例的布局与实现
...期分别使用了 React 和 Vue 完成了两个大屏可视化案例,经历了设计师和产品经理的各种 “指指点点” ,也算是对可视化大屏项目有了一点点小的经验,对于两个技术栈写组件也有一点小心得,趁着... 查看详情
一种unity2d多分辨率屏幕适配方案
此文将阐述一种简单有效的Unity2D多分辨率屏幕适配方案,该方案适用于基于原生开发的Unity2D游戏,即没有使用第三方2D插件,如Uni2D,2Dtoolkit等开发的游戏,NGUI插件不受这个方案影响,可以完美和此方案配合使用。-------------------... 查看详情