preface

I haven’t written an article for nearly a year, and I don’t know what the level has become. I recently made a demand for an activity page, and stayed up late for more than 20 days. I feel that some content can be summarized, so I write it out and share it. The article is long, I hope you can read it patiently, in case some code is useful to you? All the code in this article is in the Github repository, and you are welcome to use it directly.

The article is long, it is recommended to collect ~

To tell you the truth, this is my first time to do front-end development work related to the activity page, so this is a summary after the initial experience:

  • First: Activity experience will improve the technical level of a business front end in a short term

    If you never write activity type of demand, for the first time to do it, you will be on the front, especially the c-terminal scenes have a deeper understanding of, for example, if you really to write complex interaction logic (every movement even a simple click event will also have interactive effect), and so on, after you’ve written, in a short period of time may be you and I also have some summary.

  • Second: activities (618, Double 11) this kind of, for a front end is more of a physical test

    From May Day to the end of May, I only had one day off, and the working hours were basically stable at 10 to 12. I worked overtime to write the page effect and optimize the user experience, which was not so much a technical challenge, but more accurately a challenge of body and willpower.

  • Third: the coherence of the field of activity page function, more is CV operation

    Generally speaking, the activities of an APP are throughout the whole year, such as Valentine’s Day -> Women’s Day -> 520 -> Mid-year (618) -> Chinese Valentine’s Day -> Double Eleven -> Spring Festival, etc. There are differences in each family, but on the whole, they are the same. There are so many events in a year, but in fact, the overall gameplay and appearance of your event page is basically throughout, that is, your first event is developed, the basic effect is finished, the rest of the first few events to do is copy and paste (ctrlC + ctrlV) and style theme color small adjustments. So, the front end of the activity can be really boring if you do it throughout the year.

    To sum up, the H5 requirements related to activities can improve a front-end understanding of the development of C-end pages and a lot of technical points and related to the experience level of user interaction in the short term, but in the long term, it will be slightly boring, and it is a great burden both psychologically and physically.

The above is from a business front end perspective to give you an analysis of some of the experience of doing the activity page H5 requirements, and then to give you a brief share of the recent closed for a month on the front end of the activity page some thinking and technical summary. Here’s a small demo that runs through what I’m covering in this article:

There is a QR code below, you can scan the mobile browser to have a look, although it is very simple, but the function in the article is simple to achieve:

Demo address _https: / / the activity – Demo. Vercel. App /

Demo_github_ address

I – Page adaptation

Page fit has long been an inescapable topic for H5 developers — how to fit various screen sizes and double and triple screens with minimal code. There are many solutions: CSS Media Query, REM layout, VW/VH layout, etc. Choosing a suitable adaptation scheme can make us focus more on the business logic code and less on the compatibility of UI layout during the development process. A piece of code can be adapted to various screens without additional code compatibility, which can save the development time and improve the development efficiency.

Therefore, my side for the page adaptation here summed up the simple dry solution is – – fixed device width for THE WIDTH of the UI draft, the development of all according to the SIZE of the UI draft to write, the zooming adaptation and other work by the browser to complete automatically.

The core code is as follows, and as you can see, it’s really very simple and easy to use compared to other solutions:

var $_DESIGN_LAYOUT_WIDTH = 414
<meta name="viewport" content="width=$_DESIGN_LAYOUT_WIDTH ,user-scalable=no,viewport-fit=cover">// 375 design draft<meta name="viewport" content="width=375, user-scalable=no, viewport-fit=cover">// 414 Design draft<meta name="viewport" content="width=414, user-scalable=no, viewport-fit=cover">
Copy the code

A quick look at the effect:

  • H5 code written using the normal Viewport tag
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Old</title>
  <style>
    p {
      font-size: 14px;
    }
  </style>
</head>
<body>
  <h2>The page title</h2>
  <p>- First: Activity experience will improve the technical level of a business front end in a short term. The author has never done the C-end activity page before. After this, I have some simple technical feelings.<br />I work in the TOP3 short video e-commerce industry in China. From May Day to the end of may, I have only one day off. My working hours are basically stable from 10 a.m. to 12 p.m.</p>
