阿里云主机折上折
  • 微信号
Current Site:Index > Mobile adaptation solutions (REM, Flexible, etc.)

Mobile adaptation solutions (REM, Flexible, etc.)

Author:Chuan Chen 阅读数:13606人阅读 分类: HTML

The Necessity of Mobile Adaptation

The fragmentation of mobile device screen sizes is severe, ranging from 320px to 414px and even larger screen widths. Traditional fixed-pixel layouts cannot adapt to this diversity, causing pages to display abnormally on certain devices. The core goal of mobile adaptation is to ensure that pages are displayed reasonably on screens of different sizes while maintaining a consistent visual experience.

Viewport Basics

The foundation of adaptation lies in a correct understanding of the viewport. The default viewport width of mobile browsers is usually larger than the device width, which causes the page to be scaled. The viewport behavior can be controlled via the meta tag:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

This setting ensures the viewport width equals the device width, prevents user scaling, and lays the groundwork for subsequent adaptation solutions. Without proper viewport settings, any adaptation solution will fail.

The Principle of REM Adaptation

REM (root em) is a unit relative to the font size of the root element (html). By dynamically calculating the font-size of the html element, proportional scaling of page elements can be achieved. The basic formula is:

html font-size = (clientWidth / designWidth) * baseFontSize

Assuming the design width is 750px and the base font size is 100px, on a device with a width of 375px:

document.documentElement.style.fontSize = (375 / 750) * 100 + 'px';

Here, 1rem equals 50px, and all elements using rem units will scale proportionally.

Implementation Details of Flexible.js

Taobao's Flexible solution is a classic implementation of REM adaptation. It addresses issues like 1px borders and high-definition screen adaptation. The core logic is as follows:

(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()
  // Listen for page resize events
  window.addEventListener('resize', setRemUnit)
  window.addEventListener('pageshow', function(e) {
    if (e.persisted) {
      setRemUnit()
    }
  })
  
  // Detect 0.5px support
  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 Plugins for Development

Manually calculating rem values is inefficient. PostCSS plugins can automate the conversion:

module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 100, // Design width / 10
      propList: ['*'],
      selectorBlackList: ['.norem'] // Ignore classes starting with 'norem'
    }
  }
}

This allows writing design dimensions directly in CSS:

.box {
  width: 750px; /* Converts to 7.5rem */
  height: 200px; /* Converts to 2rem */
}

1px Border Solution

On high-definition screens, 1px physical pixels may correspond to multiple device pixels, making borders appear too thick. The Flexible solution addresses this with transform scaling:

.border-1px {
  position: relative;
}
.border-1px::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: #000;
  transform: scaleY(0.5);
  transform-origin: 0 0;
}

For devices with different dpr, targeted handling is possible:

[data-dpr="2"] .border-1px::after {
  transform: scaleY(0.5);
}
[data-dpr="3"] .border-1px::after {
  transform: scaleY(0.333);
}

High-Definition Image Adaptation

Load images of different resolutions based on device dpr:

<img src="image@2x.png" 
     srcset="image@1x.png 1x, image@2x.png 2x, image@3x.png 3x">

Or use background images with media queries:

.icon {
  background-image: url(image@1x.png);
}
@media (-webkit-min-device-pixel-ratio: 2) {
  .icon {
    background-image: url(image@2x.png);
    background-size: 100% 100%;
  }
}

Combining Flex Layout with REM

REM solves sizing issues, while Flex solves layout issues. Combining them yields better results:

.container {
  display: flex;
  padding: 0.2rem;
}
.item {
  flex: 1;
  margin: 0.1rem;
  height: 1rem;
}

Optimizing Dynamic REM Calculation

Traditional solutions may scale too much on very large screens. Limit the maximum size:

function setRem() {
  const designWidth = 750;
  const maxWidth = 768;
  const clientWidth = Math.min(document.documentElement.clientWidth, maxWidth);
  const rem = (clientWidth / designWidth) * 100;
  document.documentElement.style.fontSize = rem + 'px';
}

Combining Responsive Layout with REM

Adjust the REM baseline with media queries for finer control:

@media screen and (min-width: 320px) {
  html {
    font-size: 42.6667px;
  }
}
@media screen and (min-width: 360px) {
  html {
    font-size: 48px;
  }
}

Example of a Real-World Project Configuration

Complete webpack configuration example:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('postcss-pxtorem')({
                    rootValue: 75,
                    propList: ['*'],
                    minPixelValue: 2
                  })
                ]
              }
            }
          }
        ]
      }
    ]
  }
}

Handling Common Issues

Font Size Issues: To prevent text from scaling with REM, use media queries to fix it:

.title {
  font-size: 16px;
}
@media (min-width: 375px) {
  .title {
    font-size: 18px;
  }
}

Third-Party Component Adaptation: Some UI libraries use px units. Override them globally:

.third-party-component {
  transform: scale(0.5);
  transform-origin: 0 0;
}

Comparing Modern CSS Solutions

CSS viewport units (vw/vh) are becoming a new alternative:

html {
  font-size: 10vw; /* 10% of viewport width */
}
.box {
  width: 7.5rem; /* Equivalent to 75vw */
}

However, compatibility and calculation precision issues must be considered.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.