preface

The project is built based on the VUE framework, using the elementUI component library and the Echarts plug-in. The development process needs to achieve a requirement is to be able to make the home page to achieve large screen adaptation effect. The general completion idea is based on the flexible. Js of mobile Taobao, which is to obtain different screen widths and modify the font size of the HTML root element. In this way, REM, which is tied to the font size of the root element, can monitor screen changes in real time and adapt to different screens.

In fact, the adaptation of mobile terminal is the same, but there seems to be a lot of pits, but this project does not have the requirements of mobile terminal adaptation, so I temporarily adopted a simple REM adaptation method. Since there are echarts forms on the home page, we need to be able to monitor screen changes, so in addition to flexible. Js, we also need to customize a resize.

The solution

Basic definition

The simplest and most direct way is to set the size of the element directly with the percentile; Element size adaptation can be achieved, but font size adaptation cannot be achieved, and size conversion to percentage calculation is cumbersome. What we need is a unit that is positively related to the width of the screen, and that is easily interchangeable with PX. This allows us to use this unit for element size and font size Settings.

Em is a unit of relative length calculated based on the font size of the parent element of the current element. However, when the parent element changes, em changes frequently, so REM was later introduced to replace em units.

The rem unit is also a unit of relative length, 1rem is equal to the font size set on the HTML element; We can change the size represented by REM simply by setting the font size on the HTML.

Vw and vh are both relative length units of the ViewPort window. 100vw represents the width of the viewport window and 100vh represents the height of the ViewPort window.

Device Pixel Ratio, or DPR, is the ratio of physical pixels to individual pixels on the device. A larger device pixel ratio means a higher definition of your phone’s screen.

For example, the DPR of computers is 1, and the DPR of iphone7 is 2, so the 1px on the design draft should be 0.5px in order to adapt iphone7. Some browsers will parse it to 1px when parsing at 0.5px, so it will render 2px. This is the classic 1px problem.

1 px problem

Solution: Since one CSS pixel represents two physical pixels and the device doesn’t recognize 0.5px, draw 1px and then cut the line width in half by any means possible.

1. Picture method and background gradient. The principle of these two schemes is the same. Half of the elements are colored and half are transparent. For example, for a 2px height image,1px is the color we want and 1px is transparent.

2, zoom method. This is also the flexible. Js solution, which adjusts the corresponding zoom ratio according to the corresponding DPR, so as to achieve the purpose of adaptation, directly scale the page.

3. Use pseudo-element scaling. The transform: scale (1, 0.5); Achieve the function of scaling.

Flex flex box layout

When we use flex layout, Flex ADAPTS itself to the width of the screen. The flex adaptation solution is relatively easy, and is usually used in conjunction with REM for interface adaptation with different screen widths. This is a brief introduction to the basic flex concepts, but the layout is simple enough to understand.

Parent container: display:flex; Flex-direction Determines the flex spindle layout direction. The following sections, context-content, align-items, and align-content, determine how flex items handle white space in flex container space.

The child elements in the Flex container become Flex items. The Flex attribute is a shorthand for flex-grow, flex-shrink, and Flex-basis attributes. Grow and shrink represent growth and contraction factors respectively. Basis stands for the initial base size. The default value is flex: 0 1 auto. Flex-basis specifies a fixed length value with precedence over width; When flex-basis specifies a percentage value, its reference object is main Size. So flex-basis: percent * mainSize px.

Rem adaptation

In fact, the principle has been described before, is to identify different screen length and width to set the font size of different HTML root elements, thus using dynamic REM interface configuration. The key is how to recognize different screen widths.

1, use media query: @media screen and (min-width:XXX) to determine the size of the device, and then set the HTML fontSize, more difficult and need to consider more.

2. Set fontSize with js. The following code is designed for iphone6, and the result is 1rem=100px actual pixels. Since the pixel ratio of iphone6 device is 2, 1REM is 50px in the preview of the browser. In other words, 1REM is 7.5 times the width of the device, and the actual size of the device will also change if the width of the device changes by 1rem.

However, I used flexible. Js, the open source of mobile Taobao, and realized the requirements after a little modification. The code is much more complex than this example, and the specific code will be posted at the end of the article.

function setRem () {
        let htmlRem = document.documentElement.clientWidth
        document.documentElement.style.fontSize = htmlRem/7.5 + 'px'
      }
setRem()
Copy the code

3, using VM, VH: VW, VH is a new relative unit is to divide the width and height of the visible area into 100 copies, similar to the percentage layout, this scheme does not need to write JS, but the compatibility is a little poor.

html{
    font-size: 10vw
}
Copy the code

Px adaptation

Different px values are calculated according to different screen widths, so when we change the size of the Apple, the website will refresh to dynamically calculate the corresponding PX values, so as to achieve the purpose of adaptation. The specific implementation code is not found, but in fact, the principle of logic is not very different.

The project practice

The adaptation of the project probably has two parts: 1. Use flexible. Js to achieve different screen adaptation; In vue based projects, the flexibility.js is imported directly into main.js.


