vue3范围选择组件封装

这篇具有很好参考价值的文章主要介绍了vue3范围选择组件封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

个人项目地址: SubTopH前端开发个人站

(自己开发的前端功能和UI组件,一些有趣的小功能,感兴趣的伙伴可以访问,欢迎提出更好的想法,私信沟通,网站属于静态页面)

vue3范围选择组件封装,javascript,前端,vue.js,css3,html5

SubTopH前端开发个人站https://subtop.gitee.io/subtoph.github.io/#/home

以上 👆 是个人前端项目,欢迎提出您的建议😊

以下是正文内容...............

实现效果

vue3范围选择组件封装,javascript,前端,vue.js,css3,html5

直接上代码

组件文件

<template>
  <div class="swh-range-page" :id="onlyId" ref="rangeRef">
    <div class="swh-range-selection" :id="onlyId + '_selection'">
      <!-- 滑动槽 -->
      <div class="swh-trough" @click="handleTroughClick"></div>
      <!-- 选中范围高亮条 -->
      <p
        class="swh-drag-trough"
        :id="onlyId + '_drag-trough'"
        @click="handleTroughClick"
      ></p>
      <!-- 拖拽按钮 -->
      <p class="swh-drag-btn" :id="onlyId + '_drag-btn'"></p>
      <p class="drag-value" :id="onlyId + '_drag-value'">{{ rangValue }}</p>
    </div>
  </div>
</template>

