IndexerView.ets 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. /**
  2. * A custom indexer view with motion animation.
  3. */
  4. @Component
  5. export struct IndexerView {
  6. /**
  7. * The index data of the indexer view.
  8. */
  9. @Prop indexArray: Array<string>
  10. /**
  11. * The size of the char text.
  12. */
  13. @Prop textSize: number
  14. /**
  15. * The value to observe the selected index.
  16. */
  17. @Prop selectedIndex: number
  18. @State isPressed: boolean = false
  19. @State charWidth: number = 0
  20. @State charHeight: number = 0
  21. private viewHeight: number = 0
  22. /* ------------------ heightLight style ------------------*/
  23. /**
  24. * The color of current selected index.
  25. */
  26. selectedColor: ResourceColor = "#000000"
  27. /**
  28. * The color of unselected index.
  29. */
  30. normalColor: ResourceColor = "#ff737373"
  31. /* ------------------ float selected view style ------------------*/
  32. /**
  33. * Display the float selected view or not.
  34. */
  35. isShowFloatSelectedView: boolean = true
  36. /**
  37. * The size of float selected view.
  38. */
  39. floatSelectedViewSize: number = 32
  40. /**
  41. * The color of float selected view text.
  42. */
  43. floatSelectedTextColor: ResourceColor = "#ffffff"
  44. /**
  45. * The color of float selected view background.
  46. */
  47. floatSelectedViewColor: ResourceColor = "#000000"
  48. /* ------------------ animation style ------------------*/
  49. /**
  50. * The duration of animation for this view.
  51. */
  52. animationDuration: number = 300
  53. /**
  54. * The translation size of animation.
  55. */
  56. animationTranslation: number = 32
  57. /**
  58. * The observe to watch the index changed by user.
  59. */
  60. onIndexChanged: ((index: number) => void) | null = null
  61. build() {
  62. Stack() {
  63. // anchor
  64. Column() {
  65. ForEach(this.indexArray, (char: string, index: number) => {
  66. Text(char)
  67. .fontSize(this.textSize)
  68. .fontColor(this.selectedIndex == index ? this.selectedColor : this.normalColor)
  69. .fontWeight(this.selectedIndex == index ? FontWeight.Bold : FontWeight.Normal)
  70. .textAlign(TextAlign.Center)
  71. .width("100%")
  72. .height(this.charHeight)
  73. .translate({
  74. x: this.selectedIndex == index && this.isPressed ? -this.animationTranslation :
  75. this.selectedIndex == index + 1 && this.isPressed || this.selectedIndex == index - 1 && this.isPressed ? -this.animationTranslation * 0.75 :
  76. this.selectedIndex == index + 2 && this.isPressed || this.selectedIndex == index - 2 && this.isPressed ? -this.animationTranslation * 0.5 :
  77. this.selectedIndex == index + 3 && this.isPressed || this.selectedIndex == index - 3 && this.isPressed ? -this.animationTranslation * 0.25 : 0
  78. })
  79. .animation({
  80. duration: this.animationDuration
  81. })
  82. })
  83. }
  84. .width("100%")
  85. .height("100%")
  86. // float selected view
  87. if (this.isShowFloatSelectedView) {
  88. Text(this.indexArray[this.selectedIndex])
  89. .fontColor(this.floatSelectedTextColor)
  90. .textAlign(TextAlign.Center)
  91. .fontSize(this.floatSelectedViewSize * 0.6)
  92. .width(this.floatSelectedViewSize)
  93. .height(this.floatSelectedViewSize)
  94. .border({ radius: this.floatSelectedViewSize })
  95. .visibility(this.isPressed && this.selectedIndex >= 0 && this.selectedIndex <= this.indexArray.length - 1 ? Visibility.Visible : Visibility.Hidden)
  96. .backgroundColor(this.floatSelectedViewColor)
  97. .position({
  98. x: -this.animationTranslation - this.floatSelectedViewSize * 1.2,
  99. y: this.selectedIndex * this.charHeight - (this.floatSelectedViewSize - this.charHeight) * 0.5
  100. })
  101. .animation({
  102. duration: this.animationDuration
  103. })
  104. }
  105. }
  106. .align(Alignment.TopStart)
  107. .onAreaChange((_, newSize) => {
  108. this.charWidth = newSize.width as number
  109. this.viewHeight = newSize.height as number
  110. this.charHeight = this.viewHeight / this.indexArray.length
  111. })
  112. .onTouch((event) => {
  113. let motionEvent = event.touches[0]
  114. switch (motionEvent.type) {
  115. case TouchType.Down:
  116. case TouchType.Move:
  117. this.isPressed = true
  118. let currentY = motionEvent.y
  119. if (currentY <= 0) {
  120. this.selectedIndex = 0
  121. } else if (currentY > this.viewHeight) {
  122. this.selectedIndex = this.indexArray.length - 1
  123. } else {
  124. this.selectedIndex = Math.round(currentY / this.charHeight)
  125. }
  126. this.onIndexChanged?.(this.selectedIndex)
  127. break
  128. case TouchType.Up:
  129. case TouchType.Cancel:
  130. this.isPressed = false
  131. break
  132. default:
  133. break
  134. }
  135. })
  136. }
  137. }