SoFunction
Updated on 2025-04-04

Simulate the swiper component of WeChat applet based on angular

During this period, my main business was to complete a housekeeping mini program, and finally it was reviewed and released. I have to say that the mini program ecosystem of WeChat is quite thoughtful. Putting aside some of its existing problems, the component system it provides looks quite cool at first glance. For example, a view component called swiper can save a lot of time and code when writing interfaces, and can be used for carousel pictures and slideable lists. This is why I want to complete such a component when I come back to write an angular project. This article will use angular component capabilities and service capabilities to complete such a relatively general and low coupling swiper.

First of all, you need to choose the technology you use. What you want to implement is to deal with the interface, which is naturally implemented into a component. The ultimate effect is to write such code to complete a sliding view:

<swipers>
<swiper>view1</swiper>
<swiper>view2</swiper>
</swipers>

Then you need to write down the most basic component definition, obviously you need to define two components here. The first one is the parent component, and the selector name is ytm-swipers. What you are doing is to create a shell to define the basic style. The child tags in use will be inserted into the ng-content tag.

@Component({
  selector: 'ytm-swipers',
  template: `
    <div class="view-body">
      <ng-content></ng-content>
    </div>
    `,
  styles: [`
    .view-body{height: 100%;width: 100%;overflow: hidden;position: relative;}
  `]
})

The second is the child view. Under the parent component, each child component will be covered with parent components, and only the current child component will be displayed. When switching views, what is actually done is to change the display method of these child components. The simplest thing to say is, this child component is just used to add a child shell and add a basic style to the shell. The actual page content is placed in the ng-content tag.

@Component({
  selector: 'swiper',
  template: `
    <div class="view-child" *ngIf="(childId) >= 0"
    [ngClass]="{'active': [0] === childId,
    'prev': [2] === childId, 'next': [1] === childId}">
      <ng-content></ng-content>
    </div>
  `,
  styles: [`
    .view-child{
      height: 100%;width: 100%;position: absolute;top: 0;
      transition: 0.5s linear;background: #fff;
      overflow-x: hidden;
    }
    .{left: 0;z-index: 9;}
    .{left: 100%;z-index: 7;}
    .{left: -100%;z-index: 8;}
  `]
})

The next step is to let these two father-son components complete spiritual communication. To be honest, you can use ElementRef to force the DOM to operate directly, but here is the service within the component. It is no different from ordinary services, but its provider is declared in a certain component, so this service can only be injected and used in this component and child components.

@Injectable()
class SwiperService {
  public swiperList: number[];
  public displayList: number[]; // 0 is the current one is the next one 2 is the previous one  public current: number;
  private changing: boolean;
  constructor() {
     = false;
     = [];
     = [];
     = 0;
  }
  public Add(id: number) {
    (id);
    switch () {
      case 1:
        [0] = id;
        return;
      case 2:
        [1] = id;
        return;
      default:
        [2] = id;
        return;
    }
  }
  public Next(): Promise&lt;any&gt; {
    if () {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return reject('on changing');
      });
    }
     = true;
    let c = ([0]);
    let n = ([1]);
    let p = ([2]);
    p = c;
    c = n;
    n = (c + 1) % ;
    [0] = [c];
    [2] = [p];
    [1] = -1;
    setTimeout(() =&gt; {
      [1] = [n];
       = false;
    }, 500);
    return new Promise&lt;any&gt;((resolve, reject) =&gt; {
      return resolve([0]);
    });
  }
  public Prev(): Promise&lt;any&gt; {
    if () {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return reject('on changing');
      });
    }
     = true;
    let c = ([0]);
    let n = ([1]);
    let p = ([2]);
    n = c;
    c = p;
    p = p - 1 &lt; 0 ?  - 1 : p - 1;
    [0] = [c];
    [1] = [n];
    [2] = -1;
    setTimeout(() =&gt; {
      [2] = [p];
       = false;
    }, 500);
    return new Promise&lt;any&gt;((resolve, reject) =&gt; {
      return resolve([0]);
    });
  }
  public Skip(index: number): Promise&lt;any&gt; {
    let c = ([0]);
    if ( || c === index) {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        reject('on changing or no change');
      });
    }
     = true;
    let n = (index + 1) % ;
    let p = index - 1 &lt; 0 ?  - 1 : index - 1;
    [0] = [index];
    if (index &gt; c) {
      [2] = [p];
      [1] = -1;
      setTimeout(() =&gt; {
        [1] = [n];
         = false;
      }, 500);
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return resolve([0]);
      });
    } else {
      [1] = [n];
      [2] = -1;
      setTimeout(() =&gt; {
        [2] = [p];
         = false;
      }, 500);
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return resolve([0]);
      });
    }
  }
}

The variables used include: The changing variable ensures that only one switching can be performed at the same time, so that the next switching can be performed after the switching is completed; the swiperList loads the id of all views, and this id is generated when the view is initialized; the displayList array has only three members, and the loading is the index of the current view in the swiperList, the index of the next view, and the index of the previous view; the current variable user indicates the id of the currently displayed view. The control of display in the actual view is to use the ngClass directive to append the corresponding class according to the displayList and view id. The current view will be displayed just right, the previous view will be blocked just right, and the next view will be blocked just right.

At the same time, the service also provides several methods: Add is used to add a view with a defined id, Next is used to switch to the next view (called when swiping left), Prev is used to switch to the previous view (called when swiping right), and Skip is used to switch to the view with a specified id directly.

Injecting this service into a subview requires generating an id when the subview is initialized and adding it to the view list:

export class YTMSwiperViewComponent {
    public childId: number;
    constructor(@Optional() @Host() public swiper: SwiperService) {
        =

@Injectable()
class SwiperService {
  public swiperList: number[];
  public displayList: number[]; // 0 is the current one is the next one 2 is the previous one  public current: number;
  private changing: boolean;
  constructor() {
     = false;
     = [];
     = [];
     = 0;
  }
  public Add(id: number) {
    (id);
    switch () {
      case 1:
        [0] = id;
        return;
      case 2:
        [1] = id;
        return;
      default:
        [2] = id;
        return;
    }
  }
  public Next(): Promise&lt;any&gt; {
    if () {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return reject('on changing');
      });
    }
     = true;
    let c = ([0]);
    let n = ([1]);
    let p = ([2]);
    p = c;
    c = n;
    n = (c + 1) % ;
    [0] = [c];
    [2] = [p];
    [1] = -1;
    setTimeout(() =&gt; {
      [1] = [n];
       = false;
    }, 500);
    return new Promise&lt;any&gt;((resolve, reject) =&gt; {
      return resolve([0]);
    });
  }
  public Prev(): Promise&lt;any&gt; {
    if () {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return reject('on changing');
      });
    }
     = true;
    let c = ([0]);
    let n = ([1]);
    let p = ([2]);
    n = c;
    c = p;
    p = p - 1 &lt; 0 ?  - 1 : p - 1;
    [0] = [c];
    [1] = [n];
    [2] = -1;
    setTimeout(() =&gt; {
      [2] = [p];
       = false;
    }, 500);
    return new Promise&lt;any&gt;((resolve, reject) =&gt; {
      return resolve([0]);
    });
  }
  public Skip(index: number): Promise&lt;any&gt; {
    let c = ([0]);
    if ( || c === index) {
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        reject('on changing or no change');
      });
    }
     = true;
    let n = (index + 1) % ;
    let p = index - 1 &lt; 0 ?  - 1 : index - 1;
    [0] = [index];
    if (index &gt; c) {
      [2] = [p];
      [1] = -1;
      setTimeout(() =&gt; {
        [1] = [n];
         = false;
      }, 500);
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return resolve([0]);
      });
    } else {
      [1] = [n];
      [2] = -1;
      setTimeout(() =&gt; {
        [2] = [p];
         = false;
      }, 500);
      return new Promise&lt;any&gt;((resolve, reject) =&gt; {
        return resolve([0]);
      });
    }
  }
}
;
        ();
    }
}

This id is actually the index accumulation of the existing list, and once a new view is initialized, it will be added to the list (it is cool to support dynamic addition, although I don’t know if there will be any hidden problems).

The parent component must first configure a provider declaration service:

@Component({
  selector: 'ytm-swipers',
  template: `
    <div class="view-body">
      <ng-content></ng-content>
    </div>
    `,
  styles: [`
    .view-body{height: 100%;width: 100%;overflow: hidden;position: relative;}
  `],
  providers: [SwiperService]
})

Then you have to listen to the gesture sliding event and make corresponding switches. And pass in a current variable. Whenever this variable is updated, it must be switched to the corresponding id view. The actual use effect is:

<ytm-swipers [current]="1">...</ytm-swipers>You can switch the view to the view of id feed 1, which is the second view.

 export class YTMSwiperComponent implements OnChanges {
  @Input() public current: number;
  @Output() public onSwiped = new EventEmitter&lt;Object&gt;();
  private touchStartX;
  private touchStartY;
  constructor(private swiper: SwiperService) {
     = 0;
  }
  public ngOnChanges(sc: SimpleChanges) {
    if ( &amp;&amp;  !== undefined &amp;&amp;
     !== ) {
      ().then((id) =&gt; {
        (id);
        ({current: id, bySwipe: false});
      }).catch((err) =&gt; {
        (err);
      });
    }
  }
  @HostListener('touchstart', ['$event']) public onTouchStart(e) {
     = [0].clientX;
     = [0].clientY;
  }
  @HostListener('touchend', ['$event']) public onTouchEnd(e) {
    let moveX = [0].clientX - ;
    let moveY = [0].clientY - ;
    if ((moveY) &lt; (moveX)) {
      /**
        * Y-axis movement is less than X-axis and is determined to be lateral sliding
        */
      if (moveX &gt; 50) {
        ().then((id) =&gt; {
          //  = id;
          ({current: id, bySwipe: true});
        }).catch((err) =&gt; {
          (err);
        });
      } else if (moveX &lt; -50) {
        ().then((id) =&gt; {
          //  = id;
          ({current: id, bySwipe: true});
        }).catch((err) =&gt; {
          (err);
        });
      }
    }
     =  = -1;
  }
}

In addition, a callback function is added to the code, which can execute incoming callbacks when the view completes the switch. This uses the angular EventEmitter capability.

All of the above are implemented, and the actual usage examples are like this:

&lt;ytm-swipers [current]="0" (onSwiped)="Switch callback($event)"&gt;
   &lt;swiper&gt;
     view1
   &lt;/swiper&gt;
   &lt;swiper&gt;
     view2
   &lt;/swiper&gt;
   &lt;swiper&gt;
     view3
   &lt;/swiper&gt;
 &lt;/ytm-swipers&gt;

There are two ways to switch views. One is gesture sliding, but there is no real-time dragging. It is just to judge the left and right sliding to react, and the other is to update the value of the [current] node.

The implementation of the entire component does not use some of the underlying capabilities of angular, but only plays with the css style, component nesting and interacts with the outside world through service, as well as Input and Output. In comparison, the components of ionic are much more powerful and profound, and the author still has a long way to go.

The above is the swiper component of the angular implementation simulation WeChat mini program Swiper component introduced to you. I hope it will be helpful to you. If you have any questions, please leave me a message. The editor will reply to you in time!