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
中看到
我必须创建一个可以确定角度的圆形滑块。我正在关注 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
中看到