当前位置: 首页 > news >正文

ReactNative【实战】轮播图(含组件封装 ImageSlider)

最终效果

在这里插入图片描述
点击可全屏预览图片

在这里插入图片描述

核心组件 ImageSlider

在这里插入图片描述

components/slidePager/index.tsx

import AntDesign from "@expo/vector-icons/AntDesign";
import React, { useCallback, useEffect, useRef, useState } from "react";
import {Animated,Dimensions,Image,ImageSourcePropType,LayoutAnimation,Modal,Platform,StyleSheet,TouchableOpacity,View,
} from "react-native";
import { PropsTypes } from "./PropsTypes";
import { Indicator } from "./indicator";
import { styles } from "./style";
const { width } = Dimensions.get("screen");
const Os = Platform.OS;
export type DataType = { img: ImageSourcePropType };
export const ImageSlider = ({data = [],previewImageContainerStyle = {},previewImageStyle = {},caroselImageStyle = {},caroselImageContainerStyle = {},timer = 2000,autoPlay = false,showIndicator = true,activeIndicatorStyle = {},inActiveIndicatorStyle = {},indicatorContainerStyle = {},onItemChanged = (itemData) => {},localImg = false,onClick = (item: DataType, index: number) => {},preview = true,children,blurRadius = 50,
}: PropsTypes) => {const scrollX = React.useRef(new Animated.Value(0)).current;const imageW = width * 0.7;const [selectedIndex, setSelectedIndex] = useState(0);const [imageViewer, setImageViewer] = useState(false);const [currentIndex, setCurrentIndex] = useState(0);const slider = useRef(null);const timerRef = useRef<any>(null);const onViewRef = React.useRef(({ viewableItems }: any) => {// Use viewable items in state or as intendedif (viewableItems.length > 0) {let index = viewableItems[0].index;onItemChanged(viewableItems[0].item);setSelectedIndex(index);}});const viewConfigRef = React.useRef({ viewAreaCoveragePercentThreshold: 50 });useEffect(() => {if (autoPlay) {if (data.length > 0) startAutoPlay(imageViewer ? true : false);}}, []);useEffect(() => {if (!imageViewer) {if (autoPlay) {if (data.length > 0) startAutoPlay(imageViewer ? true : false);}} else {clearTimeout(timerRef?.current);}}, [currentIndex, imageViewer]);const switchViewer = useCallback(() => {LayoutAnimation.easeInEaseOut();setImageViewer(!imageViewer);}, [imageViewer]);const changeSliderListIndex = () => {if (slider.current) {if (currentIndex == data.length - 1) {setCurrentIndex(0);// @ts-ignoreslider.current.scrollToIndex({index: currentIndex,animated: true,});} else {setCurrentIndex(currentIndex + 1);// @ts-ignoreslider.current.scrollToIndex({index: currentIndex,animated: true,});}}};const startAutoPlay = (isViewer: boolean) => {if (!imageViewer) {((viewer) => {let viewBool = viewer;timerRef.current = setTimeout(() => {if (!viewBool) {changeSliderListIndex();}}, timer);})(isViewer);}};const previewImage = () => {return (<Modalvisible={imageViewer}onDismiss={switchViewer}onRequestClose={switchViewer}><View style={StyleSheet.absoluteFillObject}>{data.map((val, ind) => {const inputRange = [(ind - 1) * width,ind * width,(ind + 1) * width,];const opacity = scrollX.interpolate({inputRange,outputRange: [0, 1, 0],});return (<Animated.Imagekey={`image-${ind}`}// @ts-ignoresource={localImg ? val.img : { uri: val.img }}style={[StyleSheet.absoluteFillObject, { opacity }]}blurRadius={blurRadius}/>);})}</View><Animated.FlatListdata={data}keyExtractor={(_, index) => index.toString()}onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }],{ useNativeDriver: true })}horizontalpagingEnabledinitialScrollIndex={selectedIndex}pinchGestureEnabled={true}onScrollToIndexFailed={(info) => {const wait = new Promise((resolve) => setTimeout(resolve, 500));wait.then(() => {// @ts-ignoreslider.current?.scrollToIndex({index: info.index,animated: true,});});}}showsHorizontalScrollIndicator={false}renderItem={({ item, index }) => {return (<Viewstyle={[styles.previewImageContainerStyle,previewImageContainerStyle,]}><Image// @ts-ignoresource={localImg ? item.img : { uri: item.img }}style={[styles.previewImageStyle, previewImageStyle]}/><TouchableOpacityonPress={() => {setSelectedIndex(index);switchViewer();}}style={{position: "absolute",top: Os == "ios" ? 30 : 12,left: 12,}}><AntDesign name="close" size={24} color="white" /></TouchableOpacity></View>);}}/></Modal>);};return (<View>{imageViewer && previewImage()}<Animated.FlatListref={slider}data={data}keyExtractor={(_, index) => index.toString()}onScroll={Animated.event([{ nativeEvent: { contentOffset: { x: scrollX } } }],{ useNativeDriver: true })}horizontalpagingEnabledsnapToInterval={width}decelerationRate="fast"pinchGestureEnabled={true}showsHorizontalScrollIndicator={false}onViewableItemsChanged={onViewRef.current}viewabilityConfig={viewConfigRef.current}initialScrollIndex={selectedIndex}onScrollToIndexFailed={(info) => {const wait = new Promise((resolve) => setTimeout(resolve, 500));wait.then(() => {// flatList.current?.scrollToIndex({ index: info.index, animated: true });});}}renderItem={({ item, index }) => {return (<View style={[caroselImageContainerStyle]}><><TouchableOpacityactiveOpacity={0.9}onPress={() => {if (!preview) {onClick(item, index);} else {setSelectedIndex(index);switchViewer();}}}><Image// @ts-ignoresource={localImg ? item.img : { uri: item.img }}style={[styles.caroselImageStyle, caroselImageStyle]}/></TouchableOpacity>{children}</></View>);}}/><Viewstyle={{width: "100%",position: "absolute",alignSelf: "center",backgroundColor: "red",bottom: 20,}}>{showIndicator && (<Indicatordata={data}currenIndex={selectedIndex}indicatorContainerStyle={indicatorContainerStyle}activeIndicatorStyle={activeIndicatorStyle}inActiveIndicatorStyle={inActiveIndicatorStyle}/>)}</View></View>);
};

components/slidePager/indicator.tsx

import React from 'react';
import {StyleSheet, View, ViewStyle} from 'react-native';
import {DataType} from '.';export const Indicator = ({data = [],currenIndex = 0,activeIndicatorStyle = {},inActiveIndicatorStyle = {},indicatorContainerStyle = {},
}: {data: DataType[];currenIndex: number;activeIndicatorStyle: ViewStyle;inActiveIndicatorStyle: ViewStyle;indicatorContainerStyle: ViewStyle;
}) => {return (<View style={[styles.main, indicatorContainerStyle]}>{data.map((value, index) => {if (index == currenIndex) {return (<Viewkey={index}style={[styles.activeIndicatorStyle,activeIndicatorStyle,]}/>);} else {return (<Viewkey={index}style={[styles.inActiveIndicatorStyle,inActiveIndicatorStyle,]}/>);}})}</View>);
};const styles = StyleSheet.create({main: {flexDirection: 'row',alignItems: 'center',alignSelf: 'center',justifyContent: 'center',position: 'absolute',},activeIndicatorStyle: {height: 5,width: 20,borderRadius: 100,backgroundColor: '#62b5f0',margin: 5,},inActiveIndicatorStyle: {height: 10,width: 10,borderRadius: 100,backgroundColor: 'rgb(223, 231, 245)',margin: 5,},
});

components/slidePager/PropsTypes.ts

import { ReactNode } from "react";
import { ImageSourcePropType, ImageStyle, ViewStyle } from 'react-native';export interface DataType { img: ImageSourcePropType }export interface PropsTypes  {/*** Set array of images path- these paths can contain http url link or local images path using require('./pathOfImage')*/data: DataType[];/*** its define whats type image urls you provide if its true it means you provide local images path* @default: false*/localImg?: boolean;/*** if its false its not show preview now its click listner of onClick function props* @default: false*/preview?: boolean/*** if its true its shows a header on slider* @default false*/showHeader?: boolean;/*** for displaying right component in header* @default null*/children?: React.ReactNode | React.ReactNode[];/*** for displaying right component in header* @default null*/headerRightComponent?: React.ReactNode;/*** for displaying left component in header* @default null*/headerLeftComponent?: React.ReactNode;/*** for displaying center component in header* @default null*/headerCenterComponent?: ReactNode;/*** for change style of header* @default {}*/headerStyle?: ViewStyle;/*** for change style of previewImageContainer* @default {}*/previewImageContainerStyle?: ViewStyle;/*** for change style of previewImage* @default {}*/previewImageStyle?: ImageStyle;/*** for change style of caroselImageContainerStyle* @default {}*/caroselImageContainerStyle?: ViewStyle;/*** for change style of caroselImageStyle* @default {}*/caroselImageStyle?: ImageStyle;/*** for auto scrolling* @default false*/autoPlay?: boolean;/*** timeinterval for changing slider* @default 2000*/timer?: number;/*** for Showing indicator* @default false*/showIndicator?: boolean;/*** for change style of activeIndicator* @default {}*/activeIndicatorStyle?: ViewStyle;/*** for change style of inActiveIndicatorStyle* @default {}*/inActiveIndicatorStyle?: ViewStyle;/*** for change style of indicatorContainerStyle* @default {}*/indicatorContainerStyle?: ViewStyle;/*** when item changed its give item data in parameter* @default (itemData) => {}*/onItemChanged?: (itemData: DataType) => void;/*** when click on any item its give item data in parameter and when onClick Present so slider not show IMAGE PREVIEW on Click* @default (item, index) => {},*/onClick?: (item: DataType, index: number) => void;/*** Image Preview cross icon color* @default #000*/closeIconColor?: string;/*** Image Preview Background Blur Radius* @default 50*/blurRadius?: number;
}

components/slidePager/style.tsx

import {StyleSheet, Dimensions} from 'react-native';const { width, height } = Dimensions.get('screen');
export const styles = StyleSheet.create({caroselImageStyle: {width: width,resizeMode: 'contain',height: 300,},previewImageContainerStyle: {width,justifyContent: 'center',alignItems: 'center',},previewImageStyle: {width: width - 32,resizeMode: 'contain',height: height - 72,},
});

页面使用

import { ImageSlider } from "@/components/slidePager";
        <ImageSliderdata={data}autoPlay={false}closeIconColor="white"caroselImageStyle={{ height }}indicatorContainerStyle={{ bottom: -40 }}activeIndicatorStyle={styles.activeDot}inActiveIndicatorStyle={styles.inActiveDot}/>
    const data: any[] = detail.images.map((i) => {return { img: i };});
    images: ["https://img0.baidu.com/it/u=1168912228,741609775&fm=253&fmt=auto&app=120&f=JPEG?w=1422&h=800","https://gips3.baidu.com/it/u=1014935733,598223672&fm=3074&app=3074&f=PNG?w=1440&h=2560","https://img0.baidu.com/it/u=2779316202,721068756&fm=253&app=120&f=JPEG?w=1422&h=800",],

高度随第一张图片动态变化

const [height, setHeight] = useState<number>(300);
  useEffect(() => {if (!detail?.images) {return;}const firstImg = detail?.images[0];Image.getSize(firstImg, (width: number, height: number) => {const showHeight = (SCREEN_WIDTH * height) / width;setHeight(showHeight);});// eslint-disable-next-line react-hooks/exhaustive-deps}, []);

指示点的样式

 activeDot: {width: 6,height: 6,backgroundColor: "#ff2442",borderRadius: 3,},inActiveDot: {width: 6,height: 6,backgroundColor: "#c0c0c0",borderRadius: 3,},
http://www.lryc.cn/news/583074.html

相关文章:

  • 洛谷P1044 栈(学习向)
  • react16-react19都更新哪些内容?
  • clickhouse 各个引擎适用的场景
  • 【TCP/IP】2. 计算机网络与因特网体系结构
  • 手机文件夹隐藏工具,一键保护隐私
  • 数据库性能优化指南:解决ORDER BY导致的查询性能问题( SQL Server )
  • Dify 文本语意识别与自动补全工作流
  • MyBatisPlus-03-扩展功能
  • C#基础篇(11)泛型类与泛型方法详解
  • 1068.产品销售分析Ⅰ
  • huggingface 笔记: Trainer
  • 打造自己的组件库(二)CSS工程化方案
  • 跨服务sqlplus连接oracle数据库
  • 54页|PPT|新型数字政府综合解决方案:“一网 一云 一中台 N应用”平台体系 及“安全+运营”服务体系
  • 人工智能的基石:TensorFlow与PyTorch在图像识别和NLP中的应用
  • 影石(insta360)X4运动相机视频删除的恢复方法
  • 【视频观看系统】- 需求分析
  • 【DB2】load报错SQL3501W、SQL3109N、SQL2036N
  • Tensorflow的安装记录
  • django 一个表中包括id和parentid,如何通过parentid找到全部父爷id
  • react+ts 移动端页面分页,触底加载下一页
  • 板凳-------Mysql cookbook学习 (十一--------6)
  • 安卓设备信息查看器 - 源码编译
  • Android-重学kotlin(协程源码第二阶段)新学习总结
  • 中望CAD2026亮点速递(5):【相似查找】高效自动化识别定位
  • uniapp AndroidiOS 定位权限检查
  • Android ViewModel机制与底层原理详解
  • upload-labs靶场通关详解:第19关 条件竞争(二)
  • 池化思想-Mysql异步连接池
  • 5.注册中心横向对比:Nacos vs Eureka vs Consul —— 深度解析与科学选型指南