There are several screen adaptation schemes for visual large screens, there is always one that you need

Suppose we are developing a visual drag-and-drop construction platform, which can generate a workbench or a large-scale visualization screen by dragging and dropping, or directly develop a large-scale screen. One of the first issues that must be considered is how to adapt the page to the screen, because we are building or developing Generally, it is based on a fixed width and height, but the actual screen size may vary. Next, we will try several simple and common solutions, and briefly analyze the pros and cons.

demo

First write a basic one demofor subsequent use:

<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>

The width and height of each chart component are set 100%and then Widgetwrapped by the component, so the actual width and height depend on Widgetthe component, Widgetthe component is absolutely positioned, and the width, height, and position are passed propsin to simulate our drag and drop operation. For simplicity, We set the width and height of all charts to be the same.

WidgetComponents:

<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>

The overall container of the component is the element with the class name canvas, which is relatively positioned, and the width and height are also dynamically set. The width and height canvasof the parent canvasBoxelement of the element are set to be consistent with the screen width and height.

fixed size

That is, the width and height are fixed, and if the width and height are smaller than the screen width and height, it will be centered on the screen.

This is the simplest solution, which is equivalent to not fitting the screen. The size of the canvas configuration is actually the size, and does not change with the change of the screen. Therefore, the width and height of each component will not change after configuration. It is generally used for fixed size and A large visual screen that will not change later.

Our previous demoinitial is this way:

Of course, if the width and height are smaller than the screen, the logic of centering needs to be added. There are many methods of centering, both can be passed css, jsand you can do it according to your preferences:

// 画布的位置
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>

Determine whether the width and height of the window are greater than the width and height of the canvas, if yes, adjust by leftor :top

adaptive width

That is, the width adapts to the screen, and the height remains unchanged. The disadvantage of this solution is that there will be scroll bars in the vertical direction.

For example, if the width of the canvas is set to 1920, but the actual screen width is 1280, 1.5then the width of the canvas and each component needs to be reduced by 1.5a factor of 2, and leftthe value of each component also needs to be adjusted dynamically.

First implement canvasthe size adjustment of the container element:

// 保存原始画布的宽度
const originCanvasWidth = ref(canvasWidth.value);
// 宽度缩放比例
const ratioWidth = ref(1);

// 当前窗口的宽度
let windowWidth = window.innerWidth;
// 将画布宽度设置为当前窗口的宽度
canvasWidth.value = windowWidth;
// 计算当前宽度和原始宽度的比例
ratioWidth.value = windowWidth / originCanvasWidth.value;

Then pass this ratio to Widgetthe component for adjustment:

<Widget :ratioWidth="ratioWidth">
    <LineChart></LineChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
    <BarChart></BarChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
    <PieChart></PieChart>
</Widget>
<Widget :ratioWidth="ratioWidth">
    <FunnelChart></FunnelChart>
</Widget>

In Widgetthe component, we only leftneed to multiply the width and sum by this ratio. Why is it multiplied? It's very simple:

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>

adaptive screen

That is, the width and height are both adaptive. Compared with the previous solution, there will be no scroll bars in this way, and it can completely cover the screen.

The implementation is also very simple, just add height adaptation on the basis of the previous [Adaptive Width].

// 画布原始宽高
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;

Also pass the ratio to Widgetthe component for adjustment:

<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>

Overall scaling

That is, the component container is scaled as a whole through the attribute, keeping the original proportion, and displaying it in the center of the screen. Of course, you can choose to only scale the width or height, but this will cssdeform transform.canvas

For the previous two solutions, we must consider the width and height of the container when developing components, that is, we need to adapt, but the aspect ratio is too extreme to be honest, it is difficult to deal with, and the display effect is definitely relatively poor, but this kind of The overall proportional adaptation does not need to consider this situation.

In actual projects, if there is a large screen that needs to adapt to the screen, I usually use this method. The advantage is that it is simple, and the disadvantage is that there may be blank space in the horizontal or vertical space, but the background is full screen, so the effect will not be bad.

