通过使用antd的slider组件,来实现用大括号标记节

antd-slider-parenthesis-demo

版权声明:本文为博主原创文章,未经博主允许不得转载。欢迎 Issues 留言。

点击查看 codesandbox 中 demo 示例

一、背景

在实际开发过程中,遇到个有趣的小功能,这里分享一下实现过程。主要是通过 antd 的 slider 组件进行实现,并且用括号标注一个区间进行显示。如下图所示:

我们要实现的效果是:

动态的根据时间数据给出的时间节点进行显示 如果当前时间在两个时间节点之间,显示进度在节点之间 不能手动对 Slider 进行拖拽。 二、功能实现 1. 数据准备

首先我们的时间数据是动态获取的,时间顺序可能也没有排序,所以需要对获取到得时间做一个处理。 下面显示的是我们处理完后的假数据格式如下:

// 比如我们获取的时间数据如下 // data.js export const timeArray = [ { time: "2022-03-01", type: "start" // 标记括号区间开始位置 }, { time: "2022-03-10" }, { time: "2022-02-26" }, { time: "2022-06-07" }, { time: "2022-05-08" } ]; 2. Slider 使用

查看 antd 的slider 的 API,可以看到我们需要的大概有如下的参数:

marks 刻度标记 step 步长,取值必须大于 0 value 设置当前取值 disable 值为 true 时,滑块为禁用状态 tooltipVisible 值为 true 时,Tooltip 将会始终显示;否则始终不显示,哪怕在拖拽及移入时

import { Slider } from "antd"; ... <Slider marks={marks} // 刻度标记 step={step} // 步长 value={value} // 当前取值 disabled // 值为 true 时,滑块为禁用状态 tooltipVisible={false} // 始终不显示tooltip /> 3. 处理 Slider 需要的参数

我们需要定义一个方法getParam来对组件 Slider 需要的参数进行一个处理。

首先处理动态显示时间节点的问题:

1) 定义 marks 来存放处理好的 slider 的节点。

// utils.js import { timeArray } from "./data.js"; export const getParam = () => { const marks = {}; //slider上的标记对象 ... }

marks 对象的格式是{ number: ReactNode } 或者{ number: { style: CSSProperties, label: ReactNode } }

// antd demo显示 const marks = { 0: "0°C", 26: "26°C", 37: "37°C", 100: { style: { color: "#f50" }, label: <strong>100°C</strong> } };

所以后续我们需要处理后得到如 demo 所示的数据结果。

2) timeArray 先通过 sort 对时间进行排序,保证时间顺序正确

import moment from "moment"; ... // 对时间数组排序 const newTimeArray = timeArray.sort((a, b) => moment(a.time).isSameOrAfter(b.time) ? 1 : -1 ); 3)根据数组内的时间节点个数,动态计算节点 number 值和节点步长 step。

比如当前数组内有4 个时间节点,那么 marks 对象的 0 和 100 会分别占据 2 个节点,剩余 2 个节点要在 0 至 100 之间平均分配。也就是 0 至 100 会被平均分成 3 份,每一份的长度 就是我们要计算的nmark

因为我们的需求第 2 点提到如果当前时间在两个时间节点之间,显示进度在节点之间,所以,我们的步长 nstep 其实是前面nmark长度的一半。

// 根据时间节点计算节点位置 let nmark = newTimeArray.length > 1 ? (100 / (newTimeArray.length - 1)).toFixed(2) : 0; // 两节点之间的中间值为步长,表示在两时间节点中的状态 let nstep = (nmark / 2).toFixed(2); 4) 在 marks 对象进行刻度标记

当我们计算好每份的长度和步长以后,我们需要通过遍历时间数组,对每个数据显示的节点的在 marks 对象进行刻度标记。

并且跟今天的日期做对比,记录当前我们应该显示的 value 值是什么。

如果今天的日期和某个标记日期正好相等,value 就是标记日期的刻度值 如果今天日期 在某两个刻度(比如刻度 1、2)之间,则 value 值是这两个刻度之间的刻度值(这里就是为什么前面 nstep 是 nmark 的一半的原因),既 value = 刻度1的mark值 + nstep,则正好位于刻度 1、2 之间

