How do I solve the problem of vertically centered fonts on Android

One coding afternoon, my boss suddenly sent me a screenshot, [single product page label text is not centered]

Take out your iPhone tester and check?? In the middle…

It’s not that simple. It’s basically an Android compatibility issue

Changed two Android phones test, sure enough, there are different degrees of offset

And found on the same page, some will be offset and some can be normal vertical center

Through testing, it was found that fonts less than 12px produced a significant offset

Question:

Android phones cannot be vertically centered if the font is less than 12px and supported by lineheight or padding

Listen to their said

Font reasons and solutions

Font is basically undesirable; the UI already specifies a font

Practice is the mother of wisdom

  • The table layout
<div class="solution" style="display: table; height: 16px;">
  <span style=" display: table-cell; font-size: 10px; vertical-align: middle;">The table layout</span>
</div>
Copy the code

The text.

  • Flex layout
<div class="solution" style="display: inline-flex; align-items: center; height: 16px; line-height: 1; font-size: 10px;" > <span> Flex layout </span> </div>Copy the code

The text.

  • Zoom zoom
<div class="solution" style="height: 32px; line-height: 32px; font-size: 20px; zoom: 0.5;">
  <span>Zoom zoom</span>
</div>
Copy the code

The text.

  • Fixed height + padding + Line height set to font size
<div class="solution" style="box-sizing: border-box; height: 16px; padding: 3px 0; line-height: 10px; font-size: 10px;">
  <span>Fixed height + padding + Line height set to font size</span>
</div>
Copy the code

The text.

  • Fixed height + inner margin + line height set to Normal
<div class="solution" style="box-sizing: border-box; height: 16px; padding: 3px; line-height: normal; font-size: 10px;">
  <span>Fixed height + inner margin + line height set to Normal</span>
</div>
Copy the code

The text.

  • Inside margin + line height set to Normal
<div class="solution" style="box-sizing: border-box; padding: 2px; line-height: normal; font-size: 10px;">
  <span>Inside margin + line height set to Normal</span>
</div>
Copy the code

The text is centered, but not on some clients

  • Line height + font size set to Initial
<div class="solution" style="line-height: 16px; font-size: initial;">
  <span style="font-size: 10px;">Line height + font size set to Initial</span>
</div>
Copy the code

Text is centered, not centered on the latest Chrome browser

  • The transform scaling
<div class="solution" style="height: 32px; line-height: 32px; font-size: 20px; The transform: scale (0.5, 0.5); transform-origin: left top;">
  <span>The transform scaling</span>
</div>
Copy the code

The text is centered, but the Transform does not restore the size of the dom occupied by the element

From what has been discussed above

Only transform scaling can completely solve the centring problem, and the problem is that the transform does not cause rearrangement, so large areas of space will be occupied

So the only way we can do that is if we can get rid of the extra space

How do you solve the problem of taking up extra space?

It is well known that margin can be set to negative values and there are some applications that implement layouts with margin set to negative values

Then, can we solve the occupation of excess space by setting margin?

<div class="solution" style="height: 32px; line-height: 32px; margin: -8px -8%; font-size: 20px; The transform: scale (0.5, 0.5); transform-origin: center center;">
  <span>The transform scaling</span>
</div>
Copy the code

The results are surprisingly good, achieving roughly the same footprint

So far, the basic direction has been determined, but the fixed value is still a bit rigid, and the margin is not entirely accurate as the DOM width changes

At this point, I thought of using JS to determine the margin value

<template> <span class="tag-view" :class="{ 'tag-view_android': isAndroid }" :style="`margin: 0.16 REM -${widthMeth - 4} px-0.16 REM -${widthMeth}px '" v-show="message" > {{message}} </span> </template> <script> export default { data() { return { isAndroid: this.isAndroid(), widthMeth: 0, }; }, props: { message: { type: String, default: "", }, }, methods: { isAndroid() { const u = navigator.userAgent return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1 } }, watch: { message: { handler() { this.$nextTick(() => { this.widthMeth = this.$el.offsetWidth / 4; }); }, immediate: true, }, }, }; </script> <style lang="less" scoped> .tag-view { background: rgba(240, 242, 245, 1); Border - the radius: 0.04 rem; 0.15 rem padding: 0; Height: 0.32 rem; The line - height: 0.32 rem; The font - size: 0.22 rem; color: rgba(66, 72, 84, 1); display: inline-block; Margin - right: 0.1 rem; } .tag-view_android { font-family: miui; 0.3 rem padding: 0; Height: 0.64 rem; The line - height: 0.64 rem; The font - size: 0.44 rem; The transform: scale (0.5, 0.5); transform-origin: center center; Margin: 0.16 rem - 9%; } </style>Copy the code

So far, the accuracy of margin has been solved

But if you want to apply a style to a project, it is too cumbersome to encapsulate a component, and there are a lot of changes to existing styles

Could that be done with a custom instruction like V-bind?

Just do it!

Vue.directive('tag', {
  inserted(el, binding, vnode) {
    if(! isAndroid())return
    const needBook = [
      'fontSize'.'height'.'paddingTop'.'paddingLeft'.'paddingBottom'.'paddingRight',
    ]

    needBook.map(item= > {
      let value = getComputedStyle(el)[item]
      if (getValue(value)) {
        el.style[item] = getValue(value) * 2 + 'px'
        if(item === 'height') {
          el.style.lineHeight = getValue(value) * 2 + 'px'
        }
      }
    })

    el.style.transform = 'scale (0.5, 0.5)'
    el.style.transformOrigin = 'center center'
    el.style.margin = ` -${el.offsetHeight / 4}px -${el.offsetWidth / 4 - 4}px -${el.offsetHeight / 4}px -${el.offsetWidth / 4}px`

    function getValue(params) {
      if(! params)return ' '
      const arr = params.split('px')
      return arr && arr[0]}function isAndroid() {
      const u = navigator.userAgent
      return u.indexOf('Android') > -1 || u.indexOf('Adr') > -1}}})Copy the code

A meal operation, finally realized the effect, can’t wait to go to the real machine to verify some

It’s not in the middle…

Puzzled, can only ask Baidu again ~

If the size is odd, the font will be offset

Make another change to the code

needBook.map(item= > {
  let value = getComputedStyle(el)[item]
  if (getValue(value)) {
    - el.style[item] = getValue(value) * 2 + 'px'
    - if(item === 'height') {
    -   el.style.lineHeight = getValue(value) * 2 + 'px'-} +if(item === 'height') {+const num = Math.ceil(getValue(value))
    +   el.style[item] = (num - 1) * 2 + 'px'
    +   el.style.lineHeight = num * 2 + 'px'
    +   return
    + }
    + el.style[item] = getValue(value) * 2 + 'px'}})Copy the code

The obtained height is rounded up and the fault tolerance error is reduced by 1

So far, the logic has been realized

Finally, we share a custom instruction self – use file structure

  1. New Cache folder
  2. In the Caching folder, create the tag.js copy code
  3. In the Cache folder, create index.js
import tag from './tag'
// Custom instruction
const directives = {
  tag,
}

export default {
  install(Vue) {
    Object.keys(directives).forEach((key) = > {
      Vue.directive(key, directives[key])
    })
  },
}
Copy the code
  1. Mount it in main.js
import Vue from 'vue'
import Directives from './directives'

Vue.use(Directives)
Copy the code

Thank you for seeing THE END!