2021SC@SDUSC
Slider滑动输入条
用法:
滑动型输入器,展示当前值和可选范围。
当用户需要在数值区间/自定义区间内进行选择时,可为连续或离散值。
API
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
allowClear | 支持清除, 单选模式有效 | boolean | false |
defaultValue | 设置初始取值。当 range 为 false 时,使用 number,否则用 [number, number] | number | [number, number] | 0 | [0, 0] |
disabled | 值为 true 时,滑块为禁用状态 | boolean | false |
dots | 是否只能拖拽到刻度上 | boolean | false |
getTooltipPopupContainer | Tooltip 渲染父节点,默认渲染到 body 上 | (triggerNode) => HTMLElement | () => document.body |
included | marks 不为空对象时有效,值为 true 时表示值为包含关系,false 表示并列 | boolean | true |
marks | 刻度标记,key 的类型必须为 number 且取值在闭区间 [min, max] 内,每个标签可以单独设置样式 | object | { number: ReactNode } or { number: { style: CSSProperties, label: ReactNode } } |
max | 最大值 | number | 100 |
min | 最小值 | number | 0 |
range | 双滑块模式 | boolean | range | false |
reverse | 反向坐标轴 | boolean | false |
step | 步长,取值必须大于 0,并且可被 (max - min) 整除。当 marks 不为空对象时,可以设置 step 为 null,此时 Slider 的可选值仅有 marks 标出来的部分 | number | null | 1 |
tipFormatter | Slider 会把当前值传给 tipFormatter ,并在 Tooltip 中显示 tipFormatter 的返回值,若为 null,则隐藏 Tooltip | value => ReactNode | null | IDENTITY |
tooltipPlacement | 设置 Tooltip 展示位置。 | string | - |
tooltipVisible | 值为 true 时,Tooltip 将会始终显示;否则始终不显示,哪怕在拖拽及移入时 | boolean | - |
value | 设置当前取值。当 range 为 false 时,使用 number,否则用 [number, number] | number | [number, number] | - |
vertical | 值为 true 时,Slider 为垂直方向 | boolean | false |
onAfterChange | 与 onmouseup 触发时机一致,把当前值作为参数传入 | (value) => void | - |
onChange | 当 Slider 的值发生改变时,会触发 onChange 事件,并把改变后的值作为参数传入 | (value) => void | - |
range
参数 | 说明 | 类型 | 默认值 | 版本 |
---|---|---|---|---|
draggableTrack | 范围刻度是否可被拖拽 | boolean | false | 4.10.0 |
部分源码
SliderTooltip.tsx
import * as React from 'react';
import { useRef } from 'react';
import { composeRef } from 'rc-util/lib/ref';
import raf from 'rc-util/lib/raf';
import Tooltip, { TooltipProps } from '../tooltip';
const SliderTooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
const { visible } = props;
const innerRef = useRef<any>(null);
const rafRef = useRef<number | null>(null);
function cancelKeepAlign() {
raf.cancel(rafRef.current!);
rafRef.current = null;
}
function keepAlign() {
rafRef.current = raf(() => {
innerRef.current?.forcePopupAlign();
rafRef.current = null;
});
}
React.useEffect(() => {
if (visible) {
keepAlign();
} else {
cancelKeepAlign();
}
return cancelKeepAlign;
}, [visible, props.title]);
return <Tooltip ref={composeRef(innerRef, ref)} {...props} />;
});
export default SliderTooltip;
运用到了raf
requestAnimationFrame(rAF)是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。
SliderTooltip结构较为简单,主要用到了tooltip,并设置了可见visible时就保持对齐的方法。
当 tooltipVisible 为 true 时,将始终显示 ToolTip;反之则始终不显示,即使在拖动、移入时也是如此。
index.tsx
import * as React from 'react';
import RcSlider, { Range as RcRange, Handle as RcHandle } from 'rc-slider';
import classNames from 'classnames';
import { TooltipPlacement } from '../tooltip';
import SliderTooltip from './SliderTooltip';
import { ConfigContext } from '../config-provider';
引入了react的slider,以及刚才提到的SliderTooltip。
export interface SliderMarks {
[key: number]:
| React.ReactNode
| {
style: React.CSSProperties;
label: React.ReactNode;
};
}
interface HandleGeneratorInfo {
value?: number;
dragging?: boolean;
index: number;
}
export type HandleGeneratorFn = (config: {
tooltipPrefixCls?: string;
prefixCls?: string;
info: HandleGeneratorInfo;
}) => React.ReactElement;
export interface SliderBaseProps {
prefixCls?: string;
tooltipPrefixCls?: string;
reverse?: boolean;
min?: number;
max?: number;
step?: null | number;
marks?: SliderMarks;
dots?: boolean;
included?: boolean;
disabled?: boolean;
vertical?: boolean;
tipFormatter?: null | ((value?: number) => React.ReactNode);
className?: string;
id?: string;
style?: React.CSSProperties;
tooltipVisible?: boolean;
tooltipPlacement?: TooltipPlacement;
getTooltipPopupContainer?: (triggerNode: HTMLElement) => HTMLElement;
autoFocus?: boolean;
}
此处先设置了一些Slider的基础属性,比如可拖动性以及刻度标记。
Marks刻度标记,key 的类型必须为 number 且取值在闭区间 [min, max] 内,每个标签可以单独设置样式。使用Marks可标注分段式滑块。
以下代码可实现如下效果
import { Slider } from 'antd';
const marks = {
0: '0°C',
26: '26°C',
37: '37°C',
100: {
style: {
color: '#f50',
},
label: <strong>100°C</strong>,
},
};
ReactDOM.render(
<>
<h4>included=true</h4>
<Slider marks={marks} defaultValue={37} />
<Slider range marks={marks} defaultValue={[26, 37]} />
<h4>included=false</h4>
<Slider marks={marks} included={false} defaultValue={37} />
<h4>marks & step</h4>
<Slider marks={marks} step={10} defaultValue={37} />
<h4>step=null</h4>
<Slider marks={marks} step={null} defaultValue={37} />
</>,
mountNode,
);
当 included=false 时,表明不同标记间为并列关系。
当 step=null 时,Slider 的可选值仅有 marks 标出来的部分。
以下接index
export interface SliderSingleProps extends SliderBaseProps {
range?: false;
value?: number;
defaultValue?: number;
onChange?: (value: number) => void;
onAfterChange?: (value: number) => void;
handleStyle?: React.CSSProperties;
trackStyle?: React.CSSProperties;
}
export interface SliderRangeProps extends SliderBaseProps {
range: true | SliderRange;
value?: [number, number];
defaultValue?: [number, number];
onChange?: (value: [number, number]) => void;
onAfterChange?: (value: [number, number]) => void;
handleStyle?: React.CSSProperties[];
trackStyle?: React.CSSProperties[];
}
两种slider,single和range,都继承自SliderBaseProps,相当于都有了Slider基础的属性,区别主要是range是双滑块因此值应该是个范围。
以下分别是single和range
const Slider = React.forwardRef<unknown, SliderSingleProps | SliderRangeProps>(
(props, ref: any) => {
const { getPrefixCls, direction, getPopupContainer } = React.useContext(ConfigContext);
const [visibles, setVisibles] = React.useState<Visibles>({});
const toggleTooltipVisible = (index: number, visible: boolean) => {
setVisibles((prev: Visibles) => ({ ...prev, [index]: visible }));
};
const getTooltipPlacement = (tooltipPlacement?: TooltipPlacement, vertical?: boolean) => {
if (tooltipPlacement) {
return tooltipPlacement;
}
if (!vertical) {
return 'top';
}
return direction === 'rtl' ? 'left' : 'right';
};
const handleWithTooltip: HandleGeneratorFn = ({
tooltipPrefixCls,
prefixCls,
info: { value, dragging, index, ...restProps },
}) => {
const {
tipFormatter,
tooltipVisible,
tooltipPlacement,
getTooltipPopupContainer,
vertical,
} = props;
const isTipFormatter = tipFormatter ? visibles[index] || dragging : false;
const visible = tooltipVisible || (tooltipVisible === undefined && isTipFormatter);
const rootPrefixCls = getPrefixCls();
return (
<SliderTooltip
prefixCls={tooltipPrefixCls}
title={tipFormatter ? tipFormatter(value) : ''}
visible={visible}
placement={getTooltipPlacement(tooltipPlacement, vertical)}
transitionName={`${rootPrefixCls}-zoom-down`}
key={index}
overlayClassName={`${prefixCls}-tooltip`}
getPopupContainer={getTooltipPopupContainer || getPopupContainer}
>
<RcHandle
{...restProps}
value={value}
onMouseEnter={() => toggleTooltipVisible(index, true)}
onMouseLeave={() => toggleTooltipVisible(index, false)}
/>
</SliderTooltip>
);
};
const {
prefixCls: customizePrefixCls,
tooltipPrefixCls: customizeTooltipPrefixCls,
range,
className,
...restProps
} = props;
const prefixCls = getPrefixCls('slider', customizePrefixCls);
const tooltipPrefixCls = getPrefixCls('tooltip', customizeTooltipPrefixCls);
const cls = classNames(className, {
[`${prefixCls}-rtl`]: direction === 'rtl',
});
// make reverse default on rtl direction
if (direction === 'rtl' && !restProps.vertical) {
restProps.reverse = !restProps.reverse;
}
// extrack draggableTrack from range={{ ... }}
let draggableTrack: boolean | undefined;
if (typeof range === 'object') {
draggableTrack = range.draggableTrack;
}
if (range) {
return (
<RcRange
{...(restProps as SliderRangeProps)}
step={restProps.step!}
draggableTrack={draggableTrack}
className={cls}
ref={ref}
handle={(info: HandleGeneratorInfo) =>
handleWithTooltip({
tooltipPrefixCls,
prefixCls,
info,
})
}
prefixCls={prefixCls}
/>
);
}
return (
<RcSlider
{...(restProps as SliderSingleProps)}
step={restProps.step!}
className={cls}
ref={ref}
handle={(info: HandleGeneratorInfo) =>
handleWithTooltip({
tooltipPrefixCls,
prefixCls,
info,
})
}
prefixCls={prefixCls}
/>
);
},
);
forwardRef函数,用到了之前引入的tooltip,并利用react的slider进行实现。
其中
rtl用于方向的反转,从左到右变成从右到左。
draggableTrack用于设置范围刻度整体可拖动的属性。