Range 区段选择器
如果项目中使用的是 0.x 版本的基础组件(@icedesign/base, @ali/ice, @alife/next),请在左侧导航顶部切换组件版本。
安装方法
- 在命令行中执行以下命令
npm install @alifd/next@latest -S
开发指南
区间选择
何时使用
滑块控件(Sliders,简称滑块)可以让我们通过在连续或间断的区间内滑动锚点来选择一个合适的数值。区间最小值放在左边,对应的,最大值放在右边。滑块(Sliders)可以在滑动条的左右两端设定图标来反映数值的强度。这种交互特性使得它在设置诸如音量、亮度、色彩饱和度等需要反映强度等级的选项时成为一种极好的选择。
使用注意
onChange是和value进行配置做受控处理的。onChange在滑动过程中不会触发,滑动停止后会触发。
onProcess不建议内部做setState 进行受控,因为会频繁触发,整个滑动过程中会一直触发。
API
Range
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
slider | 滑块个数可选值:'single'(单个)'double'(两个) | Enum | 'single' |
min | 最小值 | Number | 0 |
max | 最大值 | Number | 100 |
step | 步长,取值必须大于 0,并且可被 (max - min) 整除。 | Number | 1 |
value | 设置当前取值。当 slider 为 single 时,使用 Number ,否则用 [Number, Number] | Number/Array<Number> | - |
defaultValue | 设置初始取值。当 slider 为 single 时,使用 Number ,否则用 [Number, Number] | Number/Array<Number> | - |
marks | 刻度数值显示逻辑(false 代表不显示,array 枚举显示的值,number 代表按 number 平分,object 表示按 key 划分,value 值显示) | Boolean/Number/Array<Number>/Object | false |
marksPosition | marks显示在上方('above')or下方('below')可选值:'above', 'below' | Enum | 'above' |
disabled | 值为 true 时,滑块为禁用状态 | Boolean | false |
onChange | 当 Range 的值发生改变后,会触发 onChange 事件,并把改变后的值作为参数传入, 如果设置了value, 要配合此函数做受控使用签名:Function(value: String/number) => void参数:value: {String/number} null | Function | func.noop |
onProcess | 滑块拖动的时候触发的事件,不建议在这里setState, 一般情况下不需要用, 滑动时有特殊需求时使用签名:Function(value: String/number) => void参数:value: {String/number} null | Function | func.noop |
hasTip | 是否显示 tip | Boolean | true |
tipRender | 自定义 tip 显示内容签名:Function(value: Number/String) => ReactNode参数:value: {Number/String} 值返回值:{ReactNode} 显示内容 | Function | value => value |
reverse | 选中态反转 | Boolean | false |
pure | 是否pure render | Boolean | false |
fixedWidth | 是否为拖动线段类型,默认slider为double, defaultValue必传且指定区间 | Boolean | false |
tooltipVisible | tooltip是否默认展示 | Boolean | false |
rtl | 是否已rtl模式展示 | Boolean | false |
ARIA and KeyBoard
按键 | 说明 |
---|---|
Right Arrow | 控制滑块往右移动 |
Left Arrow | 控制滑块向左移动 |
Tab | 切换滑动条 |
代码示例
基本滑块,当 slider
为 double
时,渲染为双滑块。当 disabled
为 true
时,滑块处于不可用状态。
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '15px'
};
ReactDOM.render(<div style={{width: '400px', margin: '50px'}}>
<h4>single slider - from left to right</h4>
<Range defaultValue={30} style={style} hasTip={false} />
<h4>single slider - from right to left</h4>
<Range defaultValue={30} style={style} reverse hasTip={false} />
<h4>double slider - from outside to inside</h4>
<Range slider={'double'} defaultValue={[20, 40]} style={style} />
<h4>double slider - from inside to outside</h4>
<Range slider={'double'} defaultValue={[20, 40]} style={style} reverse />
<h4>Disabled</h4>
<Range defaultValue={30} disabled style={style} />
<h4>Disabled</h4>
<Range slider={'double'} defaultValue={[20, 40]} disabled style={style} />
<h4>tooltipVisible disabled</h4>
<Range disabled tooltipVisible defaultValue={60} style={style} />
<h4>tooltipVisible</h4>
<Range tooltipVisible defaultValue={60} style={style} />
<h4>tooltipVisible disabled</h4>
<Range disabled tooltipVisible slider={'double'} defaultValue={[57, 77]} style={style} />
<h4>tooltipVisible</h4>
<Range tooltipVisible slider={'double'} defaultValue={[57, 77]} style={style} />
</div>, mountNode);
默认双滑块(slider
为 double
),且指定 defaultValue
为区间值下可用。范围不可被改变,拖动所选区域改变始末滑块位置。
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '15px'
};
const styleX3 = {
marginBottom: '45px'
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: [10, 300]
};
}
//Controlled. onChange will be triggered when startValue isn't equal to endValue after sliding
onChange(value) {
console.log('onChange value:', value);
this.setState({value});
}
// This callback will be triggered when startValue and endValue aren't equal after mousedown/mousemove. You shouldn't call setState here.
onProcess(value) {
// this.setState({value});
console.log('onProcess: ', value);
}
render() {
return (
<div style={{width: '400px', margin: '50px'}}>
<h4>fixedWidth basic</h4>
<Range fixedWidth defaultValue={[20, 40]} style={style} />
<h4>fixedWidth basic with tooltipVisible</h4>
<Range tooltipVisible fixedWidth defaultValue={[70, 90]} style={styleX3} />
<Range disabled tooltipVisible fixedWidth defaultValue={[70, 90]} style={style} />
<h4>fixedWidth with marks</h4>
<Range fixedWidth step={10} defaultValue={[20, 40]} scales={10} marks={10} style={styleX3} />
<Range fixedWidth step={2} defaultValue={[60, 80]} scales={[0, 100]} marks={[0, 30, 100]} style={style} marksPosition="below" />
<h4>fixedWidth with 0.01 step</h4>
<Range fixedWidth defaultValue={[0.6, 0.7]} min={0.5} max={1} step={0.01}/>
<div style={{width: '400px', marginTop: '50px'}}>
<p>range 0 ~ 1024</p>
<Range fixedWidth value={this.state.value}
onChange={this.onChange.bind(this)}
onProcess={this.onProcess.bind(this)}
min={0} max={1024} marks={[0, 1024]} />
</div>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
通过 scales
与 marks
属性设置刻度及标识。
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '20px',
marginTop: '20px'
};
ReactDOM.render(<div style={{width: '400px', margin: '50px'}}>
<p>With minimal and maximal value</p>
<Range defaultValue={0} marks={[0, 100]} style={style} />
<Range slider={'double'} defaultValue={[20, 40]} scales={[0, 100]} marks={[0, 100]} style={style} />
<Range defaultValue={30} style={style} />
<p>Below</p>
<Range defaultValue={0} marks={[0, 100]} style={style} marksPosition="below" />
<Range slider={'double'} defaultValue={[20, 40]} scales={[0, 100]} marks={[0, 100]} style={style} marksPosition="below" />
<Range defaultValue={30} scales={[0, 100]} marks={[0, 100]} style={style} marksPosition="below" />
<p>Equal division</p>
<Range defaultValue={30} marks={5} style={style} />
<Range slider={'double'} defaultValue={[20, 40]} scales={10} marks={10} style={style} />
<Range disabled slider={'double'} defaultValue={[20, 40]} scales={10} marks={10} style={style} />
<p>Free</p>
<Range defaultValue={30} marks={[0, 26, 37, 100]} style={style} />
<Range slider={'double'} defaultValue={[20, 40]} marks={[0, 26, 37, 100]} style={style} hasTip={false}/>
<Range defaultValue={30} marks={{0: '0°C', 26: '26°C', 37: '37°C', 100: '100°C'}} style={style} />
</div>, mountNode);
可以通过 min
与 max
设置范围边界。通过 step
(被 max - min 整除) 设置移动的最小步频。min默认为0,max默认为100.
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '40px',
marginTop: '40px'
};
ReactDOM.render(
<div style={{width: '400px', margin: '50px'}}>
<p>Range 0 ~ 1024</p>
<Range defaultValue={128} min={0} max={1024} marks={[0, 1024]} style={style} />
<p>Range 0 ~ 1024,step 128</p>
<Range defaultValue={512} min={0} max={1024} step={128} marks={[0, 1024]}
style={style} />
</div>
, mountNode);
onChange,onProcess事件
查看源码在线预览
import { Range } from '@alifd/next';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 128,
value2: 300,
doubleValue: [200, 300]
};
}
//Controlled. onChange will be triggered when startValue isn't equal to endValue after sliding
onChange(value) {
console.log('onChange value:', value);
this.setState({value});
}
// This callback will be triggered when startValue and endValue aren't equal after mousedown/mousemove. You shouldn't call setState here.
onProcess(value) {
console.log('onProcess: ', value);
}
onChangeDouble(doubleValue) {
console.log('onChange doubleValue:', doubleValue);
this.setState({doubleValue});
}
onProcessDouble(doubleValue) {
console.log('onProcess: ', doubleValue);
}
render() {
return (
<div style={{width: '400px', margin: '50px'}}>
<p>range 0 ~ 1024</p>
<div style={{width: '400px', marginTop: '50px'}}>
<Range value={this.state.value} onChange={this.onChange.bind(this)}
onProcess={this.onProcess.bind(this)}
min={0} max={1024} marks={[0, 1024]} />
</div>
<p>with value and without onChange</p>
<div style={{width: '400px', marginTop: '50px'}}>
<Range value={this.state.value2}
onProcess={this.onProcess.bind(this)}
min={0} max={1024} marks={[0, 1024]} />
</div>
<p>double slider controlled</p>
<div style={{width: '400px', marginTop: '50px'}}>
<Range slider="double" value={this.state.doubleValue} onChange={this.onChangeDouble.bind(this)}
onProcess={this.onProcessDouble.bind(this)}
min={0} max={1024} marks={[0, 1024]} />
</div>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
与number-picker结合,外部控制,与Icon结合设置边界Icon
查看源码在线预览
import { Range, NumberPicker, Grid, Icon } from '@alifd/next';
const {Row, Col} = Grid;
class Demo extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
valueInt: 3,
valueDec: 0.05,
valueIcon: 1,
preIconClass: '',
afterIconClass: ''
};
}
onChangeInt(value) {
console.log(value);
this.setState(Object.assign({}, this.state, {
valueInt: value
}));
}
onChangeDec(value) {
this.setState(Object.assign({}, this.state, {
valueDec: value
}));
}
onIconRangeProcess(value) {
const mid = 50;
this.setState(Object.assign({}, this.state, {
valueIcon: value,
preIconClass: value < mid ? 'myicon-highlight' : '',
afterIconClass: value >= mid ? 'myicon-highlight' : ''
}));
}
render() {
return (
<div style={{width: '400px', margin: '50px'}}>
<h2>Used with numberPicker</h2>
<Row>
<Col span="12" style={{marginTop: '10px'}}>
<Range value={this.state.valueInt} min={0} max={20} step={1}
onChange={this.onChangeInt.bind(this)} />
</Col>
<Col span="12">
<NumberPicker value={this.state.valueInt} min={0} max={1024} step={1}
onChange={this.onChangeInt.bind(this)} />
</Col>
</Row>
<br />
<Row>
<Col span="12" style={{marginTop: '10px'}}>
<Range value={this.state.valueDec} min={0} max={1} step={0.01}
onChange={this.onChangeDec.bind(this)} />
</Col>
<Col span="12">
<NumberPicker value={this.state.valueDec} min={0} max={1} step={0.01}
onChange={this.onChangeDec.bind(this)} />
</Col>
</Row>
<h2>Used with Icon</h2>
<div className="iconWrapper">
<Icon className={`myicon ${this.state.preIconClass}`} type="cry" />
<Range min={1} max={100} onChange={this.onIconRangeProcess.bind(this)} value={this.state.valueIcon} />
<Icon className={`myicon ${this.state.afterIconClass}`} type="smile" />
</div>
</div>
);
}
}
ReactDOM.render(<Demo />, mountNode);
.iconWrapper {
position: relative;
padding: 0px 40px;
}
.iconWrapper .myicon {
position: absolute;
top: -3px;
width: 16px;
height: 16px;
line-height: 1;
font-size: 16px;
color: #ccc;
}
.iconWrapper .myicon:first-child {
left: 0;
}
.iconWrapper .myicon:last-child {
right: 0;
}
.myicon.myicon-highlight {
color: #666;
}
tipRender 示例
查看源码在线预览
import { Range } from '@alifd/next';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 128
};
}
// This callback will be triggered when startValue and endValue aren't equal after moving.
onChange(value) {
console.log('onChange value:', value);
}
// This callback will be triggered when startValue and endValue aren't equal after mousedown/mousemove. You can call setState here when using a controlled component.
onProcess(value) {
console.log('onProcess');
this.setState({value});
}
formatter(value) {
return `$${value}`;
}
render() {
return (
<div style={{width: '400px', margin: '50px'}}>
<p>Range 0 ~ 1024</p>
<div style={{width: '400px', marginTop: '50px'}}>
<Range defaultValue={256} tipRender={this.formatter.bind(this)} value={this.state.value} onChange={this.onChange.bind(this)}
onProcess={this.onProcess.bind(this)}
min={0} max={1024} marks={[0, 1024]} />
</div>
</div>
);
}
}
ReactDOM.render(<App />, mountNode);
设置reverse为true, 选中态会反转。
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '15px'
};
ReactDOM.render(
<div style={{width: '400px', margin: '50px'}}>
<Range defaultValue={30} style={style} hasTip={false} />
<Range defaultValue={30} style={style} reverse hasTip={false} />
<Range slider={'double'} defaultValue={[20, 40]} style={style} />
<Range slider={'double'} defaultValue={[20, 40]} style={style} reverse />
<Range defaultValue={30} disabled style={style} />
<Range defaultValue={30} disabled style={style} reverse />
<Range slider={'double'} defaultValue={[20, 40]} disabled style={style} />
<Range slider={'double'} defaultValue={[20, 40]} disabled style={style} reverse />
</div>, mountNode);
通过Tab键或其他方式使滑块被聚焦之后,可以通过按下键盘的左右箭头来改变滑块的位置,目前只有单滑块支持无障碍。
查看源码在线预览
import { Range } from '@alifd/next';
const style = {
marginBottom: '15px'
};
ReactDOM.render(<div style={{width: '400px', margin: '50px'}}>
<h4>single slider - from left to right</h4>
<Range defaultValue={30} style={style} hasTip={false} />
<h4>single slider - from right to left</h4>
<Range defaultValue={30} style={style} reverse hasTip={false} />
</div>, mountNode);