最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

通過React-Native實(shí)現(xiàn)自定義橫向滑動(dòng)進(jìn)度條的 ScrollView組件

 更新時(shí)間:2024年02月05日 11:36:09   作者:清風(fēng)不暖人  
開發(fā)一個(gè)首頁擺放菜單入口的ScrollView可滑動(dòng)組件,允許自定義橫向滑動(dòng)進(jìn)度條,且內(nèi)部渲染的菜單內(nèi)容支持自定義展示的行數(shù)和列數(shù),在內(nèi)容超出屏幕后,渲染順序?yàn)榭v向由上至下依次排列,對(duì)React Native橫向滑動(dòng)進(jìn)度條相關(guān)知識(shí)感興趣的朋友一起看看吧

概要

本篇文章概述了通過React-Native實(shí)現(xiàn)一個(gè)允許自定義橫向滑動(dòng)進(jìn)度條的ScrollView組件。

需求

開發(fā)一個(gè)首頁擺放菜單入口的ScrollView可滑動(dòng)組件(類似某淘首頁上的菜單效果),允許自定義橫向滑動(dòng)進(jìn)度條,且內(nèi)部渲染的菜單內(nèi)容支持自定義展示的行數(shù)和列數(shù),在內(nèi)容超出屏幕后,渲染順序?yàn)榭v向由上至下依次排列

Animated 動(dòng)畫

點(diǎn)此進(jìn)入學(xué)習(xí)

ScrollView 滑動(dòng)組件

點(diǎn)此進(jìn)入學(xué)習(xí)

自定義滑動(dòng)進(jìn)度條

確定參數(shù)

首先,讓我們確定一下自定義滑動(dòng)進(jìn)度條需要哪些參數(shù)來支持:

  • 初始位置時(shí),確定顯示進(jìn)度的條的寬度(barWidth)
  • 滑動(dòng)進(jìn)度,以此來確定上面這個(gè)條的位置現(xiàn)在應(yīng)該到哪里了(marLeftAnimated)

計(jì)算參數(shù)

1.想要確定顯示進(jìn)度的條的寬度(barWidth),那么必須先知道三個(gè)值:

  • ScrollView總寬度(containerStyle傳入)
  • 進(jìn)度條背景的寬度(indicatorBgStyle傳入)
  • ScrollView內(nèi)部?jī)?nèi)容總寬度(childWidth,通過onContentSizeChange方法測(cè)量)

然后我們就可以進(jìn)行如下計(jì)算,這樣得到的_barWidth就是顯示進(jìn)度的條的寬度(barWidth):

let _barWidth = (this.props.indicatorBgStyle.width * this.props.containerStyle.width) / this.state.childWidth;

2.想要確定顯示進(jìn)度的條的位置(marLeftAnimated),那么必須先知道兩個(gè)值:

  • ScrollView可滑動(dòng)距離(scrollDistance)
  • 進(jìn)度部分可滑動(dòng)距離(leftDistance)

然后我們就可以進(jìn)行如下定義,這樣得到的marLeftAnimated,輸出值即為進(jìn)度條的距左距離:

let scrollDistance = this.state.childWidth - this.props.containerStyle.width
	...
    //顯示滑動(dòng)進(jìn)度部分的距左距離
    let leftDistance = this.props.indicatorBgStyle.width - _barWidth;
    const scrollOffset = this.state.scrollOffset
    this.marLeftAnimated = scrollOffset.interpolate({
      inputRange: [0, scrollDistance],  //輸入值區(qū)間為內(nèi)容可滑動(dòng)距離
      outputRange: [0, leftDistance],  //映射輸出區(qū)間為進(jìn)度部分可改變距離
      extrapolate: 'clamp',  //鉗制輸出值
      useNativeDriver: true,
    })

滑動(dòng)進(jìn)度條的實(shí)現(xiàn)

通過Animated.View,定義絕對(duì)位置,將兩個(gè)條在Z軸上下重疊一起。

<View style={[{alignSelf:'center'},this.props.indicatorBgStyle]}>
      <Animated.View
        style={[this.props.indicatorStyle,{
          position: 'absolute',
          width: this.state.barWidth,
          top: 0,
          left: this.marLeftAnimated,
        }]}
      />
    </View>

之后就通過onSroll事件獲取滑動(dòng)偏移量,然后通過偏移量改變動(dòng)畫的值,這里我就不多說了,不明白的可以看我上一篇文章。

首頁定制菜單

確定參數(shù)

