今日關鍵字:carousel
接續昨天把資料整理好了
今天要把資料呈現在畫面上
(不過這裡我想的輪播其實跟一般的輪播不同,沒有要自動播放
總之先來找找看)
好就第一個了(喂)
react-native-snap-carousel
yarn add react-native-snap-carousel @types/react-native-snap-carousel
官方的範例效果很不錯,符合我的想像,直接拿來用吧
demo
首先要先把輪播寫成元件
不過先看一下基礎範例的寫法
import Carousel from 'react-native-snap-carousel';
export class MyCarousel extends Component {
_renderItem = ({item, index}) => {
return (
<View style={styles.slide}>
<Text style={styles.title}>{ item.title }</Text>
</View>
);
}
render () {
return (
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.entries}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
/>
);
}
}
這裡大概可以看出來renderItem
這個prop輸入的就是每個子項目的長相
回頭看一下這個範例
export default class App extends Component {
state = {
index: 0
}
constructor(props) {
super(props);
this._renderItem = this._renderItem.bind(this)
}
_renderItem({ item }) {
return (
<View style={styles.itemContainer}>
<Text style={styles.itemLabel}>{`Item ${item}`}</Text>
</View>
);
}
render() {
return (
<View>
<Carousel
ref={(c) => this.carousel = c}
data={DATA}
renderItem={this._renderItem}
sliderWidth={SLIDER_WIDTH}
itemWidth={ITEM_WIDTH}
containerCustomStyle={styles.carouselContainer}
inactiveSlideShift={0}
onSnapToItem={(index) => this.setState({ index })}
scrollInterpolator={scrollInterpolator}
slideInterpolatedStyle={animatedStyles}
useScrollView={true}
/>
<Text style={styles.counter}
>
{this.state.index}
</Text>
</View>
);
}
}
大致上看起來差不多...嗯?過場效果寫在哪?
import { scrollInterpolator, animatedStyles } from './utils/animations';
原來在別的檔案裡...
export function scrollInterpolator (index, carouselProps) {
const range = [1, 0, -1];
const inputRange = getInputRangeFromIndexes(range, index, carouselProps);
const outputRange = range;
return { inputRange, outputRange };
}
export function animatedStyles (index, animatedValue, carouselProps) {
const translateProp = carouselProps.vertical ? 'translateY' : 'translateX';
let animatedOpacity = {};
let animatedTransform = {};
if (carouselProps.inactiveSlideOpacity < 1) {
animatedOpacity = {
opacity: animatedValue.interpolate({
inputRange: [-1, 0, 1],
outputRange: [carouselProps.inactiveSlideOpacity, 1, carouselProps.inactiveSlideOpacity]
})
};
}
if (carouselProps.inactiveSlideScale < 1) {
animatedTransform = {
transform: [{
scale: animatedValue.interpolate({
inputRange: [-1, 0, 1],
outputRange: [carouselProps.inactiveSlideScale, 1, carouselProps.inactiveSlideScale]
}),
[translateProp]: animatedValue.interpolate({
inputRange: [-1, 0, 1],
outputRange: [
TRANSLATE_VALUE * carouselProps.inactiveSlideScale,
0,
-TRANSLATE_VALUE * carouselProps.inactiveSlideScale]
}),
}]
};
}
return {
...animatedOpacity,
...animatedTransform
};
}
這裡我是直接把兩個檔案和在一起當個元件用
過程就是複製貼上
然後我想要的子元件很簡單
就是卡片一樣的感覺,上半段是圖片,下半段是字
const renderItem = ({ item }: RenderItemProps) => (
<View style={styles.itemContainer}>
<TouchableOpacity
activeOpacity={1}
style={{ height: '100%', width: '100%' }}>
<Image
style={styles.itemImg}
source={{
uri: item.imgLink
}}
/>
<View style={styles.textContainer}>
<Text style={styles.itemLabel} numberOfLines={1}>
{item.title}
</Text>
</View>
</TouchableOpacity>
</View>
)
這裡簡單談一下元件
基本上React Native內部排版都是預設flex
,方向跟web不同是預設column
類似於web的div,可以用來包覆區塊
對應web的img,只是src變成了source
專門用來包裹文字的內容,跟web不同的是,React Native內文字不能單獨出現,一定要被Text包裹
button都不button,React Native內的button比較不好用,通常是用這個
顧名思義,按了後透明度會變淡再變回來
可以藉由設定activeOpacity={1}
來使得透明度再點擊時不改變
完成了之後就可以把昨天的playListByWeek
用
import Carousel from '../common/Carousel'
...
{playListByWeek.weeks.map(
(playList, i) =>
playList.length > 0 && (
<Fragment key={`playList${i * i}`}>
<Text style={styles.playListTitle}>
{playListByWeek.weeksTitle[i]}
</Text>
<Carousel displayed={playList} />
</Fragment>
)
)}
其中Fragment為不想額外增加DOM節點(不想多包一層)時可以使用(React元件)
不用多包一層View
,div
如果不使用key時,還可以這麼寫
<> ... </>
如果想要在條件成立時才渲染畫面
有一種方便的寫法是
{condition && <component/>}
JS中,條件不成立時會return前者,成立時會return後者
這時可以來看成果了
完美...才怪
仔細看右半部
active的區塊是疊在inactive的上方
然而inactive的圖卻壓在active上方
...
...
...WTF?
這時候看看react-native-snap-carousel的一些demo
好像很多都是撲克牌類型的上下堆疊的例子
就是z-index會遞增或遞減
跟這個demo感覺有些不同
如果強制把active的z-index設為1,其餘為0會怎麼樣?
來更改animatedStyles
的return看看
const Carousel = (...) => {
const [active, setActive] = useState(0)
...
const animatedStyles=(...)=>{
...
return {
...animatedOpacity,
...animatedTransform,
zIndex: active === index ? 1 : 0
}
}
...
return (
<CarouselSnap
...
onSnapToItem={(index) => setActive(index)}
/>
)
成果
成功啦
今天雖然遇到了奇怪的問題,但還好沒有卡很久
明天預計來談redux
參考: