preface

The cause of the incident was a wechat message sent to me by a friend

Wondering what font has to do with data binding, I asked him to write a demo for me and he sent me a code file

Code simplification is as follows:


      
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Demo</title>
    <style>
      body {
        font-size: 40px;
      }
      .time1 {
        display: inline-block;
        background: linear-gradient(to right, #a6ffcb, #1fa2ff);
        -webkit-background-clip: text;
        color: transparent;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="time1">{{ time }}</div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
    <script>
      new Vue({
        el: "#app".data: {
          time: new Date().toLocaleString()
        },
        methods: {
          updateTime() {
            this.time = new Date().toLocaleString();
          }
        },
        mounted() {
          setInterval(this.updateTime, 1000); }});</script>
    asdasd
    
  </body>
</html>
Copy the code

Click to see the Live Demo

I found that the time did not change in the browser. When I commented out the color in the console, I found that the data had changed. I’m just going to try another gradient here

CSS text gradient some methods

Background property method

I’m not going to go into detail about this but it’s easy to understand if you look at the code above

Mask attribute method

<style>
  .time2 {
    position: relative;
    color: #a6ffcb;
  }
  .time2:before {
    content: attr(time);
    position: absolute;
    z-index: 10;
    color: #1fa2ff;
    -webkit-mask: linear-gradient(to left, #1fa2ff, transparent);
  }
</style>
<div class="time2" time="time">I'm gradient text</div>
Copy the code

The selector:beforeInserts content in front of the selected element usingcontentProperty to specify what to insert.maskAttributes let parts of an element be shown or hidden

  1. Content value attr is what we use to get the value of the property. Content :attr

    content: attr(time); You can get the time attribute of the element, here the time attribute is a custom attribute, arbitrary write

    <h1 date="I'm gradient text.">I'm gradient text</h1>
    Copy the code

    And then the content property looks like this: content: attr(date); It can also work.

  2. The mask attribute allows the user to partially or completely hide the visible area of an element. This effect can be done by masking or cropping specific areas of the image.

    Details can be found at developer.mozilla.org/zh-CN/docs/…

Go back to the original question

Let’s see if the other case doesn’t render


      
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Demo</title>
    <style>
      body {
        font-size: 40px;
      }
      .time1 {
        display: inline-block;
      }
      .time1 {
        background: linear-gradient(to right, #a6ffcb, #1fa2ff);
        -webkit-background-clip: text;
        color: transparent;
      }
      .time2 {
        position: relative;
        color: #a6ffcb;
      }
      .time2:before {
        content: attr(time);
        position: absolute;
        z-index: 10;
        color: #1fa2ff;
        -webkit-mask: linear-gradient(to left, #1fa2ff, transparent);
      }
    </style>
  </head>
  <body>
    <div id="app">
  <div class="time1">{{ time }}</div>
    
  <div class="time2" :time="time">{{ time }}</div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
    <script>
      new Vue({
        el: "#app".data: {
          time: new Date().toLocaleString()
        },
        methods: {
          updateTime() {
            this.time = new Date().toLocaleString();
          }
        },
        mounted() {
          setInterval(this.updateTime, 1000); }});</script>
  </body>
</html>
Copy the code

Look at the effect

The look on my face

It occurred to me that the browser was rearranged and redrawn

Browser rearrangement and redrawing

So let’s talk about rearranging and redrawing

The browser compiles a page in five steps

  1. Process HTML to generate DOM (Document Object Model) Tree
  2. Generates the CSSOM (CSS Object Model) Tree
  3. The DOM tree is merged with the CSS-DOM tree into the Render tree
  4. Render tree layout calculation
  5. Walk through each node of the Render tree and draw to the screen

Redraw and rearrange concepts

When a DOM change affects the geometry of an element (width, height, etc.), the browser needs to recalculate the geometry of the element, and the geometry of other elements on the page may be affected. In this way, the rendering tree is changed. This process is called reflow.

If the DOM changes only affect non-geometric properties such as the background color, then it is repainted rather than rearranged because the layout has not changed

Changes in page layout and element geometry can lead to rearrangements

The following rearrangements will occur:

  • Initial page rendering
  • Add/remove visible DOM elements
  • Change element position
  • Change element dimensions (width, height, inside and outside margins, borders, etc.)
  • Changing element content (text, images, etc.)
  • Change window size
  • The extent and extent of rearrangement will be different under different conditions
  • In some cases, it even rearranges the entire page, such as sliding the scroll bar
  • The following properties or methods refresh the render queue (offsetTop, offsetLeft, offsetWidth, offsetHeight clientTop, clientLeft, clientWidth, clientHeight) ScrollTop, scrollLeft, scrollWidth, scrollHeight getComputedStyle() (currentStyle in IE)

The problem

Here the text has changed, but the size of the text has not changed. I feel that there should be no trigger rearrangement so that the background color has not changed. Then I will try to change the length of the text


      
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Demo</title>
    <style>
      body {
        font-size: 40px;
      }
      .time1 {
        display: inline-block;
      }
      .time1 {
        background: linear-gradient(to right, #a6ffcb, #1fa2ff);
        -webkit-background-clip: text;
        color: transparent;
      }
      .time2 {
        position: relative;
        color: #a6ffcb;
      }
      .time2:before {
        content: attr(time);
        position: absolute;
        z-index: 10;
        color: #1fa2ff;
        -webkit-mask: linear-gradient(to left, #1fa2ff, transparent);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="time1">{{ time }}</div>

      <div class="time2" :time="time">{{ time }}</div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
    <script>
      new Vue({
        el: "#app".data: {
          time: new Date().toLocaleString(),
          arr: [
            "The Arcane Masters Of Great And Great."."It's a stupid thing to do."."Beat dud by the hour."."Oskara's Home wire-drawing machine Decaron."]},methods: {
          updateTime() {
            // this.time = new Date().toLocaleString();
            this.time = this.arr[Math.floor(Math.random() * this.arr.length)];
          }
        },
        mounted() {
          setInterval(this.updateTime, 1000); }});</script>
  </body>
</html>
Copy the code

Results the following

It turns out that the text length does not change, the first one will not be re-rendered, once the length changes, the first one will change the rendering, and the second one will always be rendered

The problem seemed to have suddenly found its cause, but I had no intention of discovering new problems

The new problem


      
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Demo</title>
    <style>
      body {
        font-size: 40px;
      }
      .time1 {
        display: inline-block;
      }
      .time1..time3 {
        background: linear-gradient(to right, #a6ffcb, #1fa2ff);
        -webkit-background-clip: text;
        color: transparent;
      }
      .time2 {
        position: relative;
        color: #a6ffcb;
      }
      .time2:before {
        content: attr(time);
        position: absolute;
        z-index: 10;
        color: #1fa2ff;
        -webkit-mask: linear-gradient(to left, #1fa2ff, transparent);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="time1">{{ time }}</div>

      <div class="time2" :time="time">{{ time }}</div>
      <div>
        <span class="time3">{{ time }}</span>
      </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js"></script>
    <script>
      new Vue({
        el: "#app".data: {
          msg: "Hello".time: new Date().toLocaleString()
        },
        methods: {
          updateTime() {
            this.time = new Date().toLocaleString();
          }
        },
        mounted() {
          setInterval(this.updateTime, 1000); }});</script>
  </body>
</html>
Copy the code

When I change the DOM structure, it’s fine

Results the following

Okay, I’m totally dumbfounded

The last

Finally I did not understand, hope to know the big guy to help answer.

But just to be on the safe side, use the second gradient.