首先,讓我們確定一下實(shí)現(xiàn)首頁定制菜單需要哪些參數(shù)來支持:

  • 列數(shù)量(columnLimit)
  • 行數(shù)量(rowLimit)

渲染方式

根據(jù)行列數(shù)量,決定每屏的菜單總數(shù)。根據(jù)行數(shù)量,決定渲染結(jié)果數(shù)組里有幾組,一行就是一組。

let optionTotalArr = [];  //存放所有option樣式的數(shù)組
	//根據(jù)行數(shù),聲明用于存放每一行渲染內(nèi)容的數(shù)組
	for( let i = 0; i < rowLimit; i++ ) optionTotalArr.push([])

1.沒超出屏幕時(shí),確定渲染行的方式如下:

if(index < columnLimit * rowLimit){
		//沒超出一屏數(shù)量時(shí),根據(jù)列數(shù)更新行標(biāo)識(shí)
		rowIndex = parseInt(index / columnLimit)
	}

2.超出屏幕時(shí),確定渲染行的方式如下:

//當(dāng)超出一屏數(shù)量時(shí),根據(jù)行數(shù)更新行標(biāo)識(shí)
	rowIndex = index % rowLimit;

遍歷輸出

根據(jù)行數(shù),遍歷存放計(jì)算后的行內(nèi)容數(shù)組。

optionTotalArr[rowIndex].push(
        <TouchableOpacity 
          key={index} 
          activeOpacity={0.7}
          style={[styles.list_item,{width:size}]} 
          onPress={()=>alert(item.name)}
        >
          <View style={{width:size-20,backgroundColor:'#FFCC00',alignItems:'center',justifyContent:'center'}}>
           <Text style={{ fontSize:18, color:'#333',marginVertical:20}}>{item.name}</Text>
          </View>
		</TouchableOpacity>
	)

效果圖

在這里插入圖片描述

源碼

IndicatorScrollView.js

import React, { PureComponent } from 'react';
import {
  StyleSheet,
  View,
  ScrollView,
  Animated,
  Dimensions,
} from 'react-native';
import PropTypes from 'prop-types';
const { width, height } = Dimensions.get('window');
export default class IndicatorScrollView extends PureComponent {
  static propTypes = {
    //最外層樣式(包含ScrollView及滑動(dòng)進(jìn)度條的全部區(qū)域
    containerStyle: PropTypes.oneOfType([  
      PropTypes.object,
      PropTypes.array,
    ]),
    //ScrollView的樣式
    style: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.array,
    ]),
    //滑動(dòng)進(jìn)度條底部樣式
    indicatorBgStyle: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.array,
    ]),
    //滑動(dòng)進(jìn)度條樣式
    indicatorStyle: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.array,
    ]),
  }
  static defaultProps = {
    containerStyle: { width: width },
    style: {},
    indicatorBgStyle:{
      width: 200,
      height: 20, 
      backgroundColor: '#ddd'
    },
    indicatorStyle:{
      height:20,
      backgroundColor:'#000'
    },
  }
  constructor(props) {
    super(props);
    this.state = {
      //滑動(dòng)偏移量
      scrollOffset: new Animated.Value(0),
      //ScrollView子布局寬度
      childWidth: this.props.containerStyle.width,
      //顯示滑動(dòng)進(jìn)度部分條的長度
      barWidth: props.indicatorBgStyle.width / 2,
    };
  }
  UNSAFE_componentWillMount() {
    this.animatedEvent = Animated.event(
      [{
          nativeEvent: {
            contentOffset: { x: this.state.scrollOffset }
          }
      }]
    )
  }
  componentDidUpdate(prevProps, prevState) {
    //內(nèi)容可滑動(dòng)距離
    let scrollDistance = this.state.childWidth - this.props.containerStyle.width
    if( scrollDistance > 0 && prevState.childWidth != this.state.childWidth){
      let _barWidth = (this.props.indicatorBgStyle.width * this.props.containerStyle.width) / this.state.childWidth;
      this.setState({
        barWidth: _barWidth,
      })
      //顯示滑動(dòng)進(jìn)度部分的距左距離
      let leftDistance = this.props.indicatorBgStyle.width - _barWidth;
      const scrollOffset = this.state.scrollOffset
      this.marLeftAnimated = scrollOffset.interpolate({
        inputRange: [0, scrollDistance],  //輸入值區(qū)間為內(nèi)容可滑動(dòng)距離
        outputRange: [0, leftDistance],  //映射輸出區(qū)間為進(jìn)度部分可改變距離
        extrapolate: 'clamp',  //鉗制輸出值
        useNativeDriver: true,
      })
    }
  }
  render() {
    return (
      <View style={[styles.container,this.props.containerStyle]}>
        <ScrollView
          style={this.props.style}
          horizontal={true}  //橫向
          alwaysBounceVertical={false}
          alwaysBounceHorizontal={false}
          showsHorizontalScrollIndicator={false}  //自定義滑動(dòng)進(jìn)度條,所以這里設(shè)置不顯示
          scrollEventThrottle={0.1}  //滑動(dòng)監(jiān)聽調(diào)用頻率
          onScroll={this.animatedEvent}  //滑動(dòng)監(jiān)聽事件,用來映射動(dòng)畫值
          scrollEnabled={ this.state.childWidth - this.props.containerStyle.width>0 ? true : false }
          onContentSizeChange={(width,height)=>{
            if(this.state.childWidth != width){
              this.setState({ childWidth: width })
            }
          }}
        >
          {this.props.children??      
            <View 
              style={{ flexDirection: 'row', height: 200 }}
            >
              <View style={{ width: 300, backgroundColor: 'red' }} />
              <View style={{ width: 300, backgroundColor: 'yellow' }} />
              <View style={{ width: 300, backgroundColor: 'blue' }} />
            </View>
          }
        </ScrollView>
        {this.state.childWidth - this.props.containerStyle.width>0?
          <View style={[{alignSelf:'center'},this.props.indicatorBgStyle]}>
            <Animated.View
              style={[this.props.indicatorStyle,{
                position: 'absolute',
                width: this.state.barWidth,
                top: 0,
                left: this.marLeftAnimated,
              }]}
            />
          </View>:null
        }
      </View>
    );
  };
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
});

