Make writing a habit together! This is the 12th day of my participation in the “Gold Digging Day New Plan ยท April More text Challenge”. Click here for more details

In the last two articles, we learned about essential attributes and methods in elements and Angular’s custom Video for those who haven’t.

So, now that there is a need, how do you implement it?

The video TAB on the page, when the scrolling height exceeds its position, set it to be freely dragged in the visible area.

A nice Idea that can be easily implemented if you use Angular @angular/ CDK /drag-drop, but we don’t use tools here.

Well, let’s analyze the implementation idea:

  • The scrolling height of the page is greater than the position of the video: then it is the videobottomThe value relative to the visual window is less than zero, and we need to set a packagevideoOf the labeldivEasy to calculate, its height is the original settingvideoThe height of the. That is, the element is removed from the original document layout
  • videoElements can be dragged and dropped, so their positioning needs to be changed tofixed
  • videoElement is dragging freely in the viewable area, then thetop.leftValue is qualified

So we set up the following demo layout:

<div id="anchor" #anchor>
  <div class="video" id="video" #video>
    <div class="masker"></div>
    <video width="100%" height="100%" controls poster="assets/poster.png">
      <source src=".. /assets/demo.mp4" type="video/mp4" />
      Your browser does not support.
    </video>
  </div>
</div>
Copy the code

There are the following predefined styles:

<! -- styles.scss-- > <! This part needs to be in the global style -->html.body {
  height: 6000px;
  background-color: #fff;
}
Copy the code
<! -- demo.component.scss -->

#anchor {
  height: 360px;
  width: 100%;
  background-color: #F0F0F0;
}

.video {
  width: 640px;
  height: 360px;
  margin: 0 auto;
  background-color: black; <! --videoFixed layout style, not in the default layout --> &.video-fixed { 
    position: fixed;
    top: 10px;
    left: 10px;
    width: 320px;
    height: 150px;
    cursor: all-scroll;
    .masker {
         display: none;
      }
    &:hover {
      .masker {
        display: block;
        position: absolute;
        width: 100%;
        height: 100%;
        background-color: rgba(0.0.0.0.8);
        z-index: 2; }}}}Copy the code

RXJS is also introduced here to operate.

Element is removed from the original document layout

We have just analyzed the critical adjustment of the video element away from the document:

The bottom < 0 of the relative view of the #anchor element of the video’s outer div. So we have:

@ViewChild('anchor', { static: false })
publicanchor! : ElementRef;@ViewChild('video', { static: false })
publicvideo! : ElementRef;publicscroll! :any;

ngAfterViewInit(): void {
  this.scroll = fromEvent(document.'scroll');
  this.scrollFn();
}

// Page scroll
public scrollFn() {
  this.scroll
    .pipe(
      debounceTime(50), / / image stabilization
      map(() = > this.anchor.nativeElement.getBoundindClientRect().bottom < 0)
    )
    .subscribe((flag: boolean) = > {
      // Add and remove styles
      if(flag) {
        this.video.nativeElement.classList.add('video-fixed');
      } else {
        this.video.nativeElement.classList.remove('video-fixed'); }})}Copy the code

Obtain the Anchor element object first, and monitor the scrolling of the page object document (we have added the optimization of the anti-shake function here). When bottom < 0, add the related style video-fixed to the video.

Drag and drop elements

The next step is to implement drag and drop of the video element. Here we listen for three events of the video element: mousedown, mouse movement of the mousemove, and mouseup.

// demo.component.ts

publicmouseDown! :any;
publicmouseUp! :any;
publicmouseMove! :any;

ngAfterViewInit(): void {
  this.mouseDown = fromEvent(this.video.nativeElement, 'mousedown'); // Target element press, i.e. Video
  this.mouseMove = fromEvent(document.'mousemove'); // The element moves within the document
  this.mouseUp = fromEvent(document.'mouseup'); // Mouse up
  
  this.moveFn()
}

// Target element moves
public moveFn() {
  this.mouseDown
    .pipe(
      filter(() = > this.video.nativeElement.classList.contains('video-fixed')),
      map(() = > this.mouseMove.pipe(
        throttleTime(50), / / throttling
        takeUntil(this.mouseUp)
      )),
      // concatAll sequentially accepts each stream thrown by the upstream stream as its data. If the previous stream cannot end synchronously, it will hold subsequent streams and subscribe to the later stream only after the current stream completes
      concatAll(),
      withLatestFrom(this.mouseDown, (move:any, down:any) = > {
        return {
          x: this.validValue(move.clientX - down.offsetX, window.innerWidth - this.video.nativeElement.offsetWidth, 0),
          y: this.validValue(move.clientY - down.offsetY, window.innerHeight - this.video.nativeElement.offsetHeight, 0)
        }
      })
    )
    .subscribe((position: {
      x: number,
      y: number
    }) = > {
      this.video.nativeElement.style.top = position.y + 'px';
      this.video.nativeElement.style.left = position.x + 'px'; })}// Check the boundary value
public validValue = (value:number, max:number, min: number) = > {
  return Math.min(Math.max(value, min), max)
}
Copy the code

We listen for the target element (filter function) to be mouse-pressed, and then the mouse can move around the document scope (optimized here with throttling function) until we listen for the mouse lift. As you move, calculate the distance of the target element relative to the left and top of the visible window, assigning values to the left and top.

Calculated move here. ClientX – down. OffsetX, window. The innerWidth – this. Video. NativeElement. OffsetWidth, related concepts maybe you are not very clear, but it doesn’t matter, ๐Ÿ‘† above content, Just understand the idea. Related knowledge points will be introduced in the next article.

In the end, we get the following effect ๐Ÿ‘‡

ใ€ the ใ€‘ โœ