首页 前端知识 vue实现pc端、移动端、大屏适配

vue实现pc端、移动端、大屏适配

2024-11-05 23:11:54 前端知识 前端哥 192 379 我要收藏

目录看这里😊

  • 方法:postcss-pxtorem结合amfe-flexible
    • 介绍
      • amfe-flexible
        • amfe-flexiable.js的源码
      • postcss-pxtorem
        • 注意事项
      • 2. 使用
        • 2.1 在main.js入口文件引入
        • 2.2 创建postcss.config.js配置文件
  • 大屏改进方案
    • scale
      • 1. 如何缩放
      • 2. 如何居中
      • 偷懒方法-插件
  • 图表适配
    • 思路
    • 其他方法

方法:postcss-pxtorem结合amfe-flexible

介绍

amfe-flexible

amfe-flexible 是一个由阿里巴巴前端团队开发的 JavaScript 库,用于实现移动端页面的自适应布局。
它通过动态设置 HTML 根元素的 font-size 来根据屏幕宽度进行适配。这个库是 lib-flexible 的升级版本。

简言之:

  • amfe-flexible会自动将html的font-size设置为屏幕宽度clientWidth的1/10,将body的font-size设置为12 * dpr 像素(一般为24px),单位为px。
  • 动态监听resize事件,调整html/body的fontsize

下图可以看到,在main.js中引入amfe-flexible,为自动在html和body上设置行内样式 font-size

在这里插入图片描述

下图可以看到,pc端调整屏幕大小resize时,html的font-size会根据屏幕宽度自动变更,大小始终为屏幕宽度clientWidth的1/10

在这里插入图片描述

amfe-flexiable.js的源码
// 以2.2.1版本为例
(function flexible (window, document) {
  var docEl = document.documentElement
  var dpr = window.devicePixelRatio || 1

  // adjust body font size
  function setBodyFontSize () {
    if (document.body) {
      document.body.style.fontSize = (12 * dpr) + 'px'
    }
    else {
      document.addEventListener('DOMContentLoaded', setBodyFontSize)
    }
  }
  setBodyFontSize();

  // set 1rem = viewWidth / 10
  function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
  }

  setRemUnit()

  // reset rem unit on page resize
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function (e) {
    if (e.persisted) {
      setRemUnit()
    }
  })

  // detect 0.5px supports
  if (dpr >= 2) {
    var fakeBody = document.createElement('body')
    var testElement = document.createElement('div')
    testElement.style.border = '.5px solid transparent'
    fakeBody.appendChild(testElement)
    docEl.appendChild(fakeBody)
    if (testElement.offsetHeight === 1) {
      docEl.classList.add('hairlines')
    }
    docEl.removeChild(fakeBody)
  }
}(window, document))

postcss-pxtorem

ostCSS 插件,它用于将 CSS 中的像素单位(px)转换为 rem 单位。这个插件对于创建响应式设计特别有用,因为它允许开发者使用 px 单位编写样式,然后在构建过程中自动转换成相对单位 rem,这样可以更方便地通过改变根元素(html)的字体大小来调整整个页面的布局尺寸(使得再写css时,可以依据设计图直接使用px作为单位,编译时插件自动将单位由px转为rem)

注意事项
  • 不能使用行内样式
    对于行内样式,阿里手淘并不能将px转rem,所以对于需要自适应的样式,如font-size、width、height等请不要写在行内。同理,对于不需要转化的样式可以写在行内,或者使用PX(大写)作为单位。
  • 字号不使用rem
    我们都知道chrome的最小显示的字体是12px,如果字体用rem,计算出来小于12px,那么就也会以12px显示,而且我们不希望出现13px或者15px这样的奇葩尺寸,所以字体最好是用PX(大写)来表示,至于适应,我们可以写媒体查询。
