参考以下demo:
// xxx.ets
@Entry // 定义入口组件
@Component // 定义该结构体为组件
struct Index {
private settings: RenderingContextSettings = new RenderingContextSettings(true) // 渲染上下文设置
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) // 创建一个2D画布上下文对象
// 定义组件的状态变量,表示线条和点的位置、角度等
@State lineOneX: number = 300
@State lineOneY: number = 200
@State lineTwoX: number = 200
@State lineTwoY: number = 300
@State pointX: number = 200
@State pointY: number = 200
@State radius: number = 100
@State counterclockwise: boolean = false // 判断是否逆时针
@State lineOneAngle: number = 0 // 第一条线的角度
@State lineTwoAngle: number = Math.PI / 2 // 第二条线的角度
currentOneAngle: number = 0 // 当前角度,用于计算
currentTwoAngle: number = 0 // 当前角度,用于计算
startX: number = 0 // 手势起始位置X坐标
startY: number = 0 // 手势起始位置Y坐标
judgeLocationFlag: number = 0 // 判断位置的标志
build() {
// 布局设置为垂直方向对齐,并且将画布居中
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
// 绘制画布
Canvas(this.context)
.width('100%')
.height('100%')
.onReady(() => {
this.Draw() // 在画布准备好时绘制图形
})
}
.width('100%')
.height('100%')
.gesture(
PanGesture() // 监听滑动手势
.onActionStart((event) => {
// 手势开始时保存当前角度和起始位置
this.currentOneAngle = this.lineOneAngle
this.currentTwoAngle = this.lineTwoAngle
this.startX = event.fingerList[0].localX
this.startY = event.fingerList[0].localY
// 计算坐标点和位置判断
this.judgeLocationFlag = this.JudgeLocation(this.startX - this.pointX, this.startY - this.pointY)
})
.onActionUpdate((event) => {
// 手势更新时,根据偏移量计算新的角度和坐标
let currentAngle =
this.AngleOfRotation(this.startX, this.startY, this.startX + event.offsetX, this.startY + event.offsetY)
// 根据判断位置标志,更新两条线的角度和坐标
if (this.judgeLocationFlag === 3) {
this.lineOneAngle = currentAngle + this.currentOneAngle
this.lineTwoAngle = currentAngle + this.currentTwoAngle
this.lineOneX = this.radius * Math.cos(this.lineOneAngle) + this.pointX
this.lineOneY = this.radius * Math.sin(this.lineOneAngle) + this.pointY
this.lineTwoX = this.radius * Math.cos(this.lineTwoAngle) + this.pointX
this.lineTwoY = this.radius * Math.sin(this.lineTwoAngle) + this.pointY
} else if (this.judgeLocationFlag === 1) {
this.lineOneAngle = currentAngle + this.currentOneAngle
this.lineOneX = this.radius * Math.cos(this.lineOneAngle) + this.pointX
this.lineOneY = this.radius * Math.sin(this.lineOneAngle) + this.pointY
} else if (this.judgeLocationFlag === 2) {
this.lineTwoAngle = currentAngle + this.currentTwoAngle
this.lineTwoX = this.radius * Math.cos(this.lineTwoAngle) + this.pointX
this.lineTwoY = this.radius * Math.sin(this.lineTwoAngle) + this.pointY
}
this.Draw() // 重新绘制图形
})
)
}
// 判断某个点相对于扇形的角度位置
JudgeLocation(x: number, y: number) {
let angle = Math.atan(y / x) // 计算点的角度
if (x > 0 && y > 0) {
} else if (x > 0) {
} else if (y > 0) {
angle = Math.PI + angle // 根据象限调整角度
} else {
angle = -Math.PI + angle
}
// 判断点是否在两条线之间,以及与线的角度关系
if (x * x + y * y <= this.radius * this.radius) {
let lineAngle: number = this.lineOneAngle - this.lineTwoAngle
if (Math.abs(this.lineOneAngle - angle) < 0.15) {
return 1 // 角度接近第一条线
} else if (Math.abs(this.lineTwoAngle - angle) < 0.15) {
return 2 // 角度接近第二条线
} else if (lineAngle > Math.PI) {
if (angle > this.lineOneAngle || angle < this.lineTwoAngle) {
return 3 // 在两条线之间
}
} else if (lineAngle < -Math.PI) {
if (angle < this.lineOneAngle || angle > this.lineTwoAngle) {
return 3 // 在两条线之间
}
} else if (lineAngle < 0) {
if (angle > this.lineOneAngle && angle < this.lineTwoAngle) {
return 3 // 在两条线之间
}
} else {
if (angle < this.lineOneAngle && angle > this.lineTwoAngle) {
return 3 // 在两条线之间
}
}
}
return 0 // 不在扇形范围内
}
// 计算两点之间的旋转角度
AngleOfRotation(startX: number, startY: number, endX: number, endY: number) {
let x1 = startX - this.pointX
let y1 = startY - this.pointY
let x2 = endX - this.pointX
let y2 = endY - this.pointY
let res = Math.acos((x1 * x2 + y1 * y2) / ((Math.sqrt(Math.pow(x1, 2) + Math.pow(y1, 2))) * (Math.sqrt(Math.pow(x2, 2) + Math.pow(y2, 2))))) // 计算角度
if (x1 * y2 - x2 * y1 > 0) {
return res // 顺时针旋转
} else {
return -res // 逆时针旋转
}
}
// 绘制图形(扇形)
Draw() {
this.context.clearRect(0, 0, 400, 400) // 清空画布
this.context.beginPath()
this.context.lineWidth = 3
let grad = this.context.createRadialGradient(this.pointX, this.pointY, 0, this.pointX, this.pointY, 200) // 创建渐变色
grad.addColorStop(0.0, '#8000ff00')
grad.addColorStop(1.0, '#2000ff00')
this.context.strokeStyle = grad
// 绘制从中心到扇形边界的两条线
this.context.moveTo(this.pointX, this.pointY)
this.context.lineTo(this.lineOneX, this.lineOneY)
this.context.moveTo(this.pointX, this.pointY)
this.context.lineTo(this.lineTwoX, this.lineTwoY)
this.context.stroke()
// 开始绘制扇形路径
this.context.beginPath()
this.context.fillStyle = "#3000FF00"
this.context.moveTo(this.pointX, this.pointY)
if (this.lineOneAngle - this.lineTwoAngle < -Math.PI || this.lineOneAngle - this.lineTwoAngle > 0 && this.lineOneAngle - this.lineTwoAngle < Math.PI) {
this.context.arc(this.pointX, this.pointY, this.radius, this.lineTwoAngle, this.lineOneAngle, this.counterclockwise) // 绘制圆弧
} else {
this.context.arc(this.pointX, this.pointY, this.radius, this.lineOneAngle, this.lineTwoAngle, this.counterclockwise) // 绘制圆弧
}
// 填充扇形区域
this.context.closePath()
this.context.fill()
}
}