天天看點

開發移動端常見的問題集合

ios

滑動不流暢

  • 上下滑動頁面會産生卡頓,手指離開頁面,頁面立即停止運動。整體表現就是滑動不流暢,沒有滑動慣性。
  • 原來在

    iOS 5.0

    以及之後的版本,滑動有定義有兩個值

    auto

    touch

    ,預設值為

    auto

    -webkit-overflow-scrolling: touch; /* 當手指從觸摸屏上移開,會保持一段時間的滾動 */
    
    -webkit-overflow-scrolling: auto; /* 當手指從觸摸屏上移開,滾動會立即停止 */           
  • 在滾動容器上增加滾動 touch 方法

    .wrapper {
        -webkit-overflow-scrolling: touch;
    }           
  • 設定 overflow

    設定外部

    overflow

    hidden

    ,設定内容元素

    overflow

    auto

    。内部元素超出 body 即産生滾動,超出的部分 body 隐藏。
    body {
        overflow-y: hidden;
    }
    .wrapper {
        overflow-y: auto;
    }           

頁面放大或縮小不确定性行為

  • 輕按兩下或者雙指張開手指頁面元素,頁面會放大或縮小。
  • 移動端正常寫法
    <meta name="viewport" content="width=device-width, initial-scale=1.0">           
  • 是以我們可以設定

    maximum-scale

    minimum-scale

    user-scalable=no

    用來避免這個問題
    <meta name=viewport
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0 maximum-scale=1.0, user-scalable=no">           

click 點選事件延時與穿透

  • 監聽元素

    click

    事件,點選元素觸發時間延遲約

    300ms

  • 點選蒙層,蒙層消失後,下層元素點選觸發。
  • iOS

    中的 safari,為了實作輕按兩下縮放操作,在單擊

    300ms

    之後,如果未進行第二次點選,則執行

    click

    單擊操作。也就是說來判斷使用者行為是否為輕按兩下産生的。但是,在

    App

    中,無論是否需要輕按兩下縮放這種行為,

    click

    單擊都會産生

    300ms

    延遲。
  • 使用

    touchstart

    替換 click

el.addEventListener("touchstart", () => { console.log("ok"); }, false);           
  • `vue

    中使用
    <button @touchstart="handleTouchstart()">點選</button>           
  • fastclick

    import FastClick from 'fastclick';
    
    FastClick.attach(document.body, options);           

iPhone X

系列安全區域适配問題

  • 頭部劉海兩側區域或者底部區域,出現劉海遮擋文字,或者呈現黑底或白底空白區域。
  • 設定

    viewport-fit

    cover

    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover">
               
    • safe area inset

      變量
    /* 适配 iPhone X 頂部填充*/
    @supports (top: env(safe-area-inset-top)){
      body,
      .header{
          padding-top: constant(safe-area-inset-top, 40px);
          padding-top: env(safe-area-inset-top, 40px);
          padding-top: var(safe-area-inset-top, 40px);
      }
    }
    /* 判斷iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
    @supports (bottom: env(safe-area-inset-bottom)){
        body,
        .footer{
            padding-bottom: constant(safe-area-inset-bottom, 20px);
            padding-bottom: env(safe-area-inset-bottom, 20px);
            padding-top: var(safe-area-inset-bottom, 20px);
        }
    }           
    • safe-area-inset-left

      :安全區域距離左邊邊界距離
    • safe-area-inset-right

      :安全區域距離右邊邊界距離
    • safe-area-inset-top

      :安全區域距離頂部邊界距離
    • safe-area-inset-bottom

      :安全區域距離底部邊界距離
    safe-area-inset-top

    ,

    safe-area-inset-right

    ,

    safe-area-inset-bottom

    ,

    safe-area-inset-left safe-area-inset-*

    由四個定義了視口邊緣内矩形的

    top

    ,

    right

    ,

    bottom

    left

    的環境變量組成,這樣可以安全地放入内容,而不會有被非矩形的顯示切斷的風險。對于矩形視口,例如普通的筆記本電腦顯示器,其值等于零。對于非矩形顯示器(如圓形表盤,

    iPhoneX` 螢幕),在使用者代理設定的四個值形成的矩形内,所有内容均可見。

H5

