react native滑动选择器@quidone/react-native-wheel-picker

react native滑动选择器@quidone/react-native-wheel-picker

目录

一、简介

官方案例

主要特点

二、基础用法

安装

简单示例

WheelPicker 组件属性

三、代码注释与实际效果展示(便于理解)

便于理解的代码(含注释)

效果展示

* 四、官方给出的其他扩展性功能

触觉反馈功能

扩展API

钩子函数

高阶组件

额外属性

五、原创项目效果展示


一、简介

React Native Wheel Picker 是一个为 iOS 和 Android 平台设计的灵活选择器组件,采用 MIT 开源协议发布。它完全基于 JavaScript 实现,不需要原生代码支持。 官方文档

官方案例

主要特点

  • 纯 JavaScript 实现(无需原生代码)、统一的 API 接口、使用原生动画效果、支持触觉反馈、支持虚拟化渲染、兼容 Expo (Snack)、深度自定义能力、使用 TypeScript 编写

二、基础用法

官方的 Expo 在线Demo案例:Quidone React Native Wheel Picker - Snack

安装

yarn add @quidone/react-native-wheel-picker

简单示例

示例效果图如上面的 Simple Picker 所示。

import React, {useState} from 'react';
import WheelPicker from '@quidone/react-native-wheel-picker';

const data = [...Array(100).keys()].map((index) => ({
  value: index,
  label: index.toString(),
}))

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <WheelPicker
      data={data}
      value={value}
      onValueChanged={({item: {value}}) => setValue(value)}
      enableScrollByTapOnItem={true}
    />
  );
};

export default App;

WheelPicker 组件属性

属性名 描述 类型 默认值
data 选择器的数据项数组 Array<Item> 必填
value 当前选中的值 any -
itemHeight 中间项的显示高度(单位:像素) number 40
visibleItemCount 显示的项目数量(奇数:1, 3, 5...) number 5
width 选择器宽度(数字或百分比字符串) number | string '100%'
readOnly 是否只读模式(禁止交互) boolean false
enableScrollByTapOnItem 是否允许点击项目触发滚动 boolean false
testID 用于端到端测试的标识符 string -
onValueChanging 值正在变化时触发的回调函数 () => void -
onValueChanged 值改变后触发的回调(滚轮停止且无触摸时) (event) => void -
keyExtractor 从数据项提取唯一key的函数 (item: Item) => string 使用value
renderItem 自定义渲染项目内容的函数 (props) => ReactNode 显示label
renderItemContainer 自定义渲染项目容器的函数(包含动画效果) (props) => ReactNode 默认容器
renderOverlay 在选择器上方渲染覆盖层的函数 () => ReactNode null
renderList 高级用法:自定义渲染整个列表(不建议使用) (props) => ReactNode 默认列表
style 根容器的样式 StyleProp<ViewStyle> -
itemTextStyle 项目文本的样式 StyleProp<TextStyle> -
overlayItemStyle 中间覆盖元素(高亮区域)的样式 StyleProp<ViewStyle> -
contentContainerStyle 包裹所有子视图的容器样式 StyleProp<ViewStyle> -
scrollEventThrottle 滚动事件节流频率(单位:毫秒) number 16

其中,Item的类型如下:

type Item = {
  value: any;
  label: string;
  // 可包含其他自定义字段
};

三、代码注释与实际效果展示(便于理解)

便于理解的代码(含注释)

import { Text, View } from 'tamagui'
import { SafeAreaView } from 'react-native-safe-area-context';
import { useState, memo, useMemo } from 'react';
import WheelPicker, {
  usePickerItemHeight,
  useScrollContentOffset,
} from '@quidone/react-native-wheel-picker';
import { Animated, StyleSheet } from 'react-native';

// data = [
//   { value: 0, label: 'label0' }, 
//   { value: 10, label: 'label1' },
//   ...
// ]
const data = [...Array(100).keys()].map((index) => ({
  value: index * 10, // 0, 10, 20, 30, ...
  // 如果不指定renderItem属性,则默认渲染此label属性,label的值可以是jsx
  label: 'label' + index, // 'label0', 'label1', 'label2', ...
}))