<script>
import { reactive, toRefs, onBeforeMount, onMounted, ref, nextTick } from 'vue';
import { findCloseNum, handleStepNumber } from '@/utils/common.js';
export default {
  name: '',
  props: {
    minValue: {
      type: Number,
      default: 0,
      explain: '范围最小值',
      otherVal: '---'
    },
    maxValue: {
      type: Number,
      default: 100,
      explain: '范围最大值',
    },
    // 初始值
    initValue: {
      type: Number,
      default: 0,
      explain: '设置初始值',
    },
    // 是否设置初始值
    setInitValue: {
      type: Boolean,
      default: true,
      explain: '是否使用初始值(true时initValue有效)',
    },
    getRangChange: {
      type: Function,
      explain: '数值发生变化'
    }
  },
  setup(props, ctx) {
    const data = reactive({
      dragEle: null, //推拽槽元素
      rangeEle: null, //推拽按钮元素
      dragTrough: null, //推拽的条元素
      clickPos: 0, //点击移动按钮时鼠标距离父级的位置
      moveLeft: 0, //移动的left
      rangeWidth: 0, //范围盒子宽度
      btnWidth: 0, //拖拽按钮尺寸
      rangValue: 0, //选中值
      optionalRange: 0, //实际范围
      rangArr: [], //范围段数组
      onlyId: ''
    });
    const rangeRef = ref(null); // 获取当前组件中外层元素
    onBeforeMount(() => {});
    onMounted(() => {
      nextTick(() => {
        // 获取组件数量
        const ele = document.getElementsByClassName('swh-range-page');
        if (ele.length) {
          for (let i = 0; i < ele.length; i++) {
            // 组件和当前ref获取的组件本身相等就设置id
            if (rangeRef.value === ele[i]) {
              data.onlyId = `swhRangeRef_${i}`; //设置显示框id
            }
          }
        }
        nextTick(() => {
          init();
        });
      });
    });
    const init = () => {
      const { onlyId } = data;
      data.rangeCom = document.querySelector(`#${onlyId}`);
      data.dragEle = document.querySelector(`#${onlyId}_drag-btn`);
      data.dragValueEle = document.querySelector(`#${onlyId}_drag-value`);
      data.rangeEle = document.querySelector(`#${onlyId}_selection`);
      data.dragTrough = document.querySelector(`#${onlyId}_drag-trough`);
      data.btnWidth = data.dragEle.offsetWidth;
      data.rangeWidth = data.rangeEle.offsetWidth;
      data.dragEle.style.left = -data.btnWidth / 2 + 'px';
      // 设置默认值,没有就是最小值
      const { setInitValue, initValue, minValue, maxValue } = props;
      if (setInitValue) {
        // 设置默认值
        if (initValue >= minValue && initValue <= maxValue) {
          // 初始值在范围内
          data.rangValue = initValue;
        } else {
          console.error('未设置初始值或者初始值超出范围');
          data.rangValue = minValue;
        }
      } else {
        // 未设置默认值,默认最小值
        data.rangValue = props.minValue;
      }
      // 最大值减区最小值是 实际范围
      data.optionalRange = props.maxValue - props.minValue;
      // 获取分割范围数组
      data.rangArr = handleStepNumber(data.rangeWidth, data.optionalRange);
      initMovePosition();
      bindEvent();
    };
    // 初始化值所在的位置
    const initMovePosition = () => {
      const index = rangNumArr().indexOf(data.rangValue);
      if (index !== -1) {
        const proportion = index / data.optionalRange;
        const initLeft = data.rangeWidth * proportion;
        data.moveLeft = initLeft;
        moveLeft();
      }
    };
    // 范围数值数组
    const rangNumArr = () => {
      let rNumArr = [];
      for (let i = 0; i < data.optionalRange + 1; i++) {
        rNumArr.push(props.minValue + i);
      }
      return rNumArr;
    };
    // 事件监听
    const bindEvent = () => {
      data.dragEle.addEventListener('mousedown', handleMouseDown, false);
    };
    // 按下事件
    const handleMouseDown = (e) => {
      // 点击时鼠标距离父级left    减去已经实际移动的距离
      data.clickPos = e.clientX - data.rangeEle.offsetLeft - data.moveLeft;
      document.addEventListener('mousemove', handleMouseMove, false);
      document.addEventListener('mouseup', handleMouseUp, false);
    };
    // 移动处理
    const handleMouseMove = (e) => {
      // 获取实际移动的位置,移动后left减去点击时clickPos(left)是实际移动的left
      const inMoveleft = e.clientX - data.rangeEle.offsetLeft;
      // 移动的距离  -  开始点击的位置 = 实际移动距离
      data.moveLeft = inMoveleft - data.clickPos;
      moveLeft();
    };
    // 直接点击范围条,改变拖拽按钮选中位置
    const handleTroughClick = (e) => {
      // 鼠标点击位置减去元素距离body的left,获取点击在跳上的left距离
      const inMoveleft = e.clientX - data.rangeCom.getBoundingClientRect().left;
      // 距离减去按钮宽度未实际移动left
      data.moveLeft = inMoveleft - data.btnWidth;
      moveLeft();
    };
    // 移动位置
    const moveLeft = () => {
      // 调整实际移动的距离
      if (data.moveLeft > data.rangeWidth) {
        // 最大限制
        data.moveLeft = data.rangeWidth;
      } else if (data.moveLeft < 0) {
        // 最小限制
        data.moveLeft = 0;
      } else {
        // 移动至鼠标最接近的范围点上
        data.moveLeft = findCloseNum(data.rangArr, data.moveLeft);
      }
      //按键 移动的距离减去按键一半宽度
      data.dragEle.style.left = data.moveLeft - data.btnWidth / 2 + 'px';
      //设置选中范围条宽度
      data.dragTrough.style.width = data.moveLeft + 'px';
      // 移动的占比
      const proportion = data.moveLeft / data.rangeWidth;
      // 计算移动的值
      data.rangValue =
        parseInt(data.optionalRange * proportion) + props.minValue;
      // 计算提示数值的偏移位置
      const wc = (data.dragValueEle.offsetWidth - data.btnWidth) / 2;
      // 设置显示范围值的提示位置,设置按钮的位置即可
      data.dragValueEle.style.left =
        data.dragEle.offsetLeft - Math.abs(wc) + 'px';
      ctx.emit('getRangChange', data.rangValue);
    };
    // 移除事件监听
    const handleMouseUp = () => {
      document.removeEventListener('mousemove', handleMouseMove, false);
      document.removeEventListener('mouseup', handleMouseUp, false);
    };
    return {
      rangeRef,
      handleTroughClick,
      ...toRefs(data)
    };
  }
};
</script>
<style scoped lang="less">
.swh-range-page {
  position: relative;
  min-width: 160px;
  width: 100%;
  display: flex;
  padding: 0 20px;
  justify-content: center;
  border-radius: 10px;
  .swh-range-selection {
    position: relative;
    width: 100%;
    height: 30px;
    z-index: 9;
    .swh-trough {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 100%;
      height: 5px;
      background: rgb(243, 243, 243);
      border-radius: 3px;
      cursor: pointer;
    }
    .swh-drag-btn {
      position: absolute;
      top: 50%;
      left: 0;
      width: 20px;
      height: 20px;
      background: #fff;
      border: 2px solid @TSB;
      transform: translateY(-50%);
      border-radius: 50%;
      z-index: 1;
      cursor: pointer;
      box-sizing: border-box;
      &:hover {
        transform: translateY(-50%) scale(1.1);
        transition: 0.3s;
      }
    }
    .swh-drag-trough {
      cursor: pointer;
      position: absolute;
      top: 50%;
      left: 0;
      transform: translateY(-50%);
      background: @TSB;
      height: 5px;
      border-radius: 3px;
    }
    .drag-value {
      position: absolute;
      top: -20px;
      left: 0px;
      border-radius: 5px;
      width: 30px;
      height: 20px;
      background: rgba(0, 0, 0, 0.8);
      font-size: 12px;
      line-height: 20px;
      color: #fff;
      text-align: center;
    }
  }
}
</style>

 组件使用到的findCloseNum方法

