2023-04-26 一次搞懂数据大屏适配方案 (vw vh、rem、scale)

https://juejin.cn/post/7163932925955112996#heading-20


假设设计稿尺寸为 1920*1080(做之前一定问清楚 ui 设计稿的尺寸)

即:

网页宽度=1920px

网页高度=1080px

我们都知道

网页宽度=100vw

网页宽度=100vh

所以,在 1920px*1080px 的屏幕分辨率下

1920px = 100vw

1080px = 100vh

这样一来,以一个宽 300px 和 200px 的 div 来说,其所占的宽高,以 vw 和 vh 为单位,计算方式如下:

vwDiv = (300px / 1920px ) * 100vw

vhDiv = (200px / 1080px ) * 100vh

所以,就在 1920*1080 的屏幕分辨率下,计算出了单个 div 的宽高

当屏幕放大或者缩小时,div 还是以 vw 和 vh 作为宽高的,就会自动适应不同分辨率的屏幕


// 使用 scss 的 math 函数,https://sass-lang.com/documentation/breaking-changes/slash-div

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

}

const path = require("path");

function resolve(dir) {

  return path.join(__dirname, dir);

}

module.exports = {

  publicPath: "",

  configureWebpack: {

    name: "app name",

    resolve: {

      alias: {

        "@": resolve("src"),

      },

    },

  },

  css: {

    // 全局配置 utils.scs,详细配置参考 vue-cli 官网

    loaderOptions: {

      sass: {

        prependData: `@import "@/styles/utils.scss";`,

      },

    },

  },

};

<template>

    <div class="box">

    </div>

</template>

<script>

export default{

    name: "Box",

}

</script>

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



@charset "utf-8";

// 默认设计稿的宽度

@designWidth: 1920;

// 默认设计稿的高度

@designHeight: 1080;

.px2vw(@name, @px) {

  @{name}: (@px / @designWidth) * 100vw;

}

.px2vh(@name, @px) {

  @{name}: (@px / @designHeight) * 100vh;

}

.px2font(@px) {

  font-size: (@px / @designWidth) * 100vw;

}

const path = require("path");

function resolve(dir) {

  return path.join(__dirname, dir);

}

module.exports = {

  publicPath: "",

  configureWebpack: {

    name: "app name",

    resolve: {

      alias: {

        "@": resolve("src"),

      },

    },

  },

  css: {

    // 全局配置utils.scss

    loaderOptions: {

      less: {

        additionalData: `@import "@/styles/utils.less";`,

      },

    },

  },

};


<template>

    <div class="box">

    </div>

</template>

<script>

export default{

    name: "Box",

}

</script>

<style lang="less" scoped="scoped">

/*

直接使用 vw 和 vh 函数,将像素值传进去,得到的就是具体的 vw vh单位

*/

.box{

    .px2vw(width, 300);

    .px2vh(height, 100);

    .px2font(16);

    .px2vw(margin-left, 300);

    .px2vh(margin-top, 100);

    background-color: black;

}

</style>


// 定义设计稿的宽高

const designWidth = 1920;

const designHeight = 1080;

// px转vw

export const px2vw = (_px) => {

  return (_px * 100.0) / designWidth + 'vw';

};

export const px2vh = (_px) => {

  return (_px * 100.0) / designHeight + 'vh';

};

export const px2font = (_px) => {

  return (_px * 100.0) / designWidth + 'vw';

};

// 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("chart-resize", { bind, unbind });


// Echarts图表字体、间距自适应

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.fitChartFont = fitChartSize;


<template>

  <div class="chartsdom" ref="chart" v-chart-resize></div>

</template>

<script>

export default {

  name: "dashboardChart",

  data() {

    return {

      option: null,

    };

  },

  mounted() {

    this.getEchart();

  },

  methods: {

    getEchart() {

      let myChart = this.$echarts.init(this.$refs.chart);

      const option = {

        backgroundColor: "transparent",

        tooltip: {

          trigger: "item",

          formatter: "{a} <br/>{b} : {c}%",

        },

        grid: {

          left: this.fitChartSize(10),

          right: this.fitChartSize(20),

          top: this.fitChartSize(20),

          bottom: this.fitChartSize(10),

          containLabel: true,

        },

        calculable: true,

        series: [

          {

            color: ["#0db1cdcc"],

            name: "计划投入",

            type: "funnel",

            width: "45%",

            height: "70%",

            x: "5%",

            minSize: "10%",

            funnelAlign: "right",

            center: ["50%", "50%"], // for pie

            data: [

              {

                value: 30,

                name: "下单30%",

              },

              {

                value: 55,

                name: "咨询55%",

              },

              {

                value: 65,

                name: "点击65%",

              },

              {

                value: 60,

                name: "访问62%",

              },

              {

                value: 80,

                name: "展现80%",

              },

            ].sort(function (a, b) {

              return a.value - b.value;

            }),

            roseType: true,

            label: {

              normal: {

                formatter: function () {},

                position: "inside",

              },

            },

            itemStyle: {

              normal: {

                borderWidth: 0,

                shadowBlur: this.fitChartSize(20),

                shadowOffsetX: 0,

                shadowOffsetY: this.fitChartSize(5),

                shadowColor: "rgba(0, 0, 0, 0.3)",

              },

            },

          },

          {

            color: ["#0C66FF"],

            name: "实际投入",

            type: "funnel",

            width: "45%",

            height: "70%",

            x: "50%",

            minSize: "10%",

            funnelAlign: "left",

            center: ["50%", "50%"], // for pie

            data: [

              {

                value: 35,

                name: "下单35%",

              },

              {

                value: 40,

                name: "咨询40%",

              },

              {

                value: 70,

                name: "访问70%",

              },

              {

                value: 90,

                name: "点击90%",

              },

              {

                value: 95,

                name: "展现95%",

              },

            ].sort(function (a, b) {

              return a.value - b.value;

            }),

            roseType: true,

            label: {

              normal: {

                position: "inside",

              },

            },

            itemStyle: {

              normal: {

                borderWidth: 0,

                shadowBlur: this.fitChartSize(20),

                shadowOffsetX: 0,

                shadowOffsetY: this.fitChartSize(5),

                shadowColor: "rgba(0, 0, 0, 0.3)",

              },

            },

          },

        ],

      };

      myChart.setOption(option, true);

    },

  },

  beforeDestroy() {},

};

</script>

<style lang="scss" scoped>

.chartsdom {

  width: 100%;

  height: 100%;

}

</style>

<script>

export default {

mounted() {

  // 初始化自适应  ----在刚显示的时候就开始适配一次

  handleScreenAuto();

  // 绑定自适应函数  ---防止浏览器栏变化后不再适配

  window.onresize = () => handleScreenAuto();

},

deleted() {

  window.onresize = null;

},

methods: {

  // 数据大屏自适应函数

  handleScreenAuto() {

    const designDraftWidth = 1920; //设计稿的宽度

    const designDraftHeight = 960; //设计稿的高度

    // 根据屏幕的变化适配的比例

    const scale =

      document.documentElement.clientWidth /

        document.documentElement.clientHeight <

      designDraftWidth / designDraftHeight

        ? document.documentElement.clientWidth / designDraftWidth

        : document.documentElement.clientHeight / designDraftHeight;

    // 缩放比例

    document.querySelector(

      '#screen',

    ).style.transform = `scale(${scale}) translate(-50%, -50%)`;

  },

},

};

</script>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容