几行代码实现vue3数据大屏自适应~~

简介: 几行代码实现vue3数据大屏自适应~~

一:适用场景

最近在搭建vue3+ts+vite的一个后台模板,再做数据大屏的时候,放大或缩小浏览器会导致页面盒子无法铺满以及不能按照初始比例渲染;最后找到了以下这个方法可以很好地让大屏按照比例放大缩小,希望可以帮到大家。

二:先看效果

2023-04-16 (4).png

大屏自适应

三:代码实现

<script setup lang='ts'>
  import {onMounted, ref} from 'vue';
  import {useRouter} from "vue-router";
  import * as echarts from 'echarts';
  import {init, EChartsOption} from 'echarts';
  import mapJson from './modules/china.json'
  const loading = ref(true)
  // 获取自适应的盒子,也就是包裹echarts的最外层父级盒子
  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
    initChart();
    // 重点二:
    // 为浏览器绑定事件
    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();
    // });
  };
  // 注册echarts
  function initChart() {
    const myChart = init(document.getElementById('map') as HTMLDivElement)
    const myChart2 = init(document.getElementById('map2') as HTMLDivElement)
    const myChart3 = init(document.getElementById('map3') as HTMLDivElement)
    const myChart4 = init(document.getElementById('map4') as HTMLDivElement)
    const myChart5 = init(document.getElementById('map5') as HTMLDivElement)
    const myChart6 = init(document.getElementById('map6') as HTMLDivElement)
    echarts.registerMap('china', mapJson)
    const option = <EChartsOption>{
      backgroundColor: "transparent",
      series: [
        {
          type: "map",
          roam: false,
          top: 100,
          label: {
            normal: {
              show: true,
              textStyle: {
                color: "#fff",
              },
            },
            emphasis: {
              textStyle: {
                color: "#fff",
              },
            },
          },
          itemStyle: {
            normal: {
              borderColor: "#2ab8ff",
              borderWidth: 1.5,
              areaColor: "#12235c",
            },
            emphasis: {
              areaColor: "#2AB8FF",
              borderWidth: 0,
              color: "green",
            },
          },
          zoom: 1.5,
          roam: true,
          map: "china", //使用
        },
      ],
    }
    myChart.setOption(option)
    myChart2.setOption(option)
    myChart3.setOption(option)
    myChart4.setOption(option)
    myChart5.setOption(option)
    myChart6.setOption(option)
    window.addEventListener("resize", function () {
      myChart.resize();
      myChart2.resize();
      myChart3.resize();
      myChart4.resize();
      myChart5.resize();
      myChart6.resize();
    });
    setTimeout(() => {
      loading.value = false
    }, 2000)
  }
  // 返回首页
  const router = useRouter();
  function backIndex() {
    // console.log('关闭');
    router.push('/')
  }
  // 获取当前时间
  const currentDate = ref('');
  const a = ["日", "一", "二", "三", "四", "五", "六"];
  const week = ref(null);
  const str = ref('');
  const timeInterval = setInterval(()=>{
    currentDate.value = new Date().toLocaleString()
    week.value = new Date().getDay();
    str.value = "今天是星期"+ a[week.value]
  },1000)
</script>
<template>
  <div class="screen-bg" v-loading="loading">
    <div class="container" ref="dataScreenRef">
      <div class="header">
        <div class="date">{{currentDate + '&nbsp; &nbsp; &nbsp; ' + str}}</div>
        <div class="title">国内贫富差距分布图</div>
            <div class="light" @click="backIndex">
              <div></div>
              <div></div>
              <div></div>
              <div></div>
              X
            </div>
      </div>
      <div class="body">
        <div class="map-box">
          <div id="map"></div>
        </div>
        <div class="right-data">
          <div class="item-box">
            <div id="map2"></div>
          </div>
          <div class="item-box">
            <div id="map3"></div>
          </div>
        </div>
      </div>
      <div class="footer">
        <div class="item-box">
          <div id="map4"></div>
        </div>
        <div class="item-box">
          <div id="map5"></div>
        </div>
        <div class="item-box">
          <div id="map6"></div>
        </div>
      </div>
    </div>
  </div>
