博客 / 詳情

返回

屏幕適配方案——詳細完整版

適用框架:Vue2/Vue3
適用設備:pc端/移動端
適配策略:動態rem+動態scale
方案效果:可讓頁面在不同屏幕下、放大縮小時保持頁面不變形

效果示例:

當屏幕變化時:
請添加圖片描述

當放大縮小時
請添加圖片描述

安裝(屏幕適配插件)

npm i screen-adapter-plugin

適配寫法(推薦):

  • 寫class樣式時,使用px單位,class內的px單位編譯後會轉成rem;內聯樣式需要用px函數px(12)轉為rem,px函數已經掛載在Vue的this上。
  • 若想讓class樣式不被轉為rem,可使用.norem-開頭的class名稱,其大括號範圍內所有樣式不會被轉為rem,或使用大寫的PX單位(需要按文檔配置postcss.plugin)
  • rem只隨視口的寬度動態調節,若想讓元素高度隨視口高度變化,可使用vh、%或其他單位
  • 內部無法轉為rem的插件,例如echarts、relation-graph等,可在元素上綁定v-scale

    v-scale適合內部沒有rem單位的元素,通過transform的scale屬性讓該元素寬高隨視口的寬度自適應。

    它還可以傳入一個監聽函數,第一個參數為綁定該指令的元素dom,第二個參數為該元素被放大的倍數,其在視口變化時會自動執行

    // 使用方法一,例如echarts元素
    <div ref="echartsRef" style="width: 500px;height: 400px;" v-scale></div>
    
    // 使用方法二,傳入監聽函數
    <div ref="echartsRef" style="width: 500px;height: 400px;" v-scale=="handlerAdaptScale"></div>
    
    methods: {
      handlerAdaptScale(el, scale) {
        // do sth...
      },
    }

typescript註解:

// px函數註解,可轉換為rem,或在第二個入參傳入true,獲得動態number類型的px
type PX = (px: number, real: boolean) => string | number

// Vue.use時傳入的options
interface InstallOptions {
    rootValue: number
}

// 插件提供的方法
interface ScreenAdapter {
    rootFontSize: number // 根元素上動態的font-size

    init(): void         // Vue.use時會自動調用,初始化適配策略

    destroy(): void      // 銷燬適配策略

    getScale(): number      // 獲得v-scale被放大縮小的倍數

    addListener(callback: Function): void    // 添加屏幕變化時的監聽函數

    removeListener(callback: Function): void // 移除屏幕變化時的監聽函數

    px: PX
}

項目配置

// index.html (防止h5端用户手動放大縮小)
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=no;" />


// package.json 安裝postcss-plugin-px2rem
"devDependencies": {
  "postcss-plugin-px2rem": "^0.8.1",    
}

// 配置px2rem
 // postcss.config.js 寫法
 module.exports = {
    plugins: {
    'postcss-plugin-px2rem': {
      rootValue: 192, // 設計稿寬度 / 10
      propList: ["*"],
      unitPrecision: 5,
      selectorBlackList: [/.norem-.*/], // 開頭為.norem-的class的大括號範圍內所有樣式不會被轉為rem
      ignoreIdentifier: false,
      replace: true,
      mediaQuery: false,
    },
  },
 }
 // 或者vite.config.js 寫法
 export default defineConfig({
    css: {
      postcss: {
       plugins: [
        px2rem({
          rootValue: 192, // 設計稿寬度 / 10
          propList: ["*"],
          unitPrecision: 5,
          selectorBlackList: [/.norem-.*/], // 開頭為.norem-的class的大括號範圍內所有樣式不會被轉為rem
          ignoreIdentifier: false,
          replace: true,
          mediaQuery: false,
        }),
      ]
    }
  },
 })
  
  
// main.js
import screenAdapter from 'screen-adapter'

Vue.use(screenAdapter, {rootValue: 192}) // 掛載screenAdapter類,傳入跟px2rem插件一致的rootValue


// 調用方式
window.screenAdapter
this.screenAdapter
this.px(_,?_)

// 自定義指令使用方式
v-scale
v-scale="handlerAdaptScale"

常見問題

  1. 放大縮小過程中,有個別元素變形?

    • 寫內聯樣式時,未使用px函數包裹,另外有些組件例如el-table-column的寬度只支持傳入px單位的數值,不支持傳入rem,可使用px函數px(12, true),將第二個參數設置為true,此時會根據屏幕大小傳入動態的px數值
    • 設置父元素line-height:0或者font-size:0
    • 內部無法轉化為rem的組件,例如Echarts,可使用v-scale指令
  2. 文字邊緣模糊?

    • 可以增加css text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-smooth: always;或者font-weight: bold;但效果有限
  3. Echarts圖表樣式邊緣模糊?

    • 使用svg渲染器 echarts.init(this.$refs.chart, null, { renderer: "svg" })
  4. 使用v-scale時會有留白或溢出?

    • v-scale根據視口的寬度縮放元素。如果父元素使用的vh、%這種視口單位,當視口的寬高比小於元素的寬高比,父級元素就會有留白;當父級元素大於元素的寬高比,元素就會有溢出。

      1. 這些情況可把v-scale提升到上面父級
      2. 內部樣式使用不會被轉為rem的寫法

      適配完讓整個頁面的底部留白或溢出產生滾動條,這是正常的。

      如果確實不想存在留白或滾動,想要高度也自適應的頁面,可以為元素綁定key值,視口變化時讓其重新渲染:

      <template>
          <div v-scale :key="scaleKey"></div>
      </template>
      
      import { debounce } from "lodash";
      export default {
          data() {
            return {
               scaleKey: 1,
            }
          },
          created() {
            // 視口變化時讓元素重新綁定v-scale
            this.screenAdapter.addListener(this.debounceRefreshHeightScale);
          },
          beforeDestroy() {
            this.screenAdapter.removeListener(this.debounceRefreshHeightScale);
          },
          methods: {
            debounceRefreshHeightScale: debounce(function () {
              this.scaleKey++;
            }, 500),
          }
      }

      此方法比較耗費性能,請謹慎使用!

  5. 使用v-scale的元素寬高顯示有問題?

    • 如果使用v-scale的元素的寬高使用的百分比,圖表就有可能在屏幕變化時因為渲染時機問題獲得錯誤的寬高,此時可以使用真px值<Echarts width="400px" height="300px"/>讓其固定寬高,或用v-if綁定接口的數據來源<Echarts v-if="data.length > 0"/>讓其滯後渲染
  6. VScode強制將大寫PX轉為小寫px

    • 在VScode中使用Vue-official插件,並將其選為默認格式化配置,就不會格式化PX了
  7. 為什麼設計時不讓元素隨視口高度縮放?

    • 現在所有視圖設計的基本特點就是內容過多時產生垂直滾動條,並且用户天生有向下滾動的直覺,另外瀏覽器也並未提供可以一直準確有效的拿到視口高度的方法,如果想隨視口高度適應,可自行使用vh、%或其他寫法滿足需要。
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.