1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        移動端1px問題解決方案

        共 5474字,需瀏覽 11分鐘

         ·

        2020-11-08 23:13

        高清屏中1px線問題

        在移動端web開發(fā)中,UI設(shè)計稿中設(shè)置邊框為1像素,前端在開發(fā)過程中如果出現(xiàn)border:1px,測試會發(fā)現(xiàn)在retina屏機型中,1px會比較粗,即是較經(jīng)典的移動端1px像素問題。

        為什么高清屏下1px更寬

        高清屏(retina屏)是指高dpr的設(shè)備,其物理像素的密度更大。又分為有兩倍屏,三倍屏。

        dpr:物理像素/css像素

        普通屏,1個css像素對應(yīng)1個物理像素;2倍屏中,一個css像素對應(yīng)4個物理像素;三倍屏中則是9個。

        7efabed5a6d4bae13491335f24ccecbf.webp

        按照這樣的置換規(guī)則后一張相同的圖片在不同的設(shè)備上才會顯示相同的大小。

        1px的線在高清屏下本應(yīng)不需要做特殊處理。兩倍屏下會自動用兩排物理像素去展示‘1px’的細線,普通屏用一排物理像素去展示‘1px’的細線,他們應(yīng)該看起來是相同的。但是,就像數(shù)學(xué)中的概念:線是沒有寬度的,點是沒有大小的。像素同樣是沒有大小的。

        兩倍屏的物理像素密度是普通屏的兩倍,并不是每一個物理像素是普通屏的1/4大小,而是物理像素的間距是普通屏間距的1/2。

        用兩倍屏下用兩排像素去展示,自然會比普通屏中用一排像素去展示看起來更粗。

        如何修正高清屏下的1px問題

        要解決1px問題,本質(zhì)就是讓高清屏用一個物理像素去展示一個css像素

        可以按照不同屏幕的dpr作出轉(zhuǎn)換,比如在2倍屏下將1px的細線寫成border:0.5px。但這種方法只在ios上支持,安卓上會顯示成被當(dāng)成0px處理。

        更通用的方案中,有svg偽類元素兩種。

        SVG方案

        這種方案本質(zhì)上border并沒有變細,但是boder被一分為二,靠內(nèi)側(cè)的是透明的

        87b8496e4a7654c1319d0f8924603cf8.webp

        關(guān)鍵的樣式代碼是css中的svg生成函數(shù)。

        SVG即矢量圖,用xml標(biāo)簽寫在html文件中。

        通過postcss-write-svg這個postcss插件將css中svg函數(shù)生成的圖像處理成base64。這樣就可以在css文件直接調(diào)用svg函數(shù)。

        /* src/index.css */@svg?custom-name?{?  width:?4px;??  height:?4px;??   @rect?{       fill:?transparent;       width:?100%;       height:?100%;       stroke-width:?1;       stroke:?var(--color,?black);??    }}.svg-retina-border?{    border:?1px?solid;    border-image:?svg(custom-name?param(--color?green))?1?repeat;}.normal-border?{    border:?1px?solid?green;}

        處理過后的樣子59a23c1d225dd7d70a29e1492bb6ca96.webp

        剩余完整代碼

        import './index.css'const root = document.getElementById('root')const div2 = document.createElement('div')div2.innerHTML = 'SVG-retina-border'div2.className = 'svg-retina-border'root.append(div2)root.append(document.createElement('br'))const div3 = document.createElement('div')div3.innerHTML = 'normal-border'div3.className = 'normal-border'root.append(div3)

        Document
        // webpack.config.jsconst path = require('path')const HtmlPlugin = require('html-webpack-plugin')module.exports = { mode: 'development',    entry: {      entry1: './src/index.js'      },      output: {      path: path.resolve(__dirname, 'dist'),        filename: '[name].js'      },      module: {      rules: [{          test: /\.css$/,            use: ['style-loader', 'css-loader', 'postcss-loader']        }]      },      plugins: [     new HtmlPlugin({           template: './src/index.html'            })     ],      devServer: {     contentBase: path.resolve(__dirname, 'dist'),        host: '0.0.0.0',         port: 3005,        compress: true,        disableHostCheck: true      }}

        SVG

        分別直接用xml的svg標(biāo)簽和css實現(xiàn)了兩個100px,邊框?qū)挒?的矩形。

        高清屏下效果如下。

        cc4806f9b03317e9406827eb3c6619f8.webp1598073606858
          <-- 視口大小-->                <--矩形大小-->      width="100"      height="100"      fill="transparent"      <--svg中所有的單位都是px-->      stroke-width="1"      stroke="black"            />            

        stroke-widthborder一樣,都將矩形的邊設(shè)為了1px,但是用svg實現(xiàn)的矩形邊框看起來卻更細。關(guān)鍵的地方是使用svg標(biāo)記的視口大小和使用rect標(biāo)記的矩形大小相同的。

        svg中沒有盒模型的概念,它的stroke畫線并不是對應(yīng)css中的border。更像是不占空間的outline。因為不占空間,它會以rect(矩形)的邊界為中心畫線,一條線一半寬度在矩形內(nèi),一半在矩形外。

        而因為視口寬度正好等于矩形的大小,看到的線寬就只有一半了。

        ff078cd55eafbce7ac5814237159b3c4.webp(用svg畫一個100px大小+1px邊寬的方形)

        949dfecc141a78bd086f8b24b55b98ff.webp(用css畫一個100px大小+1px邊框的方形border-box)

        如果把矩形縮小一點,不占滿視口,這時候看到的border是完整的,所以和沒處理過的1px一樣粗。

        53073db97007e227359c589e15b4565a.webp

        border-image

        border-image是三個屬性的縮寫

        border-image-source: url('https://misc.aotu.io/leeenx/border-image/box.png');border-image-slice: 33% 20% 3 fill;border-image-repeat: stretch;
        • border-image-source:圖片鏈接或base64;
        • border-image-slice:圖片切割的四個位置。把圖片切成9塊,除中間一塊,其他八塊分別被當(dāng)成邊框使用。接受1-4個參數(shù)(使用類似于padding/margin的尺寸設(shè)置)??梢允前俜直龋ㄏ鄬τ趫D片自身),也可以是數(shù)字(單位是px)。最后的fill決定中間那塊圖片會不會被當(dāng)成background使用。
        • border-image-repeat:stretch/round(平鋪)/repeat(重復(fù))上下左右四個正位的圖片怎樣被當(dāng)成border使用。
        • round(平鋪)會壓縮,repeat(重復(fù))會剪裁。

        border-image必須配合border使用。最終border寬度是border-width。border-style也必須指定,border-color可以不用。

        偽類元素方案

        c56695267365150836138917187f4999.webp

        完整代碼

        // index.html                                         
        retina border

        normal border
        // index.css.retina-border {   position: relative;}.retina-border::before { content: '';   position: absolute;    width: 100%;    height: 100%;     transform-origin: left top;    box-sizing: border-box;    pointer-events: none;     border-width: 1px;    border-style: solid;    border-color: #333;}@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {  .retina-border::before {       width: 200%;     height: 200%;     transform: scale(0.5);     }}@media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {  .retina-border::before {   width: 300%;     height: 300%;       transform: scale(0.33);    }}.normal-border {    border: 1px solid #333;}

        具體實現(xiàn)

        以兩倍屏為例

        .retina-border {   position: relative;}.retina-border::before {     content: '';     position: absolute;    top: 0px;     right: 0px;     width: 200%;    height: 200%;    transform: scale(0.5);    transform-origin: left top;      box-sizing: border-box;    pointer-events: none;     border-width: 1px;      border-style: solid;    border-color: #333;}

        通過一個偽類選擇器在retinaborder元素中加了一個子元素

        126e0f2dbf762491cb5c0def64be95bd.webp

        border-width: 1px將邊框的寬度設(shè)為1px。

        width:200%然后將偽類元素的寬高都設(shè)置成父元素的2倍。(但是邊框還是1px)

        transform:scale(0.5)偽類元素的x,y軸方向都縮放到0.5倍。

        通過兩次尺寸的設(shè)置,使這個偽類子元素保持內(nèi)容的大小還是和父元素一樣,但是border:0.5px的效果。

        pointer-events: none當(dāng)有元素的層級重疊時,鼠標(biāo)點擊是無法穿透的。即絕對定位的偽類元素的層級更高,它底下的元素(即文字:retina border)無法被事件觸發(fā)。置為none時,絕對定位的元素不觸發(fā)事件,底下的那層才能被選中。

        其他css樣式作用

        • 偽類元素默認(rèn)的display:inline。而position:absolute會使元素display:block。只有塊級元素的尺寸(寬/高)設(shè)置才是有效的。

        • 其中偽類選擇器中content是必填項,不然無法生效

        • transform-origin的縮放的中心點,默認(rèn)是元素中心,

        • transform-origin的縮放的中心點,默認(rèn)是元素中心,和絕對定位的top,right一樣,相對的是padding+content部分整個空間的位置

        • 絕對定位的元素其top和right值是相對于padding+content的,默認(rèn)值是從content開始,所以要規(guī)定都是0,否則當(dāng)父元素有padding時,border就移位了

        919c98a37bc961ce01a48bb0879d6328.webp(如果刪去position:absolute)

        b85e9597571ccfae22f0d9c0beb90e49.webp(如果刪去position:absolute+display:block)

        當(dāng)使用百分比時,其父元素的高度必須顯式指定,(20px/20view)不能是由子元素撐開的,但是寬度是可以的。

        兩種方案比較

        兼容性

        svg方案經(jīng)過postcss處理,最終會影響瀏覽器兼容性的是border-image屬性

        bd6dc80a5f3dda46f04b2ff1b9c2b2a3.webp偽類元素元素:方案最終影響兼容性的是transform屬性

        02e695dfb1daca56b9661f9bec6fea3b.webp1598076296220

        結(jié)論:svg方案的兼容性更好

        靈活性

        由于svg只能畫出特定的形狀,所以無法實現(xiàn)圓角邊框。而偽類元素方案可以。

        學(xué)習(xí)成本

        svg方案所用到的border-image屬性、svg特性的理解成本較高,并且需要postcss-write-svg處理。偽類元素方案相較簡單。

        總結(jié)

        通常情況,偽類元素方案更好,無論是從成本還是靈活性出發(fā)。如果是為了更高的兼容性選擇svg方案,border-image屬性一定要使用縮寫。(不然兼容性會更差兼容性測試)




        瀏覽 59
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美性爱视频免费观看大鸡巴操死我 | 93久久精品日日躁夜夜躁欧美 | 美女被异性狂揉下部羞羞视频 | 人人爱人人摸 | 91爱爱爱爱 | 天天撸一撸免费看 | 少妇丰满日韩偷拍欧美 | 五月天色影院 | 秋霞 乱伦 | 亚洲无电影 |