This is the fourth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

0 x00 profile

This article will in-depth analysis of Backtop component source code, analysis of its implementation principle, patience to read, I believe it will be helpful to you. Component document Backtop

Packages/backtop/SRC/main. Vue file is component source code to achieve. Making the source code. The main vue

0 x01 dom structure

The template content shows that the component is a div that uses the built-in transition animation component Transition and supports slot functionality. The default value is an icon named El-icon-Caret-top.

<template> 
  <transition name='el-fade-in'>
    <div
      v-if='visible'
      @click.stop='handleClick'
      :style='{ right: styleRight, bottom: styleBottom, }'
      class='el-backtop'
    >
      <slot>
        <el-icon name='caret-top'></el-icon>
      </slot>
    </div>
  </transition>
</template>
Copy the code

The transition component implements the fade in effect, and the name component automatically generates the CSS transition class name, which automatically expands to.el-fade-in-enter,.el-Fade -in-enter, and so on.

The project defines different entry and exit animations in Packages \ theme-Chalk \ SRC \common\transition. SCSS, and sets duration and animation functions.

Div element class value is el-Backtop, whose visibility is controlled by the property visible, defines the click event (the event modifier stop is used to prevent the click event from continuing to spread), uses the computed property to build the inline style, and controls how far the component is displayed from the right margin and the bottom of the page.

0 x02 attributes attribute

The component Prop provides four properties that specify validation requirements, default values, and other Settings.

props: {
    visibilityHeight: {
      type: Number.default: 200
    },
    target: [String]./ / string
    right: {
      type: Number.default: 40
    },
    bottom: {
      type: Number.default: 40}},Copy the code

Where the target type is a string and the element selector for the object that triggers the scrolling is passed in for object lookup. If the element selector cannot be found, the error target is not existed: ${this.target} is displayed.

The property parameters are described as follows:

parameter instructions type The default value
target An object that triggers scrolling string
visibility-height The scrolling height does not appear until this parameter value is reached number 200
right Control its display position, from the right margin of the page number 40
bottom Controls its display position, distance from the bottom of the page number 40

Based on the values of the right and bottom attributes passed in, the computed attributes styleBottom and styleRight return ${number} PX formatted content for inline style building of the component.

computed: {
    styleBottom() {
      return `The ${this.bottom}px`;
    },
    styleRight() {
      return `The ${this.right}px`; }},Copy the code

0x03 Component life cycle

The lifecycle of the component is analyzed below.

<script>
// Introduce the throttling function
import throttle from 'throttle-debounce/throttle'; 

const cubic = (value) = > Math.pow(value, 3);
const easeInOutCubic = (value) = >
  value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2;