.item {
    border-bottom: 1PX #8d8d8d dashed;
    font-size: 12PX;
    line-height: 16PX;
    @media screen and (min-width: 576PX) {
        font-size: 14PX;
        line-height: 18PX;
    }
    @media screen and (min-width: 768PX) {
        font-size: 16PX;
        line-height: 28PX;
    }
    @media screen and (min-width: 992PX) {
        font-size: 16PX;
        line-height: 32PX;
    }
    @media screen and (min-width: 1200PX) {
        font-size: 18PX;
        line-height: 64PX;
    }
}
​// PC端响应式媒体断点:
​
```css
   @media (min-width: 1024px){
    body{font-size: 18px}
   }@media (min-width: 1100px) {
    body{font-size: 20px}
   } 
   @media (min-width: 1280px) {
    body{font-size: 22px;}
   }@media (min-width: 1366px) {
    body{font-size: 24px;}
   }@media (min-width: 1440px) {
     body{font-size: 25px !important;}
   }@media (min-width: 1680px) {
    body{font-size: 28px;}
   } 
   @media (min-width: 1920px) {
    body{font-size: 33px;}
   } 

### amfe-flexible不是已经给html自动设置了font-size,为什么还需要它通过pxtorem设置rootValue
+ pxtorem是将代码中的px转为rem,转换比例就是根据rootValue来计算的(1rem = rootValue的值+‘px’)
这样可以根据设计图量的的尺寸px,直接写到代码中,而不需要计算并手动折算成rem。比如设计图宽度是750px,那么rootValue=75,即按照75px=1rem,在编译的时候,将px转为rem单位,保证尺寸大小是个相对值
+ 渲染到页面上时,需要再将编译后的rem值转为px,这时候的转换比例就是1rem = 实际展示容器html的font-size值。amfe-flexible就是用来动态设置这个基准值的( 如果屏幕大小固定,其实就不需要amfe-flexible,手动设置html的font-size值就行了。但前端渲染的屏幕尺寸不同,因此需要根据屏幕尺寸大小动态的调整基准值。)
+ 简言之:pxtorem是方便我们"按照相对值复刻"设计图上的尺寸,即把px绝对值转为相对值rem(转换比例就是rootValue),保证比例不失真,渲染时需要再将相对值rem转为绝对值px(转换比例就是amfe-flexible自动设置的html的font-size)

## 安装和使用
### 1. 安装
```js
npm install amfe-flexible --save
npm install postcss-pxtorem --save-dev

2. 使用

2.1 在main.js入口文件引入
import 'amfe-flexible'
2.2 创建postcss.config.js配置文件

可在vue.config.js、.postcssrc.js、postcss.config.js其中之一配置,权重从左到右降低,没有则新建文件,只需要设置其中一个即可。

module.exports = {
	plugins:{
		autoprefixer:{}
		//f1exible配置
		'postcss-pxtorem': {
			rootVa1ue: 75, //设计稿宽度的1/10
			propList: ["*"], //需要做转化处理的属性,如`hight`、`width`、`margin`等,`*`表示全部		
			//selectorBlackList: ['van-'], // 忽略的选择器
		    //replace: true, // 直接替换原来的单位,不添加备选单位
		    //mediaQuery: false, // 是否在媒体查询中转换 px
		    //minPixelValue: 2, // 最小的转换数值
		}
    },
  },

注意点

  • rootValue根据设计稿宽度除以10进行设置,这边假设设计稿为375,即rootValuei设为37.5:
  • propList是设置需要转换的属性,这边*为所有都进行转换。

补充报错:
如果出现报错,请降低版本后重装依赖 ( 在package.json文件中将版本修改为如下,然后终端运行命令npm install即可 )

"dependencies":{
	"amfe-flexible":"^2.2.1",
	"postcss-pxtorem":"^5.1.1",
},
function setRem() {
    // 获取视口宽度
    const htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;

    // 设置最小宽度,防止字体大小过小
    const minWidth = 320;
    if (htmlWidth < minWidth) {
        htmlWidth = minWidth;
    }

    // 获取 html 元素
    const htmlDom = document.getElementsByTagName('html')[0];

    // 设置 html 元素的 font-size
    htmlDom.style.fontSize = (htmlWidth / 10) + 'px';
}

// 初始化时调用 setRem 函数
setRem();

// 监听窗口大小变化
let resizeTimeout;
window.addEventListener('resize', () => {
    // 使用 requestAnimationFrame 来减少频繁的计算
    if (resizeTimeout) {
        clearTimeout(resizeTimeout);
    }
    resizeTimeout = setTimeout(() => {
        setRem();
    }, 100); // 延迟 100 毫秒执行,避免频繁触发
});

大屏改进方案

  • 限制rem的最大值
  • 通过媒体查询,限制内容最大宽度
  • 图片使用SVG 和 Icon Font:使用 SVG 和 Icon Font 可以保证图标和图形在不同分辨率下的清晰度,避免位图在大屏上的失真问题。
    在这里插入图片描述

scale

通过 css 的 scale 属性,根据屏幕大小,对图表进行整体的等比缩放,从而达到自适应效果

  • 缺点: 在任意屏幕下保持固定比例,上下/左右会留白
  • 优化办法:对内容剧中,保证左右留白大小一致,在父容器中,设置背景颜色,从而"实现"全屏展示

1. 如何缩放

当屏幕宽高比 < 设计稿宽高比,我们需要缩放的比例是屏幕宽度 / 设计稿宽度当屏幕宽高比 > 设计稿宽高比,我们需要缩放的比例是屏幕高度 / 设计稿高度

const scale = document.documentElement.clientWidth / document.documentElement.clientHeight < designDraftWidth / designDraftHeight ?
            (document.documentElement.clientWidth / designDraftWidth) :
            (document.documentElement.clientHeight / designDraftHeight);