let sliderValue = 0; // 记录当前时间轴的值 // 对排序后的时间进行遍历 newTimeArray.forEach((item, i) => { // 数组长度 let length = newTimeArray.length; // 是否是最后一个节点 let islast = length > 1 ? i === length - 1 : false; // 计算时间的标记位置数字 0-100之间的 let markNum = (i * (nmark * 100)) / 100; // 今天的日期 let todayTime = moment(moment().format("YYYY-MM-DD")); // 如果时间是今天,时间节点是相等时间的mark值 if (moment(item.time).isSame(todayTime)) { sliderValue = Number(markNum); } // 如果今天时间在某个时间节点的后面,则mark值在这个节点和下个节点中间,所以加个step值 else if (todayTime.isAfter(moment(item.time))) { sliderValue = (Number(markNum) * 100 + Number(nstep) * 100) / 100; } // 刻度标记marks对象 marks[islast ? 100 : markNum] = { label: item.time }; ... }); return { timeArr: newTimeArray, marks, step: nstep, sliderValue, }; } 4. 大括号的实现 1)获取参数处理

这里处理的比较简单,我们这里默认显示两个节点之间的间距。所以给要显示的时间节点加个 type ,这里 是 type = “start”

{ time: "2022-03-01", type: "start" // 标记括号区间开始位置 },

然后在上面 3 中第 4)步遍历时间数组的时候,对大括号要显示的地方做个处理。

const parenthesisArr = []; // 大括号 // 对排序后的时间进行遍历 newTimeArray.forEach((item, i) => { ... // 计算时间的标记位置数字 0-100之间的 let markNum = (i * (nmark * 100)) / 100; // ------ 对大括号显示处理------// // 记录开始节点 if (item.type === "start") { parenthesisArr.push({ name: "显示间距", left: markNum // 大括号左侧位移距离 }); } ... });

然后大括号的组件处理

import Parenthesis from "./comp/parenthesis.js"; import { getParam } from "./utils"; ... const [value, set_value] = useState(0); // 当前值 const [marks, set_marks] = useState({}); // 标记 const [step, set_step] = useState(null); // 步长 const [parenthesisArr, set_parenthesisArr] = useState([]); // 显示大括号数组 useEffect(() => { let param = getParam(); set_marks(param.marks); set_step(param.step); set_value(param.sliderValue); set_parenthesisArr(param.parenthesisArr); }, []); ... {parenthesisArr.map((item, i) => { return ( <Parenthesis key={i} data={item} step={Number(step) * 2} // 默认显示相邻两个节点之前的间距 day={item.name} /> ); })} 2)大括号的样式实现

主要根据步长来设置大括号的宽度,left 是向左位移的距离。

// parenthesis.js import "./index.css"; const Parenthesis = (props) => { let { data, step, day } = props; return ( <div className="parenthesis-box" // 这里根据步长来设置大括号的宽度 style={{ left: `${data.left}%`, width: `${step}%` }} > <div className="parenthesis-inner"> <div className="parenthesis"> <div className="parenthesis-text">{day}</div> </div> </div> </div> ); }; export default Parenthesis;

css 样式如下: 主要是利用 before、after 伪类进行实现。

/*index.css*/ .parenthesis-box { position: absolute; top: 40px; } /*显示提示文案*/ .parenthesis-text { position: relative; top: -40px; } /*大括号样式*/ .parenthesis-inner { position: relative; width: 100%; height: 40px; } .parenthesis-inner::before, .parenthesis-inner::after, .parenthesis::before, .parenthesis::after { content: ""; display: block; position: absolute; width: calc(50% - 20px); height: 20px; border-style: solid; border-color: rgb(9, 113, 241); border-width: 0; } .parenthesis::before, .parenthesis::after { top: 0; border-top-width: 1px; } .parenthesis-inner::before, .parenthesis-inner::after { top: -19px; border-bottom-width: 1px; } .parenthesis::before { left: 0; border-top-left-radius: 20px; } .parenthesis::after { right: 0; border-top-right-radius: 20px; } .parenthesis-inner::before { left: 20px; border-bottom-right-radius: 20px; } .parenthesis-inner::after { right: 20px; border-bottom-left-radius: 20px; } 三、参考文章 css3 平行四边形 、大括弧 antd Slider

版权声明:

1、该文章(资料)来源于互联网公开信息,我方只是对该内容做点评,所分享的下载地址为原作者公开地址。
2、网站不提供资料下载,如需下载请到原作者页面进行下载。