Skip to content

Commit eacf4de

Browse files
committed
example: Add ScrollEndHaptic switch
1 parent a8f634c commit eacf4de

File tree

10 files changed

+77
-61
lines changed

10 files changed

+77
-61
lines changed

docs/guide/utils.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ PopupLayout(
3939
exitTransition = fadeOut(), // Optional, custom exit animation for dialog content
4040
enableWindowDim = true, // Optional, whether to enable dimming layer
4141
dimEnterTransition = fadeIn(), // Optional, custom enter animation for dim layer
42-
dimExitTransition = fadeOut() // Optional, custom exit animation for dim layer
42+
dimExitTransition = fadeOut(), // Optional, custom exit animation for dim layer
4343
transformOrigin = { TransformOrigin.Center }, // Transform origin for the popup
4444
) {
4545
// Popup content
@@ -116,7 +116,7 @@ LazyColumn(
116116
.fillMaxSize()
117117
// Add scroll end haptic feedback
118118
.scrollEndHaptic(
119-
hapticFeedbackType = HapticFeedbackType.LongPress // Default value
119+
hapticFeedbackType = HapticFeedbackType.TextHandleMove // Default value
120120
)
121121
) {
122122
// List content

docs/zh_CN/guide/utils.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ PopupLayout(
4040
exitTransition = fadeOut(), // 可选,自定义对话框对话框退出动画
4141
enableWindowDim = true, // 可选,是否启用遮罩层
4242
dimEnterTransition = fadeIn(), // 可选,自定义遮罩层进入动画
43-
dimExitTransition = fadeOut() // 可选,自定义遮罩层退出动画
43+
dimExitTransition = fadeOut(), // 可选,自定义遮罩层退出动画
4444
transformOrigin = { TransformOrigin.Center }, // 弹出窗口的起始位置
4545
) {
4646
// 弹出窗口内容

example/src/androidMain/kotlin/Preview.android.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fun UITestPreview() {
2828
fun MainPagePreview() {
2929
AppTheme {
3030
Scaffold {
31-
MainPage(MiuixScrollBehavior(), PaddingValues())
31+
MainPage(MiuixScrollBehavior(), PaddingValues(), true)
3232
}
3333
}
3434
}
@@ -38,7 +38,7 @@ fun MainPagePreview() {
3838
fun SecondPagePreview() {
3939
AppTheme {
4040
Scaffold {
41-
SecondPage(MiuixScrollBehavior(), PaddingValues())
41+
SecondPage(MiuixScrollBehavior(), PaddingValues(), true)
4242
}
4343
}
4444
}
@@ -75,6 +75,8 @@ fun ThirdPagePreview() {
7575
{},
7676
false,
7777
{},
78+
true,
79+
{},
7880
remember { mutableIntStateOf(0) }
7981
)
8082
}

example/src/commonMain/kotlin/MainPage.kt

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ import top.yukonga.miuix.kmp.utils.scrollEndHaptic
4141
@Composable
4242
fun MainPage(
4343
topAppBarScrollBehavior: ScrollBehavior,
44-
padding: PaddingValues
44+
padding: PaddingValues,
45+
scrollEndHaptic : Boolean,
4546
) {
46-
var miuixSearchValue by remember { mutableStateOf("") }
47+
var searchValue by remember { mutableStateOf("") }
4748
var expanded by remember { mutableStateOf(false) }
4849

4950
val showDialog = remember { mutableStateOf(false) }
@@ -60,20 +61,20 @@ fun MainPage(
6061
val spinnerOptionSelected = remember { mutableStateOf(0) }
6162
val spinnerOptionSelectedRight = remember { mutableStateOf(1) }
6263
val spinnerOptionSelectedDialog = remember { mutableStateOf(2) }
63-
val miuixSuperCheckbox = remember { mutableStateOf("State: false") }
64-
val miuixSuperCheckboxState = remember { mutableStateOf(false) }
65-
val miuixSuperRightCheckbox = remember { mutableStateOf("false") }
66-
val miuixSuperRightCheckboxState = remember { mutableStateOf(false) }
67-
val miuixSuperSwitch = remember { mutableStateOf("false") }
68-
val miuixSuperSwitchState = remember { mutableStateOf(false) }
69-
val miuixSuperSwitchAnimState = remember { mutableStateOf(false) }
64+
val superCheckbox = remember { mutableStateOf("State: false") }
65+
val superCheckboxState = remember { mutableStateOf(false) }
66+
val superRightCheckbox = remember { mutableStateOf("false") }
67+
val superRightCheckboxState = remember { mutableStateOf(false) }
68+
val superSwitch = remember { mutableStateOf("false") }
69+
val superSwitchState = remember { mutableStateOf(false) }
70+
val superSwitchAnimState = remember { mutableStateOf(false) }
7071

7172
val textComponent = @Composable {
7273
TextComponent(
7374
showDialog, dialogTextFieldValue, showDialog2, dialog2dropdownSelectedOption, dialog2SuperSwitchState,
7475
checkbox, checkboxTrue, switch, switchTrue, dropdownOptionSelected, dropdownOptionSelectedRight, spinnerOptionSelected,
75-
spinnerOptionSelectedRight, spinnerOptionSelectedDialog, miuixSuperCheckbox, miuixSuperCheckboxState,
76-
miuixSuperRightCheckbox, miuixSuperRightCheckboxState, miuixSuperSwitch, miuixSuperSwitchState, miuixSuperSwitchAnimState
76+
spinnerOptionSelectedRight, spinnerOptionSelectedDialog, superCheckbox, superCheckboxState,
77+
superRightCheckbox, superRightCheckboxState, superSwitch, superSwitchState, superSwitchAnimState
7778
)
7879
}
7980
val otherComponent = @Composable { OtherComponent(padding) }
@@ -84,7 +85,9 @@ fun MainPage(
8485
if (maxWidth < 840.dp) {
8586
LazyColumn(
8687
modifier = Modifier
87-
.scrollEndHaptic()
88+
.then(
89+
if (scrollEndHaptic) Modifier.scrollEndHaptic() else Modifier
90+
)
8891
.overScrollVertical()
8992
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection),
9093
contentPadding = PaddingValues(top = padding.calculateTopPadding()),
@@ -95,8 +98,8 @@ fun MainPage(
9598
SearchBar(
9699
inputField = {
97100
InputField(
98-
query = miuixSearchValue,
99-
onQueryChange = { miuixSearchValue = it },
101+
query = searchValue,
102+
onQueryChange = { searchValue = it },
100103
onSearch = { expanded = false },
101104
expanded = expanded,
102105
onExpandedChange = { expanded = it },
@@ -112,7 +115,7 @@ fun MainPage(
112115
indication = null
113116
) {
114117
expanded = false
115-
miuixSearchValue = ""
118+
searchValue = ""
116119
},
117120
text = "Cancel",
118121
style = TextStyle(fontSize = 17.sp, fontWeight = FontWeight.Bold),
@@ -132,7 +135,7 @@ fun MainPage(
132135
modifier = Modifier
133136
.fillMaxWidth(),
134137
onClick = {
135-
miuixSearchValue = resultText
138+
searchValue = resultText
136139
expanded = false
137140
}
138141
)
@@ -171,8 +174,8 @@ fun MainPage(
171174
SearchBar(
172175
inputField = {
173176
InputField(
174-
query = miuixSearchValue,
175-
onQueryChange = { miuixSearchValue = it },
177+
query = searchValue,
178+
onQueryChange = { searchValue = it },
176179
onSearch = { expanded = false },
177180
expanded = expanded,
178181
onExpandedChange = { expanded = it },
@@ -187,7 +190,7 @@ fun MainPage(
187190
indication = null
188191
) {
189192
expanded = false
190-
miuixSearchValue = ""
193+
searchValue = ""
191194
},
192195
text = "Cancel",
193196
color = MiuixTheme.colorScheme.primary
@@ -206,7 +209,7 @@ fun MainPage(
206209
modifier = Modifier
207210
.fillMaxWidth(),
208211
onClick = {
209-
miuixSearchValue = resultText
212+
searchValue = resultText
210213
expanded = false
211214
}
212215
)

example/src/commonMain/kotlin/SecondPage.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import top.yukonga.miuix.kmp.utils.scrollEndHaptic
2525
@Composable
2626
fun SecondPage(
2727
topAppBarScrollBehavior: ScrollBehavior,
28-
padding: PaddingValues
28+
padding: PaddingValues,
29+
scrollEndHaptic: Boolean
2930
) {
3031
val pullToRefreshState = rememberPullToRefreshState()
3132
var isRefreshing by remember { mutableStateOf(false) }
@@ -51,7 +52,9 @@ fun SecondPage(
5152
) {
5253
LazyColumn(
5354
modifier = Modifier
54-
.scrollEndHaptic()
55+
.then(
56+
if (scrollEndHaptic) Modifier.scrollEndHaptic() else Modifier
57+
)
5558
.overScrollVertical()
5659
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
5760
.height(getWindowSize().height.dp),

example/src/commonMain/kotlin/ThirdPage.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ fun ThirdPage(
5454
onFabPositionChange: (Int) -> Unit,
5555
enablePageUserScroll: Boolean,
5656
onEnablePageUserScrollChange: (Boolean) -> Unit,
57+
scrollEndHaptic: Boolean,
58+
onScrollEndHapticChange: (Boolean) -> Unit,
5759
colorMode: MutableState<Int>
5860
) {
5961
val showDialog = remember { mutableStateOf(false) }
6062
LazyColumn(
6163
modifier = Modifier
62-
.scrollEndHaptic()
64+
.then(
65+
if (scrollEndHaptic) Modifier.scrollEndHaptic() else Modifier
66+
)
6367
.overScrollVertical()
6468
.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
6569
.height(getWindowSize().height.dp),
@@ -168,6 +172,11 @@ fun ThirdPage(
168172
}
169173
)
170174
}
175+
SuperSwitch(
176+
title = "Enable Scroll End Haptic",
177+
checked = scrollEndHaptic,
178+
onCheckedChange = onScrollEndHapticChange
179+
)
171180
SuperSwitch(
172181
title = "Enable Page User Scroll",
173182
checked = enablePageUserScroll,

example/src/commonMain/kotlin/UITest.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ data class UIState(
9292
val showFloatingActionButton: Boolean = false,
9393
val floatingActionButtonPosition: Int = 2,
9494
val enablePageUserScroll: Boolean = false,
95-
val isTopPopupExpanded: Boolean = false
95+
val isTopPopupExpanded: Boolean = false,
96+
val scrollEndHaptic: Boolean = true
9697
)
9798

9899
@Composable
@@ -465,12 +466,14 @@ fun AppHorizontalPager(
465466
when (page) {
466467
0 -> MainPage(
467468
topAppBarScrollBehavior = topAppBarScrollBehaviorList[0],
468-
padding = padding
469+
padding = padding,
470+
scrollEndHaptic = uiState.scrollEndHaptic,
469471
)
470472

471473
1 -> SecondPage(
472474
topAppBarScrollBehavior = topAppBarScrollBehaviorList[1],
473-
padding = padding
475+
padding = padding,
476+
scrollEndHaptic = uiState.scrollEndHaptic,
474477
)
475478

476479
else -> ThirdPage(
@@ -500,6 +503,8 @@ fun AppHorizontalPager(
500503
onFabPositionChange = { onUiStateChange(uiState.copy(floatingActionButtonPosition = it)) },
501504
enablePageUserScroll = uiState.enablePageUserScroll,
502505
onEnablePageUserScrollChange = { onUiStateChange(uiState.copy(enablePageUserScroll = it)) },
506+
scrollEndHaptic = uiState.scrollEndHaptic,
507+
onScrollEndHapticChange = { onUiStateChange(uiState.copy(scrollEndHaptic = it)) },
503508
colorMode = colorMode
504509
)
505510
}

example/src/desktopMain/kotlin/Preview.desktop.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fun UITestPreview() {
2323
fun MainPagePreview() {
2424
AppTheme {
2525
Scaffold {
26-
MainPage(MiuixScrollBehavior(rememberTopAppBarState()), PaddingValues())
26+
MainPage(MiuixScrollBehavior(rememberTopAppBarState()), PaddingValues(), true)
2727
}
2828
}
2929
}
@@ -33,7 +33,7 @@ fun MainPagePreview() {
3333
fun SecondPagePreview() {
3434
AppTheme {
3535
Scaffold {
36-
SecondPage(MiuixScrollBehavior(rememberTopAppBarState()), PaddingValues())
36+
SecondPage(MiuixScrollBehavior(rememberTopAppBarState()), PaddingValues(), true)
3737
}
3838
}
3939
}
@@ -70,6 +70,8 @@ fun ThirdPagePreview() {
7070
{},
7171
false,
7272
{},
73+
true,
74+
{},
7375
remember { mutableIntStateOf(0) }
7476
)
7577
}

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/Overscroll.kt

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import kotlin.math.sqrt
4747
* so we need this variable to have the same expectations on different devices.
4848
*/
4949
@Stable
50-
fun parabolaScrollEasing(currentOffset: Float, newOffset: Float, p: Float = 25f, density: Float = 4f): Float {
50+
fun parabolaScrollEasing(currentOffset: Float, newOffset: Float, p: Float = 50f, density: Float): Float {
5151
val realP = p * density
5252
val distance = abs(currentOffset + newOffset / 2)
5353
val ratio = (realP / (sqrt(realP * distance.coerceAtLeast(Float.MIN_VALUE)))).coerceIn(Float.MIN_VALUE, 1f)
@@ -68,8 +68,8 @@ internal val DefaultParabolaScrollEasing: (currentOffset: Float, newOffset: Floa
6868
}
6969
}
7070

71-
internal const val OutBoundSpringStiff = 200f
72-
internal const val OutBoundSpringDamp = 1f
71+
internal const val OutBoundSpringStiff = 150f
72+
internal const val OutBoundSpringDamp = 0.86f
7373

7474
/**
7575
* @see overScrollOutOfBound
@@ -107,7 +107,7 @@ fun Modifier.overScrollHorizontal(
107107
* and the new offset from the gesture.
108108
* modify it to cooperate with [springStiff] to customize the sliding damping effect.
109109
* The current default easing comes from iOS, you don't need to modify it!
110-
* @param springStiff springStiff for overscroll effect,For better user experience, the new value is not recommended to be higher than[StiffnessMediumLow]
110+
* @param springStiff springStiff for overscroll effect,For better user experience, the new value is not recommended to be higher than [StiffnessMediumLow].
111111
* @param springDamp springDamp for overscroll effect,generally do not need to set.
112112
* @param isEnabled Whether to enable the overscroll effect, default is enabled on Android and iOS.
113113
*/
@@ -298,10 +298,3 @@ class OverScrollState {
298298
*/
299299
val LocalOverScrollState = compositionLocalOf<OverScrollState> { OverScrollState() }
300300

301-
/**
302-
* Remember the [OverScrollState] instance.
303-
*
304-
* @see OverScrollState
305-
*/
306-
@Composable
307-
fun rememberOverScrollState(): OverScrollState = remember { OverScrollState() }

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/utils/ScrollEndHaptic.kt

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,45 +25,44 @@ private class ScrollEndHapticConnection(
2525
private val hapticFeedbackType: HapticFeedbackType
2626
) : NestedScrollConnection {
2727

28-
private enum class OverscrollState {
29-
/** No overscroll detected. */
28+
private enum class ScrollEndHapticState {
29+
/** Not scrolled to the boundary. */
3030
Idle,
3131

32-
/** Overscroll detected at the top boundary. */
32+
/** Scrolled to the top boundary. */
3333
TopBoundaryHit,
3434

35-
/** Overscroll detected at the bottom boundary. */
35+
/** Scrolled to the bottom boundary. */
3636
BottomBoundaryHit
3737
}
3838

39-
private var overscrollState: OverscrollState = OverscrollState.Idle
39+
private var scrollEndHapticState = ScrollEndHapticState.Idle
4040

4141
private fun Float.filter(tolerance: Float): Boolean = abs(this) < tolerance
4242

4343
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
4444
// Reset state when scrolling from a boundary into content.
45-
if (overscrollState == OverscrollState.TopBoundaryHit && available.y < -1f) {
46-
overscrollState = OverscrollState.Idle
47-
} else if (overscrollState == OverscrollState.BottomBoundaryHit && available.y > 1f) {
48-
overscrollState = OverscrollState.Idle
45+
if (scrollEndHapticState == ScrollEndHapticState.TopBoundaryHit && available.y < -1f) {
46+
scrollEndHapticState = ScrollEndHapticState.Idle
47+
} else if (scrollEndHapticState == ScrollEndHapticState.BottomBoundaryHit && available.y > 1f) {
48+
scrollEndHapticState = ScrollEndHapticState.Idle
4949
}
5050
return Offset.Zero
5151
}
5252

5353
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
54-
println("overScrollState: $overscrollState")
5554
// Flinging beyond the bottom boundary.
56-
if (available.y > 1f && !consumed.y.filter(5f)) {
57-
if (overscrollState != OverscrollState.TopBoundaryHit) {
55+
if (available.y > 1f && !consumed.y.filter(25f)) {
56+
if (scrollEndHapticState != ScrollEndHapticState.TopBoundaryHit) {
5857
hapticFeedback.performHapticFeedback(hapticFeedbackType)
59-
overscrollState = OverscrollState.TopBoundaryHit
58+
scrollEndHapticState = ScrollEndHapticState.TopBoundaryHit
6059
}
6160
}
6261
// Flinging beyond the top boundary.
63-
else if (available.y < -1f && !consumed.y.filter(5f)) {
64-
if (overscrollState != OverscrollState.BottomBoundaryHit) {
62+
else if (available.y < -1f && !consumed.y.filter(25f)) {
63+
if (scrollEndHapticState != ScrollEndHapticState.BottomBoundaryHit) {
6564
hapticFeedback.performHapticFeedback(hapticFeedbackType)
66-
overscrollState = OverscrollState.BottomBoundaryHit
65+
scrollEndHapticState = ScrollEndHapticState.BottomBoundaryHit
6766
}
6867
}
6968
return Velocity.Zero
@@ -87,4 +86,4 @@ fun Modifier.scrollEndHaptic(
8786
)
8887
}
8988
Modifier.nestedScroll(connection)
90-
}
89+
}

0 commit comments

Comments
 (0)