export default function APP() {
  const [value, setValue] = useState(210); // 210: 指定首次渲染时Picker选中的item

  return (
    <SafeAreaView edges={['top']} style={{ backgroundColor: 'lightgray', flex: 1 }}>

      <View borderWidth={1} borderColor='blue' bg='white' mx={20} items={'center'}>
        <WheelPicker
          width={300}
          style={{
            borderWidth: 1,
            borderColor: 'red',
            marginTop: 30,
            overflow: 'hidden', // 必须设置overflow: 'hidden',否则实际区域下方仍然能触发点击效果
          }}
          data={data}
          value={value}
          visibleItemCount={5}

          // itemHeight: “当前选中的区域的阴影”的高度 或者说 “实际每一项的高度”
          // tip:选中区域的样式由renderOverlay属性决定(默认为阴影)
          itemHeight={80}

          onValueChanged={({ item, index }) => {
            // item为data数组中的每一个对象
            // index为data数组中的每一个对象的索引
            setValue(item.value);
          }}

          // 渲染每一项,如果无renderItem,则默认渲染item的label属性(item.label)
          renderItem={({ item, index, itemTextStyle }) => {
            // item为data数组中的每一个对象
            // index为data数组中的每一个对象的索引
            // itemTextStyles为WheelPicker组件的itemTextStyle属性,感觉很鸡肋,可有可无
            return (
              // 最外层的height最好与WheelPicker组件的itemHeight属性值一致,这里故意不一致,看一下效果
              <View borderWidth={index % 2 ? 1 : 0} borderColor='green' height={60}>
                <Text>
                  {item.label}-{item.value}-{`index:${index}`}
                </Text>
                <Text style={itemTextStyle}>
                  {`${JSON.stringify(itemTextStyle)}`}
                </Text>
              </View>
            )
          }}
          itemTextStyle={{ marginLeft: 20 }} // 一点用处没有,我没理解这个属性有什么实质性用处,可以直接忽略这个属性

          // renderItemContainer的作用是:给item容器在滑动过程中设置动画,如果用他的默认动画,则不需要这个属性
          // renderItemContainer={renderItemContainer}

          // 渲染选中区域的样式(默认为阴影)
          renderOverlay={({ itemHeight, overlayItemStyle }) => {
            return (
              <View pointerEvents={'none'} // pointerEvents={'none'} 必须有,否则会影响item的滑动
                justify='center' style={StyleSheet.absoluteFillObject}
              // bg={'#6982'}
              >
                <View height={itemHeight} style={overlayItemStyle} bg={'#0003'} />
              </View>
            );
          }}

          enableScrollByTapOnItem={true} //允许点击item后直接滚动至该item
        />
      </View>

    </SafeAreaView >
  )
}


// renderItemContainer的作用是:给item容器在滑动过程中设置动画,如果用他的默认动画,则不需要这个属性
// 下面是官方的一个示例动画
const renderItemContainer = ({ renderItem, item, index, itemTextStyle, faces }) => {
  const offset = useScrollContentOffset();
  const height = usePickerItemHeight();

  const inputRange = useMemo(
    () => faces.map((f) => height * (index + f.index)),
    [faces, height, index],
  );

  const { opacity, translateY, translateX } = useMemo(
    () => ({
      opacity: offset.interpolate({
        inputRange: inputRange,
        outputRange: faces.map((x) => x.opacity),
        extrapolate: 'clamp',
      }),
      translateY: offset.interpolate({
        inputRange: inputRange,
        outputRange: faces.map((x) => x.offsetY),
        extrapolate: 'extend',
      }),
      translateX: offset.interpolate({
        inputRange: [
          height * (index - 1),
          height * index,
          height * (index + 1),
        ],
        outputRange: [-10, 0, -10],
        extrapolate: 'extend',
      }),
    }),
    [faces, height, index, inputRange, offset],
  );

  return (
    <Animated.View
      style={[{ height, opacity, transform: [{ translateY }, { translateX }] }]}
    >
      {renderItem({ item, index, itemTextStyle })}
    </Animated.View>
  );
}

效果展示

上述代码的实际效果如下图所示,所有的边框颜色都是为了便于理解。

* 四、官方给出的其他扩展性功能

其他扩展性功能,下面是官方提供的文档内容,没有实际需求的可以忽略。

触觉反馈功能

使用 @quidone/react-native-wheel-picker-feedback 配合 onValueChanging 事件来实现触觉反馈效果。

import WheelPickerFeedback from '@quidone/react-native-wheel-picker-feedback';

const App = () => {
  return (
    <WheelPicker
      onValueChanging={() => {
        WheelPickerFeedback.triggerSoundAndImpact();
      }}
    />
  );
};

扩展API

钩子函数

API名称 描述 返回类型
usePickerItemHeight 获取通过props传递的项目高度(响应式) number
useScrollContentOffset 获取ScrollView的实时滚动偏移量(Animated.Value) Animated.Value

高阶组件

API名称 描述 增强属性
withVirtualized 为WheelPicker添加虚拟滚动能力 initialNumToRender等FlatList属性
import WheelPicker, {withVirtualized} from '@quidone/react-native-wheel-picker';

const VirtualizedWheelPicker = withVirtualized(WheelPicker);

额外属性

  • initialNumToRender? (默认 = Math.ceil(visibleItemCount / 2))

  • maxToRenderPerBatch? (默认 = Math.ceil(visibleItemCount / 2))

  • windowSize? - 原始属性

  • updateCellsBatchingPeriod? (默认 = 10) - 原始属性

五、原创项目效果展示

创作不易,如果有帮助到你,麻烦点个赞吧~~

转载请说明出处内容投诉
CSS教程网 » react native滑动选择器@quidone/react-native-wheel-picker

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买