Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have to create a circular slider which can determine an angle. I'm following this and I would like to know if there is a way to have circular boundary.

Or another easier method to implement that?

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
281 views
Welcome To Ask or Share your Answers For Others

1 Answer

Update thanks the TheWildHealer's response in this SO, we can play with [cdkDragConstrainPosition] and use cdkDrag

our .html it's equal

<div #container class="container">
  <div class="circle" [ngStyle]="styleCircle"></div>
  <div #indicator
    class="indicator"
    cdkDrag (mousedown)="initDrag($event)"
    [cdkDragConstrainPosition]="computeDragRenderPos"
    data="indicator"
  ></div>
</div>

And we need also a ViewChild to get the "indicator" and the "container". See that it's necesary use the mouseDown to get the position relative where we are "click"

initDrag(event){
    const rect=this.indicator.nativeElement.getBoundingClientRect()
    this.incr={x:event.clientX-rect.x-rect.width/2,
               y:event.clientY-rect.y-rect.height/2}
  }
  computeDragRenderPos = (pos, dragRef=null) => {
    const rect=this.container.nativeElement.getBoundingClientRect()
    const origin={x:this.origin.x+rect.x+this.incr.x,
                  y:this.origin.y+rect.y+this.incr.y}
    const angle = Math.atan2(
      pos.y - origin.y,
      pos.x - origin.x,
    );
    return {
      x: origin.x + this.radius * Math.cos(angle),
      y: origin.y + this.radius * Math.sin(angle),
    };
  };

You can see in this stackblitz

Old answer

You can not do it using "drag", a "drag" allow move the element free, so you need "play" with mousemove mousedown and mouseup. The idea is using [ngStyle] to change the position of an "indicator" changing the properties "left" and "top"

Imagine you has an .html like

<div #container class="container">
  <div class="circle" [ngStyle]="styleCircle"></div>
  <div #indicator class="indicator" (mousedown)="drag()" [ngStyle]="style"></div>
</div>

See that I use two reference variables, one for "container" and another one for "indicator" -the div with class "circle is only to show the circle-

See also that "container" should has position:relative and "indicator" has position:absolute

.container{
  position:relative
}
.indicator {
  width:3rem;
  height: 3rem;
  position: absolute;
  cursor: move;
  background-color:lightsteelblue;
}

Using ViewChild, in ngAfterViewInit we calculate the position of the "container" and the size of indicator (really the size divided by 2)

@ViewChild('indicator',{static:true}) indicator:ElementRef
@ViewChild('container',{static:true}) container:ElementRef

ngAfterViewInit()
{
    const { width, height } = this.indicator.nativeElement.getBoundingClientRect()
    const { x, y } = this.container.nativeElement.getBoundingClientRect()
    this.pos={x:x,y:y}
    this.size={width:width/2,height:height/2}
    this.origin={x:this.radius,y:this.radius}

  })
}

We are going to calculate the style given an x and a y using Math.atan2, Math.cos and Math.sin. As the position is relative to the top-left of the "item", you less the size.with and size.height (really size.with and size.height is the size divided by 2)

  calculateStyle(posx:number,posy:number)
  {
    const angle=Math.atan2(posy,posx);
    return {left:this.origin.x-this.size.width+this.radius*Math.cos(angle)+'px',
            top:this.origin.y-this.size.height+this.radius*Math.sin(angle)+'px'
    }
  }

The last is subscribe to event mousemove (while we don't mouseup) when "click" the indicator

  drag(){
    this.onDrag=true;
    fromEvent(this.document,'mousemove').pipe(
      takeWhile(()=>this.onDrag)
    ).subscribe((event:any)=>{
      this.style=this.calculateStyle(
             event.pageX-this.pos.x-this.origin.x,
             event.pageY-this.pos.y-this.origin.y)
    })
    fromEvent(this.document,'mouseup').pipe(take(1)).subscribe(_=>this.onDrag=false)
  }

See that you pass to the function calculateStyle the values taking account the "pos" of container, and the "origin" calculate in the AfterViewInit

You can see in this stackblitz


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...