如果我们拿到的设计稿宽高为: 1920 * 960 px ,而我们的屏幕大小是 1440 * 900 px,那么 1440/900 = 1.6,920/960 = 2

因为 1.6 < 2 (当前屏幕宽高比小于设计稿宽高比)
所以我们需要缩放的比例是:屏幕宽度除以设计稿宽度 = 1440/1920 = 0.75

2. 如何居中

首先我们利用 transform:translate(-50%,-50%) ,将动画的基点设为左上角

transform-origin:设置动画的基点(中心点),默认点是元素的中心点
//语法
transform-origin: x-axis y-axis z-axis;

然后利用transform:translate(-50%,-50%),将图表沿 x,y 轴移动 50%
在这里插入图片描述
接下来利用绝对定位将图表定位到中间位置

position: absolute;
left: 50%;
top: 50%;

偷懒方法-插件

v-scale-screen是使用 css 属性 transform 实现缩放效果的一个大屏自适应组件,通过 scale 进行等比例计算,达到等比例缩放的效果,同时也支持铺满全屏,宽度等比,高度等比,等自适应方案,具体可查大屏自适应终极解决方案

图表适配

思路

  • 首先容器盒子大小要使用相对值,比如%、vh、vm
  • 默认情况下,这里以你的设计稿是 1920*1080 为例,即网页宽度是 1920px (做之前一定问清楚 ui 设计稿的尺寸)
  • 把这个函数写在一个单独的工具文件dataUtil.js里面,在需要的时候调用
  • 其原理是计算出当前屏幕宽度和默认设计宽度的比值,将原始的尺寸乘以该值
  • 另外,其它 echarts 的配置项,比如间距、定位、边距也可以用该函数
  1. 编写 dataUtil.js 工具函数
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));
  1. 将函数挂载到vue原型上
import {fitChartSize} from '@src/utils/dataUtil.js'
Vue.prototype.fitChartFont = fitChartSize;
  1. 使用fitChartFont调整echar中的尺寸大小
    有两种方法:
    1. echart的option中涉及到尺寸大小的,均调用fitChartFont方法,转换一下尺寸
    2. option中还是使用绝对数值px,另外封装一个处理option的方法(绝对值转为相对值),好处是可以批量处理,坏处是每次监听resize时,都需要先调用该方法,再执行echart.resize()

方法1:

<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);
      // 监听窗口大小变化,重新应用适配
	window.addEventListener('resize', () => {
	  myChart.setOption(option);
	  myChart.resize(); // 重新布局图表
	});
  },
  beforeDestroy() {},
};
</script>
 
<style lang="scss" scoped>
.chartsdom {
  width: 100%;
  height: 100%;
}
</style>

方法2:

// 假设你已经初始化了一个 ECharts 实例
const myChart = echarts.init(document.getElementById('main'));

// 定义默认的配置项
const option = {
  title: {
    text: '示例图表',
    left: 'center',
    top: 20,
    textStyle: {
      fontSize: 18, // 默认字体大小
    }
  },
  tooltip: {
    trigger: 'axis'
  },
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
    axisLabel: {
      interval: 0,
      rotate: 30,
      margin: 15, // 默认间距
      fontSize: 12, // 默认字体大小
    }
  },
  yAxis: {
    type: 'value',
    axisLabel: {
      fontSize: 12, // 默认字体大小
    }
  },
  series: [{
    name: '销量',
    type: 'line',
    data: [820, 932, 901, 934, 1290, 1330, 1320]
  }]
};

// 应用 fitChartSize 函数调整字体大小和间距
function applyFitChartSize(option) {
  const defalteWidth = 1920;

  // 调整标题字体大小
  if (option.title && option.title.textStyle) {
    option.title.textStyle.fontSize = fitChartSize(option.title.textStyle.fontSize, defalteWidth);
  }

  // 调整 x 轴标签字体大小和间距
  if (option.xAxis && option.xAxis.axisLabel) {
    option.xAxis.axisLabel.fontSize = fitChartSize(option.xAxis.axisLabel.fontSize, defalteWidth);
    option.xAxis.axisLabel.margin = fitChartSize(option.xAxis.axisLabel.margin, defalteWidth);
  }

  // 调整 y 轴标签字体大小
  if (option.yAxis && option.yAxis.axisLabel) {
    option.yAxis.axisLabel.fontSize = fitChartSize(option.yAxis.axisLabel.fontSize, defalteWidth);
  }

  // 其他需要调整的部分...
}

// 在设置选项之前应用适配
applyFitChartSize(option);

// 设置 ECharts 选项
myChart.setOption(option);

// 监听窗口大小变化,重新应用适配
window.addEventListener('resize', () => {
  applyFitChartSize(option);
  myChart.setOption(option);
  myChart.resize(); // 重新布局图表
});

其他方法

转载请注明出处或者链接地址:https://www.qianduange.cn//article/20076.html
标签
评论
发布的文章
大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!