(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 = {});
  / * access to the dom tree: win. Document. DocumentElement, subsequent to insert the DPR HTML, the font size; Select elements from meta tags to determine whether the user has set it before. The meta tag in viewport is used to tell the browser how to properly render the Web page, and you need to tell it what device pixel ratio of the viewport is called DPR, which defines the relationship between physical pixels and device independent pixels = physical pixels/device independent pixels */

  if (metaEl) {
    console.warn("Scaling will be set based on existing meta tags");
    var match = metaEl
      .getAttribute("content")
      // eslint-disable-next-line no-useless-escape
      .match(/initial\-scale=([\d\.]+)/);
    if (match) {
      scale = parseFloat(match[1]);
      dpr = parseInt(1/ scale); }}else if (flexibleEl) {
    var content = flexibleEl.getAttribute("content");
    if (content) {
      // eslint-disable-next-line no-useless-escape
      var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
      // eslint-disable-next-line no-useless-escape
      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)); }}}/* This code checks if your meta tag is set to name= viewPort. If you set viewport and set initial-scale (the initial screen size), we will take this value as dp */

  if(! dpr && ! scale) {// eslint-disable-next-line no-unused-vars
    var isAndroid = win.navigator.appVersion.match(/android/gi);
    var isIPhone = win.navigator.appVersion.match(/iphone/gi);
    var devicePixelRatio = win.devicePixelRatio;
    if (isIPhone) {
      // On iOS, use 2x for 2 and 3 screens, and 1x for the rest
      if (devicePixelRatio >= 3&& (! dpr || dpr >=3)) {
        dpr = 3;
      } else if (devicePixelRatio >= 2&& (! dpr || dpr >=2)) {
        dpr = 2;
      } else {
        dpr = 1; }}else {
      // For other devices, use the same 1 times scheme
      dpr = 1;
    }
    scale = 1 / dpr;
  }
  docEl.setAttribute("data-dpr", dpr);
  /* If we set the scale or inital scale in meta tag name = flexible, then we can judge the multiple of different models according to the DPR ratio of iPhone Retina screen. Finally, we set the data-DPR custom property on the HTML. * /

  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); }}/* If we didn't set metaEl tags, we need to manually create meta tags to implement mobile adaptation */

  function refreshRem() {
    var width = docEl.getBoundingClientRect().width;
    // Minimum 1366px, maximum 2560px
    if (width / dpr < 1366) {
      width = 1366 * dpr;
    } else if (width / dpr > 2560) {
      width = 2560 * dpr;
    }
    // set it to 24 equal parts, 1920px at design time, so 1rem is 80px
    var rem = width / 24;
    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
  );
  /* The purpose of this code is to listen for the resize and pagesHow methods in the window to redraw the CSS style. Rem = rem; rem = rem; rem = rem; rem = rem; rem = rem; rem = rem; rem = rem * /

  if (doc.readyState === "complete") {
    doc.body.style.fontSize = 12 * dpr + "px";
  } else {
    doc.addEventListener(
      "DOMContentLoaded".// eslint-disable-next-line no-unused-vars
      function(e) {
        doc.body.style.fontSize = 12 * dpr + "px";
      },
      false
    );
  }
  /* Then we determine if the document object is in complete state. If it is, we give the body a value of font-size = 12* DPR, otherwise we judge the DOM loading method to implement the font-size setting in the body. This setting is used to specify the size of the font on the page, whereas in HTML the font size is used to specify the height, width, and other attributes of the page. * /

  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";
    }
    returnval; }; }) (window.window["lib"] | | (window["lib"] = {}));

Copy the code

2. As the echarts component is displayed on the home page, we need to define a custom function to monitor the change of the screen, and modify the size of chart in the ECharts component when the screen changes; The effect of this resize needs to be controlled by the frequency of resize using the anti-shake function. In this case, I wrote the security in a public library, so that it can be reused easily. For resize.js, it can be mixed directly into the vue file with the Echarts plugin.

export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result;

  const later = function() {
    // According to the last trigger time interval
    const last = +new Date() - timestamp;

    // The last time the wrapped function was called last was less than the set interval wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      // If the parameter is set to immediate===true, the start boundary has already been called
      if(! immediate) { result = func.apply(context, args);if(! timeout) context = args =null; }}};return function(. args) {
    context = this;
    timestamp = +new Date(a);constcallNow = immediate && ! timeout;// If the delay does not exist, reset the delay
    if(! timeout) timeout =setTimeout(later, wait);
    if (callNow) {
      result = func.apply(context, args);
      context = args = null;
    }

    return result;
  };
}

import { debounce } from '@/XXX/XXXX';
const resizeChartMethod = '$__resizeChartMethod';
export default {
  data() {
    // Map a reference to chart init to the chart property inside the component
    return {
      chart: null}; },created() {
    window.addEventListener('resize'.this[resizeChartMethod], false);
  },
  beforeDestroy() {
    window.removeEventListener('reisze'.this[resizeChartMethod]);
  },
  methods: {
    // Use loDash to control the frequency of resize
    [resizeChartMethod]: debounce(function() {
      if (this.chart) {
        this.chart.resize(); }},100),}};Copy the code