The implementation is also very simple. Calculate the original ratio of the canvas, then calculate the ratio of the screen, and then judge whether the width is consistent with the screen and the height is adaptive, or the height is consistent with the screen and the width is adaptive:

// 当前窗口宽高比例
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;
}
// ...

Assuming the screen has the same width and height, then the ratio is 1.

In the first case, assuming that the width of the canvas is twice the height, then the ratio is 2, to keep the original ratio 2to fit the screen, obviously only the width is the same as the screen, and the height is adaptive, because if the height is the same as the screen, then the width needs to be the height Twice the size, the screen obviously cannot display:

In the second case, assuming that the height of the canvas is twice the width, then the ratio is 0.5, to keep the ratio to 0.5fit the screen, the height needs to be consistent with the screen, and the width is adaptive:

After calculating the new width and height of the canvas adapted to the screen, you can then calculate its scaling ratio relative to the original width and height of the canvas:

// ...
// 相对于画布原始宽高的缩放比例
const canvasStyle = reactive({
    
    
    transform: "",
});
const scaleX = newCanvasWidth / canvasWidth.value;
const scaleY = newCanvasHeight / canvasHeight.value;
canvasStyle.transform = `scale(${
      
      scaleX}, ${
      
      scaleY})`

Just add the style canvasto the container element:

<div
      class="canvas"
      :style="{
        width: canvasWidth + 'px',
        height: canvasHeight + 'px',
        ...canvasStyle
      }"
    >
</div>

The displayed position seems to be a bit problematic. This is actually because by default, the transformation of the element is transformed with its own center point as the origin:

We just need to change the upper left corner as the origin:

const canvasStyle = reactive({
    
    
  transform: "",
  transformOrigin: `left top`// 改成以左上角为变换原点
});

Finally let's center it:

// 居中
const translateX = (windowWidth - newCanvasWidth) / 2 / scaleX;
const translateY = (windowHeight - newCanvasHeight) / 2 / scaleY;
canvasStyle.transform = `scale(${
      
      scaleX}, ${
      
      scaleY}) translate(${
      
      translateX}px, ${
      
      translateY}px)`;

The width and height of the window minus the new width and height after the canvas is adapted, that is, the remaining space, and then divided by the 2centered display, why should it be divided by the zoom value, because translatethe value of will also scalebe scaled accordingly, for example, translateXcalculated as 100, scaleXfor 0.5, then the actual final offset is 100*0.5=50, which is obviously wrong, so we divide a scaling value to offset.

This solution seems to be perfect, so is there any problem? Obviously, there is a small problem that the text may be blurred after zooming, which is not a big problem. Another problem I encountered is that if the method is used to obtain element information getBoundingClientRect, The original intention is to get the original size data of the element, but after zooming, the zoomed data is returned, so it may deviate from our original intention, for example, there is one as follows div:

<div ref="el1" style="width: 200px; height: 200px; background: red; position: absolute; left: 50px; top: 50px;"></div>

We want to dynamically divmake a copy based on this size and position 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 = `${
      
      width}px`;
el2.value.style.height = `${
      
      height}px`;
el2.value.style.left = left;
el2.value.style.top = top;

It can be seen that the obtained aspect ratio is a little smaller than the actual one, which is obviously not what we need. The solution is to either not use the getBoundingClientRectmethod, use offsetWditha method or attribute that will not be affected by scaling to obtain the element size, or use the acquired Data divided by the scaling value.

Of course, there may be other attributes or methods that also have this problem, which requires you to test it during actual development.

Summarize

This article briefly summarizes several methods of large-screen adaptation, none of which is the best, and no one is perfect. There is no way, and in many cases a certain compromise is required.

demoAddress: https://wanglin2.github.io/visual-drag-platform-fit-demo/

demoWarehouse address: https://github.com/wanglin2/visual-drag-platform-fit-demo

Guess you like

Origin blog.csdn.net/sinat_33488770/article/details/127836884