調試相關方案政策

  • vconsole

    控制台插件

    import Vconsole from 'vconsole'
    
    new Vconsole()           
  • eruda

    手機端調試
  • 隻要在我們的

    `html

    檔案中寫入下面這些代碼,在手機上,也能想浏覽器控制台一樣進行檢視。
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>
   eruda.init();
</script>           

H5

頁面播放視訊

  • H5

    頁面播放視訊,不全屏播放,在video标簽添加

    playsinline

    x5-playsinline

    設定為true就行
    <video id="my-video" class="video-js" controls preload="auto" 
                                data-setup=" {} " playsinline="true" x5-playsinline="true"> </video>           

橫屏适配

  • 很多視口我們要對橫屏和豎屏顯示不同的布局,是以我們需要檢測在不同的場景下給定不同的樣式:
    • JavaScript檢測橫屏
    window.addEventListener('resize',() =>{
        if(window.orientation === 180 || window.orientation === 0) {
            //正常方向或螢幕旋轉180度
            console.log('豎屏')
        }
        
        if(window.orientation === 80 || window.orientation === -90) {
            //螢幕順時鐘旋轉90度或螢幕逆時針旋轉90度
            console.log('橫屏')
        }
    })           

    CSS檢測橫屏

    @media screen and (orientation:portrait){
        /*豎屏...*/
    }
    
    @media screen and (orientation:landscape){
        /*橫屏...*/
    }            

移動端解決

0.5px

方案

  • 裝置像素比:dpr=window.devicePixelRatio,也就是裝置的實體像素與邏輯像素的比值
  • 0.5px 方案
    /*這是css方式*/
    .border { border: 1px solid #999 }
    @media screen and (-webkit-min-device-pixel-ratio: 2) {
        .border { border: 0.5px solid #999 }
    }
    /*ios dpr=2和dpr=3情況下border相差無幾,下面代碼可以省略*/
    @media screen and (-webkit-min-device-pixel-ratio: 3) {
        .border { border: 0.333333px solid #999 }
    }           
  • viewport + rem

  • 同時通過設定對應

    viewport

    rem

    基準值,這種方式就可以像以前一樣輕松愉快的寫1px了。

    devicePixelRatio=2

    時,設定

    meta

    <meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
               
    實作
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <title>移動端1px問題</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
        <meta name="viewport" id="WebViewport"
            content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />
        <style>
            html {
                font-size: 11px;
            }
            body {
                padding: 1rem;
            }
            * {
                padding: 0;
                margin: 0;
            }
            .item {
                padding: 1rem;
                border-bottom: 1px solid gray;
                font-size: 1.2rem;
            }
        </style>
        <script>
            var viewport = document.querySelector("meta[name=viewport]");
            var dpr = window.devicePixelRatio || 1;
            var scale = 1 / dpr;
            //下面是根據裝置dpr設定viewport
            viewport.setAttribute(
                "content", +
                "width=device-width," +
                "initial-scale=" +
                scale +
                ", maximum-scale=" +
                scale +
                ", minimum-scale=" +
                scale +
                ", user-scalable=no"
            );
    
            var docEl = document.documentElement;
            var fontsize = 10 * (docEl.clientWidth / 320) + "px";
            docEl.style.fontSize = fontsize;
        </script>
    </head>
    <body>
        <div class="item">border-bottom: 1px solid gray;</div>
        <div class="item">border-bottom: 1px solid gray;</div>
    </body>
    </html>           

移動端适配方案

  • rem适配

    rem

    适配的本質是布局等比例的縮放,通過動态設定

    html

    font-size

    來改變

    rem

    的大小。
    <meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">           
  • 引入flexible
    • flexible

      方案是阿裡早期開源的一個移動端适配解決方案,引用

      後,我們在頁面上統一使用

      rem`來布局。
      (function(win, lib) {
          var doc = win.document;
          var docEl = doc.documentElement;
          var metaEl = doc.querySelector('meta[name="viewport"]');
          var flexibleEl = doc.querySelector('meta[name="flexible"]');
          var dpr = 0;
          var scale = 0;
          var tid;
          var flexible = lib.flexible || (lib.flexible = {});
          
          if (metaEl) {
              console.warn('将根據已有的meta标簽來設定縮放比例');
              var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
              if (match) {
                  scale = parseFloat(match[1]);
                  dpr = parseInt(1 / scale);
              }
          } else if (flexibleEl) {
              var content = flexibleEl.getAttribute('content');
              if (content) {
                  var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
                  var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
                  if (initialDpr) {
                      dpr = parseFloat(initialDpr[1]);
                      scale = parseFloat((1 / dpr).toFixed(2));    
                  }
                  if (maximumDpr) {
                      dpr = parseFloat(maximumDpr[1]);
                      scale = parseFloat((1 / dpr).toFixed(2));    
                  }
              }
          }
      
          if (!dpr && !scale) {
              var isAndroid = win.navigator.appVersion.match(/android/gi);
              var isIPhone = win.navigator.appVersion.match(/iphone/gi);
              var devicePixelRatio = win.devicePixelRatio;
              if (isIPhone) {
                  // iOS下,對于2和3的屏,用2倍的方案,其餘的用1倍方案
                  if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
                      dpr = 3;
                  } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                      dpr = 2;
                  } else {
                      dpr = 1;
                  }
              } else {
                  // 其他裝置下,仍舊使用1倍的方案
                  dpr = 1;
              }
              scale = 1 / dpr;
          }
      
          docEl.setAttribute('data-dpr', dpr);
          if (!metaEl) {
              metaEl = doc.createElement('meta');
              metaEl.setAttribute('name', 'viewport');
              metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
              if (docEl.firstElementChild) {
                  docEl.firstElementChild.appendChild(metaEl);
              } else {
                  var wrap = doc.createElement('div');
                  wrap.appendChild(metaEl);
                  doc.write(wrap.innerHTML);
              }
          }
      
          function refreshRem(){
              var width = docEl.getBoundingClientRect().width;
              if (width / dpr > 540) {
                  width = 540 * dpr;
              }
              var rem = width / 10;
              docEl.style.fontSize = rem + 'px';
              flexible.rem = win.rem = rem;
          }
      
          win.addEventListener('resize', function() {
              clearTimeout(tid);
              tid = setTimeout(refreshRem, 300);
          }, false);
          win.addEventListener('pageshow', function(e) {
              if (e.persisted) {
                  clearTimeout(tid);
                  tid = setTimeout(refreshRem, 300);
              }
          }, false);
      
          if (doc.readyState === 'complete') {
              doc.body.style.fontSize = 12 * dpr + 'px';
          } else {
              doc.addEventListener('DOMContentLoaded', function(e) {
                  doc.body.style.fontSize = 12 * dpr + 'px';
              }, false);
          }
          
      
          refreshRem();
      
          flexible.dpr = win.dpr = dpr;
          flexible.refreshRem = refreshRem;
          flexible.rem2px = function(d) {
              var val = parseFloat(d) * this.rem;
              if (typeof d === 'string' && d.match(/rem$/)) {
                  val += 'px';
              }
              return val;
          }
          flexible.px2rem = function(d) {
              var val = parseFloat(d) / this.rem;
              if (typeof d === 'string' && d.match(/px$/)) {
                  val += 'rem';
              }
              return val;
          }
      
      })(window, window['lib'] || (window['lib'] = {}));           
    • 使用vw,vh布局
    • vh、vw

      方案即将視覺視口寬度

      window.innerWidth

      和視覺視口高度

      window.innerHeight

      等分為 100 份。
    • 如果視覺視口為

      375px

      ,那麼

      1vw=3.75px

      ,這時

      UI

      給定一個元素的寬為

      75px

      (裝置獨立像素),我們隻需要将它設定為

      75/3.75=20vw

    • 這裡的比例關系我們也不用自己換算,我們可以使用

      PostCSS

      postcss-px-to-viewport

      插件幫我們完成這個過程。寫代碼時,我們隻需要根據

      UI

      給的設計圖寫

      px

      機關即可。
      module.exports = {
        plugins: [
          require('autoprefixer'),
          require('postcss-import'),
          require('postcss-url'),
          require('postcss-preset-env'),
          require('postcss-aspect-ratio-mini'),
          require('postcss-write-svg'),
          require('postcss-px-to-viewport')({
            viewportWidth: 750,
            viewportHeight: 1334,
            unitPrecision: 3,
            viewportUnit: 'vw',
            selectorBlackList: [ '.ignore', '.hairlines', '.footer' ],
            minPixelValue: 1,
            mediaQuery: true
          }),
          require('cssnano')
        ]
      }
                 

快速生成HTML代碼結構

#page>div.logo+ul#navigation>li*5>a{Item $}           

可以轉換為

<div id="page">
    <div class="logo"></div>
    <ul id="navigation">
        <li><a href="">Item 1</a></li>
        <li><a href="">Item 2</a></li>
        <li><a href="">Item 3</a></li>
        <li><a href="">Item 4</a></li>
        <li><a href="">Item 5</a></li>
    </ul>
</div>           
  • >

    運算符将元素互相嵌套
    div>ul>li 生成
    <div>
        <ul>
            <li></li>
        </ul>
    </div>           
  • +

    運算符将元素彼此放置在同一水準上:
    div+p+bq 生成
    <div></div>
    <p></p>
    <blockquote></blockquote>           
  • 使用括号将複雜縮寫的子樹分組
    div>(header>ul>li*2>a)+footer>p  生成
    <div>
        <header>
            <ul>
                <li><a href=""></a></li>
                <li><a href=""></a></li>
            </ul>
        </header>
        <footer>
            <p></p>
        </footer>
    </div>           

繼續閱讀