简介
动画对于使一个应用程序具有互动性是很重要的。它们让用户感觉到与应用程序的联系更加紧密。
随着React Native的发展势头,很多产品都是用React Native构建的,或者已经迁移到了React Native。然而,虽然网页的代码在某些情况下可以在移动应用中重复使用,但一个主要的问题是,动画往往不能延续,因为API完全不同。
在这篇文章中,我们将学习如何利用React Native文件解析和react-spring(一个动画库)来编写一个单一的代码库,在React和React Native中运行动画。
我们将从学习React Native文件解析和react-spring开始,并通过构建一个非常基本的示例应用继续学习。在这个示例应用中,我们将编写一个代码,它既可以在网络上的React中运行,也可以在移动应用中的React Native中运行。
React Native的文件解析
React Native有一个高效的文件解析系统,可以让我们编写针对平台或针对本地的代码。它将检测一个文件是否有.ios.js 或.android.js 扩展名,并在需要时从其他组件加载相关平台(Android/iOS)的文件。这样,我们就可以为两个不同的平台编写两段不同的代码或组件。
同样,当我们创建带有.js 或.native.js 扩展名的文件时,.js 文件会被Node.js和web接收,而.native.js 文件会被React NativeMetro bundler接收。
这使我们有能力建立易于维护的应用程序,并在不同平台上重复使用大量的代码。
考虑一个如下结构的文件夹。
|-TestComponent
|-index.js
|-index.native.js
让我们假设我们在TestComponent 组件中使用App 组件,如下所示。
const App = () => (
<div>
<TestComponent />
</div>
)
当代码为浏览器捆绑时,TestComponent 的导入将从index.js 文件中导入该组件。然而,如果代码是为React Native捆绑的,它就会拾取index.native.js 。
我们将利用文件解析的这个方面,为两个平台编写一个单一的动画代码。
React-spring
React-spring是一个基于弹簧物理学的动画库,它应该涵盖你的大部分UI相关的动画需求。它为你提供了足够灵活的工具,可以自信地将你的想法铸成移动的界面。
React-spring是两个现有React动画库之间的桥梁。React Motion和Animated。它继承了Animated的强大插值和性能,以及React Motion的易用性。
与其他动画库相比,react-spring的主要优势在于它能够应用动画而不依赖React逐帧渲染更新。动画是基于物理学的。
没有必要(除非你有意这样做)定制持续时间或缓和。这意味着你的动画可以相当无缝地处理中断和变化。其结果是平滑、柔和、自然的动画。
React-spring也是一个跨平台的动画库。它在网页、React Native和其他平台都有类似的实现,但作为不同的包。我们可以根据自己的要求来挑选合适的模块。然而,它们在所有平台上共享相同的API。这有助于我们使用 react-spring 来实现所有平台一个代码的目标。
我们将在本文要构建的示例应用程序中使用useSpring 钩子。useSpring ,通过覆盖值来改变动画,或者通过使用API传递回调函数来动态更新值,从而将值变成动画值。
构建一个示例应用程序
让我们使用刚才学到的上述概念,构建一个示例应用程序。这个示例应用程序将使用React的网页,React Native的移动应用程序,以及react-spring的动画。从功能上来说,这将是一个非常简单的应用,在移动应用和网络上,都会出现一个文本并放大应用的负载。
然而,我们必须首先为这个应用程序的运行创建基本的构件。你可以手动配置Metro bundler和webpack,或者使用现有的启动工具包。
我使用了expo 和create-react-app 合并成一个项目。该应用程序的完整工作版本可以在这里找到。
现在让我们看看一些代码。考虑一下下面的项目。
|-src
|-Box
|-index.js
|-Box.js
|-Box.native.js
|-Text
|-index.js
|-Text.js
|-Text.native.js
|-App.js
我们这里有两个组件:Box 和Text 。这两个组件都利用了React Native中的文件解析,这使得我们能够编写出适用于两个平台的单一组件。
Box
Box 是一个容器组件。我们将使用Box 作为所有其他组件或文本的容器。
在网页中,我们使用div 或section 作为容器。然而,React Native不支持这些元素,而是使用View 组件作为容器。我们的目标是为所有的平台编写一个代码,因此我们将创建这个Box 组件,根据平台翻译成div 或View 。
考虑到React Native的文件分辨率,我们将为Box 组件创建两个文件。
Box.jsBox.native.js
Box.js 文件将被网络平台使用,它只是一个div 的别名。这将像这样导出div 元素。
// Box.js
export default 'div';
Box.native.js 文件将被React Native平台使用。我们从React Native导入View 元素,并像这样从文件中导出它。
// Box.native.js
import {View} from 'react-native'
export default View
我们创建一个通用的index.js 文件,这样在其他文件中导入和使用Box 元素就很容易,也更容易阅读。如果你看到这里,我们没有明确提到Box 是从.js 还是.native.js 文件导入的。
我们让捆绑器来决定,只是在index.js 文件中导出该组件,这样它就可以被其他组件使用。
// index.js
import Box from './Box';
export default Box
Text
Text 组件将被用于在网页和移动应用程序中添加文本。在Web中,我们使用像h1,h2, 或p 这样的元素来显示文本,而在React Native中,我们使用Text 组件。这个Text 组件现在将根据平台翻译成p 或Text 。
我们将为Text 组件创建两个文件。
Text.jsText.native.js
Text.js 文件将被网络平台使用,它只是p 的一个别名。这将像这样导出p 元素。
// Text.js
export default 'p';
Text.native.js 文件将被React Native平台使用。我们从React Native导入Text 组件,并从这里的文件导出。
// Text.native.js
import {Text} from 'react-native'
export default Text
像Box 组件一样,我们也在这里创建一个普通的index.js 文件。这使得导入,以及在其他文件中使用Text 组件变得更加容易和可读。
// index.js
import Text from './Text';
export default Text
现在我们已经有了Box 和Text 组件的基本构件,我们可以继续构建示例应用程序。
让我们创建一个新的组件,App.js ,它将使用Box 作为容器,Text 来显示我们的标题:React Spring Animation 。我们还可以给该组件添加一些样式。
它应该看起来像这样。
// App.js
import React from 'react';
import Box from './Box';
import Text from './Text';
function App() {
return (
<Box style={{ marginTop: 50}}>
<Text style={{ fontSize: 50 }}>React Spring Animation</Text>
</Box>
);
}
export default App;
上述代码为手机和网页创建了相同的文本,你可以在下面的截图中看到。在引擎盖下,代码在任何平台上执行之前都会被捆绑。
React Spring Animation 当webpack为网页捆绑代码时,它使用Box.js 和Text.js 文件,在App 组件中添加一个div 元素和p 元素,并在浏览器中运行。
然而,Metro捆绑了Box.native.js 和Text.native.js 文件,并在App 组件中添加了来自React Native的View 和Text 组件。这在移动应用程序中也会得到预期的结果。
到目前为止,这就是我们的应用程序在手机和网络上的样子。
添加动画
是时候为上述应用添加动画了。让我们通过增加字体大小给文本添加一个 "放大 "动画。
首先,将react-spring 作为依赖项安装到项目中。
npm i react-spring
yarn add react-spring
接下来,建立一个类似于Box 和Text 的Animated 组件。这个Animated 组件将是我们从react-spring 库中获取所有钩子、API 和工具的单一来源。
我们将把它与我们的Box 和Text 组件的结构类似,从而根据每个平台使用正确的钩子和API。
React-spring有两个我们将使用的模块:用于Web的react-spring 和用于React Native的react-spring/native 。在这个例子中,我们将使用react-spring 的钩子useSpring 来实现动画。
然而,我们应该从web的react-spring 中导入useSpring ,从React Native的react-spring/native 中导入useSpring 。因此,我们不能直接使用react-spring 。
考虑到这些事实,我们将建立一个Animated 组件,它将帮助我们为多个平台编写单个动画。
让我们添加一个新的组件文件夹,如下所示。
|-src
|-Animated
|-index.js
|-Animated.js
|-Animated.native.js
Animated
Animated 组件将提供所有需要的钩子和工具,从react-spring 。react-spring 在web中,我们使用useSpring 和useTransition 这样的元素来制作动画,而在React Native中,我们使用来自react-spring/native 的相同钩子。
这个Animated 组件将根据平台从react-spring 或react-spring/native 中导入useSpring 。
我们将为Animated 组件创建两个文件。
Animated.jsAnimated.native.js
Animated.js react-spring 文件将被网络平台使用,并将从 和 ,并将其导出。useSpring animated
// Animated.js
export { useSpring, animated } from 'react-spring'
Animated.native.js react-spring/native 文件将被React Native平台使用,并将从 和 ,并将其导出。useSpring animated
// Animated.native.js
export { useSpring, animated } from 'react-spring/native'
我们现在可以创建一个通用的index.js 文件,它将导入和导出Animated 组件,如Box 和Text 。这将使我们在其他文件中导入和使用Animated 组件变得更容易和更易读。
// index.js
export {useSpring, animated} from './Animated'
现在,让我们在App.js 中使用Animated 组件来制作文本的动画。
从Animated 组件中导入useSpring 和animated 。
import { useSpring, animated } from './Animated';
然后,使用Animated 功能创建一个启用动画的组件。一个组件只有在被Animated 函数扩展后才能使用react-spring制作动画。
const AnimatedText = animated(Text);
使用useSpring 函数创建动画,这与CSS的关键帧动画非常相似。
CSS中的关键帧通过为动画序列中的关键帧(或航点)定义样式来控制CSS动画序列的中间步骤。因此,我们可以在动画的不同阶段确定一个元素的样式。
useSpring 工作方式完全相同。你可以使用from 和to 属性分别定义某些样式在动画开始和结束时的样子。
const styles = useSpring({
from: {
fontSize: 10,
},
to: {
fontSize: 50,
},
})
最后,将styles 传递给AnimatedText 组件的style 属性。
<AnimatedText style={{ ...styles }}>React Spring Animation</AnimatedText>
把所有东西放在一起,App.js 应该看起来像下面这样。
import React from 'react';
import { useSpring, animated } from './Animated';
import Box from './Box';
import Text from './Text';
const AnimatedText = animated(Text)
function App() {
const styles = useSpring({
from: {
fontSize: 10,
},
to: {
fontSize: 50,
}
})
return (
<Box style={{ marginTop: 50}}>
<AnimatedText style={{ ...styles }}>React Spring Animation</AnimatedText>
</Box>
);
}
上面的代码为网页和移动端(通过React Native)的文本添加了一个 "放大 "动画。
你可以在下面的gif图片中看到它的样子。
结论
使用上述技术,我们可以只创建一次几乎任何动画,并在网络和移动平台上重复使用。这可以实现快速的原型设计和简单的维护。它也有利于在不同的平台上实现功能的一致性。
不幸的是,这种技术有一个小的注意事项:只有React和React Native都支持的CSS属性可以用这种方式制作动画。任何涉及到其他CSS属性的东西仍然应该为两个平台保持两个版本的代码。
The postRun animations in React and React Native from one codebaseappeared first onLogRocket Blog.