优化进入播放详情页、歌单详情页的动画效果

This commit is contained in:
lyswhut 2021-05-30 14:49:10 +08:00
parent a5d040af5e
commit 009a9c5e03
11 changed files with 133 additions and 74 deletions

View File

@ -7,6 +7,7 @@
- 优化应用布局对手机系统字体大小的适配 - 优化应用布局对手机系统字体大小的适配
- 调整歌单详情页现在在歌单详情页按手机上的返回键将会返回歌单列表而不是直接退出APP - 调整歌单详情页现在在歌单详情页按手机上的返回键将会返回歌单列表而不是直接退出APP
- 优化进入播放详情页、歌单详情页的动画效果
### 修复 ### 修复

View File

@ -68,7 +68,7 @@ export function pushHomeScreen() {
}, },
}) })
} }
export function pushPlayDetailScreen(componentId) { export function pushPlayDetailScreen(componentId, id) {
/* /*
Navigation.setDefaultOptions({ Navigation.setDefaultOptions({
topBar: { topBar: {
@ -119,13 +119,44 @@ export function pushPlayDetailScreen(componentId) {
}, },
animations: { animations: {
push: { push: {
content: { sharedElementTransitions: [
translationX: { {
from: Dimensions.get('window').width, fromId: `pic${id}`,
to: 0, toId: `pic${id}Dest`,
duration: 300, interpolation: { type: 'spring' },
}, },
}, ],
elementTransitions: [
{
id: 'header',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: 300,
},
translationY: {
from: -16, // Animate translationY from 16dp to 0dp
duration: 300,
},
},
{
id: 'player',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: 300,
},
translationY: {
from: 16, // Animate translationY from 16dp to 0dp
duration: 300,
},
},
],
// content: {
// translationX: {
// from: Dimensions.get('window').width,
// to: 0,
// duration: 300,
// },
// },
}, },
pop: { pop: {
content: { content: {
@ -142,7 +173,7 @@ export function pushPlayDetailScreen(componentId) {
}) })
}) })
} }
export function pushSonglistDetailScreen(componentId) { export function pushSonglistDetailScreen(componentId, id) {
InteractionManager.runAfterInteractions(() => { InteractionManager.runAfterInteractions(() => {
Navigation.push(componentId, { Navigation.push(componentId, {
component: { component: {
@ -161,32 +192,72 @@ export function pushSonglistDetailScreen(componentId) {
}, },
animations: { animations: {
push: { push: {
content: { sharedElementTransitions: [
scaleX: { {
from: 1.2, fromId: `pic${id}`,
to: 1, toId: `pic${id}Dest`,
duration: 200, interpolation: { type: 'spring' },
}, },
scaleY: { ],
from: 1.2, elementTransitions: [
to: 1, {
duration: 200, id: 'title',
alpha: {
from: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: 300,
},
translationX: {
from: 16, // Animate translationX from 16dp to 0dp
duration: 300,
},
}, },
alpha: { ],
from: 0, // content: {
to: 1, // scaleX: {
duration: 200, // from: 1.2,
}, // to: 1,
}, // duration: 200,
// },
// scaleY: {
// from: 1.2,
// to: 1,
// duration: 200,
// },
// alpha: {
// from: 0,
// to: 1,
// duration: 200,
// },
// },
}, },
pop: { pop: {
content: { sharedElementTransitions: [
alpha: { {
from: 1, fromId: `pic${id}Dest`,
to: 0, toId: `pic${id}`,
duration: 200, interpolation: { type: 'spring' },
}, },
}, ],
elementTransitions: [
{
id: 'title',
alpha: {
to: 0, // We don't declare 'to' value as that is the element's current alpha value, here we're essentially animating from 0 to 1
duration: 300,
},
translationX: {
to: 16, // Animate translationX from 16dp to 0dp
duration: 300,
},
},
],
// content: {
// alpha: {
// from: 1,
// to: 0,
// duration: 200,
// },
// },
}, },
}, },
}, },

View File

@ -63,7 +63,7 @@ export default ({ width }) => {
const handleListPress = useCallback((item, index) => { const handleListPress = useCallback((item, index) => {
// console.log(item) // console.log(item)
setSelectListInfo(item) setSelectListInfo(item)
navigations.pushSonglistDetailScreen(componentIds.home) navigations.pushSonglistDetailScreen(componentIds.home, item.id)
}, [componentIds.home, setSelectListInfo]) }, [componentIds.home, setSelectListInfo])
const itemWidth = useMemo(() => Math.max(parseInt(width * 0.125), 110), [width]) const itemWidth = useMemo(() => Math.max(parseInt(width * 0.125), 110), [width])

View File

@ -12,11 +12,11 @@ export default memo(({ data: { index, item }, width, onPress = () => {} }) => {
<View style={{ ...styles.listItem, width: width - 20 }}> <View style={{ ...styles.listItem, width: width - 20 }}>
<View style={{ ...styles.listItemImg, backgroundColor: AppColors.primary }}> <View style={{ ...styles.listItemImg, backgroundColor: AppColors.primary }}>
<TouchableOpacity activeOpacity={0.5} onPress={handlePress}> <TouchableOpacity activeOpacity={0.5} onPress={handlePress}>
<Image source={{ uri: item.img }} style={{ width: width - 20, height: width - 20 }} borderRadius={4} /> <Image source={{ uri: item.img }} nativeID={`pic${item.id}`} style={{ width: width - 20, height: width - 20 }} borderRadius={4} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<TouchableOpacity activeOpacity={0.5} onPress={handlePress}> <TouchableOpacity activeOpacity={0.5} onPress={handlePress}>
<Text style={{ ...styles.listItemTitle, color: AppColors.normal }} numberOfLines={ 2 }>{item.name}</Text> <Text style={{ ...styles.listItemTitle, color: AppColors.normal }} nativeID={`title${item.id}`} numberOfLines={ 2 }>{item.name}</Text>
</TouchableOpacity> </TouchableOpacity>
{/* <Text>{JSON.stringify(item)}</Text> */} {/* <Text>{JSON.stringify(item)}</Text> */}
</View> </View>

View File

@ -9,19 +9,21 @@ import { navigations } from '@/navigation'
export default () => { export default () => {
const playMusicInfo = useGetter('player', 'playMusicInfo') const playMusicInfo = useGetter('player', 'playMusicInfo')
const theme = useGetter('common', 'theme') const theme = useGetter('common', 'theme')
const [imgUrl, setImgUrl] = useState(null)
const setNavActiveIndex = useDispatch('common', 'setNavActiveIndex') const setNavActiveIndex = useDispatch('common', 'setNavActiveIndex')
const setPrevSelectListId = useDispatch('common', 'setPrevSelectListId') const setPrevSelectListId = useDispatch('common', 'setPrevSelectListId')
const setJumpPosition = useDispatch('list', 'setJumpPosition') const setJumpPosition = useDispatch('list', 'setJumpPosition')
// const { t } = useTranslation() // const { t } = useTranslation()
const componentIds = useGetter('common', 'componentIds') const componentIds = useGetter('common', 'componentIds')
const musicInfo = useMemo(() => {
return (playMusicInfo && playMusicInfo.musicInfo) || {}
}, [playMusicInfo])
const handlePress = useCallback(() => { const handlePress = useCallback(() => {
// console.log('') // console.log('')
// console.log(playMusicInfo) // console.log(playMusicInfo)
if (!playMusicInfo) return if (!playMusicInfo) return
navigations.pushPlayDetailScreen(componentIds.home) navigations.pushPlayDetailScreen(componentIds.home, musicInfo.songmid)
// toast(t('play_detail_todo_tip'), 'long') // toast(t('play_detail_todo_tip'), 'long')
}, [componentIds.home, playMusicInfo]) }, [componentIds.home, musicInfo, playMusicInfo])
const handleLongPress = useCallback(() => { const handleLongPress = useCallback(() => {
if (!playMusicInfo || playMusicInfo.listId == LIST_ID_PLAY_TEMP || playMusicInfo.listId == LIST_ID_PLAY_LATER) return if (!playMusicInfo || playMusicInfo.listId == LIST_ID_PLAY_TEMP || playMusicInfo.listId == LIST_ID_PLAY_LATER) return
@ -32,23 +34,17 @@ export default () => {
}) })
}, [playMusicInfo, setJumpPosition, setNavActiveIndex, setPrevSelectListId]) }, [playMusicInfo, setJumpPosition, setNavActiveIndex, setPrevSelectListId])
useEffect(() => {
const url = playMusicInfo && playMusicInfo.musicInfo ? playMusicInfo.musicInfo.img : null
if (imgUrl == url) return
setImgUrl(url)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playMusicInfo])
const component = useMemo(() => ( const component = useMemo(() => (
<TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} > <TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} >
<Image source={{ uri: imgUrl }} progressiveRenderingEnabled={true} borderRadius={2} style={{ <Image source={{ uri: musicInfo.img }} nativeID={`pic${musicInfo.songmid}`} progressiveRenderingEnabled={true} borderRadius={2} style={{
// ...styles.playInfoImg, // ...styles.playInfoImg,
backgroundColor: theme.primary, backgroundColor: theme.primary,
width: 48, width: 48,
height: 48, height: 48,
}} /> }} />
</TouchableOpacity> </TouchableOpacity>
), [handleLongPress, handlePress, imgUrl, theme]) ), [handleLongPress, handlePress, musicInfo, theme])
return component return component
} }