</body>
</html>
Copy the code

  • Code written using the viewport tag for this method
<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=414,user-scalable=no,viewport-fit=cover">
  <title>New</title>
  <style>
    p {
      font-size: 14px;
    }
  </style>
</head>
<body>
  <h2>The page title</h2>
  <p>- First: Activity experience will improve the technical level of a business front end in a short term. The author has never done the C-end activity page before. After this, I have some simple technical feelings.<br/>I work in the TOP3 short video e-commerce industry in China. From May Day to the end of may, I have only one day off. My working hours are basically stable from 10 a.m. to 12 p.m.</p>
</body>
</html>

Copy the code

summary

From the above two schemes of screenshots contrast can see come out, the same layout, the first scheme of the text size is fixed, and how much are we set how many px, px is the second scheme, the browser has to help us on the page as a whole geometric scaling, such doing can make code as long as we follow to develop a set of UI specification, The visual effect will be the same across different sizes and resolutions. For a large number of text-based tiled pages it may not be obvious the difference, but for active pages, various detailed layouts and animation effects are required, and if the layout of the page is not the same, the developer needs to put a lot of extra work into the adaptation process, which is time-consuming and laborious.

advantages

  • Setting up is simple – only one line of code is required, which greatly reduces the amount of code and computation required compared to other adaptation methods.
  • There’s no adaptation to worry about — scaling and compatibility are left to the browser, and developers can work on the UI script.
  • Restore precision – absolute equal scale, can accurately restore the visual draft.
  • Easy testing — Most testing can be done on PC, with a few tweaks as needed on mobile.

disadvantages

There is no perfect solution, and every advantage must be a disadvantage. There are also some other problems with the above solution, such as:

  • Pixel loss – For some low end phones with low resolution (this is common on low end Android phones, non-2X / 3X screen phones), the pixel may not reach the specified viewport width and the screen rendering may not be accurate. More often than not, the bezel “disappears”. (Considering the proportion of low-end machines, such problems can be ignored)

  • Failure to scale – Some Android phones do not scale the viewport according to the meta tag width, so you need to use initial-scale to scale the viewport (again, most phones work fine).

Therefore, on the whole, I prefer this scheme, and its advantages >>(far outweigh) its disadvantages. Some of you might say, that’s it? Is it that simple? It doesn’t feel like a plan. Does it really matter if you can solve something in very simple code? Is the implementation of the scheme that everyone can not understand advanced NB? So what I want to say is that the simplest solution, the simplest solution, is the best solution. The summary here is only the author’s personal suggestion to use or personal opinion of the better scheme, as for other schemes such as REM layout, there are many good articles on the Internet, here do not do comparison and expansion, interested to look up the line.

II – Drag effects

Drag and drop is certainly not new to the front end, such as implementing a set of drag and drop based on HTML5’s Drag && DRAP API or directly using the community known react-DND, etc. Let’s share a drag and drop API with you.

Implemented functions

  • 1 – Out of the box, directly introduced to use. Whether it’s Vue, React or HTML5, those who are interested can repackage based on their own framework.

  • 2 – Drag-and-drop animation on mobile is implemented, which should be fairly smooth.

  • 3 – The bounding is performed, for example, I am only allowed to park 20px on the left and right sides, not in the middle

Implementation approach

Mobile drag, useTouch event + requestAnimationFrameImplement.

Specific code

Drag and drop the code github address

