l-sliderRange.nvue 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <template>
  2. <view class="lView" ref="lViewRef">
  3. <view class="angleNumber" :style="angleNumStyle">
  4. <text class="angleTxt">{{angleNumber}}°</text>
  5. </view>
  6. <view class="outbox">
  7. <view class="plusicon" @click="clickSub">
  8. <text class="plusTxt">-</text>
  9. </view>
  10. <view class="container" ref="containerRef" @touchstart="onTouchStart" @touchmove="onTouchMove"
  11. @touchend="onTouchEnd">
  12. <view class="trackBg"></view>
  13. <view class="block" :style="blockStyle" @touchstart.stop="onBlockTouchStart"
  14. @touchmove.stop="onBlockTouchMove" @touchend.stop="onTouchEnd">
  15. <image class="barimg" src="/static/device/barimg.png" mode="aspectFill"></image>
  16. </view>
  17. </view>
  18. <view class="plusicon" @click="clickPlus">
  19. <text class="plusTxt">+</text>
  20. </view>
  21. </view>
  22. </view>
  23. </template>
  24. <script>
  25. const dom = weex.requireModule('dom')
  26. export default {
  27. props: {
  28. // 当前角度值
  29. anglenum: {
  30. type: Number,
  31. default: 90
  32. },
  33. // 最小值
  34. min: {
  35. type: Number,
  36. default: 90
  37. },
  38. // 最大值
  39. max: {
  40. type: Number,
  41. default: 150
  42. },
  43. // 步长
  44. step: {
  45. type: Number,
  46. default: 15
  47. }
  48. },
  49. data() {
  50. return {
  51. angleNumber: 90,
  52. containerWidth: 0,
  53. containerLeft: 0,
  54. lViewLeft: 0, // lView左侧偏移
  55. containerOffsetX: 0, // container相对于lView的左侧偏移
  56. blockWidth: 62, // 滑块宽度 px (约124rpx)
  57. currentX: 0, // 当前滑块left值
  58. isDragging: false
  59. }
  60. },
  61. computed: {
  62. blockStyle() {
  63. return 'left:' + this.currentX + 'px;'
  64. },
  65. angleNumStyle() {
  66. // 数字容器与滑块同宽,加上 container 相对 lView 的偏移量
  67. const left = this.currentX + this.containerOffsetX
  68. return 'left:' + left + 'px;width:' + this.blockWidth + 'px;'
  69. }
  70. },
  71. watch: {
  72. anglenum: {
  73. immediate: true,
  74. handler(newVal) {
  75. this.angleNumber = newVal
  76. this.$nextTick(() => {
  77. this.updatePositionFromValue(newVal)
  78. })
  79. }
  80. }
  81. },
  82. mounted() {
  83. setTimeout(() => {
  84. this.getContainerRect()
  85. }, 300)
  86. },
  87. methods: {
  88. getContainerRect() {
  89. // 先获取 lView 的位置
  90. dom.getComponentRect(this.$refs.lViewRef, (lRes) => {
  91. if (lRes && lRes.result) {
  92. this.lViewLeft = lRes.size.left
  93. }
  94. // 再获取 container 的位置
  95. dom.getComponentRect(this.$refs.containerRef, (res) => {
  96. if (res && res.result) {
  97. const rect = res.size
  98. this.containerWidth = rect.width
  99. this.containerLeft = rect.left
  100. // container 相对于 lView 的偏移
  101. this.containerOffsetX = rect.left - this.lViewLeft
  102. // 初始化位置
  103. this.updatePositionFromValue(this.angleNumber)
  104. }
  105. })
  106. })
  107. },
  108. // 根据值计算位置
  109. updatePositionFromValue(val) {
  110. if (this.containerWidth <= 0) return
  111. const trackWidth = this.containerWidth - this.blockWidth
  112. const ratio = (val - this.min) / (this.max - this.min)
  113. this.currentX = ratio * trackWidth
  114. },
  115. // 根据位置计算值(不吸附,实时显示)
  116. getRawValueFromPosition(x) {
  117. const trackWidth = this.containerWidth - this.blockWidth
  118. if (trackWidth <= 0) return this.min
  119. let ratio = x / trackWidth
  120. ratio = Math.max(0, Math.min(1, ratio))
  121. const rawValue = this.min + ratio * (this.max - this.min)
  122. // 四舍五入到步长
  123. const steps = Math.round((rawValue - this.min) / this.step)
  124. return Math.min(this.max, Math.max(this.min, this.min + steps * this.step))
  125. },
  126. // 容器触摸开始(点击轨道跳转)
  127. onTouchStart(e) {
  128. this.isDragging = true
  129. const touch = e.touches[0]
  130. const x = touch.screenX - this.containerLeft - this.blockWidth / 2
  131. this.smoothMove(x)
  132. },
  133. onTouchMove(e) {
  134. const touch = e.touches[0]
  135. const x = touch.screenX - this.containerLeft - this.blockWidth / 2
  136. this.smoothMove(x)
  137. },
  138. // 滑块触摸
  139. onBlockTouchStart(e) {
  140. this.isDragging = true
  141. },
  142. onBlockTouchMove(e) {
  143. if (!this.isDragging) return
  144. const touch = e.touches[0]
  145. const x = touch.screenX - this.containerLeft - this.blockWidth / 2
  146. this.smoothMove(x)
  147. },
  148. onTouchEnd(e) {
  149. this.isDragging = false
  150. // 松手时吸附到步进位置
  151. const val = this.getRawValueFromPosition(this.currentX)
  152. this.angleNumber = val
  153. this.updatePositionFromValue(val)
  154. this.$emit('change', this.angleNumber)
  155. },
  156. // 拖拽时丝滑跟手,不吸附
  157. smoothMove(x) {
  158. const trackWidth = this.containerWidth - this.blockWidth
  159. x = Math.max(0, Math.min(trackWidth, x))
  160. this.currentX = x
  161. // 实时计算角度显示(吸附到最近步进值)
  162. this.angleNumber = this.getRawValueFromPosition(x)
  163. },
  164. clickSub() {
  165. if (this.angleNumber > this.min) {
  166. this.angleNumber -= this.step
  167. this.updatePositionFromValue(this.angleNumber)
  168. this.$emit('change', this.angleNumber)
  169. }
  170. },
  171. clickPlus() {
  172. if (this.angleNumber < this.max) {
  173. this.angleNumber += this.step
  174. this.updatePositionFromValue(this.angleNumber)
  175. this.$emit('change', this.angleNumber)
  176. }
  177. }
  178. }
  179. }
  180. </script>
  181. <style scoped>
  182. .lView {
  183. height: 96rpx;
  184. position: relative;
  185. padding-top: 40rpx;
  186. }
  187. .angleNumber {
  188. position: absolute;
  189. top: 0;
  190. align-items: center;
  191. justify-content: center;
  192. }
  193. .angleTxt {
  194. font-size: 36rpx;
  195. color: #FFFFFF;
  196. font-weight: bold;
  197. text-align: center;
  198. }
  199. .outbox {
  200. height: 56rpx;
  201. background-color: rgba(255, 255, 255, 0.7);
  202. border-radius: 40rpx;
  203. flex-direction: row;
  204. justify-content: space-between;
  205. align-items: center;
  206. padding-left: 10rpx;
  207. padding-right: 10rpx;
  208. }
  209. .plusicon {
  210. width: 50rpx;
  211. height: 50rpx;
  212. justify-content: center;
  213. align-items: center;
  214. }
  215. .plusTxt {
  216. font-size: 44rpx;
  217. color: #389588;
  218. font-weight: bold;
  219. }
  220. .container {
  221. flex: 1;
  222. height: 56rpx;
  223. position: relative;
  224. justify-content: center;
  225. }
  226. .trackBg {
  227. height: 6rpx;
  228. background-color: rgba(56, 149, 136, 0.3);
  229. border-radius: 3rpx;
  230. }
  231. .block {
  232. position: absolute;
  233. width: 124rpx;
  234. height: 44rpx;
  235. background-color: #389588;
  236. border-radius: 40rpx;
  237. justify-content: center;
  238. align-items: center;
  239. }
  240. .barimg {
  241. width: 30rpx;
  242. height: 36rpx;
  243. }
  244. </style>