我在 React Native 中使用纯 js 滚动选择器,它在 ios 和 android 上运行得很好:)
我想为每个滚动添加一些声音或振动,这样它会更加用户友好
我没有找到一种方法来做到这一点,有人知道如何做到这一点吗?您是否有任何建议的声音和来源可供下载?
import React from "react";
import styled from "styled-components";
import {
View,
Text,
ScrollView,
Dimensions,
Platform,
StyleSheet,
} from "react-native";
import PropTypes from "prop-types";
import Responsive from "../responsive";
const Container = styled.View`
height: ${(props) => props.wrapperHeight}px;
flex: 1;
overflow: hidden;
align-self: center;
width: ${(props) => props.wrapperWidth}px;
background-color: ${(props) => props.wrapperBackground};
`;
export const HighLightView = styled.View`
position: absolute;
top: ${(props) => (props.wrapperHeight - props.itemHeight).toFixed(0) / 2}px;
height: ${(props) => props.itemHeight}px;
width: ${(props) => props.highlightWidth}px;
border-top-color: ${(props) => props.highlightColor};
border-bottom-color: ${(props) => props.highlightColor};
border-top-width: ${(props) => props.highlightBorderWidth}px;
border-bottom-width: ${(props) => props.highlightBorderWidth}px;
`;
export const SelectedItem = styled.View`
height: 30px;
justify-content: center;
align-items: center;
height: ${(props) => props.itemHeight}px;
`;
const deviceWidth = Dimensions.get("window").width;
export default class ScrollPicker extends React.Component {
constructor() {
super();
this.onMomentumScrollBegin = this.onMomentumScrollBegin.bind(this);
this.onMomentumScrollEnd = this.onMomentumScrollEnd.bind(this);
this.onScrollBeginDrag = this.onScrollBeginDrag.bind(this);
this.onScrollEndDrag = this.onScrollEndDrag.bind(this);
this.state = {
selectedIndex: 1,
};
}
componentDidMount() {
if (typeof this.props.selectedIndex !== "undefined") {
this.scrollToIndex(this.props.selectedIndex);
}
}
componentWillUnmount() {
if (this.timer) {
clearTimeout(this.timer);
}
}
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.selectedIndex !== prevProps.selectedIndex) {
this.scrollToIndex(this.props.selectedIndex);
}
}
render() {
const { header, footer } = this.renderPlaceHolder();
return (
<Container
wrapperHeight={this.props.wrapperHeight}
wrapperWidth={this.props.wrapperWidth}
wrapperBackground={this.props.wrapperBackground}
>
<HighLightView
highlightColor={this.props.highlightColor}
highlightWidth={this.props.highlightWidth}
wrapperHeight={this.props.wrapperHeight}
itemHeight={this.props.itemHeight}
highlightBorderWidth={this.props.highlightBorderWidth}
/>
<ScrollView
ref={(sview) => {
this.sview = sview;
}}
bounces={true}
showsVerticalScrollIndicator={false}
onTouchStart={this.props.onTouchStart}
onMomentumScrollBegin={this.onMomentumScrollBegin}
onMomentumScrollEnd={this.onMomentumScrollEnd}
onScrollBeginDrag={this.onScrollBeginDrag}
onScrollEndDrag={this.onScrollEndDrag}
>
{header}
{this.props.dataSource.map(this.renderItem.bind(this))}
{footer}
</ScrollView>
</Container>
);
}
renderPlaceHolder() {
const height = (this.props.wrapperHeight - this.props.itemHeight) / 2;
const header = <View style={{ height, flex: 1 }}></View>;
const footer = <View style={{ height, flex: 1 }}></View>;
return { header, footer };
}
renderItem(data, index) {
const isSelected = index === this.state.selectedIndex;
const item = (
<Text
style={
isSelected ? this.props.activeItemTextStyle : this.props.itemTextStyle
}
>
{data}
</Text>
);
return (
<SelectedItem key={index} itemHeight={this.props.itemHeight}>
{item}
</SelectedItem>
);
}
scrollFix(e) {
let verticalY = 0;
const h = this.props.itemHeight;
if (e.nativeEvent.contentOffset) {
verticalY = e.nativeEvent.contentOffset.y;
}
const selectedIndex = Math.round(verticalY / h);
const verticalElem = selectedIndex * h;
if (verticalElem !== verticalY) {
// using scrollTo in ios, onMomentumScrollEnd will be invoked
if (Platform.OS === "ios") {
this.isScrollTo = true;
}
if (this.sview) {
this.sview.scrollTo({ y: verticalElem });
}
}
if (this.state.selectedIndex === selectedIndex) {
return;
}
this.setState({
selectedIndex,
});
// onValueChange
if (this.props.onValueChange) {
const selectedValue = this.props.dataSource[selectedIndex];
this.props.onValueChange(selectedValue, selectedIndex);
}
}
onScrollBeginDrag() {
this.dragStarted = true;
if (Platform.OS === "ios") {
this.isScrollTo = false;
}
if (this.timer) {
clearTimeout(this.timer);
}
}
onScrollEndDrag(e) {
this.props.onScrollEndDrag();
this.dragStarted = false;
// if not used, event will be garbaged
const element = {
nativeEvent: {
contentOffset: {
y: e.nativeEvent.contentOffset.y,
},
},
};
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(() => {
if (!this.momentumStarted && !this.dragStarted) {
this.scrollFix(element, "timeout");
}
}, 10);
}
onMomentumScrollBegin() {
this.momentumStarted = true;
if (this.timer) {
clearTimeout(this.timer);
}
}
onMomentumScrollEnd(e) {
this.props.onMomentumScrollEnd();
this.momentumStarted = false;
if (!this.isScrollTo && !this.momentumStarted && !this.dragStarted) {
this.scrollFix(e);
}
}
scrollToIndex(ind) {
this.setState({
selectedIndex: ind,
});
const y = this.props.itemHeight * ind;
setTimeout(() => {
if (this.sview) {
this.sview.scrollTo({ y });
}
}, 0);
}
}
ScrollPicker.propTypes = {
style: PropTypes.object,
dataSource: PropTypes.array,
selectedIndex: PropTypes.number,
onValueChange: PropTypes.func,
renderItem: PropTypes.func,
highlightColor: PropTypes.string,
itemHeight: PropTypes.number,
wrapperBackground: PropTypes.string,
wrapperWidth: PropTypes.number,
wrapperHeight: PropTypes.number,
highlightWidth: PropTypes.number,
highlightBorderWidth: PropTypes.number,
itemTextStyle: PropTypes.object,
activeItemTextStyle: PropTypes.object,
onMomentumScrollEnd: PropTypes.func,
onScrollEndDrag: PropTypes.func,
};
ScrollPicker.defaultProps = {
dataSource: [1, 2, 3],
itemHeight: 60,
wrapperBackground: "#fff",
wrapperHeight: 180,
wrapperWidth: 150,
highlightWidth: deviceWidth,
highlightBorderWidth: 2,
highlightColor: "#fff",
onMomentumScrollEnd: () => {},
onScrollEndDrag: () => {},
itemTextStyle: {
fontSize: Responsive.font(20),
lineHeight: 26,
textAlign: "center",
color: "#fff",
fontWeight: "bold",
opacity: 0.1,
},
activeItemTextStyle: {
fontSize: Responsive.font(20),
lineHeight: 26,
textAlign: "center",
color: "#fff",
fontWeight: "bold",
},
};
要在滚动上播放声音,你只需要使用一个名为react-native-sound的库 https://www.npmjs.com/package/react-native-sound
在这个库中有几个可用的操作,例如
当用户滚动页面时,您只需要从上面触发所需的操作, 为此,您可以简单地使用 ScrollView 的 props,例如
希望你能得到你想要的结果😊
我计划在 iOS 上使用此解决方案。但对于Android我放弃了https://github.com/quidone/react-native-wheel-picker-feedback/blob/main/ios/WheelPickerFeedback.mm