HarmonyOS鸿蒙Next中目前有一个圆形文本选择器,用现在的api组件实现起来比较困难,帮忙看看技术上如何实现最省时间

HarmonyOS鸿蒙Next中目前有一个圆形文本选择器,用现在的api组件实现起来比较困难,帮忙看看技术上如何实现最省时间 目前有一个圆形文本选择器,用现在的api组件实现起来比较困难,然后项目时间又比较赶,没那么多精力去完成这个,帮忙看看技术上如何实现最省时间

4 回复

你好,可以参考一下这个demo。

@Entry
@Component
struct RoundTextChoosePage {
  @State items: number[] = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
    11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
  ]

  @State currentIndex: number = 0
  @State value: number = 1

  private radius: number = 120
  private startAngle: number = 0
  private lastAngle: number = 0
  private _rotation: number = 0

  private canvasCtx: CanvasRenderingContext2D = new CanvasRenderingContext2D()

  build() {
    Column() {
      Text('圆形文本选择器')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 30, bottom: 20 })

      Text(`日期:${this.value}号`)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 30 })

      Canvas(this.canvasCtx)
        .width(320)
        .height(320)
        .backgroundColor('#ffeecc')
        .onReady(() => this.draw())
        .onTouch(e => this.handleTouch(e))

      Button('确定')
        .width('80%')
        .margin({ top: 50 })
        .onClick(() => {
          this.value = this.items[this.currentIndex]
        })
    }
    .width('100%')
    .height('100%')
  }

  /* ---------------- 绘制 ---------------- */

  private draw() {
    const ctx = this.canvasCtx
    ctx.clearRect(0, 0, 320, 320)

    const centerX = 160
    const centerY = 160
    const step = 2 * Math.PI / this.items.length

    this.currentIndex = this.calculateCurrentIndex()

    this.items.forEach((v, i) => {
      const angle = i * step + this._rotation
      const x = centerX + this.radius * Math.cos(angle - Math.PI / 2)
      const y = centerY + this.radius * Math.sin(angle - Math.PI / 2)

      if (i === this.currentIndex) {
        ctx.fillStyle = '#ff0000'
        ctx.font = 'bold 90px sans-serif'
      } else {
        ctx.fillStyle = '#999'
        ctx.font = '40px sans-serif'
      }

      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      ctx.fillText(v.toString(), x, y)
    })
  }

  /**
   * 计算“离顶部中心最近”的 item
   * 保证任何时刻只返回一个 index
   */
  private calculateCurrentIndex(): number {
    const step = 2 * Math.PI / this.items.length
    let min = Infinity
    let index = 0

    this.items.forEach((_, i) => {
      const angle = i * step + this._rotation
      const diff =
        ((angle - Math.PI / 2) % (2 * Math.PI) + Math.PI) % (2 * Math.PI) - Math.PI

      const abs = Math.abs(diff)
      if (abs < min) {
        min = abs
        index = i
      }
    })

    return index
  }

  /* ---------------- 手势 ---------------- */

  private handleTouch(e: TouchEvent) {
    const touch = e.touches[0]

    if (e.type === TouchType.Down) {
      this.startAngle = this.getAngle(touch.x, touch.y)
      this.lastAngle = this._rotation
    }

    if (e.type === TouchType.Move) {
      const cur = this.getAngle(touch.x, touch.y)
      this._rotation = this.lastAngle + (cur - this.startAngle)
      this.draw()
    }

    if (e.type === TouchType.Up) {
      this.snapToNearest()
    }
  }

  private snapToNearest() {
    const step = 2 * Math.PI / this.items.length
    const target = Math.round(this._rotation / step) * step

    animateTo({
      duration: 200,
      curve: Curve.EaseOut,
      onFinish: () => this.draw()
    }, () => {
      this._rotation = target
    })
  }

  /* ---------------- 工具 ---------------- */

  private getAngle(x: number, y: number): number {
    return Math.atan2(y - 160, x - 160)
  }
}

更多关于HarmonyOS鸿蒙Next中目前有一个圆形文本选择器,用现在的api组件实现起来比较困难,帮忙看看技术上如何实现最省时间的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


如果加上类似于保险箱的密码的换一个数字就嘀嗒一声就好玩了,

在HarmonyOS鸿蒙Next中实现圆形文本选择器,可通过自定义组件结合Canvas绘制实现。使用Canvas的drawText方法结合Path进行环形布局,通过触摸事件处理选中逻辑。利用ArkUI的声明式UI和状态管理,可高效更新选中状态。建议参考官方文档中的Canvas和自定义组件示例。

针对圆形文本选择器的实现,如果时间紧张,建议采用以下技术方案以节省开发时间:

  1. 优先使用现有组件组合:考虑使用Canvas组件绘制圆形轨道和选择点,结合Text组件显示刻度值。通过CanvasonTouch事件处理用户滑动交互,计算角度并映射到文本值。这种方式比完全自定义组件更快捷。

  2. 复用社区开源方案:在开源社区(如Gitee)搜索HarmonyOS的圆形选择器相关示例,例如“HarmonyOS CircleSlider”或“圆形选择器”,直接参考或适配现有代码,避免从零开始。

  3. 简化交互逻辑:如果项目对精度要求不高,可将圆形离散化为固定点位(如12个刻度对应12个文本选项),通过点击或短距离滑动切换,减少连续滑动的计算复杂度。

  4. 利用声明式UI特性:通过@State装饰器绑定选择角度和文本值,实现UI自动更新,减少手动渲染代码。

示例代码框架(使用Canvas):

@Entry
@Component
struct CirclePicker {
  @State angle: number = 0
  private texts: string[] = ['A', 'B', 'C', 'D', 'E', 'F']

  build() {
    Column() {
      // 圆形轨道与选择器
      Canvas(this.onDraw)
        .onTouch((event) => this.handleTouch(event))
        .width(200).height(200)
      
      // 显示当前选中文本
      Text(this.texts[this.getIndex()]).fontSize(20)
    }
  }

  private onDraw(context: CanvasRenderingContext2D) {
    // 绘制圆形轨道和选择点(根据angle计算位置)
    context.arc(100, 100, 80, 0, Math.PI * 2)
    context.stroke()
    let x = 100 + 80 * Math.cos(this.angle)
    let y = 100 + 80 * Math.sin(this.angle)
    context.fillCircle(x, y, 10)
  }

  private handleTouch(event: TouchEvent) {
    // 计算触摸点角度并更新this.angle
    const point = event.touches[0]
    const dx = point.x - 100
    const dy = point.y - 100
    this.angle = Math.atan2(dy, dx)
  }

  private getIndex(): number {
    // 将角度映射到文本索引
    const step = (2 * Math.PI) / this.texts.length
    return Math.floor((this.angle + step / 2) / step) % this.texts.length
  }
}

此方案可在1-2天内完成基础功能,后续再根据需求优化动画和视觉效果。

回到顶部