/** * To ensure compatibility, it is recommended to use the polyfill version of requestAnimationFrame, RAF ** for ES6, import/export */
const raf = window.requestAnimationFrame;
/** * Encapsulate drag function *@param $ele Requires drag-and-drop elements *@param 1, Test for B {x = 20, y = 80} 2, test for B {x = 20, y = 80
export default function draggable($ele: HTMLElement, adsorb = { x: 20, y: 80 }) {
  if(! $ele) {throw new Error('Must be draggable.');
  }
  // The starting position
  let startX = 0;
  let startY = 0;

  // The left and top parameters are used to determine the position of the element
  let left = 0;
  let top = 0;

  // Screen width and height
  const cw = document.documentElement.clientWidth;
  const ch = document.documentElement.clientHeight;

  // Get the width and height of the element itself
  const { width, height } = $ele.getBoundingClientRect();

  /** * Start drag event */
  $ele.addEventListener(
    'touchstart'.function (event: TouchEvent) {
      startX = event.targetTouches[0].pageX;
      startY = event.targetTouches[0].pageY;

      top = $ele.offsetTop;
      left = $ele.offsetLeft;

      event.preventDefault();
    },
    false
  );

  /** * Events during drag */
  $ele.addEventListener(
    'touchmove'.function (event: TouchEvent) {
      // Offset the distance
      const offsetX = event.targetTouches[0].pageX - startX;
      const offsetY = event.targetTouches[0].pageY - startY;

      $ele.style.top = `${top + offsetY}px`;
      $ele.style.left = `${left + offsetX}px`;
      $ele.style.right = 'auto';
      $ele.style.bottom = 'auto';

      event.preventDefault();
    },
    false
  );

  function touchDone(event: TouchEvent) {
    const dx = event.changedTouches[0].pageX - startX;
    const dy = event.changedTouches[0].pageY - startY;

    const ty = top + dy;
    const tx = left + dx;

    $ele.style.top = `${ty}px`;
    $ele.style.left = `${tx}px`;
    $ele.style.right = 'auto';
    $ele.style.bottom = 'auto';

    const adsorb_safe_x = cw - width - adsorb.x;
    const adsorb_safe_y = ch - height - adsorb.y;

    raf(() = > {
      let nx;
      let ny = ty;

      if (tx + width / 2 < cw / 2) {
        nx = adsorb.x;
      } else {
        nx = adsorb_safe_x;
      }

      if (ty < adsorb.y) {
        ny = adsorb.y;
      } else if (ty > adsorb_safe_y) {
        ny = adsorb_safe_y;
      }

      $ele.style.webkitTransition = `left ${MOVE_ANIM_INTER}ms ease-in-out, top ${MOVE_ANIM_INTER}ms ease-in-out`;
      $ele.style.transition = `left ${MOVE_ANIM_INTER}ms ease-in-out, top ${MOVE_ANIM_INTER}ms ease-in-out`;

      const onAnimationDone = () = > {
        $ele.style.webkitTransition = $ele.style.transition = 'none';
        $ele.removeEventListener('webkitTransitionEnd', onAnimationDone, false);
        $ele.removeEventListener('transitionend', onAnimationDone, false);
      };

      $ele.addEventListener('webkitTransitionEnd', onAnimationDone, false);
      $ele.addEventListener('transitionend', onAnimationDone, false);
      $ele.style.top = `${ny}px`;
      $ele.style.left = `${nx}px`;
    });
  }

  $ele.addEventListener('touchend', touchDone, true);
  $ele.addEventListener('touchcancel', touchDone, true);
}

Copy the code

III – Animation effects

[Attention] : because the scene is special, so just a simple effect, can not put the complete activity effect intact move over, ugly point ugly point, we don’t mind, mainly talk about some technical details.

CSS3 – Red envelope action

As mentioned above, here is actually a CSS3 animation effect, there is no technology at all, but it needs to be seen with the drag and drop effects above, because when the red envelope disappears, it will disappear to the location of the pendant, so it needs to calculate the location of the pendant and the route of the red envelope disappearing. It’s kind of interesting, it’s not technical, but there’s a lot of computational logic here, and you can assemble whatever you want, so I’m just going to throw a few things at you.

Implementation of the specific code in the Demo warehouse, interested in can see the specific implementation logic

Lottie Web Animation effects

Here is nothing to talk about, just to provide you with some ideas of animation, front-end can have a lot of plans to make and play animation. For details, see lottie-Web

Lottie runs animations based on a JSON file, which is either generated for us by the animator or created by individual developers through software. The json for this animation looks something like this:

useEffect(() = > {
    lottieRef.current = lottie.loadAnimation({
      container: document.getElementById('lottie') as HTMLElement,
      renderer: 'svg'.loop: true.autoplay: false.path: 'https://labs.nearpod.com/bodymovin/demo/markus/halloween/markus.json'}}), [])function togglePlay() {
    if(! playing) { setPlaying(true);
      lottieRef.current.play();
      return;
    }
    setPlaying(false);
    lottieRef.current.pause();
}
Copy the code

It seems that the effect is very good, if the company has a professional animator, this littie scheme is very good ~

SVG path action

This is a new way to animate path types such as progress bars, loading animations, and timelines.

Similarly, below here is a simple SVG animation. Here is a simple broken line progress bar animation, based on which can be expanded, such as SVG to achieve loading icon animation effect, to achieve the percentage of completion animation effect, complex curve timeline animation effect and so on.

<svg
    style={svgStyle}
    className="map"
    width="300px"
    height="202px"
    viewBox="- 2 0, 302, 202"
    version="1.1"
    xmlns="http://www.w3.org/2020/svg"
  >
    <defs>
      <linearGradient id="line_1" x1="0%" x2="95.616%" y1="100%" y2="100%">
        <stop offset="0%" stopColor="#9396FF" />
        <stop offset="50.831%" stopColor="#A685FF" />
        <stop offset="100%" stopColor="#E695FC" />
      </linearGradient>
      <linearGradient id="line_2" x1="11.042%" x2="79.574%" y1="0%" y2="100%">
        <stop offset="0%" stopColor="#E094FC" />
        <stop offset="100%" stopColor="#E371FF" />
      </linearGradient>
    </defs>
    <g fill="none" fillRule="evenodd">
      <path
        d="M 2 3 L 300 3 L 300 202 L 2 202"
        strokeWidth="4"
        stroke="#fff"
        opacity="1"
        strokeLinecap="round"
      />
      <path
        d="M 2 3 L 300 3 L 300 202 L 2 202"
        strokeOpacity="1"
        strokeDasharray="800"
        strokeDashoffset="800"
        stroke="url(#line_1)"
        strokeWidth="4"
        strokeLinecap="round"
      >
        <animate
          id="mapAnimate"
          attributeName="stroke-dashoffset"
          begin="1s"
          dur="500ms"
          from="800"
          to={offset}
          fill="freeze"
          calcMode="linear"
        />
      </path>
      <g transform="translate(1)">
        <circle fill="#E294FC" opacity="201" cx="1" cy="2" r="6">
          <animateMotion
            begin="1s"
            dur={time+"ms"}
            repeatCount={repeatCount}
            fill="freeze"
            calcMode="linear"
            path="M 2 3 L 300 3 L 300 202 L 2 202"
          />
        </circle>
        <circle fill="url(#line_2)" cx="1" cy="2" r="5">
          <animateMotion
            begin="1s"
            dur={time+"ms"}
            repeatCount={repeatCount}
            fill="freeze"
            calcMode="linear"
            path="M 2 3 L 300 3 L 300 202 L 2 202"
          />
        </circle>
      </g>
    </g>
  </svg>
Copy the code

As you can see from the above effects, SVG can also achieve more complex animation effects, but with a more path-oriented approach. At the same time, some partners may ask, this looks like CSS3 can also be implemented, a GIF diagram may be more convenient. Good, the general common scene may be a GIF diagram more convenient, but if the animation effect of the route will change dynamically according to the time and progress, you need code to calculate, then 100 points to 100 GIF diagram, it is not worth the loss. So, again, there may be 100 solutions that implement a requirement, and the one that is most appropriate for development is chosen.

summary

This is just to help you expand your ideas. On the one hand, we rarely use animation effects in our development process. In addition, the animation in our mind may only come up with CSS3, Canvas, RAF and other schemes. Examples include APNG, SVG, and Lottie-Web. Specific use scenarios according to their own situation, in theory, a demand for each scheme can be implemented, depending on the complexity and implementation cost, choose the most appropriate scheme will get twice the result with half the effort.

IV – Pull down to refresh

