前端动态标尺

总体是用的vue3去写的。这里我是按照不同分辨率、浏览器宽高改变后,算容器动态宽度,按照百分比去划分标尺刻度

这个标尺刻度刻度我看了别人的写的,重点是background-image。所有代码如下

html部分

javascript 复制代码
<div class="ruler-container" >
          <div class="placeholder-div"></div>
          <!--垂直的标尺-->
          <div class="ruler v" :style="verticalRulerStyle">
            <ul class="scale-vertical">
              <li
                v-for="(scale, index) in verticalScaleList"
                :key="index"
                :style="{ top: scale + '%' }"
              >
                <template v-if="index!==0 && index !== verticalScaleList.length - 1">{{ scale }}%</template>
              </li>
            </ul>
          </div>
          <!--横向的标尺-->
          <div class="ruler h" :style="horizontalRulerStyle">
            <ul class="scale">
              <li
                v-for="(scale, index) in scaleList"
                :key="index"
                :style="{ left: scale  + '%'  }"
              >
                <template v-if="index!==0 && index !== scaleList.length - 1">{{ scale }}%</template>
              </li>
            </ul>
          </div>
        </div>

script部分

javascript 复制代码
// 计算刻度列表的函数(通用)
const calculateScaleList = (size, minSpacing = 50) => {
  if (size === 0) {
    return []
  }
  const maxScales = Math.floor(size / minSpacing) + 1
  let interval = 100
  const neededScales = maxScales - 2
  
  if (neededScales >= 9) {
    interval = 10
  } else if (neededScales >= 4) {
    interval = 20
  } else if (neededScales >= 3) {
    interval = 25
  } else if (neededScales >= 1) {
    interval = 50
  }
  
  const scales = []
  for (let i = 0; i <= 100; i += interval) {
    scales.push(i)
  }
  
  return scales
}

// 计算刻度列表(整数百分比)---横向
const scaleList = computed(() => {
  return calculateScaleList(bottomCenterWidth.value)
})

// 计算刻度列表(整数百分比)---纵向
const verticalScaleList = computed(() => {
  return calculateScaleList(bottomCenterHeight.value)
})

// 每个大刻度区间内的小刻度数量(例如 10 份)
const SUB_DIVISIONS = 10

// 横向标尺样式(通过 CSS 变量把每段刻度和小刻度的像素长度传给样式)
const horizontalRulerStyle = computed(() => {
  const count = scaleList.value.length > 1 ? scaleList.value.length - 1 : 1
  const segment = bottomCenterWidth.value > 0 ? bottomCenterWidth.value / count : 50
  const subSegment = segment / SUB_DIVISIONS
  return {
    width: bottomCenterWidth.value + 'px',
    left: '28px',
    top: 0,
    '--segment-size-x': segment + 'px',
    '--sub-segment-size-x': subSegment + 'px',
    '--major-tick-height': '30px',              // 大刻度高度 (盛满30px)
    '--minor-tick-height': '8px',               // 小刻度高度
    '--center-tick-height': '10px',             // 中刻度高度
    '--major-tick-color': '#696969',            // 大刻度颜色
    '--minor-tick-color': '#555'                // 小刻度颜色
  }
})

// 纵向标尺样式
const verticalRulerStyle = computed(() => {
  const count = verticalScaleList.value.length > 1 ? verticalScaleList.value.length - 1 : 1
  const segment = bottomCenterHeight.value > 0 ? bottomCenterHeight.value / count : 50
  const subSegment = segment / SUB_DIVISIONS
  return {
    height: bottomCenterHeight.value + 'px',
    left: 0,
    top: '29px',
    '--segment-size-y': segment + 'px',
    '--sub-segment-size-y': subSegment + 'px'
  }
})


onMounted(() => {
  nextTick(() => {
    updateBottomCenterWidth()
    if (bottomCenterRef.value) {
      resizeObserver = new ResizeObserver((entries) => {
        updateBottomCenterWidth()
      })
      resizeObserver.observe(bottomCenterRef.value)
    }
    window.addEventListener('resize', updateBottomCenterWidth)
  })
})
onUnmounted(() => {
  if (resizeObserver) {
    resizeObserver.disconnect()
    resizeObserver = null
  }
  window.removeEventListener('resize', updateBottomCenterWidth)
})

css部分