Scroll.js

import React, { Component } from 'react';
import {
  StyleSheet, 
  View,
  Dimensions,
  TouchableOpacity,
  Text,
} from 'react-native';
import IndicatorScrollView from '../../component/scroll/IndicatorScrollView';
const { width, height } = Dimensions.get('window');
const columnLimit = 4;  //option列數(shù)量
const rowLimit = 2;  //option行數(shù)量
// 編寫UI組件
export default class Scroll extends Component {
  constructor(props) {
    super(props);
    this.state = {
    };
    this.itemArr = [
      {
        name: '1'
      },
      {
        name: '2'
      },
      {
        name: '3'
      },
      {
        name: '4'
      },
      {
        name: '5'
      },
      {
        name: '6'
      },
      {
        name: '7'
      },
      {
        name: '8'
      },
      {
        name: '9'
      },
      {
        name: '10'
      },
      {
        name: '11'
      },
      {
        name: '12'
      }
    ]
  }
	renderOption(){
		let size = (width-20)/columnLimit; //每個(gè)option的寬度
		let optionTotalArr = [];  //存放所有option樣式的數(shù)組
		//根據(jù)行數(shù),聲明用于存放每一行渲染內(nèi)容的數(shù)組
		for( let i = 0; i < rowLimit; i++ ) optionTotalArr.push([])
		this.itemArr.map((item,index) => {
			let rowIndex = 0;  //行標(biāo)識(shí)
			if(index < columnLimit * rowLimit){
				//沒超出一屏數(shù)量時(shí),根據(jù)列數(shù)更新行標(biāo)識(shí)
				rowIndex = parseInt(index / columnLimit)
			}else{
				//當(dāng)超出一屏數(shù)量時(shí),根據(jù)行數(shù)更新行標(biāo)識(shí)
				rowIndex = index % rowLimit;
			}
			optionTotalArr[rowIndex].push(
        <TouchableOpacity 
          key={index} 
          activeOpacity={0.7}
          style={[styles.list_item,{width:size}]} 
          onPress={()=>alert(item.name)}
        >
          <View style={{width:size-20,backgroundColor:'#FFCC00',alignItems:'center',justifyContent:'center'}}>
           <Text style={{ fontSize:18, color:'#333',marginVertical:20}}>{item.name}</Text>
          </View>
				</TouchableOpacity>
			)
		})
    return(
			<View
				style={{flex:1,justifyContent:'center',paddingHorizontal:10}}
		  >
				{
					optionTotalArr.map((item,index)=>{
						return <View key={index} style={{flexDirection:'row'}}>{item}</View>
					})
				}
			</View>
    )
	}
  render() {
    return (
      <View style={styles.container}>
        <View style={{flex:1}}/>
        <IndicatorScrollView 
          containerStyle={styles.list_style}
          indicatorBgStyle={{marginBottom:10,borderRadius:2,width:40,height:4,backgroundColor:'#BFBFBF'}}
          indicatorStyle={{borderRadius:2,height:4,backgroundColor:'#CC0000'}}
        >
          {this.renderOption()}
        </IndicatorScrollView>
        <View style={{flex:1}}/>
      </View >
    );
  };
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  list_style:{
		flex: 1,
    width: width,
    backgroundColor:'#6699FF'
  },
  list_item:{
    marginVertical:20,
		justifyContent:'center',
    alignItems:'center',
	},
});

