RN7_实战(3)
参考:http://reactnative.cn/docs/0.41/images.html#content(进阶指南部分)
图片
JS中的静态图片引入:
只需把图片文件放在代码文件夹中某处,然后像下面这样去引用它。
<Image source={require('./my-icon.png')}/>
是哪个组件引用了这个图片,Packager就会去这个组件所在的文件夹下查找my-icon.png
。
原生平台的图片引入:
<Image source={{
uri:
'app_icon'}}style={{
width:
40,
height:
40}} />
使用已经打包到App中的图片资源。
网络图片的引入:
<Imagesource={{
uri:
'https://facebook.github.io/react/img/logo_og.png'}}
style={{
width:
400,
height:
400}
} />
注意:
在引入网络图片的时候,你需要手动指定图片的尺寸!
原因:
浏览器
中
,如果你不给图片指定尺寸,那么浏览器会首先渲染一个0x0大小的元素占位,然后下载图片,在下载完成后再基于正确的尺寸来渲染图片。
已经打包好的应用资源文件中读取图片(使用require('image!x')
语法)则无需指定尺寸
,因为它们的尺寸在加载时就可以立刻知道。
嵌入背景图片
return (
<Imagesource={...}>
<Text>Inside</Text>
</Image>
);
只需简单地创建一个<Image>
组件,然后把需要背景图的子组件嵌入其中即可。
触摸事件
点击事件
classMyButtonextendsComponent{
_onPressButton() {
console.log(
"Youtapped the button!");
}
render() {
return (
<TouchableHighlightonPress={this._onPressButton}>
<Text>Button</Text>
</TouchableHighlight>
);
}
}
长按
使用onLongPress
属性来实现。
双指伸缩
设置maximumZoomScale
和minimumZoomScale
属性即可以使用户能够缩放其中的内容
手势响应系统
摸响应系统可以使组件在不关心父组件或子组件的前提下自行处理触摸交互。具体的实现在ResponderEventPlugin.js
文件中。
响应者的生命周期
成为相应者
响应者:一个View只要实现了正确的协商方法,就可以成为触摸事件的响应者:
l View.props.onStartShouldSetResponder: (evt) => true, - 在用户开始触摸的时候(手指刚刚接触屏幕的瞬间),是否愿意成为响应者?
l View.props.onMoveShouldSetResponder: (evt) => true, - 如果View不是响应者,那么在每一个触摸点开始移动(没有停下也没有离开屏幕)时再询问一次:是否愿意响应触摸交互呢?
如果View返回true,并开始尝试成为响应者。
响应者能触发的事件:
l View.props.onResponderMove: (evt) => {} - 用户正在屏幕上移动手指时(没有停下也没有离开屏幕)。
l View.props.onResponderRelease: (evt) => {} - 触摸操作结束时触发,比如"touchUp"(手指抬起离开屏幕)。
l View.props.onResponderTerminationRequest: (evt) => true - 有其他组件请求接替响应者,当前的View是否“放权”?返回true的话则释放响应者权力。
l View.props.onResponderTerminate: (evt) => {} - 响应者权力已经交出。这可能是由于其他View通过onResponderTerminationRequest请求的,也可能是由操作系统强制夺权(比如iOS上的控制中心或是通知中心)。
Evt中包含的内容
nativeEvent:
l changedTouches - 在上一次事件之后,所有发生变化的触摸事件的数组集合(即上一次事件后,所有移动过的触摸点)
l identifier - 触摸点的ID
l locationX - 触摸点相对于父元素的横坐标
l locationY - 触摸点相对于父元素的纵坐标
l pageX - 触摸点相对于根元素的横坐标
l pageY - 触摸点相对于根元素的纵坐标
l target - 触摸点所在的元素ID
l timestamp - 触摸事件的时间戳,可用于移动速度的计算
l touches - 当前屏幕上的所有触摸点的集合
事件传递规则
onStartShouldSetResponder
与onMoveShouldSetResponder
是以冒泡的形式调用的,即嵌套最深的节点最先调用。这意味着当多个View同时在*ShouldSetResponder
中返回true时,最底层的View将优先“夺权”。
如果某个父View想要在触摸操作开始时阻止子组件成为响应者,那就应该处理onStartShouldSetResponderCapture
事件并返回true值。
l View.props.onStartShouldSetResponderCapture:(evt) => true,
l View.props.onMoveShouldSetResponderCapture:(evt) => true,
动画
初识动画:
class Playground extends React.Component {
constructor(props: any) {
super(props);
this.state= {
bounceValue:
new Animated.Value(
0),
};
}
render(): ReactElement {
return(
<Animated.Image // 可选的基本组件类型: Image, Text, View
source={{uri: 'http://i.imgur.com/XMKOH81.jpg'}}
style={{
flex: 1,
transform:[ // `transform`是一个有序数组(动画按顺序执行)
{scale: this.state.bounceValue}, // 将`bounceValue`赋值给 `scale`
]
}}
/>
);
}
componentDidMount() {
this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值
Animated.spring( // 可选的基本动画类型:spring, decay, timing
this.state.bounceValue, // 将`bounceValue`值动画化
{
toValue: 0.8, // 将其值以动画的形式改到一个较小值
friction: 1, // Bouncier spring
}
).start(); // 开始执行动画
}
}
分析:
1、bounceValue在构造函数中初始化为state的一部分,然后和图片的缩放比例进行绑定。在动画执行的背后,其数值会被不断的计算并用于设置缩放比例。
2、当组件刚刚挂载的时候,缩放比例被设置到1.5。然后紧跟着在bounceValue上执行了一个弹跳动画(spring),会逐帧刷新数值,并同步更新所有依赖本数值的绑定(在这个例子里,就是图片的缩放比例)。
核心API
两个值类型:Value
用于单个的值,而ValueXY
用于向量值
三种动画类型:spring
,decay
,还有timing
三种组件类型:View
,Text
和Image
三种动画:
三种动画类型可以用来创建几乎任何你需要的动画曲线,因为它们每一个都可以被自定义。
spring
:
基础的单次弹跳物理模型,符合Origami设计标准
l friction
: 摩擦力,默认为7.
l tension
: 张力,默认40。
decay
:
以一个初始速度开始并且逐渐减慢停止。
l velocity
: 起始速度,必填参数。
l deceleration
:速度衰减比例,默认为0.997。
timing
:
从时间范围映射到渐变的值。
l duration
: 动画持续的时间(单位是毫秒),默认为500。
l easing
:一个用于定义曲线的渐变函数。阅读Easing
模块可以找到许多预定义的函数。
l delay
: 在一段时间之后开始动画(单位是毫秒),默认为0。
动画的开始和停止:
动画可以通过调用start
方法来开始。start
接受一个回调函数,当动画结束的时候会调用此回调函数。如果动画是因为正常播放完成而结束的,回调函数被调用时的参数为{finished: true}
,但若动画是在结束之前被调用了stop
而结束(可能是被一个手势或者其它的动画打断),它会收到参数{finished: false}
。
组合动画
多个动画可以通过parallel
(同时执行)、sequence
(顺序执行)、stagger
和delay
来组合使用。它们中的每一个都接受一个要执行的动画数组,并且自动在适当的时候调用start/stop。
Eg:
Animated.sequence([
//首先执行decay动画,结束后同时执行spring和twirl动画
Animated.decay(position, {
//滑行一段距离后停止
velocity: {
x: gestureState.vx,
y:gestureState.vy},
// 根据用户的手势设置速度
deceleration:
0.997,
}),
Animated.parallel([
//在decay之后并行执行:
Animated.spring(position, {
toValue: {
x:
0,
y:
0}
// 返回到起始点开始
}),
Animated.timing(twirl, {
//同时开始旋转
toValue:
360,
}),
]),
]).start();
//执行这一整套动画序列
插值(interpolate)
插值函数interpolate
, 它可以接受一个输入区间,然后将其映射到另一个的输出区间。
Eg:
value.interpolate({
inputRange: [
0,
1],
outputRange: [
0,
100],
});
从0-1区间到0-100区间的映射。
跟踪动态值:
动画中所设的值还可以通过跟踪别的值得到。你只要把toValue设置成另一个动态值而不是一个普通数字就行了。
可以使用插值来进行组合:
Eg:
Animated.spring(follower, {
toValue:leader}).start();
Animated.timing(opacity, {
toValue:pan.x.interpolate({
inputRange: [
0,
300],
outputRange: [
1,
0],
}),
}).start();
定时器
定时器api
l setTimeout, clearTimeout
l setInterval, clearInterval
l setImmediate, clearImmediate
l requestAnimationFrame, cancelAnimationFrame
1. requestAnimationFrame(): 用来执行在一段时间内控制视图动画的代码
2. setImmediate/setTimeout/setInterval(): 在稍后执行代码。注意这有可能会延迟当前正在进行的动画。
3. runAfterInteractions(): 在稍后执行代码,不会延迟当前进行的动画。
TimerMixin
很多React Native应用发生致命错误(闪退)是与计时器有关。具体来说,是在某个组件被卸载(unmount)之后,计时器却仍然被激活。为了解决这个问题,我们引入了TimerMixin
。如果你在组件中引入TimerMixin
,就可以把你原本的setTimeout(fn, 500)
改为this.setTimeout(fn, 500)
(只需要在前面加上this.
),然后当你的组件卸载时,所有的计时器事件也会被正确的清除。
Eg:
varTimerMixin =
require(
'react-timer-mixin');
varComponent = React.createClass({
mixins: [TimerMixin],
componentDidMount:
function(){
this.setTimeout(
() => {
console.log(
'这样我就不会导致内存泄露!'); },
500
);
}
});
这里要铭记:在unmount组件时清除(clearTimeout/clearInterval)所有用到的定时器!
Eg:
import React,{
Component
}
from'react';
exportdefaultclassHelloextendsComponent{
componentDidMount() {
this.timer =setTimeout(
() => {
console.log(
'把一个定时器的引用挂在this上'); },
500
);
}
componentWillUnmount() {
//如果存在this.timer,则使用clearTimeout清空。
//如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear
this.timer&& clearTimeout(
this.timer);
}
};
总结一下从刚开始到这里的成果:
写RN1到现在,感觉RN好弱鸡。写的动画卡的不行,navigation翻页也卡,知道RN里面仅仅是JS的单线程在运行,卡也没办法。
鉴于目前是什么都没接触过的新手,感觉这十几天的学习大概RN的代码是会看一些了。基本的东西都大概了解一些,对于真正去做项目感觉还很远啊!悲伤~
之前写的东西,都会复制粘贴到demo工程中去看看效果。一些报错也大概了解。但是仅仅是了解的阶段。开发环境也熟悉了一些。同事用inteliJ可以建RN项目,感觉好棒,但是我还是默默用cmd建好了。
总之这条路真是走的恶心。仅仅对RN的开发有些熟悉(其实似乎还是什么都不太会,但是大概能看懂代码了)。虽然有android开发基础(好歹android开发我还是6的),但是吃起RN这坨屎还是比较费力的。Navigation我都没有看懂~接下来官方文档感觉对我的帮助不是很大了。我要去找一些实际开发的教学视频!接下来就围绕教学视频来写学习笔记了~