Skip to content

Feat: 添加内置导航头 #1961

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,25 @@ class RNIntersectionObserver {
const navigationLayout = navigation.layout || {
x: 0,
y: 0,
top: 0,
left: 0,
width: screen.width,
height: screen.height
}

const windowRect = {
top: navigationLayout.y - this.margins.top,
top: navigationLayout.top - this.margins.top,
left: 0 - this.margins.left,
right: navigationLayout.width + this.margins.right,
bottom: navigationLayout.y + navigationLayout.height + this.margins.bottom
bottom: navigationLayout.top + navigationLayout.height + this.margins.bottom
}

this.windowRect = windowRect
return this.windowRect
}

_getReferenceRect (targetRef) {
const navigation = getFocusedNavigation() || {}
const layout = navigation.layout || {}
const targetRefs = isArray(targetRef) ? targetRef : [targetRef]
const targetPromiseQueue = []
targetRefs.forEach((targetRefItem) => {
Expand All @@ -128,11 +131,12 @@ class RNIntersectionObserver {
targetPromiseQueue.push(new Promise((resolve) => {
target.measureInWindow(
(x, y, width, height) => {
// 安卓measureInWindow的参考值在android下为statubar的左下角,因此top需要调整一下
const boundingClientRect = {
left: x,
top: y,
top: y + layout.statusBarHeight || 0,
right: x + width,
bottom: y + height,
bottom: y + height + layout.statusBarHeight || 0,
width: width,
height: height
}
Expand All @@ -153,19 +157,16 @@ class RNIntersectionObserver {
return Math.min(Math.max(start, value), end)
}

_isInsectedFn (intersectionRatio, previousIntersectionRatio, thresholds) {
// console.log('nowintersectionRatio, previousIntersectionRatio', [intersectionRatio, previousIntersectionRatio])
let nowIndex = -1
let previousIndex = -1
_getRatioIndex (ratio, thresholds = []) {
if (ratio === 0 && thresholds.includes(0)) return -1
if (ratio === 1 && thresholds.includes(1)) return thresholds.length
let returnIndex = -1
thresholds.forEach((item, index) => {
if (intersectionRatio >= item) {
nowIndex = index
}
if (previousIntersectionRatio >= item) {
previousIndex = index
if (ratio >= item) {
returnIndex = index
}
})
return !(nowIndex === previousIndex)
return returnIndex
}

// 计算相交区域
Expand All @@ -180,10 +181,8 @@ class RNIntersectionObserver {
const targetArea = (observeRect.bottom - observeRect.top) * (observeRect.right - observeRect.left)
const visibleArea = (visibleRect.bottom - visibleRect.top) * (visibleRect.right - visibleRect.left)
const intersectionRatio = targetArea ? visibleArea / targetArea : 0

const isInsected = isInit ? intersectionRatio > this.initialRatio : this._isInsectedFn(intersectionRatio, this.previousIntersectionRatio[observeIndex], this.thresholds)
const isInsected = isInit ? intersectionRatio > this.initialRatio : !(this._getRatioIndex(intersectionRatio, this.thresholds) === this._getRatioIndex(this.previousIntersectionRatio[observeIndex], this.thresholds))
this.previousIntersectionRatio[observeIndex] = intersectionRatio

return {
intersectionRatio,
intersectionRect: {
Expand Down Expand Up @@ -217,7 +216,6 @@ class RNIntersectionObserver {
relativeRect,
isInit
})
// 初次调用的
if (isInsected) {
this.callback({
// index: index,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ function setNavigationBarTitle (options = {}) {
failHandle({ errMsg: 'setNavigationBarTitle:fail' }, fail, complete)
} else {
nextTick(() => {
navigation.setOptions({ title: title.trim() })
navigation.setPageConfig({
navigationBarTitleText: title.trim()
})
successHandle({ errMsg: 'setNavigationBarTitle:ok' }, success, complete)
})
}
Expand All @@ -20,11 +22,9 @@ function setNavigationBarColor (options = {}) {
failHandle({ errMsg: 'setNavigationBarColor:fail' }, fail, complete)
} else {
nextTick(() => {
navigation.setOptions({
headerStyle: {
backgroundColor: backgroundColor
},
headerTintColor: frontColor
navigation.setPageConfig({
navigationBarBackgroundColor: backgroundColor,
navigationBarTextStyle: frontColor
})
successHandle({ errMsg: 'setNavigationBarColor:ok' }, success, complete)
})
Expand Down
3 changes: 1 addition & 2 deletions packages/api-proxy/src/platform/api/system/rnSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ const getWindowInfo = function () {
const navigation = getFocusedNavigation() || {}
const initialWindowMetricsInset = initialWindowMetrics?.insets || {}
const navigationInsets = navigation.insets || {}
const insets = Object.assign(initialWindowMetricsInset, navigationInsets)
const insets = Object.assign({}, initialWindowMetricsInset, navigationInsets)
let safeArea = {}
const { top = 0, bottom = 0, left = 0, right = 0 } = insets

const screenHeight = __mpx_mode__ === 'ios' ? dimensionsScreen.height : dimensionsScreen.height - bottom // 解决安卓开启屏幕内三建导航安卓把安全区计算进去后产生的影响
const screenWidth = __mpx_mode__ === 'ios' ? dimensionsScreen.width : dimensionsScreen.width - right
const layout = navigation.layout || {}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mpxjs/core",
"version": "2.10.4",
"version": "2.10.4-beta.1",
"description": "mpx runtime core",
"keywords": [
"miniprogram",
Expand Down
50 changes: 14 additions & 36 deletions packages/core/src/platform/createApp.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Mpx from '../index'
import { createElement, memo, useRef, useEffect } from 'react'
import * as ReactNative from 'react-native'
import { initAppProvides } from './export/inject'
import { NavigationContainer, createStackNavigator, SafeAreaProvider } from './env/navigationHelper'
import { NavigationContainer, createNativeStackNavigator, SafeAreaProvider } from './env/navigationHelper'

const appHooksMap = makeMap(mergeLifecycle(LIFECYCLE).app)

Expand Down Expand Up @@ -52,25 +52,25 @@ export default function createApp (options) {

const pages = currentInject.getPages() || {}
const firstPage = currentInject.firstPage
const Stack = createStackNavigator()
const Stack = createNativeStackNavigator()
const getPageScreens = (initialRouteName, initialParams) => {
return Object.entries(pages).map(([key, item]) => {
const options = {
// __mpxPageStatusMap 为编译注入的全局变量
headerShown: !(Object.assign({}, global.__mpxPageConfig, global.__mpxPageConfigsMap[key]).navigationStyle === 'custom')
}
// const options = {
// // __mpxPageStatusMap 为编译注入的全局变量
// headerShown: !(Object.assign({}, global.__mpxPageConfig, global.__mpxPageConfigsMap[key]).navigationStyle === 'custom')
// }
if (key === initialRouteName) {
return createElement(Stack.Screen, {
name: key,
component: item,
initialParams,
options
initialParams
// options
})
}
return createElement(Stack.Screen, {
name: key,
component: item,
options
component: item
// options
})
})
}
Expand Down Expand Up @@ -195,32 +195,10 @@ export default function createApp (options) {

const { initialRouteName, initialParams } = initialRouteRef.current
const navScreenOpts = {
// 7.x替换headerBackTitleVisible
// headerBackButtonDisplayMode: 'minimal',
headerBackTitleVisible: false,
headerShadowVisible: false
// 整体切换native-stack时进行修改如下
// statusBarTranslucent: true,
// statusBarBackgroundColor: 'transparent'
}
if (__mpx_mode__ === 'ios') {
// ios使用native-stack
const headerBackImageSource = Mpx.config.rnConfig.headerBackImageSource || null
if (headerBackImageSource) {
navScreenOpts.headerBackImageSource = headerBackImageSource
}
} else {
// 安卓上会出现导航条闪现的问题所以默认加headerShown false(stack版本, native-stack版本可以干掉)
// iOS加上默认headerShown false的话会因为iOS根高度是screenHeight - useHeaderHeight()会导致出现渲染两次情况,因此iOS不加此默认值
navScreenOpts.headerShown = false
// 安卓和鸿蒙先用stack
const headerBackImageProps = Mpx.config.rnConfig.headerBackImageProps || null
if (headerBackImageProps) {
navScreenOpts.headerBackImage = () => {
return createElement(ReactNative.Image, headerBackImageProps)
}
}
}
headerShown: false,
statusBarTranslucent: true,
statusBarBackgroundColor: 'transparent'
}

return createElement(SafeAreaProvider,
null,
Expand Down
108 changes: 108 additions & 0 deletions packages/core/src/platform/env/nav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { createElement, useState, useMemo } from 'react'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import * as ReactNative from 'react-native'
import Mpx from '../../index'

export function useInnerHeaderHeight (pageconfig) {
if (pageconfig.navigationStyle === 'custom') {
return 0
} else {
const safeAreaTop = useSafeAreaInsets()?.top || 0
const headerHeight = safeAreaTop + getTitleHeight()
return headerHeight
}
}

// 固定写死高度
function getTitleHeight () {
return 44
}

// 计算颜色亮度
const getColorBrightness = (color) => {
const processedColor = ReactNative.processColor(color)
if (typeof processedColor === 'number') {
const r = (processedColor >> 16) & 255
const g = (processedColor >> 8) & 255
const b = processedColor & 255
return (r * 299 + g * 587 + b * 114) / 1000
}
return 0
}

const styles = ReactNative.StyleSheet.create({
header: {
elevation: 3
},
headerContent: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center'
},
backButton: {
position: 'absolute',
height: '100%',
width: 40,
left: 0,
top: 0,
alignItems: 'center',
justifyContent: 'center'
},
backButtonImage: {
width: 22,
height: 22
},
title: {
fontSize: 17,
fontWeight: 600
}
})

export function innerNav ({ props, navigation }) {
const { pageConfig } = props
const [innerPageConfig, setPageConfig] = useState(pageConfig || {})
navigation.setPageConfig = (config) => {
const newConfig = Object.assign({}, innerPageConfig, config)
setPageConfig(newConfig)
}

const isCustom = innerPageConfig.navigationStyle === 'custom'
if (isCustom) return null
const safeAreaTop = useSafeAreaInsets()?.top || 0

// 回退按钮的颜色,根据背景色的亮度来进行调节
const backButtonColor = useMemo(() => {
return getColorBrightness(innerPageConfig.navigationBarBackgroundColor) > 128 ? '#000000' : '#ffffff'
}, [innerPageConfig.navigationBarBackgroundColor])

// 假设是栈导航,获取栈的长度
const stackLength = navigation.getState()?.routes?.length
// 用于外部注册打开RN容器之前的栈长度
const beforeStackLength = Mpx.config?.rnConfig?.beforeStackLength || 0

// 回退按钮与图标
const backElement = stackLength + beforeStackLength > 1
? createElement(ReactNative.TouchableOpacity, {
style: [styles.backButton],
onPress: () => { navigation.goBack() }
}, createElement(ReactNative.Image, {
source: { uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAABICAYAAACqT5alAAAA2UlEQVR4nO3bMQrCUBRE0Yla6AYEN2nnBrTL+izcitW3MRDkEUWSvPzJvfCqgMwhZbAppWhNbbIHzB1g9wATERFRVyvpkj1irlpJ5X326D7WHh1hbdFD2CLpLmmftm7kfsEe09aNHFiBrT+wAlt/YAW2/sAKbP2BFdj6Ayuwy+ufz6XPL893krZ//O6iu2n4LT8kndLWTRTo4EC7BDo40C6BDg60S6CDA+0S6OBAuwQ6uNWiD2nrJmoIfU7cNWkR2hbb1UfbY7uuWhGWiIg+a/iHuHmA3QPs3gu4JW9Gan+OJAAAAABJRU5ErkJggg==' },
style: [styles.backButtonImage, { tintColor: backButtonColor }]
}))
: null

return createElement(ReactNative.View, {
style: [styles.header, {
paddingTop: safeAreaTop,
backgroundColor: innerPageConfig.navigationBarBackgroundColor || '#000000'
}]
},
createElement(ReactNative.View, {
style: styles.headerContent,
height: getTitleHeight()
}, backElement,
createElement(ReactNative.Text, {
style: [styles.title, { color: innerPageConfig.navigationBarTextStyle || 'white' }]
}, innerPageConfig.navigationBarTitleText?.trim() || ''))
)
}
4 changes: 2 additions & 2 deletions packages/core/src/platform/env/navigationHelper.android.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createStackNavigator } from '@react-navigation/stack'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { NavigationContainer, StackActions } from '@react-navigation/native'
import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
import { useHeaderHeight } from '@react-navigation/elements'
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
import { GestureHandlerRootView } from 'react-native-gesture-handler'

export {
createStackNavigator,
createNativeStackNavigator,
NavigationContainer,
useHeaderHeight,
StackActions,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/platform/env/navigationHelper.ios.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { createNativeStackNavigator as createStackNavigator } from '@react-navigation/native-stack'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { NavigationContainer, StackActions } from '@react-navigation/native'
import PortalHost from '@mpxjs/webpack-plugin/lib/runtime/components/react/dist/mpx-portal/portal-host'
import { useHeaderHeight } from '@react-navigation/elements'
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
import { GestureHandlerRootView } from 'react-native-gesture-handler'

export {
createStackNavigator,
createNativeStackNavigator,
NavigationContainer,
useHeaderHeight,
StackActions,
Expand Down
Loading
Loading