mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-05-23 22:37:41 +08:00
新增APP全局字体阴影
This commit is contained in:
parent
634ebe71e9
commit
eef6a189d8
@ -5,6 +5,7 @@
|
||||
- 新增实验性的添加本地歌曲到我的收藏支持,与PC端类似,在我的收藏的列表菜单中选择歌曲目录,将添加所选目录下的所有歌曲,目前支持mp3/flac/ogg/wav等格式
|
||||
- 新增歌曲标签编辑功能,允许编辑本地源且文件歌曲存在的歌曲标签信息
|
||||
- 新增动态背景,默认关闭,启用后将使用当前歌曲封面做APP背景
|
||||
- 新增APP全局字体阴影,默认开启,可到设置-主题设置关闭
|
||||
|
||||
### 优化
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { createIconSetFromIcoMoon } from 'react-native-vector-icons'
|
||||
import icoMoonConfig from '@/resources/fonts/selection.json'
|
||||
import { scaleSizeW } from '@/utils/pixelRatio'
|
||||
import { type ComponentProps } from 'react'
|
||||
import { useTheme } from '@/store/theme/hook'
|
||||
import { memo, type ComponentProps } from 'react'
|
||||
import { useTextShadow, useTheme } from '@/store/theme/hook'
|
||||
import { StyleSheet, type StyleProp, type TextStyle } from 'react-native'
|
||||
|
||||
// import IconAntDesign from 'react-native-vector-icons/AntDesign'
|
||||
// import IconEntypo from 'react-native-vector-icons/Entypo'
|
||||
@ -27,11 +28,31 @@ const IcoMoon = createIconSetFromIcoMoon(icoMoonConfig)
|
||||
|
||||
type IconType = ReturnType<typeof createIconSetFromIcoMoon>
|
||||
|
||||
export const Icon = ({ size = 15, rawSize, color, ...props }: ComponentProps<IconType> & { rawSize?: number }) => {
|
||||
const theme = useTheme()
|
||||
return <IcoMoon size={rawSize ?? scaleSizeW(size)} color={color ?? theme['c-font']} {...props} />
|
||||
interface IconProps extends Omit<ComponentProps<IconType>, 'style'> {
|
||||
style?: StyleProp<TextStyle>
|
||||
rawSize?: number
|
||||
}
|
||||
|
||||
export const Icon = memo(({ size = 15, rawSize, color, style, ...props }: IconProps) => {
|
||||
const theme = useTheme()
|
||||
const textShadow = useTextShadow()
|
||||
const newStyle = textShadow ? StyleSheet.compose({
|
||||
textShadowColor: theme['c-primary-alpha-700'],
|
||||
textShadowOffset: { width: 0, height: 0.2 },
|
||||
textShadowRadius: 2,
|
||||
}, style) : style
|
||||
return (
|
||||
<IcoMoon
|
||||
size={rawSize ?? scaleSizeW(size)}
|
||||
color={color ?? theme['c-font']}
|
||||
// @ts-expect-error
|
||||
style={newStyle}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
export {
|
||||
// IconAntDesign,
|
||||
// IconEntypo,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { type ComponentProps } from 'react'
|
||||
import { Text, type TextProps as _TextProps, StyleSheet, Animated, type ColorValue, type TextStyle } from 'react-native'
|
||||
import { useTheme } from '@/store/theme/hook'
|
||||
import { useTextShadow, useTheme } from '@/store/theme/hook'
|
||||
import { setSpText } from '@/utils/pixelRatio'
|
||||
import { useAnimateColor } from '@/utils/hooks/useAnimateColor'
|
||||
import { DEFAULT_DURATION, useAnimateNumber } from '@/utils/hooks/useAnimateNumber'
|
||||
@ -31,10 +31,23 @@ export interface TextProps extends _TextProps {
|
||||
|
||||
export default ({ style, size = 15, color, children, ...props }: TextProps) => {
|
||||
const theme = useTheme()
|
||||
const textShadow = useTextShadow()
|
||||
style = StyleSheet.compose(textShadow ? {
|
||||
fontFamily: 'System',
|
||||
textShadowColor: theme['c-primary-alpha-700'],
|
||||
textShadowOffset: { width: 0, height: 0.2 },
|
||||
textShadowRadius: 2,
|
||||
fontSize: setSpText(size),
|
||||
color: color ?? theme['c-font'],
|
||||
} : {
|
||||
fontFamily: 'System',
|
||||
fontSize: setSpText(size),
|
||||
color: color ?? theme['c-font'],
|
||||
}, style)
|
||||
|
||||
return (
|
||||
<Text
|
||||
style={StyleSheet.compose({ fontFamily: 'System', fontSize: setSpText(size), color: color ?? theme['c-font'] }, style)}
|
||||
style={style}
|
||||
{...props}
|
||||
>{children}</Text>
|
||||
)
|
||||
@ -52,13 +65,21 @@ export interface AnimatedTextProps extends _AnimatedTextProps {
|
||||
}
|
||||
export const AnimatedText = ({ style, size = 15, color, children, ...props }: AnimatedTextProps) => {
|
||||
const theme = useTheme()
|
||||
const textShadow = useTextShadow()
|
||||
style = StyleSheet.compose(textShadow ? {
|
||||
fontFamily: 'System',
|
||||
textShadowColor: theme['c-primary-alpha-700'],
|
||||
textShadowOffset: { width: 0, height: 0.2 },
|
||||
textShadowRadius: 2,
|
||||
fontSize: setSpText(size),
|
||||
color: color ?? theme['c-font'],
|
||||
} : {
|
||||
fontFamily: 'System',
|
||||
fontSize: setSpText(size),
|
||||
color: color ?? theme['c-font'],
|
||||
}, style as TextStyle)
|
||||
|
||||
return (
|
||||
<Animated.Text
|
||||
style={StyleSheet.compose({ fontFamily: 'System', fontSize: setSpText(size), color: color ?? theme['c-font'] }, style as TextStyle)}
|
||||
{...props}
|
||||
>{children}</Animated.Text>
|
||||
)
|
||||
return <Animated.Text style={style} {...props}>{children}</Animated.Text>
|
||||
}
|
||||
|
||||
|
||||
@ -79,14 +100,25 @@ export interface AnimatedColorTextProps extends _AnimatedTextProps {
|
||||
}
|
||||
export const AnimatedColorText = ({ style, size = 15, opacity: _opacity, color: _color, children, ...props }: AnimatedColorTextProps) => {
|
||||
const theme = useTheme()
|
||||
const textShadow = useTextShadow()
|
||||
|
||||
const [color] = useAnimateColor(_color ?? theme['c-font'])
|
||||
const [opacity] = useAnimateNumber(_opacity ?? 1, DEFAULT_DURATION, false)
|
||||
|
||||
return (
|
||||
<Animated.Text
|
||||
style={StyleSheet.compose({ fontSize: setSpText(size), color: color as unknown as ColorValue, opacity }, style as TextStyle)}
|
||||
{...props}
|
||||
>{children}</Animated.Text>
|
||||
)
|
||||
style = StyleSheet.compose(textShadow ? {
|
||||
fontFamily: 'System',
|
||||
textShadowColor: theme['c-primary-alpha-700'],
|
||||
textShadowOffset: { width: 0, height: 0.2 },
|
||||
textShadowRadius: 2,
|
||||
fontSize: setSpText(size),
|
||||
color: color as unknown as ColorValue,
|
||||
opacity,
|
||||
} : {
|
||||
fontFamily: 'System',
|
||||
fontSize: setSpText(size),
|
||||
color: color as unknown as ColorValue,
|
||||
opacity,
|
||||
}, style as TextStyle)
|
||||
|
||||
return <Animated.Text style={style} {...props}>{children}</Animated.Text>
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export enum COMPONENT_IDS {
|
||||
export enum NAV_SHEAR_NATIVE_IDS {
|
||||
playDetail_pic = 'playDetail_pic',
|
||||
playDetail_header = 'playDetail_header',
|
||||
playDetail_pageIndicator = 'playDetail_pageIndicator',
|
||||
// playDetail_pageIndicator = 'playDetail_pageIndicator',
|
||||
playDetail_player = 'playDetail_player',
|
||||
songlistDetail_pic = 'songlistDetail_pic',
|
||||
songlistDetail_title = 'songlistDetail_title',
|
||||
|
@ -30,9 +30,9 @@ const defaultSetting: LX.AppSetting = {
|
||||
|
||||
// 'playDetail.isZoomActiveLrc': false,
|
||||
// 'playDetail.isShowLyricProgressSetting': false,
|
||||
'playDetail.style.align': 'center',
|
||||
'playDetail.vertical.style.lrcFontSize': 176,
|
||||
'playDetail.horizontal.style.lrcFontSize': 180,
|
||||
'playDetail.style.align': 'left',
|
||||
'playDetail.vertical.style.lrcFontSize': 210,
|
||||
'playDetail.horizontal.style.lrcFontSize': 220,
|
||||
'playDetail.isShowLyricProgressSetting': false,
|
||||
|
||||
'desktopLyric.enable': false,
|
||||
@ -71,6 +71,7 @@ const defaultSetting: LX.AppSetting = {
|
||||
'theme.darkId': 'black',
|
||||
'theme.hideBgDark': false,
|
||||
'theme.dynamicBg': false,
|
||||
'theme.fontShadow': true,
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,6 +264,7 @@
|
||||
"setting_basic_theme": "Theme",
|
||||
"setting_basic_theme_auto_theme": "Follow the system light and dark mode to switch themes",
|
||||
"setting_basic_theme_dynamic_bg": "Use dynamic backgrounds",
|
||||
"setting_basic_theme_font_shadow": "Enable font shadow",
|
||||
"setting_basic_theme_hide_bg_dark": "Hide black theme",
|
||||
"setting_basic_theme_more_btn_show": "Expand themes",
|
||||
"setting_dislike_list_input_tip": "song name@artist name\nSong name\n@ singer name",
|
||||
|
@ -264,6 +264,7 @@
|
||||
"setting_basic_theme": "主题颜色",
|
||||
"setting_basic_theme_auto_theme": "跟随系统亮、暗模式切换主题",
|
||||
"setting_basic_theme_dynamic_bg": "使用动态背景",
|
||||
"setting_basic_theme_font_shadow": "启用字体阴影",
|
||||
"setting_basic_theme_hide_bg_dark": "隐藏黑色主题背景",
|
||||
"setting_basic_theme_more_btn_show": "更多主题",
|
||||
"setting_dislike_list_input_tip": "歌曲名@歌手名\n歌曲名\n@歌手名",
|
||||
|
@ -165,17 +165,6 @@ export function pushPlayDetailScreen(componentId: string) {
|
||||
duration: 300,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: NAV_SHEAR_NATIVE_IDS.playDetail_pageIndicator,
|
||||
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: -32, // Animate translationY from 16dp to 0dp
|
||||
duration: 300,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: NAV_SHEAR_NATIVE_IDS.playDetail_player,
|
||||
alpha: {
|
||||
|
@ -13,6 +13,7 @@ import { setNavActiveId } from '@/core/common'
|
||||
const hideKeys = [
|
||||
'list.isShowAlbumName',
|
||||
'list.isShowInterval',
|
||||
'theme.fontShadow',
|
||||
] as Readonly<Array<keyof LX.AppSetting>>
|
||||
|
||||
const SearchPage = () => {
|
||||
@ -71,14 +72,19 @@ const SongListPage = () => {
|
||||
if (currentId != 'nav_setting') return
|
||||
setVisible(false)
|
||||
}
|
||||
const handleConfigUpdated = (keys: Array<keyof LX.AppSetting>) => {
|
||||
if (keys.some(k => hideKeys.includes(k))) handleHide()
|
||||
}
|
||||
global.state_event.on('navActiveIdUpdated', handleNavIdUpdate)
|
||||
global.state_event.on('themeUpdated', handleHide)
|
||||
global.state_event.on('languageChanged', handleHide)
|
||||
global.state_event.on('configUpdated', handleConfigUpdated)
|
||||
|
||||
return () => {
|
||||
global.state_event.off('navActiveIdUpdated', handleNavIdUpdate)
|
||||
global.state_event.off('themeUpdated', handleHide)
|
||||
global.state_event.off('languageChanged', handleHide)
|
||||
global.state_event.on('configUpdated', handleConfigUpdated)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
@ -73,7 +73,7 @@ export default forwardRef<ListMenuType, ListMenuProps>((props, ref) => {
|
||||
if (musicInfo.source == 'local') menu.splice(4, 0, { action: 'editMetadata', disabled: !edit_metadata, label: t('edit_metadata') })
|
||||
setMenus(menu)
|
||||
void Promise.all([hasEditMetadata(musicInfo)]).then(([_edit_metadata]) => {
|
||||
console.log(_edit_metadata)
|
||||
// console.log(_edit_metadata)
|
||||
let isUpdated = true
|
||||
if (edit_metadata != _edit_metadata) {
|
||||
edit_metadata = _edit_metadata
|
||||
|
@ -25,6 +25,6 @@ export default memo(() => {
|
||||
const styles = createStyle({
|
||||
content: {
|
||||
marginTop: 5,
|
||||
marginBottom: 15,
|
||||
// marginBottom: 15,
|
||||
},
|
||||
})
|
||||
|
@ -0,0 +1,29 @@
|
||||
import { memo } from 'react'
|
||||
import { View } from 'react-native'
|
||||
|
||||
import CheckBoxItem from '../../components/CheckBoxItem'
|
||||
import { createStyle } from '@/utils/tools'
|
||||
import { useI18n } from '@/lang'
|
||||
import { updateSetting } from '@/core/common'
|
||||
import { useSettingValue } from '@/store/setting/hook'
|
||||
|
||||
export default memo(() => {
|
||||
const t = useI18n()
|
||||
const isFontShadow = useSettingValue('theme.fontShadow')
|
||||
const setIsFontShadow = (isFontShadow: boolean) => {
|
||||
updateSetting({ 'theme.fontShadow': isFontShadow })
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<CheckBoxItem check={isFontShadow} label={t('setting_basic_theme_font_shadow')} onChange={setIsFontShadow} />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
|
||||
const styles = createStyle({
|
||||
content: {
|
||||
marginTop: 5,
|
||||
marginBottom: 15,
|
||||
},
|
||||
})
|
@ -5,6 +5,7 @@ import Theme from './Theme'
|
||||
import IsAutoTheme from './IsAutoTheme'
|
||||
import IsHideBgDark from './IsHideBgDark'
|
||||
import IsDynamicBg from './IsDynamicBg'
|
||||
import IsFontShadow from './IsFontShadow'
|
||||
// import { useI18n } from '@/lang/i18n'
|
||||
|
||||
export default memo(() => {
|
||||
@ -14,6 +15,7 @@ export default memo(() => {
|
||||
<IsAutoTheme />
|
||||
<IsHideBgDark />
|
||||
<IsDynamicBg />
|
||||
<IsFontShadow />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
@ -28,7 +28,7 @@ const LrcLine = memo(({ line, lineNum, activeLine, onLayout }: LineProps) => {
|
||||
const lrcFontSize = useSettingValue('playDetail.horizontal.style.lrcFontSize')
|
||||
const textAlign = useSettingValue('playDetail.style.align')
|
||||
const size = lrcFontSize / 10
|
||||
const lineHeight = setSpText(size) * 1.25
|
||||
const lineHeight = setSpText(size) * 1.3
|
||||
|
||||
const colors = useMemo(() => {
|
||||
const active = activeLine == lineNum
|
||||
|
@ -67,7 +67,7 @@ const LrcLine = memo(({ line, lineNum, activeLine, onLayout }: LineProps) => {
|
||||
const lrcFontSize = useSettingValue('playDetail.vertical.style.lrcFontSize')
|
||||
const textAlign = useSettingValue('playDetail.style.align')
|
||||
const size = lrcFontSize / 10
|
||||
const lineHeight = setSpText(size) * 1.25
|
||||
const lineHeight = setSpText(size) * 1.3
|
||||
|
||||
const colors = useMemo(() => {
|
||||
const active = activeLine == lineNum
|
||||
|
@ -12,13 +12,9 @@ import { NAV_SHEAR_NATIVE_IDS } from '@/config/constant'
|
||||
export default memo(() => {
|
||||
return (
|
||||
<View style={styles.container} nativeID={NAV_SHEAR_NATIVE_IDS.playDetail_player}>
|
||||
<View style={styles.status}>
|
||||
<PlayInfo />
|
||||
</View>
|
||||
<PlayInfo />
|
||||
<ControlBtn />
|
||||
<View style={{ ...styles.info }} >
|
||||
<MoreBtn />
|
||||
</View>
|
||||
<MoreBtn />
|
||||
</View>
|
||||
)
|
||||
})
|
||||
@ -38,12 +34,6 @@ const styles = createStyle({
|
||||
// backgroundColor: 'red',
|
||||
flexDirection: 'column',
|
||||
},
|
||||
info: {
|
||||
flex: 0,
|
||||
flexDirection: 'row',
|
||||
// paddingBottom: 5,
|
||||
// justifyContent: 'flex-end',
|
||||
},
|
||||
status: {
|
||||
marginTop: 10,
|
||||
flexDirection: 'column',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useContext } from 'react'
|
||||
import { useContext, useEffect, useState } from 'react'
|
||||
import { ThemeContext } from './state'
|
||||
import settingState from '@/store/setting/state'
|
||||
|
||||
// export const useSetting = () => {
|
||||
// const [setting, updateSetting] = useState(state.setting)
|
||||
@ -36,3 +37,21 @@ import { ThemeContext } from './state'
|
||||
|
||||
export const useTheme = () => useContext(ThemeContext)
|
||||
|
||||
export const useTextShadow = () => {
|
||||
const [value, update] = useState(settingState.setting['theme.fontShadow'])
|
||||
|
||||
useEffect(() => {
|
||||
const handleUpdate = (keys: Array<keyof LX.AppSetting>, setting: Partial<LX.AppSetting>) => {
|
||||
if (!keys.includes('theme.fontShadow')) return
|
||||
requestAnimationFrame(() => {
|
||||
update(setting['theme.fontShadow']!)
|
||||
})
|
||||
}
|
||||
global.state_event.on('configUpdated', handleUpdate)
|
||||
return () => {
|
||||
global.state_event.off('configUpdated', handleUpdate)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return value
|
||||
}
|
||||
|
5
src/types/app_setting.d.ts
vendored
5
src/types/app_setting.d.ts
vendored
@ -81,6 +81,11 @@ declare global {
|
||||
*/
|
||||
'theme.dynamicBg': boolean
|
||||
|
||||
/**
|
||||
* 字体阴影
|
||||
*/
|
||||
'theme.fontShadow': boolean
|
||||
|
||||
/**
|
||||
* 启动时自动播放歌曲
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user