模板版本:v0.4.0
@ptomasroos/react-native-multi-slider
本项目基于 @ptomasroos/react-native-multi-slider 开发。
该第三方库的仓库已迁移至 Gitcode,且支持直接从 npm 下载,新的包名为:@react-native-ohos/react-native-multi-slider 版本所属关系如下:
| 三方库名称 | 三方库版本 | 发布信息 | 支持RN版本 | Autolink | 编译API版本 | 社区基线版本 | npm地址 |
|---|---|---|---|---|---|---|---|
| @react-native-ohos/react-native-multi-slider | ~2.3.0 | Gitcode Releases | 0.82/0.77 | 否 | API12+ | v2.2.2 | Npm Address |
| @react-native-ohos/react-native-multi-slider | ~2.2.3 | Gitcode Releases | 0.72 | 否 | API12+ | v2.2.2 | Npm Address |
| @react-native-oh-tpl/react-native-multi-slider | <= 2.2.2-0.0.2@deprecated | Github Releases(deprecated) | 0.72 | 否 | API12+ | v2.2.2 | Npm Address |
1. 安装与使用
进入到工程目录并输入以下命令:
npm
npm install @react-native-ohos/react-native-multi-slider
yarn
yarn add @react-native-ohos/react-native-multi-slider
下面的代码展示了这个库的基本使用场景:
使用时 import 的库名不变。
import React from 'react';
import { StyleSheet, View, Text, ScrollView, Alert, Animated, Image } from 'react-native';
import MultiSlider from '@ptomasroos/react-native-multi-slider';
const AnimatedView = Animated.createAnimatedComponent(View);
CustomLabel.defaultProps = {
leftDiff: 0,
};
const width = 50;
const pointerWidth = width * 0.47;
function LabelBase(props:any) {
const { position, value, leftDiff, pressed } = props;
const scaleValue = React.useRef(new Animated.Value(0.1)); // Behaves oddly if set to 0
const cachedPressed = React.useRef(pressed);
React.useEffect(() => {
Animated.timing(scaleValue.current, {
toValue: pressed ? 1 : 0.1,
duration: 200,
delay: pressed ? 0 : 2000,
useNativeDriver: false,
}).start();
cachedPressed.current = pressed;
}, [pressed]);
return (
Number.isFinite(position) &&
Number.isFinite(value) && (
<AnimatedView
style={[
labelStyles.sliderLabel,
{
left: position - width / 2,
transform: [
{ translateY: width },
{ scale: scaleValue.current },
{ translateY: -width },
],
},
]}
>
<View style={labelStyles.pointer} />
<Text style={labelStyles.sliderLabelText}>{value}</Text>
</AnimatedView>
)
);
}
function CustomLabel(props:any) {
const {
leftDiff,
oneMarkerValue,
twoMarkerValue,
oneMarkerLeftPosition,
twoMarkerLeftPosition,
oneMarkerPressed,
twoMarkerPressed,
} = props;
return (
<View style={labelStyles.parentView}>
<LabelBase
position={oneMarkerLeftPosition}
value={oneMarkerValue}
leftDiff={leftDiff}
pressed={oneMarkerPressed}
/>
<LabelBase
position={twoMarkerLeftPosition}
value={twoMarkerValue}
leftDiff={leftDiff}
pressed={twoMarkerPressed}
/>
</View>
);
}
class CustomMarker extends React.Component<any> {
render() {
return (
<View>
<Text style={markerStyles.text}>{this.props.valuePrefix}{this.props.currentValue}</Text>
<Image
style={markerStyles.image}
source={
this.props.pressed ? require('./ruby.png') : require('./diamond.png')
}
resizeMode="contain"
/>
<Text style={markerStyles.text}>{this.props.valuePrefix}{this.props.currentValue}{this.props.valueSuffix}</Text>
</View>
);
}
}
export default function MultiSliderDemo() {
let testRef = React.useRef<View>(null);
let [currentTestId, setCurrentTestId] = React.useState('');
// 滑动指标配置
const touchDimensionsConfig = {
height: 60,
width: 60,
borderRadius: 10,
slipDisplacement: 200,
};
// 当值开始变化时回调
const onValuesChangeStart = () => Alert.alert('Success', '值开始变化时回调');
// 值改变时回调
const onValuesChange = () => Alert.alert('Success', '值改变时回调');
// 当值停止变化时回调
const onValuesChangeFinish = () => Alert.alert('Success', '值停止变化时回调');
// 第一个光标切换时回调
const onToggleOne = () => Alert.alert('Success', '第一个光标切换回调');
// 第二个光标切换时回调
const onToggleTwo = () => Alert.alert('Success', '第二个光标切换回调');
// 获取绑定的testID
const getCurrentTestId = () => {
const testId = testRef.current?.props.testID || '';
setCurrentTestId(testId);
};
return (
<ScrollView>
<View style={styles.container}>
<Text style={styles.title}>MultiSlider功能验证</Text>
<View style={styles.sliders}>
<Text style={styles.text}>values(设置values,1和6)</Text>
<MultiSlider values={[1, 6]} />
<Text style={styles.text}>
onValuesChangeStart(值开始变化触发回调)
</Text>
<MultiSlider onValuesChangeStart={onValuesChangeStart} />
<Text style={styles.text}>
onValuesChange(值改变时回调触发回调)
</Text>
<MultiSlider onValuesChange={onValuesChange} />
<Text style={styles.text}>
onValuesChangeFinish(当值停止变化时回调)
</Text>
<MultiSlider onValuesChangeFinish={onValuesChangeFinish} />
<Text style={styles.text}>sliderLength(滑块长度{100})</Text>
<MultiSlider sliderLength={100} />
<Text style={styles.text}>
touchDimensions(滑动指标borderRadius 50)
</Text>
<MultiSlider touchDimensions={touchDimensionsConfig} />
<Text style={styles.text}>enableLabel(开启自定义标签)</Text>
<MultiSlider enableLabel customLabel={() => <CustomLabel />} />
<Text style={styles.text}>customMarker(自定义光标)</Text>
<MultiSlider customMarker={CustomMarker} />
<Text style={styles.text}>customMarkerLeft/Right</Text>
<Text style={styles.text}>自定义左右光标:默认未启用</Text>
<MultiSlider
values={[0, 10]}
customMarkerLeft={() => <CustomMarker />}
customMarkerRight={() => <CustomMarker pressed />}
/>
<Text style={styles.text}>isMarkersSeparated</Text>
<Text style={styles.text}>自定义左右光标:启用</Text>
<MultiSlider
values={[0, 10]}
isMarkersSeparated={true}
customMarkerLeft={() => <CustomMarker />}
customMarkerRight={() => <CustomMarker pressed />}
/>
<Text style={styles.text}>min(滑块可用最小值3)</Text>
<MultiSlider enableLabel min={3} />
<Text style={styles.text}>max(滑块可用最大值8)</Text>
<MultiSlider enableLabel max={8} />
<Text style={styles.text}>step(滑块步长5)</Text>
<MultiSlider enableLabel step={5} />
<Text style={styles.text}>optionsArray(节点标记)</Text>
<MultiSlider enableLabel optionsArray={[2, 6, 9]} />
<Text style={styles.text}>containerStyle(滑块容器样式)</Text>
<MultiSlider
containerStyle={{backgroundColor: 'lightblue', padding: 10}}
/>
<Text style={styles.text}>trackStyle(轨道样式:默认)</Text>
<MultiSlider trackStyle={{backgroundColor: 'red', height: 5}} />
<Text style={styles.text}>selectedStyle(轨道样式:滑过后)</Text>
<MultiSlider selectedStyle={{backgroundColor: 'green', height: 5}} />
<Text style={styles.text}>unselectedStyle(轨道样式:未滑过)</Text>
<MultiSlider unselectedStyle={{backgroundColor: 'red', height: 5}} />
<Text style={styles.text}>markerContainerStyle(光标容器样式)</Text>
<MultiSlider
markerContainerStyle={{
backgroundColor: 'transparent',
borderColor: 'blue',
borderWidth: 2,
}}
/>
<Text style={styles.text}>markerStyle(光标样式)</Text>
<MultiSlider
markerStyle={{
backgroundColor: 'red',
width: 20,
height: 20,
borderRadius: 10,
}}
/>
<Text style={styles.text}>
pressedMarkerStyle(接触光标后的样式)
</Text>
<MultiSlider pressedMarkerStyle={{backgroundColor: 'darkblue'}} />
<Text style={styles.text}>valuePrefix(值的前缀 b)</Text>
<MultiSlider
values={[0, 10]}
customMarker={CustomMarker}
valuePrefix="b"
/>
<Text style={{...styles.text, marginTop: 20}}>
valueSuffix(值的后缀 a)
</Text>
<MultiSlider
values={[0, 10]}
customMarker={CustomMarker}
valueSuffix="a"
/>
<Text style={styles.text}>enabledOne(禁用第一个光标)</Text>
<MultiSlider values={[0, 10]} enabledOne={false} />
<Text style={styles.text}>enabledTwo(禁用第二个光标)</Text>
<MultiSlider values={[0, 10]} enabledTwo={false} />
<Text style={styles.text}>stepsAs</Text>
<Text style={styles.text}>自定义步骤标签,默认展示</Text>
<MultiSlider
values={[0, 10]}
step={2}
showSteps
stepsAs={[
{index: 1, stepLabel: 't1', prefix: 'a1', suffix: 'b1'},
{index: 2, stepLabel: 't2', prefix: 'a2', suffix: 'b2'},
]}
/>
<Text style={styles.text}>showStepLabels</Text>
<Text style={styles.text}>隐藏/显示自定义步骤标签,这里隐藏</Text>
<MultiSlider
values={[0, 10]}
step={2}
showSteps
showStepLabels={false}
stepsAs={[
{index: 1, stepLabel: 't', prefix: 'a', suffix: 'b'},
{index: 2, stepLabel: 't', prefix: 'a', suffix: 'b'},
]}
/>
<Text style={styles.text}>
showStepMarkers(显示步骤对应的刻度点)
</Text>
<MultiSlider values={[0, 10]} step={2} showSteps showStepMarkers />
<Text style={styles.text}>onToggleOne</Text>
<Text style={styles.text}>点击第一个光标回调</Text>
<MultiSlider values={[0, 10]} onToggleOne={onToggleOne} />
<Text style={styles.text}>onToggleTwo</Text>
<Text style={styles.text}>点击第二个光标回调</Text>
<MultiSlider values={[0, 10]} onToggleTwo={onToggleTwo} />
<Text style={styles.text}>allowOverlap</Text>
<Text style={styles.text}>允许/禁止光标重叠,默认禁止,这里允许</Text>
<MultiSlider values={[0, 10]} enableLabel allowOverlap={true} />
<Text style={styles.text}>snapped(步进式移动光标)</Text>
<MultiSlider sliderLength={300} step={3} snapped />
<Text style={styles.text}>smoothSnapped(跳转最近节点)</Text>
<MultiSlider smoothSnapped sliderLength={300} step={3} />
<View style={{marginTop: 60, marginBottom: 150}}>
<Text style={styles.text}>vertical(垂直方向)</Text>
<MultiSlider vertical />
</View>
<Text style={styles.text}>markerOffsetX(横向偏移20)</Text>
<MultiSlider markerOffsetX={20} />
<Text style={styles.text}>markerOffsetY(纵向偏移20)</Text>
<MultiSlider markerOffsetY={20} />
<Text style={styles.text}>markerSize(光标与边缘的距离)</Text>
<MultiSlider markerSize={50} />
<Text style={styles.text}>minMarkerOverlapDistance</Text>
<Text style={styles.text}>避免大光标重叠,间隔100</Text>
<MultiSlider values={[0, 10]} minMarkerOverlapDistance={100} />
<Text style={styles.text}>minMarkerOverlapStepDistance</Text>
<Text style={styles.text}>避免大光标重叠,间隔2步</Text>
<MultiSlider
values={[0, 300]}
step={3}
allowOverlap={false}
smoothSnapped
minMarkerOverlapStepDistance={2}
/>
<Text style={styles.text}>imageBackgroundSource(背景图片)</Text>
<MultiSlider imageBackgroundSource={require('./logo-og.png')} />
<Text style={styles.text}>testID(设置testID)</Text>
<MultiSlider values={[0, 6]} testID={'666'} ref={testRef} />
<Text onPress={getCurrentTestId}>
{currentTestId || '点击获取testID'}
</Text>
</View>
</View>
</ScrollView>
);
}
const labelStyles = StyleSheet.create({
parentView: {
position: 'relative',
},
sliderLabel: {
position: 'absolute',
justifyContent: 'center',
bottom: '100%',
width: width,
height: width,
},
sliderLabelText: {
textAlign: 'center',
lineHeight: width,
borderRadius: width / 2,
borderWidth: 2,
borderColor: '#999',
backgroundColor: '#fff',
flex: 1,
fontSize: 18,
color: '#aaa',
},
pointer: {
position: 'absolute',
bottom: -pointerWidth / 4,
left: (width - pointerWidth) / 2,
transform: [{ rotate: '45deg' }],
width: pointerWidth,
height: pointerWidth,
backgroundColor: '#999',
},
});
const markerStyles = StyleSheet.create({
image: {
height: 40,
width: 40,
},
text: {
alignSelf: 'center',
paddingVertical: 20,
},
title: {
fontSize: 30,
},
});
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
sliders: {
margin: 20,
// width: 280,
},
text: {
alignSelf: 'center',
paddingVertical: 20,
},
title: {
fontSize: 30,
},
sliderOne: {
flexDirection: 'row',
justifyContent: 'space-around',
},
});
2. 约束与限制
2.1 兼容性
本文档内容基于以下版本验证通过:
- RNOH: 0.72.20; SDK: HarmonyOS NEXT Developer Beta1; IDE: DevEco Studio 5.0.3.200; ROM: 3.0.0.18;
- RNOH: 0.77.18; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.868; ROM: 6.0.0.112;
- RNOH: 0.82.1; SDK: HarmonyOS 6.0.0 Release SDK; IDE: DevEco Studio 6.0.0.858; ROM: 6.0.0.112;
3. 属性
"Platform"列表示该属性在原三方库上支持的平台。
"HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。
| Name | Description | Type | Required | Platform | HarmonyOS Support |
|---|---|---|---|---|---|
| values | 滑块的预设值列表 | array of numbers | NO | All | YES |
| onValuesChangeStart | 数值开始变化时的回调函数 | function | NO | All | YES |
| onValuesChange | 数值变化时的回调函数 | function | NO | All | YES |
| onValuesChangeFinish | 数值停止变化时的回调函数 | function | NO | All | YES |
| sliderLength | 滑块长度 | number | NO | All | YES |
| touchDimensions | 定义滑块触摸区域的尺寸 | object | NO | All | YES |
| enableLabel | 是否启用标签渲染 | function | NO | All | YES |
| customLabel | 用于在滑块游标上方渲染标签的组件 | function | NO | All | YES |
| customMarker | 滑块游标使用的组件 | function | NO | All | YES |
| customMarkerLeft | 左侧滑块游标使用的组件 | function | NO | All | YES |
| customMarkerRight | 右侧滑块游标使用的组件 | function | NO | All | YES |
| isMarkersSeparated | 是否启用分开的左右自定义游标 | boolean | NO | All | YES |
| min | 滑块可设置的最小值 | number | NO | All | YES |
| max | 滑块可设置的最大值 | number | NO | All | YES |
| step | 滑块步长 | number | NO | All | YES |
| optionsArray | 滑块可选值列表,设置后会忽略 min、max | array of numbers | NO | All | YES |
| containerStyle | 滑块容器样式 | style object | NO | All | YES |
| trackStyle | 滑块轨道默认样式 | style object | NO | All | YES |
| selectedStyle | 轨道滑动选中区域的样式 | style object | NO | All | YES |
| unselectedStyle | 轨道未选中区域的样式 | style object | NO | All | YES |
| markerContainerStyle | 游标容器样式 | style object | NO | All | YES |
| markerStyle | 游标样式 | style object | NO | All | YES |
| pressedMarkerStyle | 游标被触摸时的样式 | style object | NO | All | YES |
| stepStyle | 步长样式 | style object | NO | - | YES |
| stepLabelStyle | 步长标签样式 | style object | NO | - | YES |
| stepMarkerStyle | 步长标记点样式 | style object | NO | - | YES |
| valuePrefix | 数值前缀 | string | NO | All | YES |
| valueSuffix | 数值后缀 | string | NO | All | YES |
| enabledOne | 是否启用第一个游标 | boolean | NO | All | YES |
| enabledTwo | 是否启用第二个游标 | boolean | NO | All | YES |
| stepsAs | 用于自定义步长标签,需传入数组:[{index, stepLabel, prefix, suffix}],需同时开启 showSteps 和 showStepLabels | array of objects | NO | All | YES |
| showSteps | 是否显示步长 | boolean | NO | All | YES |
| showStepMarkers | 是否在轨道上显示步长标记,需同时开启 showSteps | boolean | NO | All | YES |
| showStepLabels | 是否在轨道下方显示步长标签,需同时开启 showSteps | boolean | NO | All | YES |
| onToggleOne | 第一个游标状态切换监听回调 | function callback | NO | All | YES |
| onToggleTwo | 第二个游标状态切换监听回调 | function callback | NO | All | YES |
| allowOverlap | 是否允许游标之间重叠 | boolean | NO | All | YES |
| snapped | 是否将游标固定在分段位置,滑块会被分为 N 个固定点位 | boolean | NO | All | YES |
| smoothSnapped | 与 snapped 类似,滑动时可自由拖动,松开后吸附到最近点位 | boolean | NO | All | YES |
| vertical | 是否使用垂直方向(默认水平) | boolean | NO | All | YES |
| markerOffsetX | 第一个游标 X 偏移量 | number | NO | All | YES |
| markerOffsetY | 第二个游标 Y 偏移量 | number | NO | All | YES |
| markerSize | 游标与轨道边缘的间距,防止游标超出轨道 | number | NO | All | YES |
| minMarkerOverlapDistance | 当值>0且allowOverlap=false时,设置两游标最小像素间距,防止大游标重叠 | number | NO | All | YES |
| minMarkerOverlapStepDistance | 以步长为单位设置两游标最小间距,不可与 minMarkerOverlapDistance 同时使用 | number | NO | All | YES |
| imageBackgroundSource | 背景图片资源路径 | string | NO | All | YES |
| testID | 用于端到端测试定位当前视图 | string | NO | All | YES |
4. 遗留问题
5. 其他
6. 开源协议
本项目基于The MIT License (MIT),请自由地享受和参与开源。