Skip to content

Commit

Permalink
feat: optimize image layout
Browse files Browse the repository at this point in the history
  • Loading branch information
liaoxuan committed Sep 27, 2023
1 parent df0c31e commit 19c5d97
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 22 deletions.
20 changes: 18 additions & 2 deletions components/Html/ImageRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ import { Pressable } from 'react-native'
import { CustomBlockRenderer } from 'react-native-render-html'

import { isSvgURL } from '@/utils/url'
import { useScreenWidth } from '@/utils/useScreenWidth'

import StyledImage from '../StyledImage'
import { HtmlContext } from './HtmlContext'

const ImageRenderer: CustomBlockRenderer = ({ tnode, style }) => {
const { onPreview } = useContext(HtmlContext)
const { onPreview, paddingX } = useContext(HtmlContext)

const url = useMemo(() => {
const $ = load(tnode.domNode as unknown as string)
return $('img').attr('src')
}, [tnode.domNode])

const screenWidth = useScreenWidth()
const containerWidth = !ancestorHasTdTag(tnode)
? screenWidth - paddingX
: undefined

if (url && isSvgURL(url))
return <StyledImage style={style as any} source={{ uri: url }} />

Expand All @@ -26,9 +32,19 @@ const ImageRenderer: CustomBlockRenderer = ({ tnode, style }) => {
if (url) onPreview(url)
}}
>
<StyledImage style={style as any} source={{ uri: url }} />
<StyledImage
style={style as any}
source={{ uri: url }}
containerWidth={containerWidth}
/>
</Pressable>
)
}

function ancestorHasTdTag(parent: any): boolean {
if (!parent) return false
if (parent.tagName === 'td') return true
return ancestorHasTdTag(parent.parent)
}

export default ImageRenderer
61 changes: 48 additions & 13 deletions components/StyledImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@ import { isSvgURL, resolveURL } from '@/utils/url'

const uriToSize = new Map()

function CustomImage({ style, source, onLoad, onError, ...props }: ImageProps) {
export interface StyledImageProps extends ImageProps {
containerWidth?: number
}

function CustomImage({
style,
source,
onLoad,
onError,
containerWidth,
...props
}: StyledImageProps) {
const uri =
isObject(source) && !isArray(source) && isString(source.uri)
? resolveURL(source.uri)
Expand All @@ -31,10 +42,6 @@ function CustomImage({ style, source, onLoad, onError, ...props }: ImageProps) {

const hadPassedSize = hasSize(style)

const isMiniImage = hasSize(size)
? size.width < 100 && size.height < 100
: false

return (
<Image
{...props}
Expand All @@ -60,13 +67,7 @@ function CustomImage({ style, source, onLoad, onError, ...props }: ImageProps) {
onError?.(err)
}}
style={tw.style(
!hadPassedSize &&
(isMiniImage
? size
: {
aspectRatio: size ? size.width / size.height : 1,
width: `100%`,
}),
!hadPassedSize && computeImageSize(size, containerWidth),
!hadPassedSize && uriToSize.has(uri) && !uriToSize.get(uri) && `hidden`,
style as ViewStyle,
isLoading && `img-loading`
Expand All @@ -75,6 +76,40 @@ function CustomImage({ style, source, onLoad, onError, ...props }: ImageProps) {
)
}

function computeImageSize(
size?: { width: number; height: number },
containerWidth?: number
) {
if (!hasSize(size)) {
return {
aspectRatio: 1,
width: `100%`,
}
}

const isMiniImage = size.width < 100 && size.height < 100

if (isMiniImage) {
return size
}

const aspectRatio = size.width / size.height

if (!containerWidth) {
return {
aspectRatio,
width: `100%`,
}
}

const actualWidth = Math.min(aspectRatio * 510, containerWidth)

return {
width: actualWidth,
height: actualWidth / aspectRatio,
}
}

function CustomSvgUri({ uri, style, ...props }: UriProps) {
const { data: svg, error } = useQuery({
query: svgQuery,
Expand Down Expand Up @@ -118,7 +153,7 @@ function CustomSvgUri({ uri, style, ...props }: UriProps) {
)
}

export default function StyledImage({ source, ...props }: ImageProps) {
export default function StyledImage({ source, ...props }: StyledImageProps) {
if (
isObject(source) &&
!isArray(source) &&
Expand Down
10 changes: 3 additions & 7 deletions screens/SortTabsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MaterialCommunityIcons } from '@expo/vector-icons'
import { useAtom, useAtomValue } from 'jotai'
import { findIndex, isEmpty, some } from 'lodash-es'
import { useCallback, useMemo, useState } from 'react'
import { Pressable, Text, View, useWindowDimensions } from 'react-native'
import { Pressable, Text, View } from 'react-native'
import { DragSortableView } from 'react-native-drag-sort'
import { SafeAreaView } from 'react-native-safe-area-context'

Expand All @@ -15,15 +15,11 @@ import {
} from '@/jotai/homeTabsAtom'
import { colorSchemeAtom } from '@/jotai/themeAtom'
import { navigation } from '@/navigation/navigationRef'
import { useIsLargeTablet, useIsTablet } from '@/utils/tablet'
import tw from '@/utils/tw'
import { useScreenWidth } from '@/utils/useScreenWidth'

export default function SortTabsScreen() {
const { width } = useWindowDimensions()
const isTablet = useIsTablet()
const isLargeTablet = useIsLargeTablet()
const parentWidth =
(isTablet ? width - 74 - (isLargeTablet ? 400 : 0) : width) - 24
const parentWidth = useScreenWidth() - 24
const itemWidth = parentWidth / Math.ceil(parentWidth / 100)
const itemHeight = 36
const [homeTabs, setHomeTabs] = useAtom(homeTabsAtom)
Expand Down
14 changes: 14 additions & 0 deletions utils/useScreenWidth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useWindowDimensions } from 'react-native'

import { useIsLargeTablet, useIsTablet } from './tablet'

/**
* 屏幕宽度
* 适配 iPad 布局
*/
export function useScreenWidth() {
const { width } = useWindowDimensions()
const isTablet = useIsTablet()
const isLargeTablet = useIsLargeTablet()
return isTablet ? width - 74 - (isLargeTablet ? 400 : 0) : width
}

0 comments on commit 19c5d97

Please sign in to comment.