</template>
<style scoped lang='scss'>
  // 背景
  .screen-bg {
    width: 100%;
    height: 100%;
    background: url('@/assets/dataScreen/background-6.png') no-repeat;
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-position: center;
    background-size: 100% 100%;
    background-size: cover;
    .light {
      width: 40px;
      height: 30px;
      position: absolute;
      top: 42px;
      right: 40px;
      z-index: 99999;
      transform: translate(-50%, -50%);
      /* width: fit-content; */
      text-align: center;
      line-height: 30px;
      color: #ffffff;
      font-size: 20px;
      text-transform: uppercase;
      transition: 0.5s;
      letter-spacing: 4px;
      cursor: pointer;
      overflow: hidden;
    }
    .light:hover {
      background-color: #ffffff;
      color: #050801;
      box-shadow: 0 0 5px #ffffff,
      0 0 25px #ffffff,
      0 0 50px #ffffff,
      0 0 200px #ffffff;
    }
    .light div {
      position: absolute;
    }
    .light div:nth-child(1) {
      width: 100%;
      height: 2px;
      top: 0;
      left: -100%;
      background: linear-gradient(to right, transparent, #ffffff);
      animation: animate1 1s linear infinite;
    }
    .light div:nth-child(2) {
      width: 2px;
      height: 100%;
      top: -100%;
      right: 0;
      background: linear-gradient(to bottom, transparent, #ffffff);
      animation: animate2 1s linear infinite;
      animation-delay: 0.25s;
    }
    .light div:nth-child(3) {
      width: 100%;
      height: 2px;
      bottom: 0;
      right: -100%;
      background: linear-gradient(to left, transparent, #ffffff);
      animation: animate3 1s linear infinite;
      animation-delay: 0.5s;
    }
    .light div:nth-child(4) {
      width: 2px;
      height: 100%;
      bottom: -100%;
      left: 0;
      background: linear-gradient(to top, transparent, #ffffff);
      animation: animate4 1s linear infinite;
      animation-delay: 0.75s;
    }
    @keyframes animate1 {
      0% {
        left: -100%;
      }
      50%,
      100% {
        left: 100%;
      }
    }
    @keyframes animate2 {
      0% {
        top: -100%;
      }
      50%,
      100% {
        top: 100%;
      }
    }
    @keyframes animate3 {
      0% {
        right: -100%;
      }
      50%,
      100% {
        right: 100%;
      }
    }
    @keyframes animate4 {
      0% {
        bottom: -100%;
      }
      50%,
      100% {
        bottom: 100%;
      }
    }
    // 中心容器
    .container {
      position: fixed;
      top: 50%;
      left: 50%;
      z-index: 999;
      display: flex;
      flex-direction: column;
      overflow: hidden;
      transition: all 0.3s;
      transform-origin: left top;
      /*!* 设置一个最小的宽度 *!*/
      min-width: 1500px;
      /* 设置一个最大的宽度 */
      max-width: 1500px;
      margin: 0 auto;
      padding-top: 20px;
      .header {
        position: relative;
        height: 77px;
        margin-bottom: 20px;
        background-image: url('@/assets/dataScreen/pic-12.png');
        background-repeat: no-repeat;
        background-size: cover;
        .date {
          position: absolute;
          left: 100px;
          top: 55%;
          transform: translateY(-50%);
          font-family: FZHei-B01S, Arial, sans-serif;
          font-size: 20px;
          color: rgb(255, 255, 255);
          font-weight: normal;
          justify-content: center;
          text-align: center;
        }
        .title {
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          font-family: FZHei-B01S, Arial, sans-serif;
          font-size: 30px;
          color: rgb(255, 255, 255);
          font-weight: normal;
          justify-content: center;
          text-align: center;
        }
      }
      .body {
        display: flex;
        justify-content: space-between;
        .map-box {
          width: 700px;
          height: 430px;
          margin-right: 20px;
          border-width: 2px;
          border-color: rgb(14, 71, 143);
          border-style: solid;
          // background-color: rgba(14, 71, 143, 0.32);
          // background-clip: padding-box;
          background-image: url('@/assets/dataScreen/border-2.gif');
          background-size: 100% 100%;
          background-repeat: no-repeat;
          filter: blur(0px);
          #map {
            width: 100%;
            height: 100%;
          }
        }
        .right-data {
          flex: 1;
          display: flex;
          justify-content: space-between;
          background-color: transparent;
          .item-box {
            width: 350px;
            border-width: 2px;
            border-color: rgb(14, 71, 143);
            border-style: solid;
            background-image: url('@/assets/dataScreen/border-2.gif');
            background-size: 100% 100%;
            background-repeat: no-repeat;
            filter: blur(0px);
            &:last-child {
              margin-right: 0;
            }
            #map2,
            #map3 {
              width: 100%;
              height: 100%;
            }
          }
        }
      }
      .footer {
        margin-top: 20px;
        display: flex;
        justify-content: space-between;
        .item-box {
          width: 440px;
          height: 250px;
          border-width: 2px;
          border-color: rgb(14, 71, 143);
          border-style: solid;
          background-image: url('@/assets/dataScreen/border-2.gif');
          background-size: contain;
          background-repeat: no-repeat;
          filter: blur(0px);
          #map4,
          #map5,
          #map6 {
            width: 100%;
            height: 100%;
          }
        }
      }
    }
  }</style>


目录
相关文章
|
2月前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
141 64
|
2月前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
114 60
|
10天前
|
JavaScript API 数据处理
vue3使用pinia中的actions,需要调用接口的话
通过上述步骤,您可以在Vue 3中使用Pinia和actions来管理状态并调用API接口。Pinia的简洁设计使得状态管理和异步操作更加直观和易于维护。无论是安装配置、创建Store还是在组件中使用Store,都能轻松实现高效的状态管理和数据处理。
38 3
|
2月前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
39 8
|
2月前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
32 1
|
2月前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
42 1
|
2月前
|
JavaScript
在 Vue 3 中,如何使用 v-model 来处理自定义组件的双向数据绑定?
需要注意的是,在实际开发中,根据具体的业务需求和组件设计,可能需要对上述步骤进行适当的调整和优化,以确保双向数据绑定的正确性和稳定性。同时,深入理解 Vue 3 的响应式机制和组件通信原理,将有助于更好地运用 `v-model` 实现自定义组件的双向数据绑定。
|
2月前
|
JavaScript 索引
Vue 3.x 版本中双向数据绑定的底层实现有哪些变化
从Vue 2.x的`Object.defineProperty`到Vue 3.x的`Proxy`,实现了更高效的数据劫持与响应式处理。`Proxy`不仅能够代理整个对象,动态响应属性的增删,还优化了嵌套对象的处理和依赖追踪,减少了不必要的视图更新,提升了性能。同时,Vue 3.x对数组的响应式处理也更加灵活,简化了开发流程。
|
2月前
|
JavaScript 前端开发 API
从Vue 2到Vue 3的演进
从Vue 2到Vue 3的演进
42 0
|
2月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
65 0

热门文章

最新文章