1import React from 'react'; 2import {StyleSheet, View} from 'react-native'; 3import rpx from '@/utils/rpx'; 4import globalStyle from '@/constants/globalStyle'; 5import Image from '@/components/base/image'; 6import {ImgAsset} from '@/constants/assetsConst'; 7import {ScrollView, TouchableOpacity} from 'react-native-gesture-handler'; 8import {launchImageLibrary} from 'react-native-image-picker'; 9import pathConst from '@/constants/pathConst'; 10import {copyFile} from 'react-native-fs'; 11import ImageColors from 'react-native-image-colors'; 12import ThemeText from '@/components/base/themeText'; 13import Slider from '@react-native-community/slider'; 14import Theme from '@/core/theme'; 15import Color from 'color'; 16import {showPanel} from '@/components/panels/usePanel'; 17import {grayRate} from '@/utils/colorUtil'; 18import {CustomizedColors} from '@/hooks/useColors'; 19 20export default function Body() { 21 const theme = Theme.useTheme(); 22 const backgroundInfo = Theme.useBackground(); 23 24 async function onImageClick() { 25 try { 26 const result = await launchImageLibrary({ 27 mediaType: 'photo', 28 }); 29 const uri = result.assets?.[0].uri; 30 if (!uri) { 31 return; 32 } 33 34 const bgPath = `${pathConst.dataPath}background${uri.substring( 35 uri.lastIndexOf('.'), 36 )}`; 37 await copyFile(uri, bgPath); 38 39 const colorsResult = await ImageColors.getColors(uri, { 40 fallback: '#ffffff', 41 }); 42 const colors = { 43 primary: 44 colorsResult.platform === 'android' 45 ? colorsResult.dominant 46 : colorsResult.platform === 'ios' 47 ? colorsResult.primary 48 : colorsResult.vibrant, 49 average: 50 colorsResult.platform === 'android' 51 ? colorsResult.average 52 : colorsResult.platform === 'ios' 53 ? colorsResult.detail 54 : colorsResult.dominant, 55 vibrant: 56 colorsResult.platform === 'android' 57 ? colorsResult.vibrant 58 : colorsResult.platform === 'ios' 59 ? colorsResult.secondary 60 : colorsResult.vibrant, 61 }; 62 63 const primaryGrayRate = grayRate(colors.primary!); 64 65 let themeColors: Partial<CustomizedColors>; 66 if (primaryGrayRate < -0.4) { 67 const primaryColor = Color(colors.primary!); 68 69 console.log( 70 colors.primary, 71 primaryGrayRate, 72 primaryColor 73 .whiten(3 * primaryGrayRate) 74 .hex() 75 .toString(), 76 ); 77 themeColors = { 78 appBar: colors.primary, 79 primary: primaryColor 80 .darken(primaryGrayRate * 5) 81 .toString(), 82 musicBar: colors.primary, 83 card: 'rgba(0,0,0,0.2)', 84 tabBar: primaryColor.alpha(0.2).toString(), 85 }; 86 } else if (primaryGrayRate > 0.4) { 87 themeColors = { 88 appBar: colors.primary, 89 primary: Color(colors.primary) 90 .darken(primaryGrayRate * 5) 91 .toString(), 92 musicBar: colors.primary, 93 card: 'rgba(0,0,0,0.2)', 94 }; 95 } else { 96 // const primaryColor = Color(colors.primary!); 97 98 themeColors = { 99 appBar: colors.primary, 100 primary: Color(colors.primary) 101 .saturate(Math.abs(primaryGrayRate) * 2 + 2) 102 .toString(), 103 musicBar: colors.primary, 104 card: 'rgba(0,0,0,0.2)', 105 }; 106 } 107 108 Theme.setTheme('custom', { 109 colors: themeColors, 110 background: { 111 url: `file://${bgPath}#${Date.now()}`, 112 }, 113 }); 114 // Config.set('setting.theme.colors', { 115 // primary: primaryColor, 116 // textHighlight: textHighlight, 117 // accent: textHighlight, 118 // }); 119 } catch (e) { 120 console.log(e); 121 } 122 } 123 124 return ( 125 <ScrollView style={globalStyle.fwflex1}> 126 <TouchableOpacity onPress={onImageClick}> 127 <Image 128 style={styles.image} 129 uri={backgroundInfo?.url} 130 emptySrc={ImgAsset.addBackground} 131 /> 132 </TouchableOpacity> 133 134 <View style={styles.sliderWrapper}> 135 <ThemeText>模糊度</ThemeText> 136 <Slider 137 style={styles.slider} 138 minimumTrackTintColor={theme.colors.primary} 139 maximumTrackTintColor={theme.colors.text ?? '#999999'} 140 thumbTintColor={theme.colors.primary} 141 minimumValue={0} 142 step={1} 143 maximumValue={30} 144 onSlidingComplete={val => { 145 Theme.setBackground({ 146 blur: val, 147 }); 148 }} 149 value={backgroundInfo?.blur ?? 20} 150 /> 151 </View> 152 <View style={styles.sliderWrapper}> 153 <ThemeText>透明度</ThemeText> 154 <Slider 155 style={styles.slider} 156 minimumTrackTintColor={theme.colors.primary} 157 maximumTrackTintColor={theme.colors.text ?? '#999999'} 158 thumbTintColor={theme.colors.primary} 159 minimumValue={0.3} 160 step={0.01} 161 maximumValue={1} 162 onSlidingComplete={val => { 163 Theme.setBackground({ 164 opacity: val, 165 }); 166 }} 167 value={backgroundInfo?.opacity ?? 0.7} 168 /> 169 </View> 170 <View style={styles.colorsContainer}> 171 {Theme.configableColorKey.map(key => ( 172 <View key={key} style={styles.colorItem}> 173 <ThemeText>{Theme.colorDesc[key]}</ThemeText> 174 <TouchableOpacity 175 onPress={() => { 176 showPanel('ColorPicker', { 177 // @ts-ignore 178 defaultColor: theme.colors[key], 179 onSelected(color) { 180 Theme.setColors({ 181 [key]: color.hexa().toString(), 182 }); 183 }, 184 }); 185 }} 186 style={styles.colorItemBlockContainer}> 187 <View style={[styles.colorBlockContainer]}> 188 <Image 189 resizeMode="repeat" 190 emptySrc={ImgAsset.transparentBg} 191 style={styles.transparentBg} 192 /> 193 <View 194 style={[ 195 { 196 /** @ts-ignore */ 197 backgroundColor: theme.colors[key], 198 }, 199 styles.colorBlock, 200 ]} 201 /> 202 </View> 203 <ThemeText 204 fontSize="subTitle" 205 style={styles.colorText}> 206 { 207 /** @ts-ignore */ 208 Color(theme.colors[key]).hexa().toString() 209 } 210 </ThemeText> 211 </TouchableOpacity> 212 </View> 213 ))} 214 </View> 215 </ScrollView> 216 ); 217} 218 219const styles = StyleSheet.create({ 220 container: { 221 width: '100%', 222 flex: 1, 223 }, 224 image: { 225 marginTop: rpx(36), 226 borderRadius: rpx(12), 227 width: rpx(460), 228 height: rpx(690), 229 alignSelf: 'center', 230 }, 231 sliderWrapper: { 232 marginTop: rpx(48), 233 width: '100%', 234 paddingHorizontal: rpx(24), 235 flexDirection: 'row', 236 justifyContent: 'space-between', 237 alignItems: 'center', 238 }, 239 slider: { 240 flex: 1, 241 height: rpx(40), 242 }, 243 colorsContainer: { 244 width: '100%', 245 flex: 1, 246 flexDirection: 'row', 247 flexWrap: 'wrap', 248 marginTop: rpx(48), 249 paddingHorizontal: rpx(24), 250 justifyContent: 'space-between', 251 }, 252 colorItem: { 253 flex: 1, 254 flexBasis: '40%', 255 marginBottom: rpx(36), 256 }, 257 colorBlockContainer: { 258 width: rpx(76), 259 height: rpx(50), 260 borderWidth: 1, 261 borderStyle: 'solid', 262 borderColor: '#ccc', 263 }, 264 colorBlock: { 265 width: '100%', 266 height: '100%', 267 position: 'absolute', 268 top: 0, 269 left: 0, 270 zIndex: 2, 271 }, 272 colorItemBlockContainer: { 273 marginTop: rpx(18), 274 flexDirection: 'row', 275 alignItems: 'center', 276 }, 277 colorText: { 278 marginLeft: rpx(8), 279 }, 280 transparentBg: { 281 position: 'absolute', 282 zIndex: -1, 283 width: '100%', 284 height: '100%', 285 left: 0, 286 top: 0, 287 }, 288}); 289