horizontal-rule.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template>
  2. <view>
  3. <view class="dividing-rule">
  4. <!-- 标记 -->
  5. <view class="flex-center" style="top: 1px;">
  6. <view class="triangl-down"></view>
  7. </view>
  8. <view class="flex-center" style="top: 7px;">
  9. <view class="markLine"></view>
  10. </view>
  11. <scroll-view scroll-x scroll-with-animation :scroll-left="scrollLeft" :show-scrollbar="false"
  12. @scroll="scroll" style="border-top:1px solid #ccc;padding-top: 3px;padding-bottom: 10px;">
  13. <view :style="{width: totalWidth + 'px'}">
  14. <view style="width: 100%;"
  15. :style="{paddingLeft: initPosition + 'px', paddingRight: initPosition+1 + 'px'}">
  16. <!-- 刻度线 -->
  17. <view style="display: flex;">
  18. <view v-for="(item,index) in options" :key="index" :class="item.type"
  19. :style="{minWidth: lineWidth - 1 + 'px'}">
  20. </view>
  21. </view>
  22. <!-- 刻度值 -->
  23. <view style="display: flex;margin-top: 6px;">
  24. <view v-for="(item,index) in options" :key="index" :style="{minWidth: lineWidth + 'px'}"
  25. :class="[selectIndex == index ? 'line-label' : '']">
  26. <text style="margin-left: -5px;">{{item.val}}</text>
  27. </view>
  28. </view>
  29. </view>
  30. </view>
  31. </scroll-view>
  32. </view>
  33. </view>
  34. </template>
  35. <script>
  36. import debounce from './debounce.js'
  37. export default {
  38. name: "horizontal-rule",
  39. props: {
  40. min: { // 最小值
  41. type: Number,
  42. default: 0,
  43. validator: function(val) {
  44. return parseFloat(val) >= 0
  45. }
  46. },
  47. max: { // 最大值
  48. type: Number,
  49. default: 100,
  50. validator: function(val) {
  51. return parseFloat(val) >= 0
  52. }
  53. },
  54. value: { // 初始值
  55. type: Number,
  56. default: 50,
  57. validator: function(val) {
  58. return parseFloat(val) >= 0
  59. }
  60. },
  61. multiple: { // 倍数 只支持10的倍数; multiple = 1 表示每个刻度的值等于1;multiple = 10 表示每个刻度的值等于0.1
  62. type: [Number],
  63. default: 1
  64. },
  65. lineWidth: { // 每个刻度之间的宽度
  66. type: Number,
  67. default: 6,
  68. validator: function(val) {
  69. return parseFloat(val) >= 0
  70. }
  71. }
  72. },
  73. data() {
  74. return {
  75. options: [], // 刻度线
  76. selectIndex: -1,
  77. scrollLeft: 0, //初始位置
  78. initPosition: '', // 最小刻度尺的位置
  79. totalWidth: '100%', // 尺子总宽度
  80. currentVal: '0', // 当前值
  81. };
  82. },
  83. watch: {
  84. value: function(val, oldVal) {
  85. this.init()
  86. }
  87. },
  88. mounted() {
  89. var left = 0;
  90. // 获取页面元素
  91. let view = uni.createSelectorQuery().in(this).select('.dividing-rule');
  92. view.boundingClientRect(data => {
  93. left = data.left
  94. console.log('获取页面元素', data)
  95. }).exec()
  96. // 获取指针位置
  97. let middleLine = uni.createSelectorQuery().in(this).select('.markLine');
  98. middleLine.boundingClientRect(data => {
  99. console.log('获取指针位置', data)
  100. this.initPosition = data.left - left
  101. this.init()
  102. }).exec()
  103. },
  104. methods: {
  105. // 初始化刻度尺
  106. init: function() {
  107. // 最大值不能小于最小值
  108. if (this.min >= this.max) return
  109. this.options = []
  110. let index = this.min
  111. let length = this.max * this.multiple
  112. for (let i = this.min * this.multiple; i <= length; i++) {
  113. let type = ''
  114. if (i % 5 == 0) {
  115. type = 'line-md'
  116. if (i % 10 == 0) {
  117. type = 'line-lg'
  118. }
  119. } else {
  120. type = 'line-sm'
  121. }
  122. // 每 10 格 显示一个刻度值
  123. let val = ''
  124. if (i % 10 == 0) {
  125. val = i / this.multiple
  126. }
  127. this.options.push({
  128. type: type,
  129. val: val
  130. })
  131. }
  132. console.log('总刻度', JSON.parse(JSON.stringify(this.options)))
  133. // 总宽度 = 总格子 * 每个格子的宽度
  134. this.totalWidth = ((this.max - this.min) * this.multiple) * this.lineWidth
  135. // 初始位置
  136. setTimeout(() => {
  137. // 初始值必须在min 与 max 之间
  138. if (this.value < this.min) {
  139. this.scrollLeft = 0
  140. } else if (this.value > this.max) {
  141. this.scrollLeft = this.totalWidth
  142. } else {
  143. this.scrollLeft = (this.value - this.min) * this.multiple * this.lineWidth
  144. }
  145. this.currentVal = this.value
  146. this.selectIndex = this.options.findIndex((item, index) => this.value == item.val)
  147. console.log('初始位置---------------》', this.scrollLeft)
  148. this.$emit('change', this.currentVal)
  149. }, 40)
  150. },
  151. // 监听滚动事件
  152. scroll: function(e) {
  153. var result = ((this.max - this.min) / this.totalWidth * e.detail.scrollLeft) + this.min
  154. this.currentVal = result
  155. this.$emit('change', this.currentVal)
  156. // 处理滑在两个刻度之间,自动靠近最近一个刻度
  157. debounce(() => {
  158. var num = Math.round(result * this.multiple)/this.multiple
  159. this.scrollLeft = (num - this.min) * this.multiple * this.lineWidth
  160. this.selectIndex = this.options.findIndex((item, index) => num == item.val)
  161. }, 500)
  162. }
  163. }
  164. }
  165. </script>
  166. <style>
  167. .dividing-rule {
  168. position: relative;
  169. }
  170. .flex-center {
  171. display: flex;
  172. flex-direction: row;
  173. justify-content: center;
  174. width: 100%;
  175. position: absolute;
  176. z-index: 1000;
  177. }
  178. .triangl-down {
  179. width: 0;
  180. height: 0;
  181. border-top: 12px solid #3F86FF;
  182. border-left: 6px solid transparent;
  183. border-right: 6px solid transparent;
  184. border-radius: 2px;
  185. }
  186. .markLine {
  187. width: 2px;
  188. height: 20px;
  189. z-index: 1;
  190. margin-top: -4px;
  191. background: #3F86FF;
  192. }
  193. .bg-red {
  194. background: red;
  195. }
  196. .bg-blue {
  197. background: blue;
  198. }
  199. .line {
  200. display: flex;
  201. border-top: 1px solid lightgray;
  202. padding-top: 6px;
  203. padding-bottom: 6px;
  204. }
  205. .line-lg {
  206. height: 15px;
  207. border-left: 1px solid #979797;
  208. }
  209. .line-md {
  210. height: 10px;
  211. border-left: 1px solid #e8e8e8;
  212. }
  213. .line-sm {
  214. height: 5px;
  215. border-left: 1px solid #e8e8e8;
  216. }
  217. .line-label {
  218. color: #3F86FF;
  219. }
  220. </style>