ReactNative入门 —— 动画篇(上)
在不使用任何RN动画相关API的时候,我们会想到一种非常粗暴的方式来实现我们希望的动画效果——通过修改state来不断得改变视图上的样式。
我们来个简单的示例:
- var AwesomeProject = React.createClass({
- getInitialState() {
- return { w: 200, h: 20 }
- },
- _onPress() {
- //每按一次增加近30宽高
- var count = 0;
- while(++count<30){
- requestAnimationFrame(()=>{
- this.setState({w: this.state.w + 1, h: this.state.h + 1})
- })
- }
- }
- render: function() {
- return (
- <View style={styles.container}>
- <View style={[styles.content, {width: this.state.w, height: this.state.h}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
效果如下:
这种方式实现的动画存在两大问题:
1. 将频繁地销毁、重绘视图来实现动画效果,性能体验很糟糕,常规表现为内存花销大且动画卡顿明显;
2. 动画较为生硬,毕竟web的css3不适用在RN上,无法轻易设定动画方式(比如ease-in、ease-out)。
因此在RN上设置动画,还是得乖乖使用相应的API来实现,它们都能很好地触达组件底层的动画特性,以原生的形式来实现动画效果。
LayoutAnimation
LayoutAnimation 是在布局发生变化时触发动画的接口(我们在上一篇文章里已经介绍过),这意味着你需要通过修改布局(比如修改了组件的style、插入新组件)来触发动画。
该接口最常用的方法是 LayoutAnimation.configureNext(conf<Object>) ,用来设置下次布局变化时的动画效果,需要在执行 setState 前调用。
其中 conf 参数格式参考:
- {
- duration: 700, //持续时间
- create: { //若是新布局的动画类型
- type: 'linear', //线性模式
- property: 'opacity' //动画属性,除了opacity还有一个scaleXY可以配置
- },
- update: { //若是布局更新的动画类型
- type: 'spring', //弹跳模式
- springDamping: 0.4 //弹跳阻尼系数
- }
- }
其中动画type的类型可枚举为:
- spring //弹跳
- linear //线性
- easeInEaseOut //缓入缓出
- easeIn //缓入
- easeOut //缓出
- keyboard //键入
要注意的是,安卓平台使用 LayoutAnimation 动画必须加上这么一句代码(否则动画会失效):
- UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
于是我们一开始的动画可以这么来写:
- var AwesomeProject = React.createClass({
- getInitialState() {
- return { w: 200, h: 20 }
- },
- _onPress() {
- LayoutAnimation.configureNext({
- duration: 700, //持续时间
- create: {
- type: 'linear',
- property: 'opacity'
- },
- update: {
- type: 'spring',
- springDamping: 0.4
- }
- });
- this.setState({w: this.state.w + 30, h: this.state.h + 30})
- }
- render: function() {
- return (
- <View style={styles.container}>
- <View style={[styles.content, {width: this.state.w, height: this.state.h}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
这时候动画灵活和流畅多了:
ok我们上例看到的仅仅是布局更新的情况,我们来看看新布局被创建(有新组件加入到视图上)的情况如何:
- var AwesomeProject = React.createClass({
- getInitialState() {
- return {
- showNewOne : false,
- w: 200,
- h: 20
- }
- },
- _onPress() {
- LayoutAnimation.configureNext({
- duration: 1200,
- create: {
- type: 'linear',
- property: 'opacity' //注意这里,我们设置新布局被创建时的动画特性为透明度
- },
- update: {
- type: 'spring',
- springDamping: 0.4
- }
- });
- this.setState({w: this.state.w + 30, h: this.state.h + 30, showNewOne : true})
- },
- render: function() {
- var newOne = this.state.showNewOne ? (
- <View style={[styles.content, {width: this.state.w, height: this.state.h}]}>
- <Text style={[{textAlign: 'center'}]}>new one</Text>
- </View>
- ) : null;
- return (
- <View style={styles.container}>
- <View style={[styles.content, {width: this.state.w, height: this.state.h}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </View>
- {newOne}
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
效果如下:
setNativeProps
如果我们执意使用开篇的修改state的方式,觉得这种方式更符合当前需求对动画的控制,那么则应当使用原生组件的 setNativeProps 方法来做对应实现,它会直接修改组件底层特性,不会重绘组件,因此性能也远胜动态修改组件内联style的方法。
该接口属原生组件(比如View,比如TouchableOpacity)才拥有的原生特性接口,调用格式参考如下:
- Component.setNativeProps({
- style: {transform: [{rotate:'50deg'}]}
- });
对于开篇的动画示例,我们可以做如下修改:
- var _s = {
- w: 200,
- h: 20
- };
- var AwesomeProject = React.createClass({
- _onPress() {
- var count = 0;
- while(count++<30){
- requestAnimationFrame(()=>{
- this.refs.view.setNativeProps({
- style: {
- width : ++_s.w,
- height : ++_s.h
- }
- });
- })
- }
- },
- render: function() {
- return (
- <View style={styles.container}>
- <View ref="view" style={[styles.content, {width: 200, height: 20}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
效果如下(比开篇的示例流畅多了):
Animated
本文的重点介绍对象,通过 Animated 我们可以在确保性能良好的前提下创造更为灵活丰富且易维护的动画。
不同于上述的动画实现方案,我们得在 Animated.View、Animated.Text 或 Animated.Image 动画组件上运用 Animate 模块的动画能力(如果有在其它组件上的需求,可以使用 Animated.createAnimatedComponent
方法来对其它类型的组件创建动画)。
我们先来个简单的例子:
- var React = require('react-native');
- var {
- AppRegistry,
- StyleSheet,
- Text,
- View,
- Easing,
- Animated,
- TouchableOpacity,
- } = React;
- var _animateHandler;
- var AwesomeProject = React.createClass({
- componentDidMount() {
- _animateHandler = Animated.timing(this.state.opacityAnmValue, {
- toValue: 1, //透明度动画最终值
- duration: 3000, //动画时长3000毫秒
- easing: Easing.bezier(0.15, 0.73, 0.37, 1.2) //缓动函数
- })
- },
- getInitialState() {
- return {
- opacityAnmValue : new Animated.Value(0) //设置透明度动画初始值
- }
- },
- _onPress() {
- _animateHandler.start && _animateHandler.start()
- }
- render: function() {
- return (
- <View style={styles.container}>
- <Animated.View ref="view" style={[styles.content, {width: 200, height: 20, opacity: this.state.opacityAnmValue}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity >
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
- var styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center'
- },
- content: {
- justifyContent: 'center',
- backgroundColor: 'yellow',
- },
- button: {
- marginTop: 10,
- paddingVertical: 10,
- paddingHorizontal: 20,
- backgroundColor: 'black'
- },
- buttonText: {
- color: 'white',
- fontSize: 16,
- fontWeight: 'bold',
- }
- });
点击按钮后,Animated.View 会以bezier曲线形式执行时长3秒的透明度动画(由0到1):
so 我们做了这些事情:
1. 以 new Animated.Value(0) 实例化动画的初始值给state:
- getInitialState() {
- return {
- opacityAnmValue : new Animated.Value(0) //设置透明度动画初始值
- }
- }
2. 通过 Animated.timing 我们定义了一个动画事件,在后续可以以 .start() 或 .stop() 方法来开始/停止该动画:
- componentDidMount() {
- _animateHandler = Animated.timing(this.state.opacityAnmValue, {
- toValue: 1, //透明度动画最终值
- duration: 3000, //动画时长3000毫秒
- easing: Easing.bezier(0.15, 0.73, 0.37, 1.2)
- })
- },
我们在按钮点击事件中触发了动画的 .start 方法让它跑起来:
- _onPress() {
- _animateHandler.start && _animateHandler.start()
- },
start 方法接受一个回调函数,会在动画结束时触发,并传入一个参数 {finished: true/false},若动画是正常结束的,finished 字段值为true,若动画是因为被调用 .stop() 方法而提前结束的,则 finished 字段值为false。
3. 动画的绑定是在 <Animate.View /> 上的,我们把实例化的动画初始值传入 style 中:
- <Animated.View ref="view" style={[styles.content, {width: 200, height: 20, opacity: this.state.opacityAnmValue}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
然后。。。没有然后了,这实在太简单了
这里需要讲一下的应该是定义动画事件的 Animated.timing(animateValue, conf<Object>) 方法,其中设置参数格式为:
- {
- duration: 动画持续的时间(单位是毫秒),默认为500。
- easing:一个用于定义曲线的渐变函数。阅读Easing模块可以找到许多预定义的函数。iOS默认为Easing.inOut(Easing.ease)。
- delay: 在一段时间之后开始动画(单位是毫秒),默认为0。
- }
这里提及的 Easing 动画函数模块在 react-native/Libraries/Animated/src/ 目录下,该模块预置了 linear、ease、elastic、bezier 等诸多缓动特性,有兴趣可以去了解。
另外除了 Animated.timing,Animated 还提供了另外两个动画事件创建接口:
1. Animated.spring(animateValue, conf<Object>) —— 基础的单次弹跳物理模型,支持origami标准,conf参考:
- {
- friction: 控制“弹跳系数”、夸张系数,默认为7。
- tension: 控制速度,默认40。
- }
代码参考:
- var React = require('react-native');
- var {
- AppRegistry,
- StyleSheet,
- Text,
- View,
- Easing,
- Animated,
- TouchableOpacity,
- } = React;
- var _animateHandler;
- var AwesomeProject = React.createClass({
- componentDidMount() {
- this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值
- _animateHandler = Animated.spring(this.state.bounceValue, {
- toValue: 1,
- friction: 8,
- tension: 35
- })
- },
- getInitialState() {
- return {
- bounceValue : new Animated.Value(0) //设置缩放动画初始值
- }
- },
- _onPress() {
- _animateHandler.start && _animateHandler.start()
- },
- _reload() {
- AppRegistry.reload()
- },
- render: function() {
- return (
- <View style={styles.container}>
- <Animated.View ref="view" style={[styles.content, {width: 200, height: 20, transform: [{scale: this.state.bounceValue}]}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity onPress={this._reload}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
- var styles = StyleSheet.create({
- container: {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center'
- },
- content: {
- justifyContent: 'center',
- backgroundColor: 'yellow',
- },
- button: {
- marginTop: 10,
- paddingVertical: 10,
- paddingHorizontal: 20,
- backgroundColor: 'black'
- },
- buttonText: {
- color: 'white',
- fontSize: 16,
- fontWeight: 'bold',
- }
- });
留意这里我们用了 animateValue.setValue(1.5) 方法来修改动画属性值。效果如下:
2. Animated.decay(animateValue, conf<Object>) —— 以一个初始速度开始并且逐渐减慢停止,conf参考:
- {
- velocity: 起始速度,必填参数。
- deceleration: 速度衰减比例,默认为0.997。
- }
代码参考:
- var _animateHandler;
- var AwesomeProject = React.createClass({
- componentDidMount() {
- _animateHandler = Animated.decay(this.state.bounceValue, {
- toValue: 0.2,
- velocity: 0.1
- })
- },
- getInitialState() {
- return {
- bounceValue : new Animated.Value(0.1)
- }
- },
- _onPress() {
- _animateHandler.start && _animateHandler.start()
- },
- render: function() {
- return (
- <View style={styles.container}>
- <Animated.View ref="view" style={[styles.content, {width: 200, height: 30, transform: [{scale: this.state.bounceValue}]}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
对于最后介绍的两个动画效果,可能得熟悉一些物理、数学模型才能更好地来做控制,大部分情况下,咱们直接使用 Animated.timing 就足够满足需求了。
监听动画
1. 有时候我们需要在动画的过程中监听到某动画时刻的属性值,可以通过 animateValue.stopAnimation(callback<Function>) 或 animateValue.addListener(callback<Function>) 来实现
其中 stopAnimation 会停止当前动画并在回调函数中返回一个 {value : number} 对象,value对应最后一刻的动画属性值:
- var _animateHandler,
- _isFirsPress = 0;
- var AwesomeProject = React.createClass({
- componentDidMount() {
- _animateHandler = Animated.timing(this.state.opacityAnmValue, {
- toValue: 1,
- duration: 6000,
- easing: Easing.linear
- })
- },
- getInitialState() {
- return {
- opacityAnmValue : new Animated.Value(0) //设置透明度动画初始值
- }
- },
- _onPress() {
- if(_isFirsPress == 0){
- _animateHandler.start && _animateHandler.start();
- _isFirsPress = 1
- }
- else {
- this.state.opacityAnmValue.stopAnimation(value => {
- Alert.alert(
- '动画结束,最终值:',
- JSON.stringify(value),
- [
- {text: 'OK', onPress: () => {}}
- ]
- )
- })
- }
- },
- render: function() {
- return (
- <View style={styles.container}>
- <Animated.View style={[styles.content, {width: 200, height: 20, opacity: this.state.opacityAnmValue}]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
- <TouchableOpacity onPress={this._onPress}>
- <View style={styles.button}>
- <Text style={styles.buttonText}>Press me!</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity >
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略本按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- });
而 addListener 方法会在动画的执行过程中持续异步调用callback回调函数,提供一个最近的值作为参数。
2. 有时候我们希望在某个交互事件(特别是手势)中更灵活地捕获某个事件对象属性值,并动态赋予某个变量,对于这种需求可以通过 Animated.event 来实现。
它接受一个数组为参数,数组中的层次对应绑定事件参数的相应映射,听着有点绕,看例子就很好理解了:
- var scrollX = 0,
- pan = {
- x: 0,
- y: 0
- };
- //...
- onScroll : Animated.event(
- [{nativeEvent: {contentOffset: {x: scrollX}}}] // scrollX = e.nativeEvent.contentOffset.x
- )
- onPanResponderMove : Animated.event([
- null, // 忽略原生事件
- {dx: pan.x, dy: pan.y} // 从gestureState中解析出dx和dy的值
- ]);
onScroll 是绑定给某个组件的滚动事件,而 onPanResponderMove 是 PanResponder 模块下的响应事件。
拿上方 onPanResponderMove 的例子来讲,该事件方法接收两个参数 e<event Object> 和 gestureState<Object>,其中 gestureState 的属性有:
- stateID - 触摸状态的ID。在屏幕上有至少一个触摸点的情况下,这个ID会一直有效。
- moveX - 最近一次移动时的屏幕横坐标
- moveY - 最近一次移动时的屏幕纵坐标
- x0 - 当响应器产生时的屏幕坐标
- y0 - 当响应器产生时的屏幕坐标
- dx - 从触摸操作开始时的累计横向路程
- dy - 从触摸操作开始时的累计纵向路程
- vx - 当前的横向移动速度
- vy - 当前的纵向移动速度
- numberActiveTouches - 当前在屏幕上的有效触摸点的数量
此处不了解的可以去看我上一篇RN入门文章的相关介绍。
而上方例子中,我们动态地将 gestureState.dx 和 gestureState.dy 的值赋予 pan.x 和 pan.y。
来个有简单的例子:
- class AwesomeProject extends Component {
- constructor(props) {
- super(props);
- this.state = {
- transY : new Animated.Value(0)
- };
- this._panResponder = {}
- }
- componentWillMount处预先创建手势响应器
- componentWillMount() {
- this._panResponder = PanResponder.create({
- onStartShouldSetPanResponder: this._returnTrue.bind(this),
- onMoveShouldSetPanResponder: this._returnTrue.bind(this),
- //手势开始处理
- //手势移动时的处理
- onPanResponderMove: Animated.event([null, {
- dy : this.state.transY
- }])
- });
- }
- _returnTrue(e, gestureState) {
- return true;
- }
- render() {
- return (
- <View style={styles.container}>
- <Animated.View style={[styles.content, {width: this.state.w, height: this.state.h,
- transform: [{
- translateY : this.state.transY
- }]
- }]}>
- <Text style={[{textAlign: 'center'}]}>Hi, here is VaJoy</Text>
- </Animated.View>
- <TouchableOpacity>
- <View style={styles.button} {...this._panResponder.panHandlers}>
- <Text style={styles.buttonText}>control</Text>
- </View>
- </TouchableOpacity>
- <TouchableOpacity>
- <View style={styles.button}>
- <Text style={styles.buttonText}>忽略此按钮</Text>
- </View>
- </TouchableOpacity>
- </View>
- );
- }
- }
动画的API较多,本章先介绍到这里,下篇将介绍更复杂的动画实现~ 共勉~
ReactNative入门 —— 动画篇(上)的更多相关文章
- ReactNative入门 —— 动画篇(下)
在上篇动画入门文章中我们了解了在 React Native 中简单的动画的实现方式,本篇将作为上篇的延续,介绍如何使用 Animated 实现一些比较复杂的动画. 动画组合 在 Animated 中提 ...
- jQuery-4.动画篇---上卷下拉效果
jQuery中下拉动画slideDown 对于隐藏的元素,在将其显示出来的过程中,可以对其进行一些变化的动画效果.之前学过了show方法,show方法在显示的过程中也可以有动画,但是.show()方法 ...
- ReactNative入门(安卓)——API(上)
Alert - 弹窗 通过 Alert.alert() 方法调用唤起原生弹窗,点击会触发 onPress 回调(参考下方代码)并清除弹窗. import React, { AppRegistry, C ...
- Apache Maven 入门篇 ( 上 )
作者:George Ma 写这个 maven 的入门篇是因为之前在一个开发者会的动手实验中发现挺多人对于 maven 不是那么了解,所以就有了这个想法. 这个入门篇分上下两篇.本文着重动手,用 mav ...
- React-Native入门指导之iOS篇 —— 一、准备工作
React-Native 入门指导系列教程目录 一.准备工作 (已完成) 二.项目介绍与调试 三.CSS样式与Flex布局 四.常用UI控件的使用 五.JSX在React-Native中的应用 六.事 ...
- [转]Apache Maven 入门篇 ( 上 )
原文地址:Apache Maven 入门篇 ( 上 ) 作者:George Ma 写这个 maven 的入门篇是因为之前在一个开发者会的动手实验中发现挺多人对于 maven 不是那么了解,所以就有了这 ...
- React-Native入门指导之iOS篇
React-Native 入门指导系列教程目录 一.准备工作 (已完成) 二.项目介绍与调试 三.CSS样式与Flex布局 四.常用UI控件的使用 五.JSX在React-Native中的应用 六.事 ...
- iOS React-Native入门指南之HelloWorld
React-native 作为facebook开源项目,最近是火的一塌糊涂,它采用node.js能够写ios和android的native界面代码,简直是太酷了.支持动态更新,而且appstore 提 ...
- [转] ReactNative Animated动画详解
http://web.jobbole.com/84962/ 首页 所有文章 JavaScript HTML5 CSS 基础技术 前端职场 工具资源 更多频道▼ - 导航条 - 首页 所有文章 ...
随机推荐
- SQL Server中的高可用性(2)----文件与文件组
在谈到SQL Server的高可用性之前,我们首先要谈一谈单实例的高可用性.在单实例的高可用性中,不可忽略的就是文件和文件组的高可用性.SQL Server允许在某些文件损坏或离线的情况下,允 ...
- Angular2入门系列教程4-服务
上一篇文章 Angular2入门系列教程-多个组件,主从关系 在编程中,我们通常会将数据提供单独分离出来,以免在编写程序的过程中反复复制粘贴数据请求的代码 Angular2中提供了依赖注入的概念,使得 ...
- 移动先行之谁主沉浮? 带着你的Net飞奔吧!
移动系源码:https://github.com/dunitian/Windows10 移动系文档:https://github.com/dunitian/LoTDotNet/tree/master/ ...
- Win10 IIS本地部署MVC网站时不能运行?
异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 部署后出现这个错误: 打开文件目录后发现是可以看见目录的,静态页面也是可以打开的 ...
- 【原】实时渲染中常用的几种Rendering Path
[原]实时渲染中常用的几种Rendering Path 本文转载请注明出处 —— polobymulberry-博客园 本文为我的图形学大作业的论文部分,介绍了一些Rendering Path,比较简 ...
- 构建通用的 React 和 Node 应用
这是一篇非常优秀的 React 教程,这篇文章对 React 组件.React Router 以及 Node 做了很好的梳理.我是 9 月份读的该文章,当时跟着教程做了一遍,收获很大.但是由于时间原因 ...
- 用scikit-learn学习BIRCH聚类
在BIRCH聚类算法原理中,我们对BIRCH聚类算法的原理做了总结,本文就对scikit-learn中BIRCH算法的使用做一个总结. 1. scikit-learn之BIRCH类 在scikit-l ...
- H5程序员如何利用cordova开发跨平台应用
什么是Cordova? Cordova以前也叫PhoneGap,它提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头.麦克风等.Cordova还 ...
- CSharpGL(33)使用uniform块来优化对uniform变量的读写
CSharpGL(33)使用uniform块来优化对uniform变量的读写 +BIT祝威+悄悄在此留下版了个权的信息说: Uniform块 如果shader程序变得比较复杂,那么其中用到的unifo ...
- iOS UITableView 与 UITableViewController
很多应用都会在界面中使用某种列表控件:用户可以选中.删除或重新排列列表中的项目.这些控件其实都是UITableView 对象,可以用来显示一组对象,例如,用户地址薄中的一组人名.项目地址. UITab ...