If you are a Hybrid APP developer, in theory, the capability of the APP side should help realize this function. Front-end development should have a good experience with the ability to directly invoke the pull-down refresh function through jS-Bridge. However, for some reason, my APP did not help the front-end implementation, or the compatibility is not very good, so the front-end needs to write a drop-down refresh function. No more nonsense, or first on the effect:

As you can see from the figure above, the function is simply implemented, but in consideration of the subsequent scalability and convenience for users, the author has carried out a simple encapsulation of the code. After the encapsulation, the code provides the basic logic function, as well as some additional capacity expansion. For example, as you can see from the figure above, the background color of the body is blue, the default value of the code is set to white, etc., to better match the color, simple configuration, and sometimes do not want to pull down to refresh the entire page, but to refresh the data of an interface, can also be done:

import pullRefresh from 'pull-refresh.ts'

useEffect(() = > {
    function _refreshListener() {
      swal("", {
        // @ts-ignore
        buttons: false.timer: 1000.title: 'Page refreshed successfully, interface data refreshed'.text: ' '}); }const pl = new PullRefresh({
      refreshListener: _refreshListener,
      refreshStyleConfig: {
        color: '#fff'.fontSize: '14px'.backgroundColor: 'dodgerblue',}});// You can also manually set whether to enable or disable the drop-down refresh function
    // pl.setEnabled(false);
}, [])
Copy the code

The effect is as follows:

The core code is as follows, pull-refresh

interface IPullRefreshConfig { $_ele? : HTMLElement; enabled? : boolean; refreshListener? :() = > void; refreshStyleConfig? : Record<string, string>; } enum EPullDirection {'unkonw' = 0.'down' = 1.'up' = 2
}

function __default_refresh_listener() {
  location.reload();
}

const __default_refresh_style_config = {
  color: '# 000'.fontSize: '12px'.backgroundColor: 'rgba(255, 255, 255, 1)'.// Refresh the background color of the container
}

export default class PullRefresh {
  constructor(config: IPullRefreshConfig = {}) {
    this.$_ele = config.$_ele || document.body;
    this.refreshListener = config.refreshListener || __default_refresh_listener;
    this.refreshStyleConfig = config.refreshStyleConfig || __default_refresh_style_config;
    // Initialize, and you can update
    this.init();
  }
  // Pull down the refreshed container
  $_ele: HTMLElement;

  // Pull-down refresh is available
  enabled: boolean = (window as any).__pull_refresh_enabled || false;

  // Refresh the function
  refreshListener: () = > void;

  // Drop down the position function during the refresh
  position =  {
    start_x: 0.// Start to touch position x
    start_y: 0.// Start to touch position y
    end_x: 0.// End position x
    end_y: 0.// The end position is y
    direction: EPullDirection.unkonw, // The direction of the finger movement
    scroll_on_top: true.// If the scroll bar is at the top, it can be pulled down to refresh at the top
  }

  // Whether in loading, initialize false
  loading: boolean = false;

  // Refresh the container during the process
  refreshContainer: HTMLElement | null = null;

  // Pull down the timer in the process
  timer: any;

  // Refresh the style configuration
  refreshStyleConfig: any = __default_refresh_style_config;

  setEnabled(flag: boolean) {
    this.enabled = flag;
    (window as any)._setPullRefreshEnabled(flag);
  }

  setRefreshListener(fn: any) {
    this.refreshListener = fn;
    console.log(this.999999)}setLoading(flag: boolean) {
    this.loading = flag;
  }

  setRefreshContainer(dom: HTMLElement) {
    this.refreshContainer = dom;
  }

  checkScrollIsOnTop() {
    const top = document.documentElement.scrollTop || document.body.scrollTop;
    return top <= 0;
  }

  // Initialize the TouchStart event
  initTouchStart() {
    const _self = this;
    _self.$_ele.addEventListener('touchstart'.function(e) {
      // If pull-down refresh is disabled, return directly
      if(! _self.enabled)return;
      Object.assign(_self.position, {
        scroll_on_top: _self.checkScrollIsOnTop(),
        start_x: e.touches[0].pageX,
        start_y: e.touches[0].pageY,
      });
    });
  }