css 复制代码
// 标尺相关样式
.placeholder-div{
  width: 28px;
  height: 29px;
  background-color: #303030;
}
.ruler-container{
  position: relative;
  width: 100%;
  height: 100%;
  //
}
// 横向标尺的刻度
.scale{
  list-style-type: none;
  position: relative;
  margin: 0;
  padding: 0;
  width: 100%;
  height: 30px;
  overflow: hidden;
  white-space: nowrap;
  color:$color-visual;
  font-size: 0;
  text-shadow: 0 1px 1px #000;
  -moz-user-select:none;/*火狐*/
  -webkit-user-select:none;/*webkit浏览器*/
  -ms-user-select:none;/*IE10*/
  user-select:none;
}
.scale li {
  position: absolute;
  transform: translateX(-50%);
  text-align: center;
  padding: 0 2px;
  font-size: 11px;
  white-space: nowrap;
  line-height: 30px;
}
// 纵向标尺的刻度
.scale-vertical{
  list-style-type: none;
  position: relative;
  margin: 0;
  padding: 0;
  width: 28px;
  height: 100%;
  overflow: hidden;
  color:$color-visual;
  font-size: 0;
  text-shadow: 0 1px 1px #000;
  -moz-user-select:none;
  -webkit-user-select:none;
  -ms-user-select:none;
  user-select:none;
}
.scale-vertical li {
  position: absolute;
  transform: translateY(-50%);
  text-align: center;
  padding: 0;
  font-size: 11px;
  white-space: nowrap;
  width: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ruler.h {
  position: absolute;
  background-color: #303030;
  height: 30px;
  z-index: 100;
  border-bottom: 1px solid $form-border-color;
  box-sizing: border-box;
  
  // 使用多重背景绘制刻度线
  background-image:
    /* 大刻度 (Major) */
    linear-gradient(90deg, var(--major-tick-color) 1px, transparent 1px),
    /* 中刻度 (Center - 位于大刻度中间) */
    linear-gradient(90deg, transparent calc(50% - 0.5px), var(--major-tick-color) calc(50% - 0.5px), var(--major-tick-color) calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    /* 小刻度 (Minor) */
    linear-gradient(90deg, var(--minor-tick-color) 1px, transparent 1px);
  
  background-size:
    var(--segment-size-x) var(--major-tick-height),
    var(--segment-size-x) var(--center-tick-height),
    var(--sub-segment-size-x) var(--minor-tick-height);
    
  background-position: 
    0 bottom,
    0 bottom,
    0 bottom;
    
  background-repeat: repeat-x;
}
// 纵向标尺
// 纵向标尺
.ruler.v {
  position: absolute;
  border-bottom: 1px solid $form-border-color;
  background-color: #303030;
  
  background-image:
    /* 大刻度 */
    linear-gradient(180deg, #696969 1px, transparent 1px),
    /* 中刻度 */
    linear-gradient(180deg, transparent calc(50% - 0.5px), #696969 calc(50% - 0.5px), #696969 calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    /* 小刻度 */
    linear-gradient(180deg, #555 1px, transparent 1px);

  background-size: 
    100% var(--segment-size-y, 50px),
    10px var(--segment-size-y, 50px),
    8px var(--sub-segment-size-y, 5px);

  background-position: 
    right 0,
    right 0,
    right 0;

  background-repeat: repeat-y;
  width: 28px;
  z-index: 100;
  box-sizing: border-box;
}
相关推荐
前端不太难2 小时前
RN 列表里的局部状态和全局状态边界
开发语言·前端·harmonyos
小此方2 小时前
Re: ゼロから学ぶ C++ 入門(九)类和对象·最终篇上:缓冲区同步与流绑定、取地址运算符重载、const成员函数、初始化列表
开发语言·c++·底层
技术净胜2 小时前
Python常用框架介绍
开发语言·python·sqlite
0思必得02 小时前
[Web自动化] Web安全基础
运维·前端·javascript·python·自动化·html·web自动化
曹轲恒2 小时前
jvm 局部变量表slot复用问题
java·开发语言·jvm
天天向上vir2 小时前
防抖与节流
前端·typescript·vue
222you2 小时前
SpringMVC的单文件上传
java·开发语言
宇珩前端踩坑日记2 小时前
怎么让 Vue DevTools 用 Trae 打开源码
前端·trae
小徐不会敲代码~2 小时前
Vue3 学习 6
开发语言·前端·vue.js·学习