前端动态标尺

总体是用的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;
}
相关推荐
demo007x1 分钟前
Docling 文档转换以及技术架构分析
前端·后端·程序员
京东云开发者44 分钟前
京东市民服务又“上新”!这次是黑龙江“龙易办”
前端
袋鱼不重1 小时前
我的神奇同事,AI 用多了居然写了个 Open In Codex
前端·后端·ai编程
竹林8182 小时前
Web3表单签名验证:我用 wagmi 和 ethers 给 DApp 加了一个“免密登录”,踩坑记录全在这了
javascript
用户6990304848752 小时前
try catch使用场景 处理同步代码错误兼容用的
javascript·uni-app
LDR0062 小时前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术2 小时前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园2 小时前
C++20 Modules 模块详解
java·开发语言·spring
Fireworks2 小时前
深入vue3源码解读 -- 1、响应式的基础概念
前端
程序员黑豆2 小时前
JDK 下载安装与配置详细教程
java·前端·ai编程