Angular CDK in circular boundary to create a circular slider
Asked Answered
F

1

-1

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

Festoonery answered 27/4, 2021 at 14:26 Comment(0)
S
1

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

Solorio answered 30/4, 2021 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.