  // Initialize the TouchMove event
  initTouchMove() {
    const _self = this;
    _self.$_ele.addEventListener('touchmove'.function(e) {
      // Store the offset during the move
      const { start_x, start_y, scroll_on_top } = _self.position;
      const offsetY = e.touches[0].pageY - start_y;
      const offsetX = e.touches[0].pageX - start_x;
      console.log(_self.position);
      // The downward direction is the refresh
      if (offsetY > 150 && offsetY > Math.abs(offsetX)) {
        _self.position.direction = EPullDirection.down
      } else if (offsetY < 0 && Math.abs(offsetY) > Math.abs(offsetX)) {
        _self.position.direction = EPullDirection.up
      } else {
        _self.position.direction = EPullDirection.unkonw
      }
      if (
        !_self.enabled || // If disabled, return
        _self.loading || // If loading is in progress, return directly! scroll_on_top ||// If it's not at the top, return it_self.position.direction ! == EPullDirection.down// Return directly instead of down
      ) return;
      console.log('The drop-down threshold has been reached:', offsetY);
      _self.setLoading(true);
      Object.assign(_self.$_ele.style, {
        transform: 'translate3d(0, 100px, 0)'.transition: 'all ease .5s'}); (_self.refreshContaineras HTMLElement).innerHTML = "Pull down to refresh content...";
    });
  }

  // Initialize the TouchMove event
  initTouchEnd() {
    const _self = this;
    _self.$_ele.addEventListener('touchend'.function() {
      if(! _self.enabled)return;
      const { scroll_on_top, direction } = _self.position;
      // End does not do anything if it is not at the top or loading is not triggered
      if(! scroll_on_top || direction ! == EPullDirection.down || ! _self.loading)return;
      (_self.refreshContainer as HTMLElement).innerHTML = '<div class="refresh-icon"></div>';
      _self.timer = setTimeout(function() {
        if (_self.timer) clearTimeout(_self.timer);
        (_self.refreshContainer as HTMLElement).innerHTML = ' ';
        Object.assign(_self.$_ele.style, {
          transform: 'translate3d(0, 0, 0)'.transition: 'all cubic - the bezier. 21,1.93. 53,. (64) 0.5 s'
        });
        _self.setLoading(false);
        _self.position.direction = EPullDirection.unkonw;
        setTimeout(() = > {
          // Start the refresh
          _self.refreshListener();
          setTimeout(() = > {
            // The fixed layout in the DOM will be invalid
            Object.assign(_self.$_ele.style, {
              transform: ' '.transition: ' '
            });
          }, 500)}); },1000); })}/** * initializes the style of the pull-down refresh */
  initRefreshStyle(cssStr: string = ' ') {
    if (document.getElementById('pull_refresh__style') && cssStr.length > 0) {(document.getElementById('pull_refresh__style') as HTMLElement).innerHTML = cssStr;
      return;
    }
    const styleDom = document.createElement('style');
    styleDom.id = 'pull_refresh__style';
    styleDom.innerHTML = `
      .pull_refresh__container {
        position: absolute;
        width: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        height: 100px;
        line-height: 100px;
        color: The ${this.refreshStyleConfig.color};
        font-size: The ${this.refreshStyleConfig.fontSize};
        text-align: center;
        left: 0;
        top: 0;
        background-color: The ${this.refreshStyleConfig.backgroundColor}; transform: translate3d(0, -100px, 0); } div.refresh-icon {border: 2px solid rgba(126, 126, 126, 0.2); border-top-color: #fff; border-radius: 50%; width: 26px; height: 26px; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); }} `;
    document.head.appendChild(styleDom);
  }

