@onekeyfe/react-native-bottom-tabs 6.7.0-alpha.399 → 6.7.0-alpha.401
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
package com.onekey.bottomtabs
|
|
2
2
|
|
|
3
|
-
import android.graphics.BlendMode
|
|
4
3
|
import android.graphics.Canvas
|
|
5
4
|
import android.graphics.Color
|
|
6
|
-
import android.graphics.LinearGradient
|
|
7
5
|
import android.graphics.Paint
|
|
6
|
+
import android.graphics.Path
|
|
8
7
|
import android.graphics.RenderEffect
|
|
9
8
|
import android.graphics.RenderNode
|
|
10
9
|
import android.graphics.Shader
|
|
11
10
|
import androidx.annotation.RequiresApi
|
|
12
11
|
import kotlin.math.ceil
|
|
13
|
-
import kotlin.math.max
|
|
14
12
|
|
|
15
|
-
/** A GPU-only backdrop. The source callback must draw scenes, never this overlay. */
|
|
13
|
+
/** A GPU-only backdrop confined to the tab bar bounds. The source callback must draw scenes, never this overlay. */
|
|
16
14
|
@RequiresApi(31)
|
|
17
15
|
internal class BottomBarBlurRenderer(private val density: Float) {
|
|
18
16
|
private val node = RenderNode("TabBarBackdrop")
|
|
19
17
|
private val tintPaint = Paint()
|
|
20
|
-
private val
|
|
21
|
-
private var
|
|
22
|
-
private var
|
|
18
|
+
private val clipPath = Path()
|
|
19
|
+
private var lastLeft = 0
|
|
20
|
+
private var lastTop = -1
|
|
21
|
+
private var lastRight = 0
|
|
23
22
|
private var lastBottom = 0
|
|
23
|
+
private var lastCornerRadius = -1f
|
|
24
24
|
private var lastTint = Color.TRANSPARENT
|
|
25
|
-
private var fadeTop = 0f
|
|
26
|
-
private var sampleTop = 0f
|
|
27
25
|
|
|
28
26
|
init {
|
|
29
27
|
node.setRenderEffect(RenderEffect.createBlurEffect(
|
|
@@ -35,51 +33,73 @@ internal class BottomBarBlurRenderer(private val density: Float) {
|
|
|
35
33
|
|
|
36
34
|
fun draw(
|
|
37
35
|
canvas: Canvas,
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
left: Int,
|
|
37
|
+
top: Int,
|
|
38
|
+
right: Int,
|
|
40
39
|
bottom: Int,
|
|
40
|
+
cornerRadius: Float,
|
|
41
41
|
tint: Int,
|
|
42
42
|
drawScenes: (Canvas) -> Unit,
|
|
43
43
|
) {
|
|
44
|
-
|
|
44
|
+
val width = right - left
|
|
45
|
+
val height = bottom - top
|
|
46
|
+
if (!canvas.isHardwareAccelerated || width <= 0 || height <= 0) return
|
|
45
47
|
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
if (
|
|
49
|
+
left != lastLeft ||
|
|
50
|
+
top != lastTop ||
|
|
51
|
+
right != lastRight ||
|
|
52
|
+
bottom != lastBottom ||
|
|
53
|
+
cornerRadius != lastCornerRadius ||
|
|
54
|
+
tint != lastTint
|
|
55
|
+
) {
|
|
56
|
+
node.setPosition(0, 0, ceil(width / SCALE).toInt(), ceil(height / SCALE).toInt())
|
|
57
|
+
clipPath.reset()
|
|
58
|
+
if (cornerRadius > 0f) {
|
|
59
|
+
clipPath.addRoundRect(
|
|
60
|
+
left.toFloat(),
|
|
61
|
+
top.toFloat(),
|
|
62
|
+
right.toFloat(),
|
|
63
|
+
bottom.toFloat(),
|
|
64
|
+
cornerRadius,
|
|
65
|
+
cornerRadius,
|
|
66
|
+
Path.Direction.CW,
|
|
67
|
+
)
|
|
68
|
+
}
|
|
54
69
|
val surface = (tint and 0x00ffffff) or (minOf(Color.alpha(tint), 199) shl 24)
|
|
55
70
|
tintPaint.color = surface
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
lastLeft = left
|
|
72
|
+
lastTop = top
|
|
73
|
+
lastRight = right
|
|
58
74
|
lastBottom = bottom
|
|
75
|
+
lastCornerRadius = cornerRadius
|
|
59
76
|
lastTint = tint
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
val recording = node.beginRecording()
|
|
63
80
|
try {
|
|
64
|
-
// Reuse child View display lists.
|
|
81
|
+
// Reuse child View display lists. Only sample the scene region directly behind the bar.
|
|
65
82
|
recording.scale(1f / SCALE, 1f / SCALE)
|
|
66
|
-
recording.translate(
|
|
83
|
+
recording.translate(-left.toFloat(), -top.toFloat())
|
|
67
84
|
recording.drawColor(tint or (0xff shl 24))
|
|
68
85
|
drawScenes(recording)
|
|
69
86
|
} finally {
|
|
70
87
|
node.endRecording()
|
|
71
88
|
}
|
|
72
89
|
|
|
73
|
-
val
|
|
90
|
+
val saveCount = canvas.save()
|
|
91
|
+
if (cornerRadius > 0f) {
|
|
92
|
+
canvas.clipPath(clipPath)
|
|
93
|
+
} else {
|
|
94
|
+
canvas.clipRect(left, top, right, bottom)
|
|
95
|
+
}
|
|
74
96
|
val transform = canvas.save()
|
|
75
|
-
canvas.translate(
|
|
97
|
+
canvas.translate(left.toFloat(), top.toFloat())
|
|
76
98
|
canvas.scale(SCALE, SCALE)
|
|
77
99
|
canvas.drawRenderNode(node)
|
|
78
100
|
canvas.restoreToCount(transform)
|
|
79
|
-
canvas.drawRect(
|
|
80
|
-
|
|
81
|
-
canvas.drawRect(0f, fadeTop, width.toFloat(), bottom.toFloat(), maskPaint)
|
|
82
|
-
canvas.restoreToCount(layer)
|
|
101
|
+
canvas.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat(), tintPaint)
|
|
102
|
+
canvas.restoreToCount(saveCount)
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
fun release() {
|
|
@@ -154,7 +154,14 @@ class BottomTabsView(context: Context) : LinearLayout(context) {
|
|
|
154
154
|
private val floatingRoot = object : FrameLayout(context) {
|
|
155
155
|
override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
|
|
156
156
|
if (child === floatingBar && usesBottomBlur) {
|
|
157
|
-
drawBlurBackdrop(
|
|
157
|
+
drawBlurBackdrop(
|
|
158
|
+
canvas,
|
|
159
|
+
floatingBar.left,
|
|
160
|
+
floatingBar.top,
|
|
161
|
+
floatingBar.right,
|
|
162
|
+
floatingBar.bottom,
|
|
163
|
+
floatingBar.height / 2f,
|
|
164
|
+
) { recording ->
|
|
158
165
|
super.drawChild(recording, layoutHolder, drawingTime)
|
|
159
166
|
}
|
|
160
167
|
}
|
|
@@ -994,18 +1001,31 @@ class BottomTabsView(context: Context) : LinearLayout(context) {
|
|
|
994
1001
|
}
|
|
995
1002
|
|
|
996
1003
|
private fun drawBlurBackdrop(
|
|
997
|
-
canvas: Canvas,
|
|
1004
|
+
canvas: Canvas,
|
|
1005
|
+
left: Int,
|
|
1006
|
+
top: Int,
|
|
1007
|
+
right: Int,
|
|
1008
|
+
bottom: Int,
|
|
1009
|
+
cornerRadius: Float,
|
|
1010
|
+
drawScenes: (Canvas) -> Unit,
|
|
998
1011
|
) {
|
|
999
1012
|
if (!usesBottomBlur || !canvas.isHardwareAccelerated || Build.VERSION.SDK_INT < 31) return
|
|
1000
1013
|
val blur = bottomBarBlur ?: BottomBarBlurRenderer(resources.displayMetrics.density).also {
|
|
1001
1014
|
bottomBarBlur = it
|
|
1002
1015
|
}
|
|
1003
|
-
blur.draw(canvas,
|
|
1016
|
+
blur.draw(canvas, left, top, right, bottom, cornerRadius, blurSurfaceColor, drawScenes)
|
|
1004
1017
|
}
|
|
1005
1018
|
|
|
1006
1019
|
override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
|
|
1007
1020
|
if (child === bottomNavigation && androidTabBarStyle == AndroidTabBarStyle.DEFAULT) {
|
|
1008
|
-
drawBlurBackdrop(
|
|
1021
|
+
drawBlurBackdrop(
|
|
1022
|
+
canvas,
|
|
1023
|
+
bottomNavigation.left,
|
|
1024
|
+
bottomNavigation.top,
|
|
1025
|
+
bottomNavigation.right,
|
|
1026
|
+
bottomNavigation.bottom,
|
|
1027
|
+
0f,
|
|
1028
|
+
) { recording ->
|
|
1009
1029
|
super.drawChild(recording, layoutHolder, drawingTime)
|
|
1010
1030
|
}
|
|
1011
1031
|
}
|
package/docs/SPEC.md
CHANGED
|
@@ -172,10 +172,10 @@ bottom gap, or 0 while hidden.
|
|
|
172
172
|
|
|
173
173
|
Floating appearance with background blur disabled: the bar surface is `barTintColor` (or the theme background)
|
|
174
174
|
at 95% opacity (lightened slightly for dark colors), with
|
|
175
|
-
a hairline rim and an outline shadow. When background blur is enabled (see below), the
|
|
176
|
-
|
|
177
|
-
the capsule. The existing capsule keeps its rim/shadow and uses a 12 percent
|
|
178
|
-
surface fill over the blurred
|
|
175
|
+
a hairline rim and an outline shadow. When background blur is enabled (see below), the live blur and translucent
|
|
176
|
+
surface tint are confined and clipped to the capsule bounds; the region above,
|
|
177
|
+
beside, and beneath the capsule remains unblurred. The existing capsule keeps its rim/shadow and uses a 12 percent
|
|
178
|
+
surface fill over the blurred capsule backdrop; its lens and navigation controls remain
|
|
179
179
|
sharp. The selected tab sits on a capsule pill inset 4 dp
|
|
180
180
|
(`activeIndicatorColor` when provided). The Material ripple is disabled; the
|
|
181
181
|
lens provides press feedback.
|
|
@@ -211,7 +211,7 @@ coverage. Complete consumer-app and physical-device acceptance remain pending;
|
|
|
211
211
|
API 31-35 and visual dark-mode acceptance have not been reverified for this port.
|
|
212
212
|
|
|
213
213
|
`androidBottomBlurEnabled?: boolean` controls the native Android tab bar's live
|
|
214
|
-
background blur
|
|
214
|
+
background blur and translucent surface tint behind the bar.
|
|
215
215
|
Omitting it (including `undefined`) MUST enable the effect only on Android 16
|
|
216
216
|
(API 36) and newer. `false` MUST restore the existing appearance and layout for the current
|
|
217
217
|
`androidTabBarStyle`: docked scenes reserve bar height; floating scenes retain
|
|
@@ -229,19 +229,22 @@ report zero bar height. Toggling, resizing, theme changes, and system/keyboard
|
|
|
229
229
|
insets MUST refresh geometry and appearance without remounting scenes.
|
|
230
230
|
|
|
231
231
|
The module owns the GPU resources on the UI thread. It MUST sample only the
|
|
232
|
-
scene layer
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
232
|
+
scene layer directly behind the active tab bar bounds (the docked bar rectangle
|
|
233
|
+
in `default` style or the capsule bounds in `floating` style), excluding the
|
|
234
|
+
tab bar and its own output, and clip the blurred backdrop and tint to those
|
|
235
|
+
bounds (including the capsule corner radius in `floating` style) with no blur
|
|
236
|
+
or fade bleeding above or outside the bar. It MUST use hardware
|
|
237
|
+
RenderNode/RenderEffect rendering of the bar bounds at one-quarter resolution
|
|
238
|
+
in each dimension, with a 20 dp blur radius. It MUST NOT capture bitmaps, read
|
|
239
|
+
pixels back to the CPU, emit scroll events to JS, or run a recurring frame
|
|
240
|
+
callback for this effect. Rendering MUST follow the normal view invalidation
|
|
241
|
+
cycle, using the scene's existing display lists. The translucent tint MUST
|
|
242
|
+
follow `tabBarStyle.backgroundColor` when set, otherwise the docked Material
|
|
243
|
+
surface color or floating theme background, with opacity capped at 78 percent.
|
|
244
|
+
|
|
245
|
+
Only one blur RenderNode and cached paints/paths MAY be retained per mounted
|
|
246
|
+
tab view. Its offscreen dimensions MUST be bounded by one-quarter of the bar
|
|
247
|
+
width and one-quarter of the bar height (rounded up).
|
|
245
248
|
Disabling, hiding, detaching, and dropping the native view MUST discard the
|
|
246
249
|
display list; no scene snapshots, disk cache, timers, or background workers are
|
|
247
250
|
owned. SurfaceView content is outside the normal View display list and is not
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-bottom-tabs",
|
|
3
|
-
"version": "6.7.0-alpha.
|
|
3
|
+
"version": "6.7.0-alpha.401",
|
|
4
4
|
"description": "Native bottom tabs (tab bar + scene container) for React Native on iOS and Android",
|
|
5
5
|
"source": "./src/index.tsx",
|
|
6
6
|
"main": "./lib/module/index.js",
|