Live Note

Remain optimistic

Custom Scroll Indicator for ScrollView

Demo.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import React, { useMemo, useState } from "react"
import { ScrollView, View } from "react-native"

const Demo: React.FC = () => {
const [contentSize, setContentSize] = useState(0)
const [scrollViewSize, setScrollViewSize] = useState(0)
const [scrollPosition, setScrollPosition] = useState(0)

const indicatorWidth = useMemo(() => {
if (contentSize > 0 && scrollViewSize > 0) {
return (scrollViewSize / contentSize) * scrollViewSize
}

return 0
}, [contentSize, scrollViewSize])

const indicatorPosition = useMemo(() => {
if (contentSize > 0 && scrollPosition > 0) {
return (scrollPosition / contentSize) * scrollViewSize
}

return 0
}, [contentSize, scrollPosition, scrollViewSize])

return (
<View>
<ScrollView
// disable default scroll indicator
showsHorizontalScrollIndicator={false}
onLayout={(event) => {
setScrollViewSize(event.nativeEvent?.layout?.width ?? 0)
}}
onContentSizeChange={(w) => {
setContentSize(w)
}}
horizontal // set horizontal scroll
scrollEventThrottle={16} // throttle scroll event to improve performance
onScroll={(event) => {
// change scroll position here
const { contentOffset } = event.nativeEvent

const { x = 0 } = contentOffset
const max = contentSize - scrollViewSize
const min = 0

if (x > max) {
setScrollPosition(max)
} else if (x < min) {
setScrollPosition(min)
} else {
setScrollPosition(x)
}
}}
>
{/* Content here */}
</ScrollView>

{/* custom scroll indicator */}
<View
style={{
bottom: 0,
height: 4,
width: indicatorWidth,
position: "absolute",
left: indicatorPosition,
borderRadius: 4,
backgroundColor: "#EEEEEE",
}}
/>
</View>
)
}

export default Demo

基本用法

Set 类似于数组,但是成员的值都是唯一的,没有重复。Set 本身是一个构造函数。

1
2
3
4
5
6
7
8
9
10
11
const set = new Set()
;[1, 2, 3, 4].forEach((x) => set.add(x))
for (let value of set) {
console.log(value) //1, 2, 3, 4
}

const set = new Set([1, 2, 3, 4])
;[...set] //[1, 2, 3, 4]

//数组除重
;[...new Set(array)]

在 Set 内部,NaN 是相等的,两个对象总是不相等的。

1
2
3
4
5
6
7
8
let set = new Set()
let a = NaN
let b = NaN
set.add(a).add(b)
set.size //1

set.add({}).add({})
set.size //3
Read more »

起因

如果 wrapp 的 flex-direction 为 row 的时候,Text 内部不自动 wrap。还是测试拿给我看,才发现某些机型会存在这种问题。

开始以为是 Text 内部会根据flexbox而有不同的展现,但是翻阅官方文档:

The element is unique relative to layout: everything inside is no longer using the Flexbox layout but using text layout. This means that elements inside of a are no longer rectangles, but wrap when they see the end of the line.

Text 组件内部默认是 wrap 的,除非这一整个是一个超长的单词,否则就会在 white space 的时候自动 break 了。
如果给 wrapp 设置 wrap,那么整个 text 直接就 wrap 到下一行,icon 单独一行了。(这部分官方是有案例的,根据 Text 外部的 Container,wrap 也是不一样的)

好嘛,到底是什么东西导致的?为什么 flex-direction: row 就给我炸了?

原因: element 的宽度计算方式导致的

解决

官方的解释:每个 element 的 size 大致可以分为两种方式:

  • content-driven
  • parent-driven
  1. flex-direction: column 的时候,宽度首先由 content-driven,然后再由 parent-driven,高度直接由 content-driven,完美,一点问题没有。
  2. flex-direction: row 的时候:高度由 comtent-driven,宽度呢?那当然是 content-driven 啊! … 不好意思,Text 无法计算自己的 width,why?因为 Text 是 autosize 的!(也有意外情况,如果你把 Text 给 absolute 起来,那么就可以计算了)。所以此时的 Text 的 width,是由 parent-driven,也就是整个 wrapper 的宽度了。

解决方法也很简单,Text 上挂一个 flexShrink: 1,让它自己玩去吧。

Promise 的特点

  1. 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变就不会再变,任何时候得到的都是这个结果。Promise 对象的状态改变只有两个可能:从 Pending 变为 Fulfilled,从 Pending 变为 Rejected。只要这两种情况发生,这时就成为 resolve。就算改变已经发生,再对 Promise 对象添加回调函数,也会立即得到这个结果。与 Event 完全不同,Event 一旦错过再监听是得不到结果的。

基本用法

1
2
3
4
5
6
7
8
9
var promise = new Promise(function (resolve, reject) {
//some code

if (/*异步操作成功*/) {
resolve(value);
} else {
reject(error);
}
})
Read more »

TV series

  • Game of Thrones
  • WestWorld
  • Chernobyl
  • Fantasmagorias
  • Love, Death & Robots
Read more »