Angular CDK在圆形边界创建圆形滑块

Angular CDK in circular boundary to create a circular slider

我必须创建一个可以确定角度的圆形滑块。我正在关注 this,我想知道是否有办法拥有圆形边界。

或者另一种更简单的实现方法?

谢谢

更新 感谢 TheWildHealer 在 this SO 中的回复,我们可以玩 [cdkDragConstrainPosition] 并使用 cdkDrag

我们的.html是相等的

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

我们还需要一个 ViewChild 来获取“指示器”和“容器”。看到有必要使用 mouseDown 来获取我们“点击”的相对位置

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),
    };
  };

你可以在这个看到stackblitz

旧答案

不能使用“拖动”,“拖动”允许自由移动元素,因此您需要使用 mousemove mousedown 和 mouseup 来“玩”。这个想法是使用 [ngStyle] 来改变“指标”的位置,改变属性“left”和“top”

假设您有一个 .html 喜欢

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

看到我用了两个参考变量,一个是“容器”,一个是“指标”-div和class“圆只是为了显示圆-

另请参阅“容器”应具有 position:relative,“指示器”应具有 position:absolute

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

使用 ViewChild,在 ngAfterViewInit 中我们计算“容器”的位置和指示器的大小(真正的大小 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}

  })
}

我们将使用 Math.atan2、Math.cos 和 Math.sin 计算给定 x 和 y 的样式。由于该位置是相对于“项目”的左上角的位置,因此您减去 size.with 和 size.height(实际上 size.with 和 size.height 的大小是 div由 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'
    }
  }

最后一个是当“点击”指标时订阅事件 mousemove(虽然我们没有 mouseup)

  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)
  }

请注意,您向函数 calculateStyle 传递了考虑容器“位置”的值,以及在 AfterViewInit 中计算的“原点”

你可以在this stackblitz

中看到