树是具有分层数据功能的抽象模型,前端中常见的应用场景如Dom树,级联菜单,treeview控件等
八股文-手写Promise
Promise的调用形如1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function asyncFunc(data){
return new Promise((resolve, reject)=>{
execute(data)
if(flag) {
resolve(res)
}else{
reject(err)
}
})
}
asyncFunc(data)
.then(res=>{return asyncFunc(res.data)}, err=>{console.log("step1 error")})
.then(res=>{return asyncFunc(res.data)}, err=>{console.log("step2 error")})
.then(res=>{return asyncFunc(res.data)}, err=>{console.log("step3 error")})
.catch(err=>{console.log("mission defeated")})
Promise意在解决异步函数嵌套时产生的回调地狱,使得异步操作可控以及可组合
- MyPromise应该是一个构造函数,参数是以两个回调方法为形参的函数体 函数体内执行若干异步操作,根据异步状态选择执行上述回调方法
- MyPromise内部须有一个state 待定(pending)成功(fulfilled)/失败(rejected)
- 从asyncFunc定义可知resolve,reject用于改变state状态(state后不再改变)而且分别将参数result赋给onFulfilled,参数error赋给onRejected回调. 注意onFulfilled、onRejected是then的两个参数(函数调用的实参)并不是resolve、reject
- 成员函数then函数根据state状态,成功则执行onFulfilled回调,失败则执行onRejected回调
- then返回另一个MyPromise(从而可进行链式调用) 新的MyPromise中的异步操作将成为等待前面的onFulfilled、onRejected执行结果, 如何resolve/reject由onFulfilled、onRejected的函数体控制以此类推
- catch与then类似 只注册失败的回调函数注意:代码尚缺少形参类型判断及异常处理(try catch throw) 且Promise还有其他API如resolve all finally…
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
33class MyPromise{
state = 'pending'
result = undefined
error = undefined
constructor(executor){
const resolve = {
if(this.state === 'pending'){
this.state = 'fulfilled'
}
}
const reject = {
if(this.state === 'pending'){
this.state = 'rejected'
}
}
executor(resolve, reject)
}
function then(onFulfilled, onRejected){
return new MyPromise((resolve, reject)=>{
if(this.state === 'fulfilled'){
const result = onFulfilled(this.result)
resolve(result)
}
if(this.state === 'rejected'){
const error = onRejected(this.error)
reject(error)
}
})
}
function catch(onRejected){
return this.then(null, onRejected);
}
}
精进参考-> 掘金:你能手写一个Promise吗
如何获取promise多个then之后的值
Promise.all vs Promise.race
Promise.all “要么全部成功 要么全部失败” 异步过程全部成功返回各结果组成的数组 任一个reject会立即返回失败不等待其他异步结果
Promise.race 赛跑 返回最先完成的结果 可用于请求超时返回的逻辑
返回各异步api的成功状态可以用Promise.allSettled 结果数组的项封装为:{state: ‘fulfilled’|’rejected’, value, reason}
手写这三个语法糖 遍历参数集合(异步方法数组)即可 区别在于是否提前返回 亦或结果集项数等于参数长度时返回
Promise.all:
- 用计数器跟踪完成数量
- 保持结果顺序与输入顺序一致
- 任一失败立即 reject,不等待其他
Promise.race:
- 最简单,只需要监听第一个完成的事件
- 空数组永远 pending(这是标准行为)
Promise.allSettled:
- 永远 resolve,不会 reject
- 每个结果都包装成
{status, value/reason}格式 - 无论成功失败都要计数
计算机图形学 光栅化
计算机图形学
RoadMap
- 编程基础:C++, 设计模式 —> C++ STL
- 图形管线:WebGL —> OpenGL
- 项目实践:闫令琪GAMES101 GAMES202 GAME104 TA百人计划
仿射变换和齐次坐标
仿射变换:位移 缩放 旋转 错切(单一方向等比例缩放)
仿射变换前后保持一致的“平直行” 即直线变换后仍然为直线 相互平行的关系变换后仍然平行
齐次坐标是解决仿射变换中“平移无法用矩阵乘法表示”这一问题的关键。
在二维空间中,一个笛卡尔坐标点 $(x, y)$ 对应的齐次坐标是一个三元组 $(x, y, 1)$。
使用齐次坐标的二维变换矩阵
现在,所有变换都可以表示为 $3 \times 3$ 矩阵:
- 平移:
- 缩放:
- 旋转:
- 组合变换:
变换的顺序至关重要(通常不满足交换律)。例如,先缩放后旋转,与先旋转后缩放结果不同。
要表示“先变换 $A$,再变换 $B$”,最终的变换矩阵为 $M = B \cdot A$。
例如,绕一个任意点 $(c_x, c_y)$ 旋转:- 平移至原点: $T(-c_x, -c_y)$
- 绕原点旋转: $R(\theta)$
- 平移回去: $T(c_x, c_y)$
总变换矩阵 $M = T(c_x, c_y) \cdot R(\theta) \cdot T(-c_x, -c_y)$。
三维仿射变换
笛卡尔坐标点 $(x, y, z)$ 的齐次坐标为 $(x, y, z, 1)$。
主要的三维仿射变换矩阵
所有变换都由一个 $4 \times 4$ 的矩阵表示。
平移
沿 $x, y, z$ 轴分别移动 $t_x, t_y, t_z$。
缩放
在 $x, y, z$ 轴方向分别缩放 $s_x, s_y, s_z$。
3.2.3 旋转
旋转比二维复杂,因为需要指定旋转轴。我们使用右手坐标系。
- 绕 X 轴旋转 $\theta$ (Pitch):
- 绕 Y 轴旋转 $\theta$ (Yaw):
- 绕 Z 轴旋转 $\theta$ (Roll):
组合变换
与二维相同,组合变换通过矩阵乘法实现,且顺序至关重要。例如,一个常见的场景相机的视图变换可以表示为T * R * S的形式,具体取决于实现方式。
- camera: position lookat lookup
视图变换 camera移动至原点 绕轴旋转 反向位移
透视投影(锥形视场 近大远小) 正交投影(相机无限远 远近投影一致)
旋转矩阵的转置即逆矩阵,这种矩阵为正交矩阵
采样 混叠 深度
屏幕呈现图像的过程是以屏幕分辨率对原始图像采样的结果,当采样频率与图像频率相乘的结果呈现与原始形态不同的规律形态时即发生了混叠
消除高频然后采样
DLSS(Deep Learning Super Sampling)
‘挤压’到投影空间 保留z轴分量的目的是根据投影结果深度 区分前后遮挡 投影计算的迭代过程中保留最小深度数据 复杂度O(n) 不适用于透明物体
光照强度
$k_d$为漫反射系数 光照强度与光源距离的平方成反比
https://marmoset.co/posts/physically-based-rendering-and-you-can-too/
https://marmoset.co/posts/basic-theory-of-physically-based-rendering/
纹理映射
纹理映射中随透视距离拉大 纹理图像缩小 若采样点不足 贴图出现混叠 远处呈现摩尔纹
Mipmap 多级纹理映射 生成级数下降的低分辨率纹理 根据投影距离映射不同分辨率纹理 结合滤波处理 削弱高频
texture map在没有指定uv坐标的情况下 由引擎自动按照一定规则或重复或拉伸素材生成默认的uv坐标序列 这样映射出的纹理可能存在明显的失真和接缝
blender等建模软件提供了自定义uv的功能 blender Doc: uv工具
对于接缝可指定接缝处映射
Texture can affact shading 类似蒙版用纹理深浅作用于面片法线(fake normal) 从而使之作用于光照 如凹凸贴图 法线贴图 物体边缘和自身投影会露馅 因为没有实际的起伏细节
位移贴图 作用于顶点坐标 产生实际移动
三维纹理 和 体渲染 CT影像
几何体
隐式表示和显式表示
隐式表示方式:
- 数学公式 如
- Constructive Solid Geometry—构造性立体几何(CSG) 即利用基础集合形体的交并关系
- 距离函数
- 水平集
- 分形
重心坐标
已知三角形三个顶点的坐标$P_1, P_2, P_3$ 则三角形内任一点可以用三个顶点坐标的线性组合表示
且有 $b_1+b_2+b_3=1$
贝塞尔曲线 任意t∈(0,1) 迭代二分控制点线段 得到点p(t) 其轨迹即贝塞尔曲线
光栅化阴影
从光源出发记录投影到光源的像素的深度信息 再从相机出发将投影到相机的像素深度信息与前者比较 深度值大则被遮挡 显示为阴影
因为比较结果是二值化的 因此非黑即白 称为硬阴影
现实中 阴影应有过渡 称为软阴影 光栅化阴影只能通过一些边缘模糊手段模拟软阴影
光线追踪
计算模拟光线自光源射出经过反复的折射、反射、衰减,计算量巨大常用于影视渲染等非实时的场景
微信小程序
在学习React Native时想到的微信小程序
特点:
- 自带的传播属性 依托微信分享 二维码分享
- 体量小 使用方便 跨平台
- 开发成本低 WXML + WXSS + js
其性能逊色与React Native应用 而且只支持有限的原生API,React Native不是单纯的web应用(Hybrid),可以依托C++ Jave开发原生功能模块,以及优化运算性能等。缘于微信平台限制,微信小程序拥有有限的文件访问权限,有限的websocket实时通信功能,React Native还可以开发离线推送 APNs(Apple Push Notification Service)和 FCM(Firebase Cloud Messaging)微信小程序的推送需要通过订阅号间接实现
更适合React Native的开发场景
- 高性能动画和图形处理
- 高性能计算和数据处理
- 深度集成原生功能
- 实时通信和推送通知
- 文件系统和数据库操作
- 第三方库和工具的集成
React Native Vision Camera
1 | npm i react-native-vision-camera |
camera 和 microphone 权限
AndroidManifest.xml中声明该App需要camera 和 microphone 权限:1
2
3
4<uses-permission android:name="android.permission.CAMERA" />
<!-- optionally, if you want to record audio: -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
查询权限1
2
3
4
5
6const cameraPermission = Camera.getCameraPermissionStatus()
const microphonePermission = Camera.getMicrophonePermissionStatus()
const showPermissionsPage = cameraPermission !== 'granted' || microphonePermission === 'not-determined'
// 未授权则跳转到 PermissionsPage
props.navigation.navigate(showPermissionsPage?'PermissionsPage':'CameraPage')
getXXXPermissionStatus的结果:
- granted 授权使用
- not-determined 尚未申请
- denied 拒绝(可以再次申请)
- restricted 禁用
PermissionsPage 请求权限1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const [cameraPermissionStatus, setCameraPermissionStatus] = useState('not-determined')
const [microphonePermissionStatus, setMicrophonePermissionStatus] = useState('not-determined')
useEffect(()=>{
if(cameraPermissionStatus === 'granted' && microphonePermissionStatus === 'granted'){
navigation.replace('CameraPage')
}
}, [cameraPermissionStatus, microphonePermissionStatus])
const requestPermissions = useCallback(async ()=>{
const cameraPermission = await Camera.requestCameraPermission()
if(cameraPermission === 'denied') await Linking.openSettings() // 设备设置页
setCameraPermissionStatus(cameraPermission)
// ...
})
return (<Text onPress={requestPermissions}>grant</Text>)
Camera视图
1 | const device = useCameraDevice('back') |
设备devices1
2const devices = Camera.getAvailableCameraDevices()
const device = getCameraDevice(devices, 'back')
其他针对多摄像头和外部摄像头的选择
isActive可用于暂停会话(pause the session: isActive={false}) 相比于unmount可以更快速再次调用
生命周期
onInitialized — onStarted — onPreviewStarted — onPreviewStopped — onStopped、
格式1
2
3
4const format = getCameraFormat(device, [
{ videoResolution: 'max' },
{ photoResolution: 'max' }
])
other format settings
Preview预览1
2
3
4
5
6
7
8
9
10
11const camera = useRef<Camera>(null)
...
return(
<Camera {...props}
ref={camera}
photo={true} {/* enable take photo */}
preview={isPausePreview}
onPreviewStarted={() => console.log('Preview started!')}
onPreviewStopped={() => console.log('Preview stopped!')}
androidPreviewViewType="texture-view" />
)
拍照1
2
3
4const file = await camera.current.takePhoto()
await CameraRoll.save(`file://${file.path}`, {
type: 'photo',
})
移动端开发
RN和Ionic是相似的,基于js/ts的UI桥接原生组件 而Flutter(Dart语言)使用自己的渲染引擎
发布到ios需要ios系统
20250106 Apple Store Top Paid App:
- 75 Hard 自律
- hotschedules 员工排班考勤,可能是国外版钉钉
- Procreate Pocket 绘图动画 图像处理
- Paprika Recipe Manager 3 菜谱
Law Of Attraction
“生活不会在你都准备好了以后才开始”。面试也一样,面试考验的不是那几轮的面试表现,而是你在长期生活、工作中积累的硬技能和软技能
这些包括:你的专业能力,框架能力,思维能力,性格和心态,沟通能力,价值观等等。你只是刚好在这几轮的面试里表现出了你的这些积累,吸引到了对方,最后拿到了Offer。如果临时抱佛脚,光背概念和知识点,没有结合项目深入思考,那么面试的时候会表现的很空洞,给人的印象只是在堆砌一些关键词,这样面试挂掉的可能性就很高。
———— Keraun https://zhuanlan.zhihu.com/p/145079928
“费曼学习法”
- “有结果检验的导向 + 功利 + 逻辑”
- “现学现卖,消化最快”:刚学的知识教别人,消化最快
- “即时反馈”:学习可以达到即时反馈,这是游戏引人入胜的原理之一
- “简洁”:简洁不是真相的表现形式,而是真相本身
RPC
Remote Procedure Call 远程过程调用