Jelajahi Sumber

feat(scale-ruler): 重构刻度尺组件布局与定位逻辑

- 调整刻度线和文字的布局方式,将刻度线置于上方,文字放在下方
- 修改初始定位逻辑,使选中值的单元格中心对齐指示器
- 更新滚动计算方式,基于单元格中心位置进行数值计算
- 增加组件高度以便为刻度线和文字提供足够空间
- 使用独立的.tick元素表示刻度线,并设置长短刻度样式
- 调整内部布局为纵向排列,优化视觉呈现效果
mcbaiyun 2 bulan lalu
induk
melakukan
387ffdf59f
1 mengubah file dengan 38 tambahan dan 24 penghapusan
  1. 38 24
      src/components/scale-ruler.vue

+ 38 - 24
src/components/scale-ruler.vue

@@ -4,7 +4,10 @@
       @scroll="onScroll" @touchend="onTouchEnd" @touchcancel="onTouchEnd" scroll-into-view="">
       <view class="ruler-inner" :style="{ width: totalWidth + 'px' }">
         <view v-for="(g, idx) in gridList" :key="idx" class="grid-item" :class="{ long: g.isLongGrid }"
-          :style="{ width: props.gutter + 'px', height: g.showText ? '40px' : '24px' }">
+          :style="{ width: props.gutter + 'px' }">
+          <!-- 刻度线(位于上方) -->
+          <view class="tick" :class="{ long: g.isLongGrid }"></view>
+          <!-- 刻度文字放在刻度线下面 -->
           <text v-if="g.showText" class="grid-num">{{ g.displayNum }}</text>
         </view>
       </view>
@@ -73,17 +76,17 @@ const halfWindow = windowWidth / 2
 
 // initial position
 onMounted(() => {
-  // center on initial value
+  // center on initial value (align cell center under indicator)
   const initIndex = Math.round((props.initialValue - props.min) / props.step)
-  // place the right-edge of the initIndex cell under the center indicator
-  scrollLeft.value = offsetScroll.value + (initIndex + 1) * props.gutter - halfWindow
+  // place the center of the initIndex cell under the center indicator
+  scrollLeft.value = offsetScroll.value + initIndex * props.gutter + props.gutter / 2 - halfWindow
 })
 
 function onScroll(e: any) {
   const left = e?.detail?.scrollLeft ?? e?.detail?.scrollLeft ?? 0
   actualScrollLeft.value = left
-  // compute number index (units from min) under the centered indicator
-  const numIndex = Math.round((left + halfWindow - offsetScroll.value) / props.gutter) - 1
+  // compute number index (units from min) under the centered indicator (based on cell center)
+  const numIndex = Math.round((left + halfWindow - offsetScroll.value - props.gutter / 2) / props.gutter)
   let value = numIndex * props.step + props.min
   if (value < props.min) value = props.min
   if (value > props.max) value = props.max
@@ -94,9 +97,9 @@ function adjustScrollPosition() {
   // snap so the nearest tick is centered under the indicator
   const left = actualScrollLeft.value
   // compute number index under center
-  const numIndex = Math.round((left + halfWindow - offsetScroll.value) / props.gutter) - 1
-  // compute target scrollLeft that centers this index's right-edge under the indicator
-  const targetLeft = offsetScroll.value + (numIndex + 1) * props.gutter - halfWindow
+    const numIndex = Math.round((left + halfWindow - offsetScroll.value - props.gutter / 2) / props.gutter)
+    // compute target scrollLeft that centers this index's center under the indicator
+    const targetLeft = offsetScroll.value + numIndex * props.gutter + props.gutter / 2 - halfWindow
   // clamp to valid scroll range
   const maxScroll = Math.max(0, totalWidth.value - windowWidth)
   const clamped = Math.min(Math.max(targetLeft, 0), maxScroll)
@@ -108,8 +111,8 @@ function onTouchEnd() {
   adjustScrollPosition()
   // compute value and emit change
   const left = scrollLeft.value
-  const numIndex = Math.round((left + halfWindow - offsetScroll.value) / props.gutter) - 1
-  let value = numIndex * props.step + props.min
+    const numIndex = Math.round((left + halfWindow - offsetScroll.value - props.gutter / 2) / props.gutter)
+    let value = numIndex * props.step + props.min
   if (value < props.min) value = props.min
   if (value > props.max) value = props.max
   emit('change', value)
@@ -119,38 +122,49 @@ function onTouchEnd() {
 <style scoped>
 .ruler-wrapper {
   position: relative;
-  height: 80rpx;
+  /* 增高以给刻度线下面留白 */
+  height: 120rpx;
   display: flex;
-  align-items: center
+  align-items: flex-start;
 }
 
 .ruler-scroll {
   width: 100%;
-  height: 80rpx
+  height: 120rpx;
 }
 
 .ruler-inner {
   display: flex;
-  align-items: flex-end
+  /* 让刻度线靠上排列,文字在下方 */
+  align-items: flex-start;
 }
 
 .grid-item {
   width: 10px;
-  height: 24px;
+  /* 不固定高度,让内部内容控制布局 */
   display: flex;
-  align-items: flex-end;
-  justify-content: center;
-  border-right: 1px solid #ccc
+  flex-direction: column;
+  align-items: center;
+  justify-content: flex-start;
+  padding-top: 8rpx; /* 给刻度线一点顶部间距 */
 }
 
-.grid-item.long {
-  height: 40px;
-  border-right: 2px solid #333
+
+/* 刻度线样式,短刻度和长刻度高度不同 */
+.tick {
+  width: 2px;
+  height: 24rpx;
+  background: #ccc;
+}
+.tick.long {
+  height: 40rpx;
+  background: #333;
 }
 
 .grid-num {
   font-size: 24rpx;
-  color: #666
+  color: #666;
+  margin-top: 8rpx; /* 刻度线下面的间距 */
 }
 
 .indicator {
@@ -160,6 +174,6 @@ function onTouchEnd() {
   transform: translateX(-50%);
   width: 2px;
   height: 100%;
-  background: #ff6a00
+  background: #ff6a00;
 }
 </style>