Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(image): image component optimization. #1738

Merged
merged 10 commits into from
Dec 12, 2024
106 changes: 75 additions & 31 deletions packages/webpack-plugin/lib/runtime/components/react/mpx-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ import {
DimensionValue,
ImageLoadEventData
} from 'react-native'
import { noop } from '@mpxjs/utils'
import { SvgCssUri } from 'react-native-svg/css'
import useInnerProps, { getCustomEvent } from './getInnerListeners'
import useNodesRef, { HandlerRef } from './useNodesRef'
import { SVG_REGEXP, useLayout, useTransformStyle, extendObject } from './utils'
import { SVG_REGEXP, useLayout, useTransformStyle, renderImage, extendObject } from './utils'

export type Mode =
| 'scaleToFill'
Expand Down Expand Up @@ -54,10 +55,19 @@ export interface ImageProps {
'parent-font-size'?: number
'parent-width'?: number
'parent-height'?: number
'enable-fast-image'?: boolean
bindload?: (evt: NativeSyntheticEvent<ImageLoadEventData> | unknown) => void
binderror?: (evt: NativeSyntheticEvent<ImageErrorEventData> | unknown) => void
}

interface ImageState {
viewWidth?: number
viewHeight?: number
imageWidth?: number
imageHeight?: number
ratio?: number
}

const DEFAULT_IMAGE_WIDTH = 320
const DEFAULT_IMAGE_HEIGHT = 240

Expand Down Expand Up @@ -101,6 +111,7 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
'enable-var': enableVar,
'external-var-context': externalVarContext,
'parent-font-size': parentFontSize,
'enable-fast-image': enableFastImage,
'parent-width': parentWidth,
'parent-height': parentHeight,
bindload,
Expand All @@ -119,35 +130,54 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
{ overflow: 'hidden' }
)

const nodeRef = useRef(null)

const onLayout = ({ nativeEvent: { layout: { width, height } } }: LayoutChangeEvent) => {
setViewWidth(width)
setViewHeight(height)
}

const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })
const state = useRef<ImageState>({})

const nodeRef = useRef(null)
useNodesRef(props, ref, nodeRef, {
style: normalStyle
defaultStyle
})

const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef, onLayout })

const { width, height } = normalStyle

const isSvg = SVG_REGEXP.test(src)
const isWidthFixMode = mode === 'widthFix'
const isHeightFixMode = mode === 'heightFix'
const isCropMode = cropMode.includes(mode)
const isLayoutMode = isWidthFixMode || isHeightFixMode || isCropMode
const resizeMode: ImageResizeMode = ModeMap.get(mode) || 'stretch'

const onLayout = ({ nativeEvent: { layout: { width, height } } }: LayoutChangeEvent) => {
state.current.viewWidth = width
state.current.viewHeight = height

if (state.current.imageWidth && state.current.imageHeight && state.current.ratio) {
setViewWidth(width)
setViewHeight(height)
setRatio(state.current.ratio)
setImageWidth(state.current.imageWidth)
setImageHeight(state.current.imageHeight)
state.current = {}
setLoaded(true)
}
}

const { normalStyle, hasSelfPercent, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight })

const { layoutRef, layoutStyle, layoutProps } = useLayout({
props,
hasSelfPercent,
setWidth,
setHeight,
nodeRef,
onLayout: isLayoutMode ? onLayout : noop
})

const { width, height } = normalStyle

const [viewWidth, setViewWidth] = useState(isNumber(width) ? width : 0)
const [viewHeight, setViewHeight] = useState(isNumber(height) ? height : 0)
const [imageWidth, setImageWidth] = useState(0)
const [imageHeight, setImageHeight] = useState(0)
const [ratio, setRatio] = useState(0)
const [loaded, setLoaded] = useState(!isLayoutMode)

const fixedHeight = useMemo(() => {
const fixed = viewWidth * ratio
Expand Down Expand Up @@ -327,9 +357,23 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
useEffect(() => {
if (!isSvg && isLayoutMode) {
RNImage.getSize(src, (width: number, height: number) => {
setRatio(!width ? 0 : height / width)
setImageWidth(width)
setImageHeight(height)
state.current.imageWidth = width
state.current.imageHeight = height
state.current.ratio = !width ? 0 : height / width

if (isWidthFixMode
? state.current.viewWidth
: isHeightFixMode
? state.current.viewHeight
: state.current.viewWidth && state.current.viewHeight) {
state.current.viewWidth && setViewWidth(state.current.viewWidth)
state.current.viewHeight && setViewHeight(state.current.viewHeight)
setRatio(!width ? 0 : height / width)
setImageWidth(width)
setImageHeight(height)
state.current = {}
setLoaded(true)
}
})
}
}, [src, isSvg, isLayoutMode])
Expand Down Expand Up @@ -372,20 +416,20 @@ const Image = forwardRef<HandlerRef<RNImage, ImageProps>, ImageProps>((props, re
modeStyle
)}
/>
: <RNImage
source={{ uri: src }}
resizeMode={resizeMode}
onLoad={bindload && onImageLoad}
onError={binderror && onImageError}
style={extendObject(
{
transformOrigin: 'top left',
width: isCropMode ? imageWidth : '100%',
height: isCropMode ? imageHeight : '100%'
},
isCropMode ? modeStyle : {}
)}
/>
: loaded && renderImage({
source: { uri: src },
resizeMode: resizeMode,
onLoad: bindload && onImageLoad,
onError: binderror && onImageError,
style: extendObject(
{
transformOrigin: 'top left',
width: isCropMode ? imageWidth : '100%',
height: isCropMode ? imageHeight : '100%'
},
isCropMode ? modeStyle : {}
)
}, enableFastImage)
}
</View>
)
Expand Down
Loading