  /** * You can use the body as a pull-down refresher for H5. You can use the #app as a pull-down refresher for SPA
  initRefreshContainer() {
    const refreshDom = document.createElement('div');
    refreshDom.classList.add('pull_refresh__container');
    If the first element does not exist, insert it directly
    if (!this.$_ele.firstElementChild) {
      this.$_ele.appendChild(refreshDom);
      return;
    }
    // Insert before the first element
    this.$_ele.insertBefore(refreshDom, this.$_ele.firstElementChild);
    // Initialize the drop-down refresh container
    setTimeout(() = > {
      this.setRefreshContainer(refreshDom);
    }, 0);
  }

  init(){(window as any)._setPullRefreshEnabled = function(flag: boolean) {(window as any).__pull_refresh_enabled = flag;
    }
    this.setEnabled(true);
    // Initialization of the page style and structure can be done in the new Class process
    this.initRefreshStyle();
    this.initRefreshContainer();
    this.initTouchStart();
    this.initTouchMove();
    this.initTouchEnd(); }}Copy the code

There are two reasons to consider encapsulating a small NPM package. On the one hand: the function is highly customized, such as the content of the pull-down refresh and how to display (icon, copy, length and trigger time, etc.) each demand is different from each page, so it is more convenient to take the code directly to change; On the other hand, the general App has helped us to provide a drop-down refresh function, in fact, there is no need to customize their own development, here is just to write a simple summary, if you really have a need, can be used to change it. At present, it is only a simple effect Demo. In the future, more functions may be updated continuously and higher customization will be made, so as to achieve the status that I personally think can be sent NPM package. Those who are interested can build together and optimize the experience.

V – Precautions for mobile H5 active page

The above introduces several technical points summarized in the development process of this activity, and there are many places to borrow some plans of the big guy and carry out some modifications. Of course, some partners may say, these are not the basic front-end knowledge? There’s nothing to tell. This is also what I want to say, why activities will exercise people, the above content you may see the effect of what to use, but when it comes to the real needs, to think about, if you are not a front end of the activity page, usually business you will have the following scene?

  • Nothing will write a red envelope in that around?

  • Is it going to implement a drag and drop widget? (The red envelope pendants of Didi/Meituan and so on are not draggable, indicating that this function varies from person to person and differs from product to product and is not just needed)

  • Red envelope disappear is not directly hidden but disappear to the draggable pendant location?

  • How do you do this if your end does not support pull-down refresh?

So, you know how the effect works and you can make it work for the product. The feeling and purpose of stepping on the pit is that if you encounter similar needs and similar effects when you can save some time, the time saved to learn other technologies or more than two bugs, is not more meaningful.

Here are a few front-end pit problems and solutions encountered during the development process, here is also a brief summary to share, to emphasize, not requirements, but the mobile terminal development process actually encountered several pits:

1 – Optimized for mobile performance and animation — CSS hardware acceleration

When doing mobile development, most front-end development may use the browser development, and then finally to the real machine debugging, but sometimes you will find that the development effect on PC is completely fine, but on the real machine effect does not take effect, what is the specific reason? In fact, there are many possible reasons, but I have encountered the following two – the 3D animation does not work and the page shake.

  • Case 1: red envelope effect

You should see the animation effect of the red envelope rolled out, the core code is as follows:

.red__packet_rotate {
    -webkit-transform: scale(0.1) rotateY(270deg);
    transform: scale(0.1) rotateY(270deg);
}

.red__packet_rotate_active {
    -webkit-transform: scale(1) rotateY(0) translate3d(0px.0px);
    transform: scale(1) rotateY(0) translate3d(0px.0px);
    -webkit-transform-origin: center center;
    transform-origin: center center;
    -webkit-transition: transform .6s cubic-bezier(0.33333333.0.0.66666667.1);
    transition: transform .6s cubic-bezier(0.33333333.0.0.66666667.1);
}

Copy the code

Translate3d (0, 0) is used, but if you look at the code, you can see that the z-axis is not used, so translate(0, 0) is ok, I didn’t care when I was developing, it is translated (0, 0), In the browser the effect is exactly the same and perfect, but when on the phone, it looks like this:

You can see that the red envelope does not flip (but if it is a browser, it is normal effect). After debugging for a long time, the above problem was found, so translate3D is needed, which is an experience to avoid the pit.

  • Case 2: Pulldown refresh jitter

Drop-down refresh this piece of logic, too, did not elaborate, drop-down refreshes the use effect of the transition is also a range of animation effect, if you don’t open hardware acceleration, the browser preview without any problems, but on a mobile phone, up and down shaking particularly severe, also said the above, you can add some code to open the CSS hardware acceleration to avoid shaking ~

The solution to these two problems is to enable CSS hardware acceleration through the following code.

// 1- usetransform: translateZ(0)
body {
    -webkit-transform: translateZ(0);
    -moz-transform: translateZ(0);
    -ms-transform: translateZ(0);
    -o-transform: translateZ(0);
    transform: translateZ(0); } / /2- usetransform: translate3d(0.0.0)

body {
    -webkit-transform: translate3d(0.0.0);
    -moz-transform: translate3d(0.0.0);
    -ms-transform: translate3d(0.0.0);
    transform: translate3d(0.0.0); } / /3- In Chrome and Safari, we may see flickering effects when using CSS changes or animationsbody {
    -webkit-backface-visibility: hidden;
    -moz-backface-visibility: hidden;
    -ms-backface-visibility: hidden;
    backface-visibility: hidden;

    -webkit-perspective: 1000;
    -moz-perspective: 1000;
    -ms-perspective: 1000;
    perspective: 1000;
}

Copy the code

To sum up, if you’re writing for mobile and it involves animation, hardware acceleration is the way to go

2 – Click through and solutions on mobile

  • Case 1: red envelope layer scrolling through

The body element of the page is the container for the drop-down refresh. As mentioned above, we use the touch event to implement this. When the red envelope layer comes out, we don’t want the page to scroll, but the actual effect is as follows:

Get straight to the solution:

1-HTML or Body overflow: hidden

Add the following code when the shell layer appears and remove it when it disappears.

html {
    overflow: hidden;
}
Copy the code

Advantages: Straightforward and simple, suitable for both PC and mobile.

Cons: The overall experience is not good, because if the user has already scrolled the page, then the layer comes out, the scroll bar disappears after setting the above properties, and when the layer disappears, the scroll bar reappears, the page will have a shaking process.

2 – Passive.

// Add an event listener to the element to prevent penetration
(document.getElementById('coupon_wrap') as any).addEventListener(
  'touchmove'.function(e: any) {
    e.preventDefault()
  },
  { passive: false } // // Passive effect is prohibited
);
Copy the code

For details about the passive: false attribute, you can see how using passive improves the scrolling performance

Advantages: The mobile terminal works well. The passive mode applies to most mobile scenarios.

Disadvantages: At present, it is perfect in mobile terminal, but in PC terminal, in addition to touch events, if you want to be more compatible, you need to listen to mouse events, etc., which is more troublesome.

For mobile terminal, I suggest to use the second scheme, which has a good experience and is relatively easy. It will not affect other layout related content and only deal with the elastic layer.

3 – Fixed layout related issues on mobile

  • Case 1: Drop down to refresh the bottom fixed layout is not fixed anymore

Normal fixed layout:

Abnormal fixed layout:

As you can see, fixed layout is losing its effect. Why is that? To be honest, if I had not encountered this Bug in the development process, I did not know that fixed layout would still be affected, finally looked up, can not help but sigh, the CSS world may be much more detailed than the JS world. And, the MDN document is really awesome, YYDS

[Reason] : During development, the fixed layout disappeared because of an effect that added the transform attribute to the body tag. The final solution was to empty the transform attribute when the body effect was complete.

conclusion

I hope you have your own design and consideration of solution architecture before each requirement begins, and your own technical precipitation and summary after each requirement ends. Over time, this knowledge will become your wealth.

Here is a small warehouse named Useful kit, the purpose of which is to summarize and sort out the small functions used in daily development that are not so easy to be sent into NPM package, so as to facilitate business use and work with simple transformation. Interested ones can be built together

useful-kit

I really wasted my writing for various reasons before, but I decided to start writing again this time. I hope I can stick to it from the second half of this year. Finally, if you are interested, please leave a message to communicate with me at any time