ReactNative之从“拉皮条”来看RN中的Spring动画
上篇博客我们聊了RN中关于Timing的动画,详情请参见于《ReactNative之结合具体示例来看RN中的的Timing动画》本篇博客我们将从一个“拉皮条”的一个动画说起,然后来看一下RN中Spring动画的使用方式以及具体效果。Spring从名字中不难看出是弹性弹簧的意思,也就是我们可以使用Spring这个动画来实现一些弹性的动画效果。本部分我们先通过一个“拉皮条”的示例来简单的看一下Spring动画的使用方式,然后在看一下Spring动画中可配置的属性以及每个属性的作用。
一、从“拉皮条”谈起
此拉皮条非彼“拉皮条”,此拉皮条是正经拉皮条,简单的说,就是有一个皮条,我们用劲拉他,然后再松开观察皮条的运行轨迹。下方就是我们“拉皮条”的示例,在这个“拉皮条”的示例中,我们主要使用了Animation中的Spring动画。下方这个Demo中这个灰色的带子就是我们要拉的皮条,一边是黑色的固定皮条的东西,一端是可以拉动的红色方框,我们往一边拉动红色方块,这个皮条就会被拉伸,放手后皮条就会拉动我们的方块到原位置,当然这个拉动的过程中是符合弹簧拉伸效果的。
下方是调整方块质量的操作区,从下方效果中不难看出,当质量越大时惯性就越大,方块来回摆动的幅度就越大,这也是符合弹簧的特性的。
效果就是这么个效果,接下来,我们来看一下上述效果的具体代码实现,代码也不算太多,下方会把核心的代码拿出来聊聊。首先来看一下上述示例中用到的State。在State中有三个值,如下所示:
animationValue: 该值的类型为 Animated.ValueXY,ValueXY存放的是{x, y}的对象,其中这个x的值就是拖动后皮条拉伸后的X值,这个y值我们用来设置皮条的粗细度,也就是皮条的height。
mass: 然后就是这个mass(质量),我们用他来存放方块的质量的。
moveX: 该值用来存放手指移动时的X值的,用作在移动时实时更新皮条的拉伸度以及方块位置。
看完上述的State,接下来我们来看一下本Demo中涉及的手势操作。下方的这个 DisplayView 就是整个皮条以及方块所在的父View。下方是该View所涉及的手势操作:
onStartShouldSetResponder: 首先通过该属性开启手势相应者,在该属性接收到方法中返回true来打开响应者。
onResponderMove: 该属性所设置的方法就是是手指移动时所执行的回调,对应着iOS中的 touchMove 事件。通过该事件我们可以实时的拿到移动过程中的相关坐标。
onResponderRelease: 该属性所对应的方法会在手指离开屏幕时触发,我们可以在该事件中来打开 “皮条” 收缩的动画。
而下方截图中的这个 touchUp 事件就是手指离开屏幕时所触发的动作。在该事件中,我们更新了 State 中的moveX,我们使用的是pageX,也就是相对应页面的X值,这个MoveX我们设置的是方块的中心位置,根据具体的布局,我们需要做个 45 的纠正,这个纠正后的值就是方块要移动的地方。简单的说也就是手指移动的地方就是方块的中心点。设置完 MoveX 后,我们就开启了Spring动画,这个方块就会随着皮条的拉动往回走。
而这个 MoveView 方法就是随着手指的移动试试的更新State中的MoveX的值,而方块的位置就是根据这个State中MoveX的值决定的。
上述是我们本次动画中所涉及的几个事件,当然还有其他好多的手势事件,以后有机会可以在其他博客中详细的来介绍一下RN中常用的手势操作,关于手势在此就不做过多赘述了。
下方就是上述在 touchUp 方法中调用的启动Spring动画的相关方法,代码比较简单。就是设置了一下animation的目标值,及下方的animationValue, 以及设置了一下Spring动画的配置对象,即下方的config对象,其中的 mass 就是本示例中方块的质量。具体代码如下所示:
下方代码就是对应的就是红色方块的代码实现,在该代码中,我们为方块动态设置了 left。在手动滑动时,这个left的值随着手指移动的位置变化而变化,而当开始动画时,这个Left的值对应的就是 animationValue 中的x的值。具体如下所示:
关于本次这个 “拉皮条” 的示例的介绍就先到这儿,毕竟篇幅有限,下方是上述示例的完整代码:
“拉皮条”示例代码
二、“拉皮条” XS Max版本
Spring动画有好多属性,这些属性对应着弹簧的各个物理特性,下方这个Demo 是上述“拉皮条”的一个升级版本,通过该Demo,我们可以很好的来观察Spring动画中各个属性的作用,从而可以判断相关属性的各个使用场景。下方是“拉皮条” 的XS Max 版本。
备注:在上面第一个gif的最后有一个报错,下方是具体的报错内容,该错误的原因是我们设置的Spring的动画属性中冲突了。根据提示我们不难发现那些属性会冲突。我们可以根据错误提示把属性分为三组,(bounciness、speed ), (tenson、friction)以及(stiffness、damping、mass)如果设置了其中一个组的任何一个属性,那么其他两组中的属性都能再设置了,因为设置完后违反弹簧相关的物理定律,是不合规的,所以会报错。
You can define one of bounciness/speed, tenson/friction, or stiffness/damping/mass, but not more than one。
下方是该Demo中所涉及的属性:
1、friction - 摩擦力
“摩擦摩擦,在光滑的地板上摩擦……”,关于什么是摩擦力就不多说了,因为大家都知道穿着滑板鞋在光滑的地板上摩擦~摩擦~。该属性对应的就是滑块的摩擦力,根据物理常识摩擦力越大滑块被皮条拉伸的也就越慢,当摩擦力达到一定程度时,滑块就是匀速的运动了,而不是拉不动的情况,下方是具体的表现效果:
2、tension - 张力
"张力,物理学名词。物体受到拉力作用时,存在于其内部而垂直于两邻部分接触面上的相互牵引力。", 额~上面就是张力的解释,从物理字面量看,张力越大,方块被拉回的速度也就越快。下方这个Demo就能体现出这一点。从下方的图片中不难看出,随着张力的逐渐增大,这个方块被拉回的速度也就越快。
从上面的备注中我们可知,张力是可以和摩擦力一块设置的,所以下方我们设置tension的时候,也选中了friction。摩擦力大的话会使张力对滑块的作用力减小,这也是符合物理规律的。
3、bounciness - 抖
一个字儿概括就是“抖”,bounciness的值越大,这个滑块被拉回来是抖的就越厉害。下方就是这个“抖”的具体示例,从下方不难看出这个抖的值越大,方块回去时就越抖。
4、speed - 速度
速度及滑块被“皮条”拉回的速度, 当这个 speed 的值越大时,滑块就越容易被拉回,而且speed是可以和上面的“抖”bounciness一块设置的。下方就是Speed的相关效果。
5、stiffness - 刚度
刚度这个玩意儿也是个物理名词,刚度指材料或结构在受力时抵抗弹性变形的能力。通过这个解释我们不难看出,刚度越大,说明弹簧越不容易变形,越不容易变形的情况下,如果拉伸后就越快的恢复原形。对于这个刚度可以简单的理解为弹簧的刚度越好,那么这个弹簧的弹性就越好。下方就是刚度的表现:
6、damping - 阻尼
阻尼(damping) 的物理意义是力的衰减,或物体在运动中的能量耗散。通俗地讲,就是阻止物体继续运动。当物体受到外力作用而振动时,会产生一种使外力衰减的反力,称为阻尼力(或减震力) 。换句话说,阻尼就是“减震”,作用就是用来防止物体来回抖动的,这个与上面聊的那个“抖” - bounciness 正好相反。阻尼越大,物体在运动过程中就越不抖,越小就抖的厉害。
阻尼的值必须大于零,而且阻尼可以与上面的刚度- stiffness 一块设置。两个阻尼相同,刚度越大抖的越厉害。
7、 mass - 质量
上面第一部分我们就聊质量了,物体的质量越大,惯性越大。同样一根弹簧,质量越大就抖的越厉害。在Spring动画中,stiffness(刚度)、damping(阻尼)和mass(质量)这三者是可以一块设置的。具体效果如下所示:
8、delay - 延迟
这个就比较好理解了,就是在滑块被皮条拉回去时的一个延迟,单位是毫秒。下方就是关于delay的演示。
上述就是RN中Spring中常用的配置参数了,可以根据不同的效果来具体设置不同的值。这些参数在不设置时也是有值的,下方是上述各个参数的默认值。
在本Demo中还用到了动画的一个知识点,那就是同步执行动画,一个是负责滑块的动画,一个负责皮条的动画。
下方是该部分Demo的全部代码,代码不多也就200行左右。
import {
Animated,
TouchableOpacity,
View,
Text,
StyleSheet,
GestureResponderEvent
} from 'react-native'
import { Component } from 'react'
import React from 'react' type States = {
animationValue: Animated.Value
heightValue: Animated.Value
configValue: any
configLineValue: any
moveX: number
} // BorderView
export default class SpringAnimationView extends Component<null, States> {
isStartAnimation = false
configKey = [
'friction', // 摩擦力
'tension', // 张力
'bounciness', // 弹性
'speed', // 速度
'stiffness', // 刚度
'damping', // 阻尼
'mass', // 质量
'delay' // 延迟
] // 各个参数的默认值
defaultValue = {
friction: 7,
tension: 40,
bounciness: 8,
speed: 12,
stiffness: 100,
damping: 10,
mass: 1,
delay: 0
} constructor (props) {
super(props)
this.state = {
animationValue: new Animated.Value(0),
heightValue: new Animated.Value(0),
configValue: { },
configLineValue: { },
moveX: 30
}
} // 拖动抬起时执行的回调方法
touchUp = (evt) => {
this.isStartAnimation = true
this.setState({ moveX: evt.nativeEvent.pageX - 45 })
this.startAnimation()
} // 移动View执行的方法
moveView = (evt: GestureResponderEvent) => {
this.isStartAnimation = false
this.setState({ moveX: evt.nativeEvent.pageX - 45 })
} // 开始动画
startAnimation = () => {
this.state.animationValue.setValue(this.state.moveX)
this.state.heightValue.setValue(300 / this.state.moveX)
Animated.parallel([
Animated.spring(this.state.animationValue, this.getConfigValue(30)),
Animated.spring(this.state.heightValue, this.getSecondConfigValue(10))
]).start()
} // 获取动画执行的配置项
getConfigValue = (toValue: number) => {
let config = this.state.configValue
config.toValue = toValue
return config
} // 获取动画执行的配置项
getSecondConfigValue = (toValue: number) => {
let config = this.state.configLineValue
config.toValue = toValue
return config
} // 点击配置项所执行的事件
clickConfigPress = (key: string) => () => {
let config = this.state.configValue
if (config[key] === undefined) {
config[key] = this.defaultValue[key]
} else {
config[key] = undefined
}
this.setState({ configValue: config, configLineValue: { ...config } })
} add = (key: string) => () => {
this.defaultValue[key] += 5
this.updateStateValue(key)
} desc = (key: string) => () => {
this.defaultValue[key] -= 5
if (this.defaultValue[key] < 0) {
this.defaultValue[key] = 0
}
this.updateStateValue(key)
} updateStateValue = (key: string) => {
let config = this.state.configValue
if (config[key] !== undefined) {
config[key] = this.defaultValue[key]
}
this.setState({ configValue: config })
} addOrDescView = (title: string, presse: () => void) => {
return (
<TouchableOpacity onPress={presse}>
<View style={style.textView}>
<Text style={ style.textStyle}> {title} </Text>
</View>
</TouchableOpacity>
)
} configView = (key: string, index: number) => {
const {
configValue
} = this.state
let backgroundColor = '#000'
if (configValue[key] !== undefined) {
backgroundColor = '#f00'
}
return (
<View key={index} style={{ flex: 1, flexDirection: 'row', height: 60 }}>
<TouchableOpacity onPress={this.clickConfigPress(key)}>
<View style={[style.textView, { backgroundColor: backgroundColor }]}>
<Text style={ style.textStyle}> {key} </Text>
</View>
</TouchableOpacity> {this.addOrDescView('-', this.desc(key))} <View style={[style.textView, { backgroundColor: '#fff' }]}>
<Text style={ [style.textStyle, { color: '#000' }]}> {this.defaultValue[key]} </Text>
</View>
{this.addOrDescView('+', this.add(key))}
</View>
)
} animatedView = () => {
let left: any = this.state.moveX
if (this.isStartAnimation) {
left = this.state.animationValue
}
return (
<Animated.View
style={{
height: 50,
width: 50,
left: left,
backgroundColor: '#f00',
position: 'absolute',
borderRadius: 10
}}/>
)
} displayView = () => {
let width: any = this.state.moveX
let height: any = 300 / this.state.moveX
if (this.isStartAnimation) {
width = this.state.animationValue
height = this.state.heightValue
}
return (
<View style={style.displayView}
onStartShouldSetResponder={() => { return true }}
onResponderRelease={this.touchUp}
onResponderMove={this.moveView}>
<Animated.View style={{ height: height, width: width, backgroundColor: '#fff' }}/>
{this.animatedView()}
</View>
)
} render () {
return (
<View style={{ flex: 1 , margin: 10 }}>
{/*拖动的View*/}
{this.displayView()} {/*操作配置项的View*/}
{
this.configKey.map((key, index) => {
return this.configView(key, index)
})
}
</View>
)
}
} const style = StyleSheet.create({
textView: {
justifyContent: 'center',
alignItems: 'center',
height: 50,
backgroundColor: '#000',
margin: 10,
padding: 10,
borderRadius: 10,
borderWidth: 1
},
textStyle: {
textAlignVertical: 'center',
color: '#fff'
},
displayView: {
width: '100%',
height: 50,
backgroundColor: '#ccc',
borderLeftColor: '#000',
borderLeftWidth: 3,
borderBottomColor: '#000',
borderBottomWidth: 1,
flexDirection: 'row',
alignItems: 'center'
}
})
“拉皮条”升级版代码
本篇的“拉皮条”的动画就到这儿吧。
ReactNative之从“拉皮条”来看RN中的Spring动画的更多相关文章
- ReactNative之结合具体示例来看RN中的的Timing动画
今天继续更新RN相关的博客.上篇博客详细的聊了RN中关于Flex布局的相关东西,具体请参见<ReactNative之参照具体示例来看RN中的FlexBox布局>.本篇博客继续更新RN的动画 ...
- ReactNative之参照具体示例来看RN中的FlexBox布局
今天是重阳节,祝大家节日快乐,今天继续更新RN相关的博客.上篇博客<ReactNative之从HelloWorld中看环境搭建.组件封装.Props及State>中我们通过一个HelloW ...
- React Native(十五)——RN中的分享功能
终于,终于,可以总结自己使用RN时的分享功能了-- 为什么呢?且听我慢慢道来吧: 从刚开始接触React Native(2017年9月中旬)就着手于分享功能,直到自己参与公司的rn项目开发中,再到现在 ...
- RN中的常用组件-----图片
1.RN中的常用组件-----图片 本地图片: <Image source={require('../src/assets/x.jpg')}/> 本地图片可以无需指定尺寸(因为导入/打包 ...
- Dubbo中对Spring配置标签扩展
Spring提供了可扩展Schema的支持,完成一个自定义配置一般需要以下步骤: 设计配置属性和JavaBean 编写XSD文件 编写NamespaceHandler和BeanDefinitionPa ...
- 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事
Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...
- (转) [教程] Unity3D中角色的动画脚本的编写(一)
ps: 这两天研究unity3d,对动画处理特别迷糊,不知FBX导入以后,接下来应该怎么操作,看到这篇文章,感觉非常好,讲解的很详细. 已有好些天没写什么了,今天想起来该写点东西了.这次我所介绍的内容 ...
- [Mugeda HTML5技术教程之18]如何在Android应用中使用Mugeda动画内容
1.简介 本文主要介绍如何在Android应用程序中使用Mugeda动画.Mgeda动画是标准HTML5格式的动画,在Android应用程序中可以使用WebView来加载Mugeda动画.动画内容本身 ...
- jQuery动画高级用法(上)——详解animation中的.queue()动画队列插队函数
决定对animate方面做一些总结,希望能给大家一些启发和帮助 从一个实际应用谈起 今天不谈animate().fadeIn().fadeOut().slideUp().show().hide()诸如 ...
随机推荐
- 关于mui 中popover与下拉刷新冲突问题
最近用mui做app混合式开发时,作为一个后端开发,高前端确实有点吃了,期间遇到的问题肯定也不少.这两天app做更新,为了装逼,将更新的提示搞得好看些,用到了mui中的popover,结果把自己整死了 ...
- Docker for Windows 使用 VMware WorkStation
一.前言 Docker for Windows 不同于 Docker Toolbox.Docker for Windows 对系统的要求至少为Windows 10专业版,因为它需要Hyper-V的支持 ...
- WinForm中DataGridView对XML文件的读取
转自http://www.cnblogs.com/a1656344531/archive/2012/11/28/2792863.html c#读取XML XML文件是一种常用的文件格式,例如Win ...
- 如何把Python脚本导出为exe程序
一.pyinstaller简介 pyinstaller将Python脚本打包成可执行程序,使在没有Python环境的机器上运行 最新版是pyinstaller 3.1.1.支持python2.7和py ...
- BDD测试之selenium控制滚动条
一.对于页面存在滚动条,可以通过插入JS控制滚动条(最常用的方法) (1)将滚动条移动到指定坐标位置处 ((JavascriptExecutor) driver).executeScript(&quo ...
- Java中的Unsafe类111
1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...
- Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)
在Spring Boot官方介绍中,首一段话是这样的(如下图).我们可以大概了解到其所表达的含义:我们可以利用Spring Boot写很少的配置来创建一个非常方便的基于Spring整合第三方类库的单体 ...
- BZOJ_3238_[Ahoi2013]差异_后缀自动机
BZOJ_3238_[Ahoi2013]差异_后缀自动机 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sam ...
- 硬木地板 JDFZ1667
Description 举行计算机科学家盛宴的大厅的地板为M×N (1<=M<=9, 1<=N<=9)的矩形.现在必须要铺上硬木地板砖.可以使用的地板砖形状有两种:1) 2×1 ...
- Java的LockSupport.park()实现分析(转载)
LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语.LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数: p ...