// 判断当前数字  最靠近数组中那个数字
export function findCloseNum(arr, num) {
  var index = 0; // 保存最接近数值在数组中的索引
  var old_value = Number.MAX_VALUE; // 保存差值绝对值,默认为最大数值
  for (var i = 0; i < arr.length; i++) {
    var new_value = Math.abs(arr[i] - num); // 新差值
    if (new_value <= old_value) { // 如果新差值绝对值小于等于旧差值绝对值,保存新差值绝对值和索引
      if (new_value === old_value && arr[i] < arr[index]) { // 如果数组中两个数值跟目标数值差值一样,取大
        continue;
      }
      index = i;
      old_value = new_value;
    }
  }
  return arr[index] // 返回最接近的数值
}

 组件使用到的handleStepNumber方法

export function handleStepNumber(w, r) {
  const itemPx = w / r;
  let rangArr = [];
  for (let i = 0; i < r+1; i++) {
    rangArr.push(Math.ceil(itemPx * i));
  }
  return rangArr;
};

1.组件可以实现最小值和最大值的设置

2.可初始化值

3.组件长度根据父组件自定义

4.滑动和点击会改变范围值

根据自己的需求可进行更多扩展文章来源地址https://www.toymoban.com/news/detail-669553.html

到了这里,关于vue3范围选择组件封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JavaScript - 判断当前时间是否在指定区间内,例如:9:00~12:00(检查当前时间是否处于规定的两个时间段范围内),适用于 vue.js / uniapp / 微信小程序等前端项目

    例如,您想知道当前时间是否处于 9:00 ~ 12:00 时间区间内,然后根据这个判断进而实现业务逻辑。 如下示例所示, 本文提供一个函数,您只需要传入 2 个时间区间,便可得出当前时间是否在该时间区间范围内: 您可以一键复制,直接粘贴到您的项目中。 您只需要传入开始时

    2024年02月16日
    浏览(63)
  • Vue组件封装:基于Vue3+wangeditor富文本组件二次封装

    1.简介         开源 Web 富文本编辑器,开箱即用,配置简单。一个产品的价值,就在于解决用户的问题,提高效率、降低成本、增加稳定性和扩展性。wangEditor 不是为了做而做,也不是单纯的模仿谁,而是经过上述问题分析之后,给出一个系统的解决方案。旨在真正去解决用

    2024年04月08日
    浏览(47)
  • vue3 echart组件封装

    项目中用到了很多echart图表,进行了简单的组件封装,主要包含以下功能: 创建图表实例,渲染图表 支持传入自定义函数,可拿到图表实例,实现个性化功能 支持配置更新后图表自动刷新,可配置是清空后再刷新 loading状态控制 resize时图表更新 支持饼图默认高亮功能 echa

    2024年03月09日
    浏览(61)
  • 使用VUE自定义组件封装部门选择功能

    照惯例,先交待下背景,从真实需求出发,讲述实现效果、设计思路和实现方式。 软件系统中,会有一些常见常用的选择功能,如部门选择、人员选择等,用于填报表单,使用频率很高。直接使用一方面会比较繁琐,另一方面造成代码重复,当需要调整时,则需要遍历整个项

    2024年02月07日
    浏览(33)
  • vue3组件封装系列-表单请求

    我们在开发一些后台管理系统时,总是会写很多的列表查询页面,如果不封装组件,就会无限的复制粘贴,而且页面很冗余,正常情况下,我们都是要把组件进行二次封装,来达到我们想要效果。这里我分享一下我近期封装的关于列表的组件封装。 vue3+element-plus srccomponents

    2024年04月23日
    浏览(34)
  • Vue3 封装 element-plus 图标选择器

    效果一: 效果二:   效果一的这个是把全部的icon图标都让它显示出来,让我们自己选择说选图标 2.1. 全局注册 icon 组件 2.2. 组件实现  2.3. 使用  效果二的这个是渲染后端返回的icon图标 3.1. 全局注册 icon 组件 3.2. 组件实现  3.3. 使用 

    2024年02月07日
    浏览(101)
  • 记录--Vue3 封装 ECharts 通用组件

    配置文件这里就不再赘述,内容都是一样的,主打一个随用随取,按需导入。 chartRef :当前的 DOM 节点,即 ECharts 的容器; chartInstance :当前 DOM 节点挂载的 ECharts 实例,可用于调用实例上的方法,注册事件,自适应等; draw :用于绘制 ECharts 图表,本质是调用实例的 setOptio

    2024年02月09日
    浏览(47)
  • vue3组件二次封装Ui处理

    在Vue开发中,我们常常需要使用UI框架提供的组件。但是UI框架的组件可能并不符合我们的需求,这时候就需要进行二次封装。下面是一些关于Vue组件二次封装Ui处理的技巧: 子组件代码: 父组件使用: 如果使用props接收弊端: 基本上组件不会只有一两个属性,属性多的话接

    2023年04月14日
    浏览(83)
  • vue3+ts+vite项目引入echarts,vue3项目echarts组件封装

    技术栈 :Vue3 + Ts + Vite + Echarts 简介 : 图文详解,教你如何在 Vue3 项目中 引入Echarts , 封装Echarts组件 ,并实现常用Echarts图例 1.1 静态效果 1.2 动态效果 2.1 安装 Echarts npm: pnpm: 2.2 main.ts 中引入 2.3 Echarts 组件封装 /src/components/ReEcharts/index.vue 文件中写入如下代码 3.1 柱状图 实现

    2024年02月09日
    浏览(64)
  • vue3自定义封装组件:消息提示、轮播图、加载更多、骨架屏组件

    定义组件:src/components/library/xtx-infinite-loading.vue 注册组件:src/components/library/index.js  引用组件:src/main.js 使用组件: .vue文件 首先是轮播图的样式:src/components/library/xtx-carousel.vue  然后是轮播图的结构与逻辑封装:src/components/library/xtx-carousel.vue 插件注册:src/components/library

    2024年02月12日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包