注:本文為作者原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處。

到此這篇關(guān)于通過React-Native實(shí)現(xiàn)自定義橫向滑動(dòng)進(jìn)度條的 ScrollView組件的文章就介紹到這了,更多相關(guān)React Native橫向滑動(dòng)進(jìn)度條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解如何使用React和MUI創(chuàng)建多選Checkbox樹組件

    詳解如何使用React和MUI創(chuàng)建多選Checkbox樹組件

    這篇文章主要為大家詳細(xì)介紹了如何使用 React 和 MUI(Material-UI)庫來創(chuàng)建一個(gè)多選 Checkbox 樹組件,該組件可以用于展示樹形結(jié)構(gòu)的數(shù)據(jù),并允許用戶選擇多個(gè)節(jié)點(diǎn),感興趣的可以了解下
    2024-01-01
  • webpack3+React 的配置全解

    webpack3+React 的配置全解

    本篇文章主要介紹了webpack3+React 的配置全解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 深入理解React虛擬Dom

    深入理解React虛擬Dom

    虛擬DOM是一種輕量級(jí)的JavaScript對(duì)象,用于描述真實(shí)網(wǎng)頁的DOM結(jié)構(gòu)和屬性,存在于內(nèi)存中,本文就來詳細(xì)的介紹React虛擬Dom的實(shí)現(xiàn),感興趣的可以了解一下
    2026-01-01
  • React經(jīng)典面試題之倒計(jì)時(shí)組件詳解

    React經(jīng)典面試題之倒計(jì)時(shí)組件詳解

    這些天也都在面試,面試的內(nèi)容也大多千篇一律,無外乎vue、react這些框架的一些原理,和使用方法,但是也遇到些有趣的題目,這篇文章主要給大家介紹了關(guān)于React經(jīng)典面試題之倒計(jì)時(shí)組件的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • React Native 截屏組件的示例代碼

    React Native 截屏組件的示例代碼

    本篇文章主要介紹了React Native 截屏組件的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • react腳手架如何配置less和ant按需加載的方法步驟

    react腳手架如何配置less和ant按需加載的方法步驟

    這篇文章主要介紹了react腳手架如何配置less和ant按需加載的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-11-11
  • react中的虛擬dom和diff算法詳解

    react中的虛擬dom和diff算法詳解

    這篇文章主要介紹了react中的虛擬dom和diff算法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 詳解如何在React中優(yōu)雅的使用addEventListener

    詳解如何在React中優(yōu)雅的使用addEventListener

    這篇文章主要為大家詳細(xì)介紹了如何在React中優(yōu)雅的使用addEventListener,文中的示例代碼簡(jiǎn)潔易懂,對(duì)大家學(xué)習(xí)React有一定的幫助,需要的可以參考一下
    2023-01-01
  • 回顧Javascript React基礎(chǔ)

    回顧Javascript React基礎(chǔ)

    這篇文章主要介紹了Javascript React基礎(chǔ),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的同學(xué)可以參考一下
    2019-06-06
  • React自定義tab組合組件的實(shí)例詳解

    React自定義tab組合組件的實(shí)例詳解

    本文介紹了如何利用React的cloneElementAPI實(shí)現(xiàn)自定義Tab組合組件,通過將TabItem作為Tab的子組件,使用React.cloneElement方法傳遞props和事件處理函數(shù),實(shí)現(xiàn)了父子組件狀態(tài)聯(lián)動(dòng),本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2026-02-02

最新評(píng)論

西城区| 嘉兴市| 望江县| 南丹县| 鱼台县| 巴彦淖尔市| 安远县| 噶尔县| 中山市| 大邑县| 肇庆市| 芮城县| 定远县| 响水县| 绍兴县| 恩平市| 高青县| 达孜县| 华亭县| 定州市| 兰坪| 张北县| 乌兰浩特市| 镇康县| 峨边| 镇巴县| 沂水县| 舞阳县| 永济市| 延川县| 奉化市| 商南县| 韶山市| 图木舒克市| 唐山市| 通许县| 定州市| 浏阳市| 天镇县| 峨眉山市| 开鲁县|