View File

@ -5,23 +5,19 @@ import { useLayout } from '@/utils/hooks'
export default memo(() => { export default memo(() => {
const playMusicInfo = useGetter('player', 'playMusicInfo') const playMusicInfo = useGetter('player', 'playMusicInfo')
const [imgUrl, setImgUrl] = useState(null)
const theme = useGetter('common', 'theme') const theme = useGetter('common', 'theme')
const { onLayout, ...layout } = useLayout() const { onLayout, ...layout } = useLayout()
useEffect(() => { const musicInfo = useMemo(() => {
const url = playMusicInfo ? playMusicInfo.musicInfo.img : null return (playMusicInfo && playMusicInfo.musicInfo) || {}
if (imgUrl == url) return
setImgUrl(url)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playMusicInfo]) }, [playMusicInfo])
const imgWidth = useMemo(() => layout.width * 0.8, [layout.width]) const imgWidth = Math.max(layout.width * 0.8, 100)
return ( return (
<View style={styles.container} onLayout={onLayout}> <View style={styles.container} onLayout={onLayout}>
<View style={{ ...styles.content }}> <View style={{ ...styles.content }}>
<Image source={{ uri: imgUrl }} progressiveRenderingEnabled={true} borderRadius={2} style={{ <Image source={{ uri: musicInfo.img }} nativeID={`pic${musicInfo.songmid}Dest`} progressiveRenderingEnabled={true} borderRadius={2} style={{
...styles.img, ...styles.img,
backgroundColor: theme.primary, backgroundColor: theme.primary,
width: imgWidth, width: imgWidth,

View File

@ -16,7 +16,7 @@ export default memo(({ playNextModes }) => {
return ( return (
<View style={{ ...styles.container, backgroundColor: theme.primary }}> <View style={{ ...styles.container, backgroundColor: theme.primary }}>
<View style={{ ...styles.info }}> <View style={{ ...styles.info }} >
<Title /> <Title />
<MoreBtn /> <MoreBtn />
</View> </View>

View File

@ -59,7 +59,7 @@ export default memo(() => {
<View style={{ ...styles.pageIndicatorItem, backgroundColor: pageIndex == 0 ? theme.secondary20 : theme.normal60 }}></View> <View style={{ ...styles.pageIndicatorItem, backgroundColor: pageIndex == 0 ? theme.secondary20 : theme.normal60 }}></View>
<View style={{ ...styles.pageIndicatorItem, backgroundColor: pageIndex == 1 ? theme.secondary20 : theme.normal60 }}></View> <View style={{ ...styles.pageIndicatorItem, backgroundColor: pageIndex == 1 ? theme.secondary20 : theme.normal60 }}></View>
</View> </View>
<View style={styles.player}> <View style={styles.player} nativeID="player">
<Player /> <Player />
</View> </View>
</View> </View>

View File

@ -17,7 +17,7 @@ export default memo(() => {
} }
return ( return (
<View style={{ ...styles.header, backgroundColor: theme.primary }}> <View style={{ ...styles.header, backgroundColor: theme.primary }} nativeID="header">
<StatusBar backgroundColor="rgba(0,0,0,0)" barStyle="dark-content" translucent={true} /> <StatusBar backgroundColor="rgba(0,0,0,0)" barStyle="dark-content" translucent={true} />
<View style={{ ...styles.container }}> <View style={{ ...styles.container }}>
<TouchableOpacity onPress={back} style={{ ...styles.button }}> <TouchableOpacity onPress={back} style={{ ...styles.button }}>

View File

@ -1,5 +1,5 @@
import React, { memo } from 'react' import React, { memo, useMemo } from 'react'
import { View, Text, StyleSheet, ImageBackground } from 'react-native' import { View, Text, StyleSheet, Image } from 'react-native'
import { AppColors, BorderWidths } from '@/theme' import { AppColors, BorderWidths } from '@/theme'
import { useGetter } from '@/store' import { useGetter } from '@/store'
import ButtonBar from './ActionBar' import ButtonBar from './ActionBar'
@ -13,16 +13,16 @@ const Header = memo(() => {
<View style={{ ...styles.container, borderBottomColor: AppColors.borderColor }}> <View style={{ ...styles.container, borderBottomColor: AppColors.borderColor }}>
<View style={{ flexDirection: 'row', flexGrow: 0, flexShrink: 0, padding: 10 }}> <View style={{ flexDirection: 'row', flexGrow: 0, flexShrink: 0, padding: 10 }}>
<View style={{ ...styles.listItemImg, backgroundColor: AppColors.primary }}> <View style={{ ...styles.listItemImg, backgroundColor: AppColors.primary }}>
<ImageBackground source={{ uri: selectListInfo.img || listDetailDataInfo.img || null }} borderRadius={4} style={{ flex: 1, resizeMode: 'cover', justifyContent: 'flex-end' }}> <Image nativeID={`pic${selectListInfo.id}Dest`} source={{ uri: selectListInfo.img || listDetailDataInfo.img || null }} borderRadius={4} style={{ flex: 1, resizeMode: 'cover', justifyContent: 'flex-end' }}>
{ {/* {
playCount playCount
? <Text style={{ fontSize: 12, paddingLeft: 3, paddingRight: 3, backgroundColor: 'rgba(0, 0, 0, 0.5)', color: AppColors.primary, borderBottomLeftRadius: 4, borderBottomRightRadius: 4 }} numberOfLines={ 1 }>{playCount}</Text> ? <Text style={{ fontSize: 12, paddingLeft: 3, paddingRight: 3, backgroundColor: 'rgba(0, 0, 0, 0.5)', color: AppColors.primary, borderBottomLeftRadius: 4, borderBottomRightRadius: 4 }} numberOfLines={ 1 }>{playCount}</Text>
: null : null
} } */}
</ImageBackground> </Image>
</View> </View>
<View style={{ flexDirection: 'column', flexGrow: 1, flexShrink: 1, paddingLeft: 5 }}> <View style={{ flexDirection: 'column', flexGrow: 1, flexShrink: 1, paddingLeft: 5 }} nativeID="title">
<Text style={{ fontSize: 13, color: AppColors.normal }} numberOfLines={ 1 }>{selectListInfo.name || listDetailDataInfo.name}</Text> <Text style={{ fontSize: 13, color: AppColors.normal }} numberOfLines={ 1 }>{selectListInfo.name}</Text>
<View style={{ flexGrow: 0, flexShrink: 1 }}> <View style={{ flexGrow: 0, flexShrink: 1 }}>
<Text style={{ fontSize: 10, color: AppColors.normal40 }} numberOfLines={ 4 }>{selectListInfo.desc || listDetailDataInfo.desc}</Text> <Text style={{ fontSize: 10, color: AppColors.normal40 }} numberOfLines={ 4 }>{selectListInfo.desc || listDetailDataInfo.desc}</Text>
</View> </View>

View File

@ -9,19 +9,21 @@ import { navigations } from '@/navigation'
export default () => { export default () => {
const playMusicInfo = useGetter('player', 'playMusicInfo') const playMusicInfo = useGetter('player', 'playMusicInfo')
const theme = useGetter('common', 'theme') const theme = useGetter('common', 'theme')
const [imgUrl, setImgUrl] = useState(null)
const setNavActiveIndex = useDispatch('common', 'setNavActiveIndex') const setNavActiveIndex = useDispatch('common', 'setNavActiveIndex')
const setPrevSelectListId = useDispatch('common', 'setPrevSelectListId') const setPrevSelectListId = useDispatch('common', 'setPrevSelectListId')
const setJumpPosition = useDispatch('list', 'setJumpPosition') const setJumpPosition = useDispatch('list', 'setJumpPosition')
// const { t } = useTranslation() // const { t } = useTranslation()
const componentIds = useGetter('common', 'componentIds') const componentIds = useGetter('common', 'componentIds')
const musicInfo = useMemo(() => {
return (playMusicInfo && playMusicInfo.musicInfo) || {}
}, [playMusicInfo])
const handlePress = useCallback(() => { const handlePress = useCallback(() => {
// console.log('') // console.log('')
// console.log(playMusicInfo) // console.log(playMusicInfo)
if (!playMusicInfo) return if (!playMusicInfo) return
navigations.pushPlayDetailScreen(componentIds.home) navigations.pushPlayDetailScreen(componentIds.home, musicInfo.songmid)
// toast(t('play_detail_todo_tip'), 'long') // toast(t('play_detail_todo_tip'), 'long')
}, [componentIds.home, playMusicInfo]) }, [componentIds.home, musicInfo, playMusicInfo])
const handleLongPress = useCallback(() => { const handleLongPress = useCallback(() => {
if (!playMusicInfo || playMusicInfo.listId == LIST_ID_PLAY_TEMP || playMusicInfo.listId == LIST_ID_PLAY_LATER) return if (!playMusicInfo || playMusicInfo.listId == LIST_ID_PLAY_TEMP || playMusicInfo.listId == LIST_ID_PLAY_LATER) return
@ -32,23 +34,16 @@ export default () => {
}) })
}, [playMusicInfo, setJumpPosition, setNavActiveIndex, setPrevSelectListId]) }, [playMusicInfo, setJumpPosition, setNavActiveIndex, setPrevSelectListId])
useEffect(() => {
const url = playMusicInfo && playMusicInfo.musicInfo ? playMusicInfo.musicInfo.img : null
if (imgUrl == url) return
setImgUrl(url)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [playMusicInfo])
const component = useMemo(() => ( const component = useMemo(() => (
<TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} > <TouchableOpacity onLongPress={handleLongPress} onPress={handlePress} activeOpacity={0.7} >
<Image source={{ uri: imgUrl }} progressiveRenderingEnabled={true} borderRadius={2} style={{ <Image source={{ uri: musicInfo.img }} nativeID={`pic${musicInfo.songmid}`} progressiveRenderingEnabled={true} borderRadius={2} style={{
// ...styles.playInfoImg, // ...styles.playInfoImg,
backgroundColor: theme.primary, backgroundColor: theme.primary,
width: 48, width: 48,
height: 48, height: 48,
}} /> }} />
</TouchableOpacity> </TouchableOpacity>
), [handleLongPress, handlePress, imgUrl, theme]) ), [handleLongPress, handlePress, musicInfo, theme])
return component return component
} }