export default {
  name: 'ElBacktop'.// ...
  
  / / data property
  data() {
    return {
      el: null.// Triggers the scrolling object
      container: null.// Triggers the scrolling object
      visible: false  // Whether the component is visible
    };
  }, 

  mounted() { 
    // Component initialization
    this.init();
    // Check whether the component is visible
    this.onScroll();
    // Select * from 'throttling' where 'throttling' is used.
    this.throttledScrollHandler = throttle(300.this.onScroll);
    // Add the scroll event listener
    this.container.addEventListener('scroll'.this.throttledScrollHandler);
  },

  methods: {
    // Component initialization
    init() {
      // ...
    },
    // Check whether the component is visible
    onScroll() {
      // ...
    },
    // Click the button to trigger the event
    handleClick(e) {
      // ...
    },
    // Return to the action at the top of the page
    scrollToTop() {
      // ...}},// Call before instance destruction
  beforeDestroy() {
    // Delete the Scroll event listener
    this.container.removeEventListener('scroll'.this.throttledScrollHandler); }}; </script>Copy the code

mounted()

After the component instance is mounted, init() is called, onScroll() is called, and onScroll event listener is added.

init()

Gets the object that triggers scrolling. By default, document is the object that triggers scrolling.

If the target attribute has a value passed in, it looks for a matching element and updates it to the object that triggers scrolling. If the exception is not found, target is not existed.

init() {
  // Load the page DOM tree
  this.container = document; 
  // Get the read-only attribute of the root element of the document object - root element < HTML >
  this.el = document.documentElement; 
  // If target is specified
  if (this.target) {
    // The referenced querySelector() method looks for a matching HTMLElement object.
    // Return null if no match is found.
    this.el = document.querySelector(this.target);
    // Failed to find a match, throw exception target is not existed
    if (!this.el) {
      throw new Error(`target is not existed: The ${this.target}`);
    }
    // Assign the matching element to container
    this.container = this.el; }},Copy the code

onScroll()

According to the number of pixels (scrollTop) of the object that triggers scrolling, determine whether the value of the property visibilityHeight is exceeded. If it is exceeded, set the property visible value to true, and then the component is visible on the page.

onScroll() {
  // Get the number of pixels that the content of an element should scroll vertically.
  const scrollTop = this.el.scrollTop;
  // The operation button will not appear until the scrolling height reaches this parameter value
  this.visible = scrollTop >= this.visibilityHeight;
}
Copy the code

Event listeners

Add onScoll event listener for the object that triggers scrolling. When the page is scrolling, the triggered event calls onScroll(), which is used to display or hide components.

// Select * from 'throttling' where 'throttling' is used.
this.throttledScrollHandler = throttle(300.this.onScroll); 
// Add the scroll event listener
this.container.addEventListener('scroll'.this.throttledScrollHandler);
Copy the code

We added a throttling function to the onScroll() method call, which only executes once for 300 milliseconds. The main thing is that scroll events are fired frequently, but sometimes you don’t want to execute functions that often while the event is firing.

beforeDestroy

Call before instance destruction to delete the scroll event listener on the object that triggers scroll.

beforeDestroy() {
    // Delete the Scroll event listener
    this.container.removeEventListener('scroll'.this.throttledScrollHandler);
}
Copy the code

0 x04 events event

The component provides the click event. @click.stop=’handleClick’ @click.stop=’handleClick’

handleClick(e)

First call scrollToTop() to return to the top of the page, then call this.$emit(‘click’, e) to trigger the click event on the current instance.

handleClick(e) {
  // Return to the top of the page
  this.scrollToTop();
  // Triggers events on the current instance
  this.$emit('click', e);
},
Copy the code

scrollToTop()

This method returns the top of the page by setting the scrollTop value of the target object. Ease functions and requestAnimationFrame are used to make the experience better.

scrollToTop() {
  // Specify an object
  const el = this.el;
  // Click the button to timestamp UTC milliseconds
  const beginTime = Date.now(); 
  console.log(beginTime);
  // Get the number of pixels that the content of an element should scroll vertically.
  const beginValue = el.scrollTop;
  const rAF =
    window.requestAnimationFrame || ((func) = > setTimeout(func, 16));

  const frameFunc = () = > {
    const progress = (Date.now() - beginTime) / 500;  // Set rate to 500 milliseconds to return to top of page
    if (progress < 1) {
      el.scrollTop = beginValue * (1 - easeInOutCubic(progress));
      rAF(frameFunc);
    } else {
      // If more than 500 goes directly back to the top of the page
      el.scrollTop = 0; }}; rAF(frameFunc); }Copy the code

Slow function

Ease the rate at which a user-defined parameter of a function changes over time. Easing functions allow mathematical formulas to be applied to animations.

In real life, objects don’t start or stop suddenly, and they certainly don’t keep moving at a constant speed. Just like when we open a drawer, the initial pull is quick, but when the drawer is pulled out we unconsciously slow down. Or an object that falls to the floor, it starts off falling very fast, and then it bounces back and forth across the floor until it stops.

const cubic = (value) = > Math.pow(value, 3);
const easeInOutCubic = (value) = >
  value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2;
Copy the code

Equivalent TypeScript code implementation. The variable x represents values in the range 0 (start of animation) to 1 (end of animation).

function easeInOutCubic(x: number) :number {
    return x < 0.5 ? 4 * x * x * x : 1 - pow(-2 * x + 2.3) / 2;
}
Copy the code

In CSS, use the Transition and animation properties. The results can be found in cubic-bezier.com.

.block {
    transition: transform 0.6 s cubic-bezier(0.65.0.0.35.1);
}
Copy the code

The easing function is used to click on the component to trigger the scrollToTop method, rolling back the rate at the top of the viewport to make it look more realistic.

scrollToTop() { 
    // ...
    el.scrollTop = beginValue * (1 - easeInOutCubic(progress)); 
    // ...
}
Copy the code

requestAnimationFrame()

Tell the browser window. RequestAnimationFrame () – you want to perform an animation, and required the browser until the next redraw calls the specified callback function to update the animation. This method needs to be passed a callback function as an argument, which will be executed before the browser next redraw. Thus achieved through the browser optimization, animation more fluid.

RequestAnimationFrame () is implemented recursively.

const rAF =
    window.requestAnimationFrame || ((func) = > setTimeout(func, 16));

const frameFunc = () = > { 
    // ...
    rAF(frameFunc);
    // ...
};
rAF(frameFunc);
Copy the code

0x05 Theme – Chalk style implemented

BEM naming conventions

Component styles use BEM naming convention, BEM naming convention [block-name]__[element-name]–[modifier-name], that is, block + element + modifier.

.block{}
.block__element{}
.block--modifier{}

Copy the code
  • .blockRepresents a higher level of abstraction or component.
  • .block__elementA descendant of. Block, used to form a whole of. Block.
  • .block--modifierRepresents different states or versions of. Blocks.

The BEM name gives it more description and a clearer structure. From its name, you can know the meaning and function of a tag. Make front-end code easier to read and understand, easier to collaborate, easier to control, more robust and unambiguous, and more rigorous.

src/backtop.scss

SCSS generates component styles using the MIXin instructions of SCSS. With the mixin, styles can be easily shared across different parts of the style sheet. Since the DOM structure of the component is relatively simple, only @mixin b($block) is used to generate blocks.

@import "mixins/mixins";
@import "common/var";

@include b(backtop) {
  position: fixed;
  background-color: $--backtop-background-color;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: $--backtop-font-color;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  box-shadow: 0 0 6px rgba(0.0.0.12);
  cursor: pointer;
  z-index: 5;
  
  // The identifier of the parent selector &
  &:hover {
    background-color: $--backtop-hover-background-color}}Copy the code

src/common/var.scss

Packages \ theme-Chalk \ SRC \common\var.scss defines global common style variables.

/* Backtop
--------------------------*/
/// color||Color|0
$--backtop-background-color: $--color-white! default;/// color||Color|0
$--backtop-font-color: $--color-primary! default;/// color||Color|0
$--backtop-hover-background-color: $--border-color-extra-light! default;/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409EFF! default;/// color|1|Background Color|4
$--color-white: #FFFFFF! default;/// color|1|Border Color|3
$--border-color-extra-light: #F2F6FC! default;Copy the code

src/mixins/config.scss

Packages \ theme-Chalk \ SRC \mixins\config. SCSS defines variables for the BEM naming convention.

$namespace: 'el'; // The element namespace abbreviation
$element-separator: '__'; // Define the element separator
$modifier-separator: The '-'; // Define the state separator
$state-prefix: 'is-';  // The status prefix
Copy the code

src/mixins/mixins.scss

Packages \theme-chalk\ SRC \mixins\mixins. Define b, e, m methods in SCSS.

/* BEM -------------------------- */
@mixin b($block) {
  // Global parameter declaration component class name
  $B: $namespace+The '-'+$block! global; . # {$B} {
    @content; }}@mixin e($element) {
  // ...
}
@mixin m($modifier) {
  // ...
}
Copy the code

$b: $namespace+’-‘+$block! global; =>$B:el-backtop ! global;

Using #{} interpolation directly uses variables.

$B:el-backtop ! global; . # {$B} {
  @content} // compile to.el-backtop{//... }Copy the code

Use @Content to import the content into the blend style and compile the final result

.el-backtop {
  position: fixed;
  background-color: #fff;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: #409eff; 
  display: flex; 
  align-items: center; 
  justify-content: center;
  font-size: 20px; 
  box-shadow: 0 0 6px rgba(0.0.0.0.12);
  cursor: pointer;
  z-index: 5;
  
  &:hover {
    background-color: #f2f6fc; }}Copy the code

lib/backtop.css

Gulpfile.js is used to compile SCSS files and convert them into CSS. After browser compatibility and format compression, packages\theme-chalk\lib\backtop. CSS are generated.

.el-backtop {
  position: fixed;
  background-color: #fff;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  color: #409eff;
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: center;
  -ms-flex-pack: center;
  justify-content: center;
  font-size: 20px;
  -webkit-box-shadow: 0 0 6px rgba(0.0.0.0.12);
  box-shadow: 0 0 6px rgba(0.0.0.0.12);
  cursor: pointer;
  z-index: 5;
}
.el-backtop:hover {
  background-color: #f2f6fc;
}
Copy the code

0x06 Example

Component should be aware of the CSS properties of the object that target triggers scrolling.

<template>
  <div id="app" class="app">
    <div style="min-height:2400px">
      <el-backtop target="#app" @click="top"
        ><div
          style="{ height: 100%; width: 100%; background-color: #f2f5f6; Box-shadow: 0 0 6px rgba(0,0,0,.12); text-align: center; line-height: 40px; color: #1989fa; }"
        >
          UP
        </div>
      </el-backtop>
    </div>
  </div>
</template>

<style>
.app {
  height: 100vh;
  overflow-x: hidden;
}
</style>
Copy the code

0 x07 📚 reference

‘ViewPort ‘,MDN ‘requestAnimationFrame’,MDN’ Easing Function ‘, easings.net ‘BEM’,bemcss.com ‘BEM’, SegmentFault