From 460aeb05c9dddb5635f0006f4dfd0cd317513d8a Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 27 Nov 2024 19:29:58 +0800 Subject: [PATCH 01/35] feat: update initialOffset --- .../components/react/mpx-picker-view-column.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index ff9b724907..4da7b850ac 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -71,7 +71,7 @@ const _PickerViewColumn = forwardRef, const initialOffset = useMemo(() => ({ x: 0, y: itemRawH * initialIndex - }), [itemRawH]) + }), [itemRawH, initialIndex]) const snapToOffsets = useMemo( () => columnData.map((_, i) => i * itemRawH), @@ -124,14 +124,6 @@ const _PickerViewColumn = forwardRef, onLayout: onScrollViewLayout }) - const onContentSizeChange = (w: number, h: number) => { - scrollViewRef.current?.scrollTo({ - x: 0, - y: itemRawH * initialIndex, - animated: false - }) - } - const onItemLayout = (e: LayoutChangeEvent) => { const { height: rawH } = e.nativeEvent.layout if (rawH && itemRawH !== rawH) { @@ -257,7 +249,6 @@ const _PickerViewColumn = forwardRef, contentContainerStyle={contentContainerStyle} contentOffset={initialOffset} snapToOffsets={snapToOffsets} - onContentSizeChange={onContentSizeChange} onScroll={onScroll} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} From 41015c2e1749aae50b427f01e300a7f6326208e7 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Fri, 29 Nov 2024 17:03:09 +0800 Subject: [PATCH 02/35] feat: add mask & remove animated value --- .../react/mpx-picker-view-column.tsx | 110 +++++++----------- .../components/react/mpx-picker-view.tsx | 10 +- .../components/react/pickerViewMask.tsx | 52 +++++++++ ...ickerOverlay.tsx => pickerViewOverlay.tsx} | 8 +- 4 files changed, 100 insertions(+), 80 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx rename packages/webpack-plugin/lib/runtime/components/react/{pickerOverlay.tsx => pickerViewOverlay.tsx} (74%) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 4da7b850ac..2edd9241cd 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,16 +1,15 @@ -import { View, Animated, SafeAreaView, NativeScrollEvent, NativeSyntheticEvent, LayoutChangeEvent, ScrollView } from 'react-native' -import React, { forwardRef, useRef, useState, useMemo, useCallback, useEffect } from 'react' +import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, View } from 'react-native' +import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react' import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, usePrevious } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' -import { createFaces } from './pickerFaces' -import PickerOverlay from './pickerOverlay' +import PickerOverlay from './pickerViewOverlay' +import PickerMask from './pickerViewMask' interface ColumnProps { children?: React.ReactNode columnData: React.ReactNode[] initialIndex: number - onColumnItemRawHChange: Function getInnerLayout: Function onSelectChange: Function style: { @@ -26,10 +25,7 @@ interface ColumnProps { columnIndex: number } -// 默认的单个选项高度 const DefaultPickerItemH = 36 -// 默认一屏可见选项个数 -const visibleCount = 5 const _PickerViewColumn = forwardRef, ColumnProps>((props: ColumnProps, ref) => { const { @@ -37,7 +33,6 @@ const _PickerViewColumn = forwardRef, columnIndex, initialIndex, onSelectChange, - onColumnItemRawHChange, getInnerLayout, style, wrapperStyle, @@ -60,7 +55,7 @@ const _PickerViewColumn = forwardRef, useNodesRef(props, ref, scrollViewRef, {}) const { height: pickerH, itemHeight = DefaultPickerItemH } = wrapperStyle - const [itemRawH, setItemRawH] = useState(0) // 单个选项真实渲染高度 + const [itemRawH, setItemRawH] = useState(0) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) const touching = useRef(false) const scrolling = useRef(false) @@ -71,20 +66,32 @@ const _PickerViewColumn = forwardRef, const initialOffset = useMemo(() => ({ x: 0, y: itemRawH * initialIndex - }), [itemRawH, initialIndex]) + }), [itemRawH]) const snapToOffsets = useMemo( () => columnData.map((_, i) => i * itemRawH), [columnData, itemRawH] ) + const paddingHeight = useMemo( + () => Math.round(pickerH - itemRawH) / 2, + [pickerH, itemRawH] + ) + const contentContainerStyle = useMemo(() => { - return [ - { - paddingVertical: Math.round(pickerH - itemRawH) / 2 - } - ] - }, [pickerH, itemRawH]) + return [{ paddingVertical: paddingHeight }] + }, [paddingHeight]) + + const onContentSizeChange = (_w: number, h: number) => { + if (itemRawH * initialIndex > h) { + return + } + scrollViewRef.current?.scrollTo({ + x: 0, + y: itemRawH * initialIndex, + animated: false + }) + } useEffect(() => { if ( @@ -128,7 +135,6 @@ const _PickerViewColumn = forwardRef, const { height: rawH } = e.nativeEvent.layout if (rawH && itemRawH !== rawH) { setItemRawH(rawH) - onColumnItemRawHChange(rawH) } } @@ -162,61 +168,18 @@ const _PickerViewColumn = forwardRef, } } - const offsetY = useRef(new Animated.Value(0)).current - - const onScroll = useMemo( - () => - Animated.event([{ nativeEvent: { contentOffset: { y: offsetY } } }], { - useNativeDriver: true - }), - [offsetY] - ) - - const faces = useMemo(() => createFaces(itemRawH, visibleCount), [itemRawH]) - - const getTransform = useCallback( - (index: number) => { - const inputRange = faces.map((f) => itemRawH * (index + f.index)) - return { - opacity: offsetY.interpolate({ - inputRange: inputRange, - outputRange: faces.map((x) => x.opacity), - extrapolate: 'clamp' - }), - rotateX: offsetY.interpolate({ - inputRange: inputRange, - outputRange: faces.map((x) => `${x.deg}deg`), - extrapolate: 'extend' - }), - translateY: offsetY.interpolate({ - inputRange: inputRange, - outputRange: faces.map((x) => x.offsetY), - extrapolate: 'extend' - }) - } - }, - [offsetY, faces, itemRawH] - ) - const renderInnerchild = () => columnData.map((item: React.ReactNode, index: number) => { const InnerProps = index === 0 ? { onLayout: onItemLayout } : {} const strKey = `picker-column-${columnIndex}-${index}` - const { opacity, rotateX, translateY } = getTransform(index) return ( - @@ -229,13 +192,13 @@ const _PickerViewColumn = forwardRef, textProps } )} - + ) }) const renderScollView = () => { return ( - , {...layoutProps} scrollEventThrottle={16} contentContainerStyle={contentContainerStyle} + onContentSizeChange={onContentSizeChange} contentOffset={initialOffset} snapToOffsets={snapToOffsets} - onScroll={onScroll} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} onTouchCancel={onTouchCancel} @@ -257,17 +220,28 @@ const _PickerViewColumn = forwardRef, onMomentumScrollEnd={onMomentumScrollEnd} > {renderInnerchild()} - + ) } const renderOverlay = () => ( - + + ) + + const renderMask = () => ( + ) return ( {renderScollView()} + {renderMask()} {renderOverlay()} ) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 67c6ed89c9..9a8aee6af8 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -1,5 +1,5 @@ import { View } from 'react-native' -import React, { forwardRef, useState, useRef } from 'react' +import React, { forwardRef, useRef } from 'react' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' import { @@ -76,7 +76,6 @@ const _PickerView = forwardRef, PickerViewProp // 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传 const indicatorStyle = parseInlineStyle(props['indicator-style']) const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle - const [pickMaxH, setPickMaxH] = useState(0) const nodeRef = useRef(null) const cloneRef = useRef(null) const activeValueRef = useRef(value) @@ -100,12 +99,6 @@ const _PickerView = forwardRef, PickerViewProp const { textProps } = splitProps(props) const { textStyle } = splitStyle(normalStyle) - const onColumnItemRawHChange = (height: number) => { - if (height > pickMaxH) { - setPickMaxH(height) - } - } - const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 300) const onSelectChange = (columnIndex: number, selectedIndex: number) => { @@ -161,7 +154,6 @@ const _PickerView = forwardRef, PickerViewProp height: normalStyle?.height || 0, itemHeight: indicatorH || 0 }, - onColumnItemRawHChange, onSelectChange: onSelectChange.bind(null, index), initialIndex, pickerOverlayStyle diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx new file mode 100644 index 0000000000..6a815c4298 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx @@ -0,0 +1,52 @@ +import React from 'react' +import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' +import LinearGradient from 'react-native-linear-gradient' + +type OverlayProps = { + height: number; + itemHeight: number; + maskContainerStyle?: StyleProp; +}; + +const _PickerViewMask = ({ + height, + itemHeight, + maskContainerStyle +}: OverlayProps) => { + return ( + + + + + ) +} +const styles = StyleSheet.create({ + overlayContainer: { + ...StyleSheet.absoluteFillObject, + zIndex: 100 + } +}) + +_PickerViewMask.displayName = 'MpxPickerViewMask' +export default _PickerViewMask diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerOverlay.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx similarity index 74% rename from packages/webpack-plugin/lib/runtime/components/react/pickerOverlay.tsx rename to packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx index 4a11903d6e..aeebec3f21 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerOverlay.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx @@ -7,7 +7,7 @@ type OverlayProps = { overlayContainerStyle?: StyleProp } -const Overlay = ({ itemHeight, overlayItemStyle, overlayContainerStyle }: OverlayProps) => { +const _PickerViewOverlay = ({ itemHeight, overlayItemStyle, overlayContainerStyle }: OverlayProps) => { return ( @@ -19,7 +19,8 @@ const styles = StyleSheet.create({ overlayContainer: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', - alignItems: 'center' + alignItems: 'center', + zIndex: 200 }, selection: { borderTopWidth: 1, @@ -29,4 +30,5 @@ const styles = StyleSheet.create({ } }) -export default React.memo(Overlay) +_PickerViewOverlay.displayName = 'MpxPickerViewOverlay' +export default _PickerViewOverlay From 81d394c4b4fd1df4dd52a3a1de912f75da55d883 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Fri, 29 Nov 2024 17:34:02 +0800 Subject: [PATCH 03/35] feat: support mask-style prop --- .../runtime/components/react/mpx-picker-view-column.tsx | 3 +++ .../lib/runtime/components/react/mpx-picker-view.tsx | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 2edd9241cd..1fbb58ba32 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -21,6 +21,7 @@ interface ColumnProps { height: number itemHeight: number } + pickerMaskStyle: Record pickerOverlayStyle: Record columnIndex: number } @@ -36,6 +37,7 @@ const _PickerViewColumn = forwardRef, getInnerLayout, style, wrapperStyle, + pickerMaskStyle, pickerOverlayStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext @@ -235,6 +237,7 @@ const _PickerViewColumn = forwardRef, ) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 9a8aee6af8..dcfcbece17 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -21,8 +21,9 @@ import type { AnyFunc } from './types/common' * ✘ bindpickend * ✘ mask-class * ✔ indicator-style: 优先级indicator-style.height > pick-view-column中的子元素设置的height + * WebView Only: * ✘ indicator-class - * ✘ mask-style + * ✔ mask-style * ✘ immediate-change */ @@ -36,6 +37,7 @@ interface PickerViewProps { [key: string]: any } 'indicator-style'?: string + 'mask-style'?: string 'enable-var': boolean 'external-var-context'?: Record, 'enable-offset': boolean @@ -75,6 +77,7 @@ const _PickerView = forwardRef, PickerViewProp // indicatorStyle 需要转换为rn的style // 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传 const indicatorStyle = parseInlineStyle(props['indicator-style']) + const pickerMaskStyle = parseInlineStyle(props['mask-style']) const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle const nodeRef = useRef(null) const cloneRef = useRef(null) @@ -156,7 +159,8 @@ const _PickerView = forwardRef, PickerViewProp }, onSelectChange: onSelectChange.bind(null, index), initialIndex, - pickerOverlayStyle + pickerOverlayStyle, + pickerMaskStyle }, extraProps ) From 10ebd1a6fdccbb6b3bd02105900248ba9890add6 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Fri, 29 Nov 2024 18:39:57 +0800 Subject: [PATCH 04/35] fix: default picker item height --- .../react/mpx-picker-view-column.tsx | 9 ++--- .../components/react/mpx-picker-view.tsx | 6 ++-- .../components/react/pickerViewMask.tsx | 36 ++++--------------- 3 files changed, 14 insertions(+), 37 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 1fbb58ba32..919ba27f45 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -26,8 +26,6 @@ interface ColumnProps { columnIndex: number } -const DefaultPickerItemH = 36 - const _PickerViewColumn = forwardRef, ColumnProps>((props: ColumnProps, ref) => { const { columnData, @@ -56,8 +54,8 @@ const _PickerViewColumn = forwardRef, const scrollViewRef = useRef(null) useNodesRef(props, ref, scrollViewRef, {}) - const { height: pickerH, itemHeight = DefaultPickerItemH } = wrapperStyle - const [itemRawH, setItemRawH] = useState(0) + const { height: pickerH, itemHeight } = wrapperStyle + const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) const touching = useRef(false) const scrolling = useRef(false) @@ -180,7 +178,7 @@ const _PickerViewColumn = forwardRef, {...InnerProps} style={[ { - height: itemHeight || DefaultPickerItemH, + height: itemHeight, width: '100%' } ]} @@ -235,7 +233,6 @@ const _PickerViewColumn = forwardRef, const renderMask = () => ( diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index dcfcbece17..28385b5a61 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -64,6 +64,8 @@ const styles: { [key: string]: Object } = { } } +const DefaultPickerItemH = 36 + const _PickerView = forwardRef, PickerViewProps>((props: PickerViewProps, ref) => { const { children, @@ -154,8 +156,8 @@ const _PickerView = forwardRef, PickerViewProp columnIndex: index, key: `pick-view-${index}`, wrapperStyle: { - height: normalStyle?.height || 0, - itemHeight: indicatorH || 0 + height: normalStyle?.height || DefaultPickerItemH, + itemHeight: indicatorH || DefaultPickerItemH }, onSelectChange: onSelectChange.bind(null, index), initialIndex, diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx index 6a815c4298..3c8773fc39 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx @@ -3,41 +3,19 @@ import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' import LinearGradient from 'react-native-linear-gradient' type OverlayProps = { - height: number; - itemHeight: number; - maskContainerStyle?: StyleProp; -}; + itemHeight: number + maskContainerStyle?: StyleProp +} const _PickerViewMask = ({ - height, itemHeight, maskContainerStyle }: OverlayProps) => { return ( - - - + + + + ) } From 1b897d7b600e13537b0f2e94bd8eea10dfb93b18 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Mon, 2 Dec 2024 20:08:21 +0800 Subject: [PATCH 05/35] fix: inherit font-related styles from picker-view --- .../react/mpx-picker-view-column.tsx | 57 ++++++++++--------- .../components/react/mpx-picker-view.tsx | 7 +-- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 919ba27f45..4b77bc95f8 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,7 +1,7 @@ import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, View } from 'react-native' -import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react' -import { useTransformStyle, splitStyle, splitProps, wrapChildren, useLayout, usePrevious } from './utils' +import React, { forwardRef, useRef, useState, useMemo, useEffect, cloneElement } from 'react' +import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, extendObject, wrapChildren } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' import PickerMask from './pickerViewMask' @@ -9,6 +9,7 @@ import PickerMask from './pickerViewMask' interface ColumnProps { children?: React.ReactNode columnData: React.ReactNode[] + columnStyle: Record initialIndex: number getInnerLayout: Function onSelectChange: Function @@ -30,6 +31,7 @@ const _PickerViewColumn = forwardRef, const { columnData, columnIndex, + columnStyle, initialIndex, onSelectChange, getInnerLayout, @@ -49,7 +51,8 @@ const _PickerViewColumn = forwardRef, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext }) - const { textStyle } = splitStyle(normalStyle) + const { textStyle: textStyleFromParent = {} } = splitStyle(columnStyle) + const { textStyle = {} } = splitStyle(normalStyle) const { textProps } = splitProps(props) const scrollViewRef = useRef(null) useNodesRef(props, ref, scrollViewRef, {}) @@ -169,30 +172,29 @@ const _PickerViewColumn = forwardRef, } const renderInnerchild = () => - columnData.map((item: React.ReactNode, index: number) => { - const InnerProps = index === 0 ? { onLayout: onItemLayout } : {} - const strKey = `picker-column-${columnIndex}-${index}` - return ( - - {wrapChildren( - { children: item }, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } - )} - + columnData.map((item: React.ReactElement, index: number) => { + const restProps = index === 0 ? { onLayout: onItemLayout } : {} + const itemProps = extendObject( + { + key: `picker-column-item-${index}`, + style: extendObject( + { height: itemHeight, width: '100%' }, + textStyleFromParent, + textStyle, + item.props.style + ) + }, + restProps + ) + const realItem = cloneElement(item, itemProps) + return wrapChildren( + { children: realItem }, + { + hasVarDec, + varContext: varContextRef.current, + textStyle, + textProps + } ) }) @@ -208,6 +210,7 @@ const _PickerViewColumn = forwardRef, showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} {...layoutProps} + decelerationRate="fast" scrollEventThrottle={16} contentContainerStyle={contentContainerStyle} onContentSizeChange={onContentSizeChange} diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 28385b5a61..0a3c51c4d3 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -104,7 +104,7 @@ const _PickerView = forwardRef, PickerViewProp const { textProps } = splitProps(props) const { textStyle } = splitStyle(normalStyle) - const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 300) + const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 200) const onSelectChange = (columnIndex: number, selectedIndex: number) => { bindchangeDebounce.clear() @@ -146,7 +146,6 @@ const _PickerView = forwardRef, PickerViewProp ) const renderColumn = (child: React.ReactElement, index: number, columnData: React.ReactNode[], initialIndex: number) => { - const extraProps = {} const childProps = child?.props || {} const wrappedProps = extendObject( childProps, @@ -159,12 +158,12 @@ const _PickerView = forwardRef, PickerViewProp height: normalStyle?.height || DefaultPickerItemH, itemHeight: indicatorH || DefaultPickerItemH }, + columnStyle: normalStyle, onSelectChange: onSelectChange.bind(null, index), initialIndex, pickerOverlayStyle, pickerMaskStyle - }, - extraProps + } ) const realElement = React.cloneElement(child, wrappedProps) return wrapChildren( From 4899726cb07310e8b7749f38318f9188a4863638 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Tue, 3 Dec 2024 19:20:08 +0800 Subject: [PATCH 06/35] fix: integerize ScrollView width to prevent snapToOffsets failure --- .../react/mpx-picker-view-column.tsx | 44 ++++++++++++------- .../components/react/mpx-picker-view.tsx | 2 +- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 4b77bc95f8..eb3c663717 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,5 +1,5 @@ -import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, View } from 'react-native' +import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import React, { forwardRef, useRef, useState, useMemo, useEffect, cloneElement } from 'react' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, extendObject, wrapChildren } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' @@ -58,6 +58,7 @@ const _PickerViewColumn = forwardRef, useNodesRef(props, ref, scrollViewRef, {}) const { height: pickerH, itemHeight } = wrapperStyle + const [scrollViewWidth, setScrollViewWidth] = useState('100%') const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) const touching = useRef(false) @@ -85,17 +86,6 @@ const _PickerViewColumn = forwardRef, return [{ paddingVertical: paddingHeight }] }, [paddingHeight]) - const onContentSizeChange = (_w: number, h: number) => { - if (itemRawH * initialIndex > h) { - return - } - scrollViewRef.current?.scrollTo({ - x: 0, - y: itemRawH * initialIndex, - animated: false - }) - } - useEffect(() => { if ( !scrollViewRef.current || @@ -118,7 +108,7 @@ const _PickerViewColumn = forwardRef, }) }, [itemRawH, initialIndex]) - const onScrollViewLayout = () => { + const _onLayout = () => { getInnerLayout && getInnerLayout(layoutRef) } @@ -131,9 +121,23 @@ const _PickerViewColumn = forwardRef, setWidth, setHeight, nodeRef: scrollViewRef, - onLayout: onScrollViewLayout + onLayout: _onLayout }) + const onScrollViewLayout = (e: LayoutChangeEvent) => { + const widthInt = Math.round(e.nativeEvent.layout.width) + if (widthInt !== scrollViewWidth) { + setScrollViewWidth(widthInt) + } + } + + const onContentSizeChange = (_w: number, h: number) => { + const y = itemRawH * initialIndex + if (y <= h) { + scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) + } + } + const onItemLayout = (e: LayoutChangeEvent) => { const { height: rawH } = e.nativeEvent.layout if (rawH && itemRawH !== rawH) { @@ -210,17 +214,19 @@ const _PickerViewColumn = forwardRef, showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} {...layoutProps} + style={[{ width: scrollViewWidth }]} decelerationRate="fast" scrollEventThrottle={16} - contentContainerStyle={contentContainerStyle} - onContentSizeChange={onContentSizeChange} contentOffset={initialOffset} snapToOffsets={snapToOffsets} + onLayout={onScrollViewLayout} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} onTouchCancel={onTouchCancel} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} + onContentSizeChange={onContentSizeChange} + contentContainerStyle={contentContainerStyle} > {renderInnerchild()} @@ -242,7 +248,7 @@ const _PickerViewColumn = forwardRef, ) return ( - + {renderScollView()} {renderMask()} {renderOverlay()} @@ -250,5 +256,9 @@ const _PickerViewColumn = forwardRef, ) }) +const styles = StyleSheet.create({ + wrapper: { display: 'flex', flex: 1 } +}) + _PickerViewColumn.displayName = 'MpxPickerViewColumn' export default _PickerViewColumn diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 0a3c51c4d3..3f4cd26058 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -104,7 +104,7 @@ const _PickerView = forwardRef, PickerViewProp const { textProps } = splitProps(props) const { textStyle } = splitStyle(normalStyle) - const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 200) + const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 10) const onSelectChange = (columnIndex: number, selectedIndex: number) => { bindchangeDebounce.clear() From c4d3dd3959923d501248bdad75a46292768705bf Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 4 Dec 2024 14:23:15 +0800 Subject: [PATCH 07/35] perf: remove debounce --- .../runtime/components/react/mpx-picker-view.tsx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 3f4cd26058..d69923b0cc 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -9,8 +9,6 @@ import { wrapChildren, parseInlineStyle, useTransformStyle, - useDebounceCallback, - useStableCallback, extendObject } from './utils' import type { AnyFunc } from './types/common' @@ -29,8 +27,6 @@ import type { AnyFunc } from './types/common' interface PickerViewProps { children: React.ReactNode - // 初始的defaultValue数组中的数字依次表示 picker-view 内的 picker-view-column 选择的第几项(下标从 0 开始), - // 数字大于 picker-view-column 可选项长度时,选择最后一项。 value?: Array bindchange?: AnyFunc style: { @@ -75,9 +71,6 @@ const _PickerView = forwardRef, PickerViewProp 'enable-var': enableVar, 'external-var-context': externalVarContext } = props - - // indicatorStyle 需要转换为rn的style - // 微信设置到pick-view上上设置的normalStyle如border等需要转换成RN的style然后进行透传 const indicatorStyle = parseInlineStyle(props['indicator-style']) const pickerMaskStyle = parseInlineStyle(props['mask-style']) const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle @@ -96,7 +89,6 @@ const _PickerView = forwardRef, PickerViewProp } = useTransformStyle(style, { enableVar, externalVarContext }) useNodesRef(props, ref, nodeRef, {}) const { - // 存储layout布局信息 layoutRef, layoutProps, layoutStyle @@ -104,10 +96,7 @@ const _PickerView = forwardRef, PickerViewProp const { textProps } = splitProps(props) const { textStyle } = splitStyle(normalStyle) - const bindchangeDebounce = useDebounceCallback(useStableCallback(bindchange), 10) - const onSelectChange = (columnIndex: number, selectedIndex: number) => { - bindchangeDebounce.clear() const activeValue = activeValueRef.current activeValue[columnIndex] = selectedIndex const eventData = getCustomEvent( @@ -115,7 +104,7 @@ const _PickerView = forwardRef, PickerViewProp {}, { detail: { value: activeValue, source: 'change' }, layoutRef } ) - bindchangeDebounce(eventData) + bindchange?.(eventData) } const onInitialChange = (value: number[]) => { @@ -124,7 +113,7 @@ const _PickerView = forwardRef, PickerViewProp {}, { detail: { value, source: 'change' }, layoutRef } ) - bindchange?.(eventData) // immediate + bindchange?.(eventData) } const innerProps = useInnerProps( From e8936907d6c68625053af5a38987fa111fe4832f Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 4 Dec 2024 14:55:55 +0800 Subject: [PATCH 08/35] feat: update --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index eb3c663717..20da12d383 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -125,8 +125,9 @@ const _PickerViewColumn = forwardRef, }) const onScrollViewLayout = (e: LayoutChangeEvent) => { - const widthInt = Math.round(e.nativeEvent.layout.width) - if (widthInt !== scrollViewWidth) { + const width = e.nativeEvent.layout.width + const widthInt = Math.round(width) + if (width !== widthInt && widthInt !== scrollViewWidth) { setScrollViewWidth(widthInt) } } From bc3b8093a24c9fb9181695f19d9ab49e3f480617 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 4 Dec 2024 14:58:54 +0800 Subject: [PATCH 09/35] feat: update --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 20da12d383..79d2505595 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -125,7 +125,7 @@ const _PickerViewColumn = forwardRef, }) const onScrollViewLayout = (e: LayoutChangeEvent) => { - const width = e.nativeEvent.layout.width + const { width } = e.nativeEvent.layout const widthInt = Math.round(width) if (width !== widthInt && widthInt !== scrollViewWidth) { setScrollViewWidth(widthInt) From 791488c4f66d008b1a823cc3e9d3a237184a52ea Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Sat, 7 Dec 2024 15:16:27 +0800 Subject: [PATCH 10/35] fix: add column normal style --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 79d2505595..82663f0a89 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -249,7 +249,7 @@ const _PickerViewColumn = forwardRef, ) return ( - + {renderScollView()} {renderMask()} {renderOverlay()} From 08efcef3a263627a500145f1caccd4423d030934 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Thu, 12 Dec 2024 20:37:59 +0800 Subject: [PATCH 11/35] fix: scrollTo animate --- .../components/react/mpx-picker-view-column.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 82663f0a89..5b7c5b4e63 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,4 +1,3 @@ - import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import React, { forwardRef, useRef, useState, useMemo, useEffect, cloneElement } from 'react' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, extendObject, wrapChildren } from './utils' @@ -55,7 +54,10 @@ const _PickerViewColumn = forwardRef, const { textStyle = {} } = splitStyle(normalStyle) const { textProps } = splitProps(props) const scrollViewRef = useRef(null) - useNodesRef(props, ref, scrollViewRef, {}) + + useNodesRef(props, ref, scrollViewRef, { + style: normalStyle + }) const { height: pickerH, itemHeight } = wrapperStyle const [scrollViewWidth, setScrollViewWidth] = useState('100%') @@ -74,7 +76,7 @@ const _PickerViewColumn = forwardRef, const snapToOffsets = useMemo( () => columnData.map((_, i) => i * itemRawH), - [columnData, itemRawH] + [maxIndex, itemRawH] ) const paddingHeight = useMemo( @@ -104,7 +106,7 @@ const _PickerViewColumn = forwardRef, scrollViewRef.current.scrollTo({ x: 0, y: itemRawH * initialIndex, - animated: false + animated: true }) }, [itemRawH, initialIndex]) @@ -135,7 +137,7 @@ const _PickerViewColumn = forwardRef, const onContentSizeChange = (_w: number, h: number) => { const y = itemRawH * initialIndex if (y <= h) { - scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) + scrollViewRef.current?.scrollTo({ x: 0, y, animated: true }) } } From 99ffac9b68c50d1609d62946d1db150a1e66cec1 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Mon, 16 Dec 2024 15:59:57 +0800 Subject: [PATCH 12/35] feat: update --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 5b7c5b4e63..18ffcef564 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -106,7 +106,7 @@ const _PickerViewColumn = forwardRef, scrollViewRef.current.scrollTo({ x: 0, y: itemRawH * initialIndex, - animated: true + animated: false }) }, [itemRawH, initialIndex]) @@ -137,7 +137,7 @@ const _PickerViewColumn = forwardRef, const onContentSizeChange = (_w: number, h: number) => { const y = itemRawH * initialIndex if (y <= h) { - scrollViewRef.current?.scrollTo({ x: 0, y, animated: true }) + scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) } } From 050475ff36078936c4c912780581bcad3ca59a45 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Tue, 17 Dec 2024 20:00:06 +0800 Subject: [PATCH 13/35] refactor: reanimated --- .../react/mpx-picker-view-column-item.tsx | 85 ++++++++++ .../react/mpx-picker-view-column.tsx | 154 ++++++++---------- .../components/react/mpx-picker-view.tsx | 26 ++- .../components/react/pickerVIewContext.ts | 18 ++ 4 files changed, 192 insertions(+), 91 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx create mode 100644 packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx new file mode 100644 index 0000000000..4ee6eead43 --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -0,0 +1,85 @@ +import React, { memo, useEffect } from 'react' +import { LayoutChangeEvent } from 'react-native' +import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' +import { wrapChildren, extendObject } from './utils' +import { createFaces } from './pickerFaces' +import { usePickerViewColumnAnimationContext } from './pickerVIewContext' + +interface PickerColumnItemProps { + item: React.ReactElement + index: number + itemHeight: number + textStyleFromParent: Record + textStyle: Record + hasVarDec: boolean + varContext: Record + visibleCount: number + textProps?: any + onItemLayout?: (e: LayoutChangeEvent) => void +} + +const _PickerViewColumnItem: React.FC = ({ + item, + index, + itemHeight, + textStyleFromParent, + textStyle, + hasVarDec, + varContext, + textProps, + visibleCount, + onItemLayout +}) => { + const offsetYShared = usePickerViewColumnAnimationContext() + const facesShared = useSharedValue(createFaces(itemHeight, visibleCount)) + // const indexShared = useSharedValue(index) + + useEffect(() => { + facesShared.value = createFaces(itemHeight, visibleCount) + }, [itemHeight]) + + const animatedStyles = useAnimatedStyle(() => { + const inputRange = facesShared.value.map((f) => itemHeight * (index + f.index)) + return { + transform: [ + { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.EXTEND) + 'deg' }, + { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) } + ] + } + }) + + const strKey = `picker-column-item-${index}` + const restProps = index === 0 ? { onLayout: onItemLayout } : {} + const itemProps = extendObject( + { + style: extendObject( + { height: itemHeight, width: '100%' }, + textStyleFromParent, + textStyle, + item.props.style + ) + }, + restProps + ) + const realItem = React.cloneElement(item, itemProps) + + return ( + + {wrapChildren( + { children: realItem }, + { + hasVarDec, + varContext, + textStyle, + textProps + } + )} + + ) +} + +_PickerViewColumnItem.displayName = 'MpxPickerViewColumnItem' +export default _PickerViewColumnItem diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 18ffcef564..d00d86237a 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,16 +1,18 @@ +import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' -import React, { forwardRef, useRef, useState, useMemo, useEffect, cloneElement } from 'react' -import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, extendObject, wrapChildren } from './utils' +import Reanimated, { AnimatedRef, scrollTo, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useScrollViewOffset, useSharedValue } from 'react-native-reanimated' +import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' import PickerMask from './pickerViewMask' +import MpxPickerVIewColumnItem from './mpx-picker-view-column-item' +import { PickerViewColumnAnimationContext } from './pickerVIewContext' interface ColumnProps { children?: React.ReactNode columnData: React.ReactNode[] columnStyle: Record initialIndex: number - getInnerLayout: Function onSelectChange: Function style: { [key: string]: any @@ -26,6 +28,8 @@ interface ColumnProps { columnIndex: number } +const visibleCount = 5 + const _PickerViewColumn = forwardRef, ColumnProps>((props: ColumnProps, ref) => { const { columnData, @@ -33,7 +37,6 @@ const _PickerViewColumn = forwardRef, columnStyle, initialIndex, onSelectChange, - getInnerLayout, style, wrapperStyle, pickerMaskStyle, @@ -53,9 +56,11 @@ const _PickerViewColumn = forwardRef, const { textStyle: textStyleFromParent = {} } = splitStyle(columnStyle) const { textStyle = {} } = splitStyle(normalStyle) const { textProps } = splitProps(props) - const scrollViewRef = useRef(null) + // const scrollViewRef = useRef(null) + const scrollViewRef = useAnimatedRef() + const offsetYShared = useScrollViewOffset(scrollViewRef as AnimatedRef) - useNodesRef(props, ref, scrollViewRef, { + useNodesRef(props, ref, scrollViewRef as AnimatedRef, { style: normalStyle }) @@ -69,18 +74,28 @@ const _PickerViewColumn = forwardRef, const prevIndex = usePrevious(initialIndex) const prevMaxIndex = usePrevious(maxIndex) - const initialOffset = useMemo(() => ({ - x: 0, - y: itemRawH * initialIndex - }), [itemRawH]) + const { + layoutProps + } = useLayout({ + props, + hasSelfPercent, + setWidth, + setHeight, + nodeRef: scrollViewRef + }) + + // const initialOffset = useMemo(() => ({ + // x: 0, + // y: itemRawH * initialIndex + // }), [itemRawH]) const snapToOffsets = useMemo( () => columnData.map((_, i) => i * itemRawH), - [maxIndex, itemRawH] + [columnData, itemRawH] ) const paddingHeight = useMemo( - () => Math.round(pickerH - itemRawH) / 2, + () => Math.round((pickerH - itemRawH) / 2), [pickerH, itemRawH] ) @@ -101,31 +116,16 @@ const _PickerViewColumn = forwardRef, ) { return } - + setTimeout(() => { + scrollViewRef.current?.scrollTo({ + x: 0, + y: itemRawH * initialIndex, + animated: false + }) + }, 0) activeIndex.current = initialIndex - scrollViewRef.current.scrollTo({ - x: 0, - y: itemRawH * initialIndex, - animated: false - }) }, [itemRawH, initialIndex]) - const _onLayout = () => { - getInnerLayout && getInnerLayout(layoutRef) - } - - const { - layoutRef, - layoutProps - } = useLayout({ - props, - hasSelfPercent, - setWidth, - setHeight, - nodeRef: scrollViewRef, - onLayout: _onLayout - }) - const onScrollViewLayout = (e: LayoutChangeEvent) => { const { width } = e.nativeEvent.layout const widthInt = Math.round(width) @@ -180,59 +180,49 @@ const _PickerViewColumn = forwardRef, const renderInnerchild = () => columnData.map((item: React.ReactElement, index: number) => { - const restProps = index === 0 ? { onLayout: onItemLayout } : {} - const itemProps = extendObject( - { - key: `picker-column-item-${index}`, - style: extendObject( - { height: itemHeight, width: '100%' }, - textStyleFromParent, - textStyle, - item.props.style - ) - }, - restProps - ) - const realItem = cloneElement(item, itemProps) - return wrapChildren( - { children: realItem }, - { - hasVarDec, - varContext: varContextRef.current, - textStyle, - textProps - } + return ( + ) }) const renderScollView = () => { return ( - - {renderInnerchild()} - + + + {renderInnerchild()} + + ) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 4465b5482d..acf76e0c06 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -78,6 +78,7 @@ const _PickerView = forwardRef, PickerViewProp const cloneRef = useRef(null) const activeValueRef = useRef(value) activeValueRef.current = value.slice() + const snapActiveValueRef = useRef(null) const { normalStyle, @@ -109,15 +110,23 @@ const _PickerView = forwardRef, PickerViewProp { detail: { value: activeValue, source: 'change' }, layoutRef } ) bindchange?.(eventData) + snapActiveValueRef.current = activeValueRef.current } - const onInitialChange = (value: number[]) => { - const eventData = getCustomEvent( - 'change', - {}, - { detail: { value, source: 'change' }, layoutRef } - ) - bindchange?.(eventData) + const hasDiff = (a: number[] = [], b: number[]) => { + return a.some((v, i) => v !== b[i]) + } + + const onInitialChange = (isInvalid: boolean, value: number[]) => { + if (isInvalid || !snapActiveValueRef.current || hasDiff(snapActiveValueRef.current, value)) { + const eventData = getCustomEvent( + 'change', + {}, + { detail: { value, source: 'change' }, layoutRef } + ) + bindchange?.(eventData) + snapActiveValueRef.current = value.slice() + } } const innerProps = useInnerProps( @@ -201,7 +210,7 @@ const _PickerView = forwardRef, PickerViewProp validValue.push(validIndex) renderColumns.push(renderColumn(item, index, columnData, validIndex)) }) - isInvalid && onInitialChange(validValue) + onInitialChange(isInvalid, validValue) return renderColumns } @@ -213,5 +222,4 @@ const _PickerView = forwardRef, PickerViewProp }) _PickerView.displayName = 'MpxPickerView' - export default _PickerView diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts new file mode 100644 index 0000000000..6733ebb73d --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts @@ -0,0 +1,18 @@ +import { createContext, useContext } from 'react' +import { SharedValue } from 'react-native-reanimated' + +type ContextValue = SharedValue + +export const PickerViewColumnAnimationContext = createContext< + ContextValue | undefined +>(undefined) + +export const usePickerViewColumnAnimationContext = () => { + const value = useContext(PickerViewColumnAnimationContext) + if (value === undefined) { + throw new Error( + 'usePickerViewColumnAnimationContext must be called from within PickerViewColumnAnimationContext.Provider!' + ) + } + return value +} From d03eaf4d4e8cf9153845c06ae2532529c56c22c7 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Tue, 17 Dec 2024 21:08:49 +0800 Subject: [PATCH 14/35] fix: snapToOffsets --- .../components/react/mpx-picker-view-column.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index d00d86237a..469c17b55b 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,6 +1,6 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' -import Reanimated, { AnimatedRef, scrollTo, useAnimatedRef, useAnimatedScrollHandler, useDerivedValue, useScrollViewOffset, useSharedValue } from 'react-native-reanimated' +import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' @@ -68,6 +68,7 @@ const _PickerViewColumn = forwardRef, const [scrollViewWidth, setScrollViewWidth] = useState('100%') const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) + const maxScrollViewWidth = useRef(-1) const touching = useRef(false) const scrolling = useRef(false) const activeIndex = useRef(initialIndex) @@ -128,8 +129,15 @@ const _PickerViewColumn = forwardRef, const onScrollViewLayout = (e: LayoutChangeEvent) => { const { width } = e.nativeEvent.layout - const widthInt = Math.round(width) - if (width !== widthInt && widthInt !== scrollViewWidth) { + const widthInt = Math.ceil(width) + if (widthInt !== scrollViewWidth) { + const maxW = maxScrollViewWidth.current + if (maxW !== -1 && widthInt > maxW) { + return + } + if (maxW === -1) { + maxScrollViewWidth.current = Math.ceil(widthInt * 1.5) + } setScrollViewWidth(widthInt) } } From 3a31d170b13719723c1a6c0c1cd663640398c3ac Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 18 Dec 2024 19:28:01 +0800 Subject: [PATCH 15/35] feat: update style && add vibrate --- .../react/mpx-picker-view-column-item.tsx | 5 +-- .../react/mpx-picker-view-column.tsx | 33 ++++++++++++++++++- .../runtime/components/react/pickerFaces.ts | 22 +++++++++---- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx index 4ee6eead43..6b4e2344c2 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -32,7 +32,6 @@ const _PickerViewColumnItem: React.FC = ({ }) => { const offsetYShared = usePickerViewColumnAnimationContext() const facesShared = useSharedValue(createFaces(itemHeight, visibleCount)) - // const indexShared = useSharedValue(index) useEffect(() => { facesShared.value = createFaces(itemHeight, visibleCount) @@ -41,9 +40,11 @@ const _PickerViewColumnItem: React.FC = ({ const animatedStyles = useAnimatedStyle(() => { const inputRange = facesShared.value.map((f) => itemHeight * (index + f.index)) return { + opacity: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.opacity), Extrapolation.CLAMP), transform: [ { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.EXTEND) + 'deg' }, - { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) } + { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) }, + { scale: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.scale), Extrapolation.EXTEND) } ] } }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 469c17b55b..666706c3e3 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,6 +1,8 @@ -import React, { forwardRef, useRef, useState, useMemo, useEffect } from 'react' +import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' +// @ts-expect-error ignore +import { vibrateShort } from '@mpxjs/api-proxy' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' @@ -69,6 +71,7 @@ const _PickerViewColumn = forwardRef, const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) const maxScrollViewWidth = useRef(-1) + const prevScrollingInfo = useRef({ index: initialIndex, y: 0 }) const touching = useRef(false) const scrolling = useRef(false) const activeIndex = useRef(initialIndex) @@ -104,6 +107,11 @@ const _PickerViewColumn = forwardRef, return [{ paddingVertical: paddingHeight }] }, [paddingHeight]) + const getIndex = useCallback((y: number) => { + const calc = Math.round(y / itemRawH) + return Math.max(0, Math.min(calc, maxIndex)) + }, [itemRawH, maxIndex]) + useEffect(() => { if ( !scrollViewRef.current || @@ -158,6 +166,10 @@ const _PickerViewColumn = forwardRef, const onTouchStart = () => { touching.current = true + prevScrollingInfo.current = { + index: activeIndex.current, + y: activeIndex.current * itemRawH + } } const onTouchEnd = () => { @@ -186,6 +198,23 @@ const _PickerViewColumn = forwardRef, } } + const onScroll = (e: NativeSyntheticEvent) => { + const { y } = e.nativeEvent.contentOffset + const { index: prevIndex, y: _y } = prevScrollingInfo.current + if (touching.current || scrolling.current) { + if (Math.abs(y - _y) >= itemRawH) { + const currentId = getIndex(y) + if (currentId !== prevIndex) { + prevScrollingInfo.current = { + index: currentId, + y: currentId * itemRawH + } + vibrateShort() + } + } + } + } + const renderInnerchild = () => columnData.map((item: React.ReactElement, index: number) => { return ( @@ -215,10 +244,12 @@ const _PickerViewColumn = forwardRef, removeClippedSubviews={false} showsVerticalScrollIndicator={false} showsHorizontalScrollIndicator={false} + scrollEventThrottle={16} {...layoutProps} style={[{ width: scrollViewWidth }]} decelerationRate="fast" snapToOffsets={snapToOffsets} + onScroll={onScroll} onLayout={onScrollViewLayout} onTouchStart={onTouchStart} onTouchEnd={onTouchEnd} diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts index f25dc0313f..b5e852e890 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts @@ -8,6 +8,7 @@ export type Faces = { deg: number offsetY: number opacity: number + scale: number screenHeight: number } @@ -54,6 +55,9 @@ export const createFaces = ( for (let i = 0; i < index; i++) { offset += freeSpaces[i] } + if (index === 0) { + offset *= 0.6 + } return offset }) as unknown as T return [screenHeights, offsets] @@ -62,17 +66,21 @@ export const createFaces = ( const getOpacity = (index: number) => { const map: Record = { 0: 0, - 1: 0.2, - 2: 0.35, - 3: 0.45, - 4: 0.5 + 1: 0.8, + 2: 0.9 // 0.35 + // 3: 0.45, // 0.45 + // 4: 0.5 // 0.5 } - return map[index] ?? Math.min(1, map[4] + index * 0.5) + return map[index] ?? Math.min(1, map[2] + index * 0.05) } const degrees = getDegreesRelativeCenter() const [screenHeight, offsets] = getScreenHeightsAndOffsets(degrees) + const scale = (index: number) => { + return 0.92 - index * 0.1 + } + return [ // top items ...degrees @@ -82,13 +90,14 @@ export const createFaces = ( deg: degree, opacity: getOpacity(degrees.length - 1 - index), offsetY: -1 * offsets[index], + scale: scale(index), screenHeight: screenHeight[index] } }) .reverse(), // center item - { index: 0, deg: 0, opacity: 1, offsetY: 0, screenHeight: itemHeight }, + { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1, screenHeight: itemHeight }, // bottom items ...degrees.map((degree, index) => { @@ -97,6 +106,7 @@ export const createFaces = ( deg: -1 * degree, opacity: getOpacity(degrees.length - 1 - index), offsetY: offsets[index], + scale: scale(index), screenHeight: screenHeight[index] } }) From ad235ac110f751f606478862caa4edebd1b11f1d Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Wed, 18 Dec 2024 20:30:06 +0800 Subject: [PATCH 16/35] feat: update vibrate --- .../runtime/components/react/mpx-picker-view-column.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 666706c3e3..dd8a7ea52d 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' -import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' +import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, Platform, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' // @ts-expect-error ignore import { vibrateShort } from '@mpxjs/api-proxy' @@ -199,6 +199,9 @@ const _PickerViewColumn = forwardRef, } const onScroll = (e: NativeSyntheticEvent) => { + if (Platform.OS === 'android') { + return + } const { y } = e.nativeEvent.contentOffset const { index: prevIndex, y: _y } = prevScrollingInfo.current if (touching.current || scrolling.current) { @@ -209,7 +212,7 @@ const _PickerViewColumn = forwardRef, index: currentId, y: currentId * itemRawH } - vibrateShort() + vibrateShort({ type: 'selection' }) } } } From 6513c608825516c58a5010d9e25101c00295dd2b Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Thu, 19 Dec 2024 19:12:40 +0800 Subject: [PATCH 17/35] feat: update style --- .../components/react/mpx-picker-view-column-item.tsx | 4 ++-- .../lib/runtime/components/react/pickerFaces.ts | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx index 6b4e2344c2..12a8dda8d2 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect } from 'react' +import React, { useEffect } from 'react' import { LayoutChangeEvent } from 'react-native' import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { wrapChildren, extendObject } from './utils' @@ -42,7 +42,7 @@ const _PickerViewColumnItem: React.FC = ({ return { opacity: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.opacity), Extrapolation.CLAMP), transform: [ - { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.EXTEND) + 'deg' }, + { rotateX: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.deg), Extrapolation.CLAMP) + 'deg' }, { translateY: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.offsetY), Extrapolation.EXTEND) }, { scale: interpolate(offsetYShared.value, inputRange, facesShared.value.map((x) => x.scale), Extrapolation.EXTEND) } ] diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts index b5e852e890..06f76cb43c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts @@ -34,7 +34,7 @@ export const createFaces = ( const maxStep = Math.trunc((visibleCount + 2) / 2) // + 2 because there are 2 more faces at 90 degrees const stepDegree = 90 / maxStep - const result = [] + const result: number[] = [] for (let i = 1; i <= maxStep; i++) { result.push(i * stepDegree) } @@ -77,9 +77,7 @@ export const createFaces = ( const degrees = getDegreesRelativeCenter() const [screenHeight, offsets] = getScreenHeightsAndOffsets(degrees) - const scale = (index: number) => { - return 0.92 - index * 0.1 - } + const scales = [1, 0.925, 0.8] return [ // top items @@ -90,14 +88,14 @@ export const createFaces = ( deg: degree, opacity: getOpacity(degrees.length - 1 - index), offsetY: -1 * offsets[index], - scale: scale(index), + scale: scales[index], screenHeight: screenHeight[index] } }) .reverse(), // center item - { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1, screenHeight: itemHeight }, + { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1.031, screenHeight: itemHeight }, // bottom items ...degrees.map((degree, index) => { @@ -106,7 +104,7 @@ export const createFaces = ( deg: -1 * degree, opacity: getOpacity(degrees.length - 1 - index), offsetY: offsets[index], - scale: scale(index), + scale: scales[index], screenHeight: screenHeight[index] } }) From 6fc089495caa239fecab8d89ea8104af538eff94 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Thu, 19 Dec 2024 19:19:58 +0800 Subject: [PATCH 18/35] fix: ts lint --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index dd8a7ea52d..18a4992c88 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,7 +1,6 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, Platform, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' -// @ts-expect-error ignore import { vibrateShort } from '@mpxjs/api-proxy' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' From 416aa72098fb15773b369c038ffd3eb73b760d1f Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Thu, 19 Dec 2024 20:46:24 +0800 Subject: [PATCH 19/35] feat: update --- .../lib/runtime/components/react/mpx-picker-view-column.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 18a4992c88..8ab6aed4d1 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -152,7 +152,9 @@ const _PickerViewColumn = forwardRef, const onContentSizeChange = (_w: number, h: number) => { const y = itemRawH * initialIndex if (y <= h) { - scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) + setTimeout(() => { + scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) + }, 0) } } From 7f032a681fedc95da58a12272c1160ddc23cdf31 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Fri, 20 Dec 2024 17:49:21 +0800 Subject: [PATCH 20/35] fix: width style --- .../react/mpx-picker-view-column-item.tsx | 50 ++++++++++--------- .../react/mpx-picker-view-column.tsx | 25 ++++++---- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx index 12a8dda8d2..43995331d4 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -6,22 +6,24 @@ import { createFaces } from './pickerFaces' import { usePickerViewColumnAnimationContext } from './pickerVIewContext' interface PickerColumnItemProps { - item: React.ReactElement - index: number - itemHeight: number - textStyleFromParent: Record - textStyle: Record - hasVarDec: boolean - varContext: Record - visibleCount: number - textProps?: any - onItemLayout?: (e: LayoutChangeEvent) => void + item: React.ReactElement + index: number + itemHeight: number + itemWidth: number | '100%' + textStyleFromParent: Record + textStyle: Record + hasVarDec: boolean + varContext: Record + visibleCount: number + textProps?: any + onItemLayout?: (e: LayoutChangeEvent) => void } const _PickerViewColumnItem: React.FC = ({ item, index, itemHeight, + itemWidth, textStyleFromParent, textStyle, hasVarDec, @@ -65,20 +67,20 @@ const _PickerViewColumnItem: React.FC = ({ const realItem = React.cloneElement(item, itemProps) return ( - - {wrapChildren( - { children: realItem }, - { - hasVarDec, - varContext, - textStyle, - textProps - } - )} - + + {wrapChildren( + { children: realItem }, + { + hasVarDec, + varContext, + textStyle, + textProps + } + )} + ) } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 8ab6aed4d1..85cb594ce6 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -57,7 +57,6 @@ const _PickerViewColumn = forwardRef, const { textStyle: textStyleFromParent = {} } = splitStyle(columnStyle) const { textStyle = {} } = splitStyle(normalStyle) const { textProps } = splitProps(props) - // const scrollViewRef = useRef(null) const scrollViewRef = useAnimatedRef() const offsetYShared = useScrollViewOffset(scrollViewRef as AnimatedRef) @@ -67,6 +66,7 @@ const _PickerViewColumn = forwardRef, const { height: pickerH, itemHeight } = wrapperStyle const [scrollViewWidth, setScrollViewWidth] = useState('100%') + const [itemRawW, setItemRawW] = useState('100%') const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) const maxScrollViewWidth = useRef(-1) @@ -87,6 +87,8 @@ const _PickerViewColumn = forwardRef, nodeRef: scrollViewRef }) + console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length) + // const initialOffset = useMemo(() => ({ // x: 0, // y: itemRawH * initialIndex @@ -134,6 +136,15 @@ const _PickerViewColumn = forwardRef, activeIndex.current = initialIndex }, [itemRawH, initialIndex]) + const onContentSizeChange = (_w: number, h: number) => { + const y = itemRawH * initialIndex + if (y <= h) { + setTimeout(() => { + scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) + }, 0) + } + } + const onScrollViewLayout = (e: LayoutChangeEvent) => { const { width } = e.nativeEvent.layout const widthInt = Math.ceil(width) @@ -147,14 +158,8 @@ const _PickerViewColumn = forwardRef, } setScrollViewWidth(widthInt) } - } - - const onContentSizeChange = (_w: number, h: number) => { - const y = itemRawH * initialIndex - if (y <= h) { - setTimeout(() => { - scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) - }, 0) + if (itemRawW === '100%') { + setItemRawW(widthInt) } } @@ -227,6 +232,7 @@ const _PickerViewColumn = forwardRef, item={item} index={index} itemHeight={itemHeight} + itemWidth={itemRawW} textStyleFromParent={textStyleFromParent} textStyle={textStyle} hasVarDec={hasVarDec} @@ -244,6 +250,7 @@ const _PickerViewColumn = forwardRef, Date: Mon, 23 Dec 2024 17:30:37 +0800 Subject: [PATCH 21/35] fix: Android config --- .../react/mpx-picker-view-column.tsx | 24 ++++++++++++------- .../lib/runtime/components/react/utils.tsx | 5 +++- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 85cb594ce6..75cdf51a6c 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,8 +1,7 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' -import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, Platform, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' +import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' -import { vibrateShort } from '@mpxjs/api-proxy' -import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious } from './utils' +import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' import PickerMask from './pickerViewMask' @@ -87,8 +86,6 @@ const _PickerViewColumn = forwardRef, nodeRef: scrollViewRef }) - console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length) - // const initialOffset = useMemo(() => ({ // x: 0, // y: itemRawH * initialIndex @@ -114,6 +111,7 @@ const _PickerViewColumn = forwardRef, }, [itemRawH, maxIndex]) useEffect(() => { + // console.log('[mpx-picker-view-column], useEffect000 --->', 'columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'prevIndex=', prevIndex, 'activeIndex=', activeIndex.current, 'maxIndex=', maxIndex, 'prevMaxIndex=', prevMaxIndex) if ( !scrollViewRef.current || !itemRawH || @@ -132,7 +130,7 @@ const _PickerViewColumn = forwardRef, y: itemRawH * initialIndex, animated: false }) - }, 0) + }, isAndroid ? 200 : 0) activeIndex.current = initialIndex }, [itemRawH, initialIndex]) @@ -146,6 +144,10 @@ const _PickerViewColumn = forwardRef, } const onScrollViewLayout = (e: LayoutChangeEvent) => { + if (isAndroid) { + return + } + // RN iOS bug: https://github.com/facebook/react-native/issues/36135 const { width } = e.nativeEvent.layout const widthInt = Math.ceil(width) if (widthInt !== scrollViewWidth) { @@ -205,7 +207,12 @@ const _PickerViewColumn = forwardRef, } const onScroll = (e: NativeSyntheticEvent) => { - if (Platform.OS === 'android') { + if (isAndroid) { + return + } + // 全局注册的震动触感 hook + const pickerVibrate = global.__mpx.config.rnConfig.pickerVibrate + if (typeof pickerVibrate !== 'function') { return } const { y } = e.nativeEvent.contentOffset @@ -218,7 +225,8 @@ const _PickerViewColumn = forwardRef, index: currentId, y: currentId * itemRawH } - vibrateShort({ type: 'selection' }) + // vibrateShort({ type: 'selection' }) + pickerVibrate() } } } diff --git a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx index a00bb771ea..1e1b48fe46 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx @@ -1,5 +1,5 @@ import { useEffect, useCallback, useMemo, useRef, ReactNode, ReactElement, isValidElement, useContext, useState, Dispatch, SetStateAction, Children, cloneElement } from 'react' -import { LayoutChangeEvent, TextStyle, ImageProps, Image } from 'react-native' +import { LayoutChangeEvent, TextStyle, ImageProps, Image, Platform } from 'react-native' import { isObject, isFunction, isNumber, hasOwn, diffAndCloneA, error, warn, getFocusedNavigation } from '@mpxjs/utils' import { VarContext } from './context' import { ExpressionParser, parseFunc, ReplaceSource } from './parser' @@ -18,6 +18,9 @@ export const HIDDEN_STYLE = { opacity: 0 } +export const isIOS = Platform.OS === 'ios' +export const isAndroid = Platform.OS === 'android' + const varDecRegExp = /^--.*/ const varUseRegExp = /var\(/ const calcUseRegExp = /calc\(/ From 948cbf2599f90674c677f4aa8851f9402a1e2263 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Mon, 23 Dec 2024 19:41:31 +0800 Subject: [PATCH 22/35] fix: padding height --- .../components/react/mpx-picker-view-column.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 75cdf51a6c..e01c9f8234 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -91,16 +91,16 @@ const _PickerViewColumn = forwardRef, // y: itemRawH * initialIndex // }), [itemRawH]) + const paddingHeight = useMemo( + () => Math.round((pickerH - itemHeight) / 2), + [pickerH, itemHeight] + ) + const snapToOffsets = useMemo( () => columnData.map((_, i) => i * itemRawH), [columnData, itemRawH] ) - const paddingHeight = useMemo( - () => Math.round((pickerH - itemRawH) / 2), - [pickerH, itemRawH] - ) - const contentContainerStyle = useMemo(() => { return [{ paddingVertical: paddingHeight }] }, [paddingHeight]) @@ -111,7 +111,6 @@ const _PickerViewColumn = forwardRef, }, [itemRawH, maxIndex]) useEffect(() => { - // console.log('[mpx-picker-view-column], useEffect000 --->', 'columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'prevIndex=', prevIndex, 'activeIndex=', activeIndex.current, 'maxIndex=', maxIndex, 'prevMaxIndex=', prevMaxIndex) if ( !scrollViewRef.current || !itemRawH || From 52769298bbbc19fac065736b2d8e43b566dd3b3b Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Thu, 26 Dec 2024 18:07:00 +0800 Subject: [PATCH 23/35] fix: reset scroll position --- .../react/mpx-picker-view-column-item.tsx | 4 +- .../react/mpx-picker-view-column.tsx | 98 +++++++++---------- .../runtime/components/react/pickerFaces.ts | 2 +- .../lib/runtime/components/react/utils.tsx | 5 +- 4 files changed, 51 insertions(+), 58 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx index 43995331d4..ac111ffe82 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -9,7 +9,7 @@ interface PickerColumnItemProps { item: React.ReactElement index: number itemHeight: number - itemWidth: number | '100%' + itemWidth?: number | '100%' textStyleFromParent: Record textStyle: Record hasVarDec: boolean @@ -23,7 +23,7 @@ const _PickerViewColumnItem: React.FC = ({ item, index, itemHeight, - itemWidth, + itemWidth = '100%', textStyleFromParent, textStyle, hasVarDec, diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index e01c9f8234..3d56db860b 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,7 +1,7 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' -import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid } from './utils' +import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, useDebounceCallback, useStableCallback } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerOverlay from './pickerViewOverlay' import PickerMask from './pickerViewMask' @@ -64,11 +64,8 @@ const _PickerViewColumn = forwardRef, }) const { height: pickerH, itemHeight } = wrapperStyle - const [scrollViewWidth, setScrollViewWidth] = useState('100%') - const [itemRawW, setItemRawW] = useState('100%') const [itemRawH, setItemRawH] = useState(itemHeight) const maxIndex = useMemo(() => columnData.length - 1, [columnData]) - const maxScrollViewWidth = useRef(-1) const prevScrollingInfo = useRef({ index: initialIndex, y: 0 }) const touching = useRef(false) const scrolling = useRef(false) @@ -86,10 +83,7 @@ const _PickerViewColumn = forwardRef, nodeRef: scrollViewRef }) - // const initialOffset = useMemo(() => ({ - // x: 0, - // y: itemRawH * initialIndex - // }), [itemRawH]) + // console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length, 'pickerH=', pickerH, 'itemRawH=', itemRawH, 'itemHeight=', itemHeight) const paddingHeight = useMemo( () => Math.round((pickerH - itemHeight) / 2), @@ -110,6 +104,27 @@ const _PickerViewColumn = forwardRef, return Math.max(0, Math.min(calc, maxIndex)) }, [itemRawH, maxIndex]) + const getYofIndex = useCallback((index: number) => { + return index * itemRawH + }, [itemRawH]) + + const stableResetScrollPosition = useStableCallback((y: number) => { + console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current) + if (touching.current || scrolling.current) { + return + } + // needReset.current = true + if (y % itemRawH !== 0) { + scrolling.current = true + const targetIndex = getIndex(y) + const targetY = getYofIndex(targetIndex) + scrollViewRef.current?.scrollTo({ x: 0, y: targetY, animated: false }) + } else { + onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } }) + } + }) + const debounceResetScrollPosition = useDebounceCallback(stableResetScrollPosition, 10) + useEffect(() => { if ( !scrollViewRef.current || @@ -126,7 +141,7 @@ const _PickerViewColumn = forwardRef, setTimeout(() => { scrollViewRef.current?.scrollTo({ x: 0, - y: itemRawH * initialIndex, + y: getYofIndex(initialIndex), animated: false }) }, isAndroid ? 200 : 0) @@ -134,7 +149,7 @@ const _PickerViewColumn = forwardRef, }, [itemRawH, initialIndex]) const onContentSizeChange = (_w: number, h: number) => { - const y = itemRawH * initialIndex + const y = getYofIndex(initialIndex) if (y <= h) { setTimeout(() => { scrollViewRef.current?.scrollTo({ x: 0, y, animated: false }) @@ -142,28 +157,6 @@ const _PickerViewColumn = forwardRef, } } - const onScrollViewLayout = (e: LayoutChangeEvent) => { - if (isAndroid) { - return - } - // RN iOS bug: https://github.com/facebook/react-native/issues/36135 - const { width } = e.nativeEvent.layout - const widthInt = Math.ceil(width) - if (widthInt !== scrollViewWidth) { - const maxW = maxScrollViewWidth.current - if (maxW !== -1 && widthInt > maxW) { - return - } - if (maxW === -1) { - maxScrollViewWidth.current = Math.ceil(widthInt * 1.5) - } - setScrollViewWidth(widthInt) - } - if (itemRawW === '100%') { - setItemRawW(widthInt) - } - } - const onItemLayout = (e: LayoutChangeEvent) => { const { height: rawH } = e.nativeEvent.layout if (rawH && itemRawH !== rawH) { @@ -171,36 +164,39 @@ const _PickerViewColumn = forwardRef, } } - const onTouchStart = () => { + const onScrollBeginDrag = () => { + isIOS && debounceResetScrollPosition.clear() touching.current = true prevScrollingInfo.current = { index: activeIndex.current, - y: activeIndex.current * itemRawH + y: getYofIndex(activeIndex.current) } } - const onTouchEnd = () => { - touching.current = false - } - - const onTouchCancel = () => { + const onScrollEndDrag = (e: NativeSyntheticEvent) => { touching.current = false + const { y } = e.nativeEvent.contentOffset + if (isIOS) { + if (y > 0 && y < snapToOffsets[maxIndex]) { + debounceResetScrollPosition(y) + } + } } const onMomentumScrollBegin = () => { + isIOS && debounceResetScrollPosition.clear() scrolling.current = true } - const onMomentumScrollEnd = (e: NativeSyntheticEvent) => { + const onMomentumScrollEnd = (e: NativeSyntheticEvent | { nativeEvent: { contentOffset: { y: number } } }) => { scrolling.current = false - if (!itemRawH) { - return - } const { y: scrollY } = e.nativeEvent.contentOffset - let calcIndex = Math.round(scrollY / itemRawH) + if (isIOS && scrollY % itemRawH !== 0) { + return debounceResetScrollPosition(scrollY) + } + const calcIndex = getIndex(scrollY) activeIndex.current = calcIndex if (calcIndex !== initialIndex) { - calcIndex = Math.max(0, Math.min(calcIndex, maxIndex)) || 0 onSelectChange(calcIndex) } } @@ -222,9 +218,8 @@ const _PickerViewColumn = forwardRef, if (currentId !== prevIndex) { prevScrollingInfo.current = { index: currentId, - y: currentId * itemRawH + y: getYofIndex(currentId) } - // vibrateShort({ type: 'selection' }) pickerVibrate() } } @@ -239,7 +234,6 @@ const _PickerViewColumn = forwardRef, item={item} index={index} itemHeight={itemHeight} - itemWidth={itemRawW} textStyleFromParent={textStyleFromParent} textStyle={textStyle} hasVarDec={hasVarDec} @@ -264,14 +258,12 @@ const _PickerViewColumn = forwardRef, showsHorizontalScrollIndicator={false} scrollEventThrottle={16} {...layoutProps} - style={[{ width: scrollViewWidth }]} + style={[{ width: '100%' }]} decelerationRate="fast" snapToOffsets={snapToOffsets} onScroll={onScroll} - onLayout={onScrollViewLayout} - onTouchStart={onTouchStart} - onTouchEnd={onTouchEnd} - onTouchCancel={onTouchCancel} + onScrollBeginDrag={onScrollBeginDrag} + onScrollEndDrag={onScrollEndDrag} onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} onContentSizeChange={onContentSizeChange} diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts index 06f76cb43c..f8f7ac03f1 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts @@ -95,7 +95,7 @@ export const createFaces = ( .reverse(), // center item - { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1.031, screenHeight: itemHeight }, + { index: 0, deg: 0, opacity: 1, offsetY: 0, scale: 1, screenHeight: itemHeight }, // bottom items ...degrees.map((degree, index) => { diff --git a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx index 1e1b48fe46..9c15d936f9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/utils.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/utils.tsx @@ -537,13 +537,14 @@ export const debounce = ( ): ((...args: Parameters) => void) & { clear: () => void } => { let timer: any const wrapper = (...args: ReadonlyArray) => { - clearTimeout(timer) + timer && clearTimeout(timer) timer = setTimeout(() => { func(...args) }, delay) } wrapper.clear = () => { - clearTimeout(timer) + timer && clearTimeout(timer) + timer = null } return wrapper } From 5e304f4756dcd569277ee7b7e83815d3c2834fd0 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Fri, 27 Dec 2024 20:10:40 +0800 Subject: [PATCH 24/35] feat: update --- .../react/mpx-picker-view-column.tsx | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 3d56db860b..13e183a4a9 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -83,8 +83,6 @@ const _PickerViewColumn = forwardRef, nodeRef: scrollViewRef }) - // console.log('[mpx-picker-view-column], render ---> columnIndex=', columnIndex, 'initialIndex=', initialIndex, 'columnData=', columnData.length, 'pickerH=', pickerH, 'itemRawH=', itemRawH, 'itemHeight=', itemHeight) - const paddingHeight = useMemo( () => Math.round((pickerH - itemHeight) / 2), [pickerH, itemHeight] @@ -109,7 +107,6 @@ const _PickerViewColumn = forwardRef, }, [itemRawH]) const stableResetScrollPosition = useStableCallback((y: number) => { - console.log('[mpx-picker-view-column], reset --->', 'columnIndex=', columnIndex, 'y=', y, touching.current, scrolling.current) if (touching.current || scrolling.current) { return } @@ -159,8 +156,9 @@ const _PickerViewColumn = forwardRef, const onItemLayout = (e: LayoutChangeEvent) => { const { height: rawH } = e.nativeEvent.layout - if (rawH && itemRawH !== rawH) { - setItemRawH(rawH) + const roundedH = Math.round(rawH) + if (roundedH && roundedH !== itemRawH) { + setItemRawH(roundedH) } } @@ -177,7 +175,7 @@ const _PickerViewColumn = forwardRef, touching.current = false const { y } = e.nativeEvent.contentOffset if (isIOS) { - if (y > 0 && y < snapToOffsets[maxIndex]) { + if (y >= 0 && y <= snapToOffsets[maxIndex]) { debounceResetScrollPosition(y) } } @@ -195,18 +193,15 @@ const _PickerViewColumn = forwardRef, return debounceResetScrollPosition(scrollY) } const calcIndex = getIndex(scrollY) - activeIndex.current = calcIndex - if (calcIndex !== initialIndex) { + if (calcIndex !== activeIndex.current) { + activeIndex.current = calcIndex onSelectChange(calcIndex) } } const onScroll = (e: NativeSyntheticEvent) => { - if (isAndroid) { - return - } - // 全局注册的震动触感 hook - const pickerVibrate = global.__mpx.config.rnConfig.pickerVibrate + // 全局注册的振动触感 hook + const pickerVibrate = global.__mpx?.config?.rnConfig?.pickerVibrate if (typeof pickerVibrate !== 'function') { return } @@ -220,6 +215,7 @@ const _PickerViewColumn = forwardRef, index: currentId, y: getYofIndex(currentId) } + // vibrateShort({ type: 'selection' }) pickerVibrate() } } From 0a577b664eae7f7276a1d9485e42ff01062ca492 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Mon, 30 Dec 2024 17:33:00 +0800 Subject: [PATCH 25/35] feat: update scales --- .../webpack-plugin/lib/runtime/components/react/pickerFaces.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts index f8f7ac03f1..562f246798 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerFaces.ts @@ -77,7 +77,7 @@ export const createFaces = ( const degrees = getDegreesRelativeCenter() const [screenHeight, offsets] = getScreenHeightsAndOffsets(degrees) - const scales = [1, 0.925, 0.8] + const scales = [0.973, 0.9, 0.8] return [ // top items From 249fdeac8359747acb2b4e2857fb9e6c8d903ed2 Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Sun, 5 Jan 2025 22:54:26 +0800 Subject: [PATCH 26/35] wip: review --- .../react/mpx-picker-view-column-item.tsx | 15 +++----- .../react/mpx-picker-view-column.tsx | 28 ++++++--------- .../components/react/mpx-picker-view.tsx | 14 ++++---- .../components/react/pickerVIewContext.ts | 14 ++++++++ .../components/react/pickerViewIndicator.tsx | 34 +++++++++++++++++++ .../components/react/pickerViewMask.tsx | 8 ++--- .../components/react/pickerViewOverlay.tsx | 34 ------------------- 7 files changed, 75 insertions(+), 72 deletions(-) create mode 100644 packages/webpack-plugin/lib/runtime/components/react/pickerViewIndicator.tsx delete mode 100644 packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx index ac111ffe82..421eb499f5 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column-item.tsx @@ -3,17 +3,14 @@ import { LayoutChangeEvent } from 'react-native' import Reanimated, { Extrapolation, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { wrapChildren, extendObject } from './utils' import { createFaces } from './pickerFaces' -import { usePickerViewColumnAnimationContext } from './pickerVIewContext' +import { usePickerViewColumnAnimationContext, usePickerViewStyleContext } from './pickerVIewContext' interface PickerColumnItemProps { item: React.ReactElement index: number itemHeight: number itemWidth?: number | '100%' - textStyleFromParent: Record textStyle: Record - hasVarDec: boolean - varContext: Record visibleCount: number textProps?: any onItemLayout?: (e: LayoutChangeEvent) => void @@ -24,14 +21,12 @@ const _PickerViewColumnItem: React.FC = ({ index, itemHeight, itemWidth = '100%', - textStyleFromParent, textStyle, - hasVarDec, - varContext, textProps, visibleCount, onItemLayout }) => { + const textStyleFromAncestor = usePickerViewStyleContext() const offsetYShared = usePickerViewColumnAnimationContext() const facesShared = useSharedValue(createFaces(itemHeight, visibleCount)) @@ -57,7 +52,7 @@ const _PickerViewColumnItem: React.FC = ({ { style: extendObject( { height: itemHeight, width: '100%' }, - textStyleFromParent, + textStyleFromAncestor, textStyle, item.props.style ) @@ -74,8 +69,8 @@ const _PickerViewColumnItem: React.FC = ({ {wrapChildren( { children: realItem }, { - hasVarDec, - varContext, + hasVarDec: false, + varContext: {}, textStyle, textProps } diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 13e183a4a9..0be2c2ed2b 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,9 +1,9 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' -import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, SafeAreaView, ScrollView, StyleSheet, View } from 'react-native' +import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, useDebounceCallback, useStableCallback } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' -import PickerOverlay from './pickerViewOverlay' +import PickerIndicator from './pickerViewIndicator' import PickerMask from './pickerViewMask' import MpxPickerVIewColumnItem from './mpx-picker-view-column-item' import { PickerViewColumnAnimationContext } from './pickerVIewContext' @@ -11,7 +11,6 @@ import { PickerViewColumnAnimationContext } from './pickerVIewContext' interface ColumnProps { children?: React.ReactNode columnData: React.ReactNode[] - columnStyle: Record initialIndex: number onSelectChange: Function style: { @@ -24,7 +23,7 @@ interface ColumnProps { itemHeight: number } pickerMaskStyle: Record - pickerOverlayStyle: Record + pickerIndicatorStyle: Record columnIndex: number } @@ -34,26 +33,22 @@ const _PickerViewColumn = forwardRef, const { columnData, columnIndex, - columnStyle, initialIndex, onSelectChange, style, wrapperStyle, pickerMaskStyle, - pickerOverlayStyle, + pickerIndicatorStyle, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props const { normalStyle, - hasVarDec, - varContextRef, hasSelfPercent, setWidth, setHeight } = useTransformStyle(style, { enableVar, externalVarContext }) - const { textStyle: textStyleFromParent = {} } = splitStyle(columnStyle) const { textStyle = {} } = splitStyle(normalStyle) const { textProps } = splitProps(props) const scrollViewRef = useAnimatedRef() @@ -230,10 +225,7 @@ const _PickerViewColumn = forwardRef, item={item} index={index} itemHeight={itemHeight} - textStyleFromParent={textStyleFromParent} textStyle={textStyle} - hasVarDec={hasVarDec} - varContext={varContextRef.current} textProps={textProps} visibleCount={visibleCount} onItemLayout={onItemLayout} @@ -271,10 +263,10 @@ const _PickerViewColumn = forwardRef, ) } - const renderOverlay = () => ( - ( + ) @@ -286,11 +278,11 @@ const _PickerViewColumn = forwardRef, ) return ( - + {renderScollView()} {renderMask()} - {renderOverlay()} - + {renderIndicator()} + ) }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index acf76e0c06..6ccb2d27f1 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -11,6 +11,7 @@ import { useTransformStyle, extendObject } from './utils' +import { PickerViewStyleContext } from './pickerVIewContext' import type { AnyFunc } from './types/common' /** * ✔ value @@ -73,7 +74,7 @@ const _PickerView = forwardRef, PickerViewProp } = props const indicatorStyle = parseInlineStyle(props['indicator-style']) const pickerMaskStyle = parseInlineStyle(props['mask-style']) - const { height: indicatorH, ...pickerOverlayStyle } = indicatorStyle + const { height: indicatorH, ...pickerIndicatorStyle } = indicatorStyle const nodeRef = useRef(null) const cloneRef = useRef(null) const activeValueRef = useRef(value) @@ -162,10 +163,9 @@ const _PickerView = forwardRef, PickerViewProp height: normalStyle?.height || DefaultPickerItemH, itemHeight: indicatorH || DefaultPickerItemH }, - columnStyle: normalStyle, onSelectChange: onSelectChange.bind(null, index), initialIndex, - pickerOverlayStyle, + pickerIndicatorStyle, pickerMaskStyle } ) @@ -215,9 +215,11 @@ const _PickerView = forwardRef, PickerViewProp } return ( - - {renderPickerColumns()} - + + + {renderPickerColumns()} + + ) }) diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts b/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts index 6733ebb73d..97dd5ef381 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerVIewContext.ts @@ -16,3 +16,17 @@ export const usePickerViewColumnAnimationContext = () => { } return value } + +export const PickerViewStyleContext = createContext< + Record | undefined +>(undefined) + +export const usePickerViewStyleContext = () => { + const value = useContext(PickerViewStyleContext) + if (value === undefined) { + throw new Error( + 'usePickerViewStyleContext must be called from within PickerViewStyleContext.Provider!' + ) + } + return value +} diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerViewIndicator.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewIndicator.tsx new file mode 100644 index 0000000000..3c745b059d --- /dev/null +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerViewIndicator.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' + +type IndicatorProps = { + itemHeight: number + indicatorItemStyle?: StyleProp + indicatorContainerStyle?: StyleProp +} + +const _PickerViewIndicator = ({ itemHeight, indicatorItemStyle, indicatorContainerStyle }: IndicatorProps) => { + return ( + + + + ) +} + +const styles = StyleSheet.create({ + indicatorContainer: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'center', + alignItems: 'center', + zIndex: 200 + }, + selection: { + borderTopWidth: 1, + borderBottomWidth: 1, + borderColor: 'rgba(0, 0, 0, 0.05)', + alignSelf: 'stretch' + } +}) + +_PickerViewIndicator.displayName = 'MpxPickerViewIndicator' +export default _PickerViewIndicator diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx index 3c8773fc39..a0858122ba 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/pickerViewMask.tsx @@ -2,7 +2,7 @@ import React from 'react' import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' import LinearGradient from 'react-native-linear-gradient' -type OverlayProps = { +type MaskProps = { itemHeight: number maskContainerStyle?: StyleProp } @@ -10,9 +10,9 @@ type OverlayProps = { const _PickerViewMask = ({ itemHeight, maskContainerStyle -}: OverlayProps) => { +}: MaskProps) => { return ( - + @@ -20,7 +20,7 @@ const _PickerViewMask = ({ ) } const styles = StyleSheet.create({ - overlayContainer: { + maskContainer: { ...StyleSheet.absoluteFillObject, zIndex: 100 } diff --git a/packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx b/packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx deleted file mode 100644 index aeebec3f21..0000000000 --- a/packages/webpack-plugin/lib/runtime/components/react/pickerViewOverlay.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native' - -type OverlayProps = { - itemHeight: number - overlayItemStyle?: StyleProp - overlayContainerStyle?: StyleProp -} - -const _PickerViewOverlay = ({ itemHeight, overlayItemStyle, overlayContainerStyle }: OverlayProps) => { - return ( - - - - ) -} - -const styles = StyleSheet.create({ - overlayContainer: { - ...StyleSheet.absoluteFillObject, - justifyContent: 'center', - alignItems: 'center', - zIndex: 200 - }, - selection: { - borderTopWidth: 1, - borderBottomWidth: 1, - borderColor: 'rgba(0, 0, 0, 0.05)', - alignSelf: 'stretch' - } -}) - -_PickerViewOverlay.displayName = 'MpxPickerViewOverlay' -export default _PickerViewOverlay From 00803cb8cb0bd5e38415efb01a4f20923ae273fb Mon Sep 17 00:00:00 2001 From: wangshunnn Date: Mon, 6 Jan 2025 19:32:57 +0800 Subject: [PATCH 27/35] feat: update get rn style --- .../wx/component-config/picker-view.js | 6 +-- .../react/mpx-picker-view-column.tsx | 26 +++++++----- .../components/react/mpx-picker-view.tsx | 21 ++++++---- .../lib/template-compiler/compiler.js | 40 +++++++++++++------ 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/packages/webpack-plugin/lib/platform/template/wx/component-config/picker-view.js b/packages/webpack-plugin/lib/platform/template/wx/component-config/picker-view.js index d18e4ae2d7..7ac0bce94c 100644 --- a/packages/webpack-plugin/lib/platform/template/wx/component-config/picker-view.js +++ b/packages/webpack-plugin/lib/platform/template/wx/component-config/picker-view.js @@ -6,9 +6,7 @@ module.exports = function ({ print }) { const ttPropLog = print({ platform: 'bytedance', tag: TAG_NAME, isError: false }) const ttEventLog = print({ platform: 'bytedance', tag: TAG_NAME, isError: false, type: 'event' }) const jdEventLog = print({ platform: 'jd', tag: TAG_NAME, isError: false, type: 'event' }) - const iosPropLog = print({ platform: 'ios', tag: TAG_NAME, isError: false }) const iosEventLog = print({ platform: 'ios', tag: TAG_NAME, isError: false, type: 'event' }) - const androidPropLog = print({ platform: 'android', tag: TAG_NAME, isError: false }) const androidEventLog = print({ platform: 'android', tag: TAG_NAME, isError: false, type: 'event' }) return { @@ -28,9 +26,7 @@ module.exports = function ({ print }) { props: [ { test: /^(indicator-class|mask-class)$/, - tt: ttPropLog, - ios: iosPropLog, - android: androidPropLog + tt: ttPropLog } ], event: [ diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx index 0be2c2ed2b..d3dd3d80c2 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view-column.tsx @@ -1,7 +1,7 @@ import React, { forwardRef, useRef, useState, useMemo, useEffect, useCallback } from 'react' import { LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, StyleSheet, View } from 'react-native' import Reanimated, { AnimatedRef, useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated' -import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS, useDebounceCallback, useStableCallback } from './utils' +import { useTransformStyle, splitStyle, splitProps, useLayout, usePrevious, isAndroid, isIOS } from './utils' import useNodesRef, { HandlerRef } from './useNodesRef' import PickerIndicator from './pickerViewIndicator' import PickerMask from './pickerViewMask' @@ -64,6 +64,7 @@ const _PickerViewColumn = forwardRef, const prevScrollingInfo = useRef({ index: initialIndex, y: 0 }) const touching = useRef(false) const scrolling = useRef(false) + const timerResetPosition = useRef(null) const activeIndex = useRef(initialIndex) const prevIndex = usePrevious(initialIndex) const prevMaxIndex = usePrevious(maxIndex) @@ -101,11 +102,10 @@ const _PickerViewColumn = forwardRef, return index * itemRawH }, [itemRawH]) - const stableResetScrollPosition = useStableCallback((y: number) => { + const resetScrollPosition = (y: number) => { if (touching.current || scrolling.current) { return } - // needReset.current = true if (y % itemRawH !== 0) { scrolling.current = true const targetIndex = getIndex(y) @@ -114,8 +114,14 @@ const _PickerViewColumn = forwardRef, } else { onMomentumScrollEnd({ nativeEvent: { contentOffset: { y } } }) } - }) - const debounceResetScrollPosition = useDebounceCallback(stableResetScrollPosition, 10) + } + + const clearTimerResetPosition = () => { + if (timerResetPosition.current) { + clearTimeout(timerResetPosition.current) + timerResetPosition.current = null + } + } useEffect(() => { if ( @@ -158,7 +164,7 @@ const _PickerViewColumn = forwardRef, } const onScrollBeginDrag = () => { - isIOS && debounceResetScrollPosition.clear() + isIOS && clearTimerResetPosition() touching.current = true prevScrollingInfo.current = { index: activeIndex.current, @@ -171,13 +177,15 @@ const _PickerViewColumn = forwardRef, const { y } = e.nativeEvent.contentOffset if (isIOS) { if (y >= 0 && y <= snapToOffsets[maxIndex]) { - debounceResetScrollPosition(y) + timerResetPosition.current = setTimeout(() => { + resetScrollPosition(y) + }, 10) } } } const onMomentumScrollBegin = () => { - isIOS && debounceResetScrollPosition.clear() + isIOS && clearTimerResetPosition() scrolling.current = true } @@ -185,7 +193,7 @@ const _PickerViewColumn = forwardRef, scrolling.current = false const { y: scrollY } = e.nativeEvent.contentOffset if (isIOS && scrollY % itemRawH !== 0) { - return debounceResetScrollPosition(scrollY) + return resetScrollPosition(scrollY) } const calcIndex = getIndex(scrollY) if (calcIndex !== activeIndex.current) { diff --git a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx index 6ccb2d27f1..6029682bce 100644 --- a/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx +++ b/packages/webpack-plugin/lib/runtime/components/react/mpx-picker-view.tsx @@ -7,7 +7,6 @@ import { splitProps, splitStyle, wrapChildren, - parseInlineStyle, useTransformStyle, extendObject } from './utils' @@ -18,10 +17,10 @@ import type { AnyFunc } from './types/common' * ✔ bindchange * ✘ bindpickstart * ✘ bindpickend - * ✘ mask-class + * ✔ mask-class * ✔ indicator-style: 优先级indicator-style.height > pick-view-column中的子元素设置的height * WebView Only: - * ✘ indicator-class + * ✔ indicator-class * ✔ mask-style * ✘ immediate-change */ @@ -33,8 +32,8 @@ interface PickerViewProps { style: { [key: string]: any } - 'indicator-style'?: string - 'mask-style'?: string + 'indicator-style'?: Record, + 'mask-style'?: Record, 'enable-var': boolean 'external-var-context'?: Record, 'enable-offset': boolean @@ -69,11 +68,11 @@ const _PickerView = forwardRef, PickerViewProp value = [], bindchange, style, + 'indicator-style': indicatorStyle = {}, + 'mask-style': pickerMaskStyle = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext } = props - const indicatorStyle = parseInlineStyle(props['indicator-style']) - const pickerMaskStyle = parseInlineStyle(props['mask-style']) const { height: indicatorH, ...pickerIndicatorStyle } = indicatorStyle const nodeRef = useRef(null) const cloneRef = useRef(null) @@ -145,7 +144,13 @@ const _PickerView = forwardRef, PickerViewProp ), layoutProps }), - ['enable-offset'], + [ + 'enable-offset', + 'indicator-style', + 'indicator-class', + 'mask-style', + 'mask-class' + ], { layoutRef } ) diff --git a/packages/webpack-plugin/lib/template-compiler/compiler.js b/packages/webpack-plugin/lib/template-compiler/compiler.js index dbe300bf09..6c04a9312d 100644 --- a/packages/webpack-plugin/lib/template-compiler/compiler.js +++ b/packages/webpack-plugin/lib/template-compiler/compiler.js @@ -38,7 +38,7 @@ const endTag = new RegExp(('^<\\/' + qnameCapture + '[^>]*>')) const doctype = /^]+>/i const comment = /^