首发于公众号 前端混合开发,欢迎关注。

在计算机科学中,"掩码"是指使用一个数字元素(掩码)来修改或提取第二个数字元素的数据。程序员在各种计算机科学应用中使用掩码:在开发软件系统/API时使用位掩码,在前端开发中使用输入掩码,在开发GUI应用程序时使用基于计算机图形的掩码。

在移动应用中,我们经常使用视觉信号来提高应用的质量和用户友好性,包括这些基于遮罩的静态图形和动画。例如,现代移动应用包含包含图像纹理或渐变层的矢量形状或文本。一些移动应用通过动画矢量遮罩(例如,Twitter的iOS应用)展示主应用屏幕,以创造独特的欢迎用户体验。

react-native-masked-view 库允许你创建蒙版视图,为 React Native 应用添加引人注目的视觉效果。在本教程中,我将通过一些实际的示例来解释如何使用 react-native-masked-view 库,你可以在自己的 React Native 应用中使用这些示例。

react-native-masked-view 的主要特点

了解库的特点有助于验证实现设计思路的可行性。因此,在使用库之前,让我们先研究一下库的一些特点。该库具有以下主要特点:

跨平台支持

早期的 React Native 版本提供了官方内置的 MaskedViewIOS 框架组件,用于在iOS上实现蒙版视图。但在v0.59版本中,React Native团队将 MaskedViewIOS 组件移至一个名为 react-native-masked-view 的独立社区包中,以便更好的管理;现在,react-native-masked-view 支持Android和iOS平台。

元素类型无关的蒙版

这个库并不仅仅通过提供基于图像的蒙版来限制应用开发者;可以使用任何有效的React元素进行遮罩处理。使用这个库,可以创建基于文本的、基于图像的,或者任何基于React元素的蒙版。

动画支持

React Native 框架为开发者提供了内置的动画API,用于动画化平台特定的原生UI元素。可以整合内置的动画API和这个蒙版库,以在React Native应用中创建引人入胜的动态视觉效果。使用矢量形状遮罩创建独特的开场动画非常简单!

现在我们已经了解了蒙版库提供的主要特性,我们开始使用该库,以便更熟悉它。

将 react-native-masked-view 集成到你的项目中

首先,我们需要在React Native项目中安装这个库。你可以在现有的项目或新项目中使用即将出现的示例。如果你打算创建一个新项目,使用以下命令:

npx react-native init MaskedViewDemo
cd MaskedViewDemo

按照以下步骤安装库:

npm install @react-native-masked-view/masked-view
# --- or ---
yarn add @react-native-masked-view/masked-view

运行项目以确保包安装成功:

npx react-native run-android
# --- or --- 
npx react-native run-ios

创建带有背景纹理的文本蒙版

我们可以使用 Text 组件和 Image 组件创建一个简单的蒙版元素来开始。在这里,我们需要使用 Text 组件作为蒙版元素,并使用 Image 作为蒙版的纹理。换句话说,我们需要用背景纹理填充一个文本元素。

首先,从Unsplash网站下载中等大小的图片文件到你的应用目录,并将其重命名为 texture.jpg 。接下来,将以下代码添加到你的 App.js 文件中:

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native';
import MaskedView from '@react-native-masked-view/masked-view';
import backgroundTexture from './texture.jpg';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <MaskedView
        style={styles.maskedView}
        maskElement={
          <View style={styles.maskWrapper}>
            <Text style={styles.mask}>Text Mask</Text>
          </View>
        }>
        <Image
          source={backgroundTexture}
          style={styles.image}
        />
      </MaskedView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#444'
  },
  maskedView: {
    flex: 1,
    flexDirection: 'row',
    height: '100%'
  },
  maskWrapper: {
    backgroundColor: 'transparent',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mask: {
    fontSize: 60,
    color: 'black',
    fontWeight: 'bold',
  },
  image: {
    flex: 1,
    height: '100%',
    backgroundColor: '#324376'
  },
});

export default App;

以上,我们创建了一个文本遮罩,并在 maskElement 属性中添加了一个 Text 组件。为了定位的目的,我们用一个透明的 View 包裹了 Text 组件。 Image 组件会在文本遮罩后面渲染,所以我们只会在遮罩区域内看到图像内容。

image.png

MaskedView 组件根据 maskElementalpha 通道显示背景组件,因此我们可以使用部分透明度来增加我们样式的趣味性。例如,我们可以如下所示部分显示文本元素周围的背景图像:

maskWrapper: {
  backgroundColor: 'rgba(0, 0, 0, 0.3)',
  flex: 1,
  justifyContent: 'center',
  alignItems: 'center',
},

上述样式定义通过 rgba 样式语法为遮罩元素设置了一个透明度值。现在,你将看到以下结果:

image.png

我们也可以改变文本阴影来在文本元素周围应用模糊效果:

maskWrapper: {
  backgroundColor: 'rgba(0, 0, 0, 0.08)',
  flex: 1,
  justifyContent: 'center',
  alignItems: 'center',
},
mask: {
  fontSize: 60,
  color: 'black',
  fontWeight: 'bold',
  textShadowRadius: 30,
  textShadowColor: 'rgba(0, 0, 0, 0.8)'
},

在你的应用中使用上述代码后,你会看到文本元素周围有模糊效果。使用高阿尔法值的阴影会更多地揭示背景图像,由于遮罩处理过程使用非透明区域来激活遮罩,因此会模糊文本元素。看看下面的预览:

image.png

创建矢量形状蒙版

早些时候,我们使用了带有背景图像的文本遮罩。现在,我们将使用矢量形状作为遮罩。我们可以通过使用 View 组件开始使用形状遮罩。如你所知,我们可以通过调整其边框半径使用 View 组件创建一个圆形。

按照以下方式将 Text 组件替换为 View 组件:

maskElement={
  <View style={styles.maskWrapper}>
    <View style={styles.mask}></View>
  </View>
}>

接下来,更改内部遮罩元素的样式定义:

mask: {
  width: 200,
  height: 200,
  backgroundColor: '#000',
  borderRadius: 100
},

请注意,我们仍然使用 maskWrapper 进行定位。上述样式片段为内部的 View 设置了边框半径,而外部的 View 使用了低透明度值,以稍微显示背景图像,如下预览所示:

image.png

在之前的示例中,我们使用了带有遮罩的图像背景,但是遮罩库支持在遮罩元素后面使用任何React元素。例如,你可以按照以下方式在遮罩元素后面使用众所周知的 LinearGradient 组件:

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native';
import MaskedView from '@react-native-masked-view/masked-view';
import LinearGradient from 'react-native-linear-gradient';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <MaskedView
        style={styles.maskedView}
        maskElement={
          <View style={styles.maskWrapper}>
            <View style={styles.mask}></View>
          </View>
        }>
        <LinearGradient
          start={{x: 0.0, y: 0.25}}
          end={{x: 0.5, y: 1.0}}
          locations={[0.2, 0.32, 0.4]}
          colors={['#0059FF', '#18AF00', '#6B99ED']}
          style={styles.image}
        />
      </MaskedView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#444'
  },
  maskedView: {
    flex: 1,
    flexDirection: 'row',
    height: '100%'
  },
  maskWrapper: {
    backgroundColor: 'rgba(0, 0, 0, 0.2)',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mask: {
    width: 200,
    height: 200,
    backgroundColor: '#000',
    borderRadius: 100
  },
  image: {
    flex: 1,
    height: '100%',
  },
});

export default App;

上述代码渲染出以下的遮罩:

image.png

使用PNG的矢量图形创建渐变背景

使用相同的方法,你可以使用来自PNG文件的矢量图形来创建带有渐变背景的遮罩元素。让我们试试吧!

将此PNG图像(来自FlatIcon的心形符号矢量图像)下载到你的项目目录中,并将其重命名为 heart.png 。接下来,将以下代码添加到你的 App.js 文件中:

import React from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  View,
  Image
} from 'react-native';
import MaskedView from '@react-native-masked-view/masked-view';
import LinearGradient from 'react-native-linear-gradient';
import heartImage from './heart.png';
import backgroundImage from './texture.jpg';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <MaskedView
        style={styles.maskedView}
        maskElement={
          <View style={styles.maskWrapper}>
            <Image source={heartImage} style={styles.mask}/>
          </View>
        }>
        <LinearGradient
          start={{x: 0.0, y: 0.25}}
          end={{x: 0.5, y: 1.0}}
          locations={[0.2, 0.32, 0.35]}
          colors={['#AF0000', '#ED2F2F', '#FF5656']}
          style={styles.image}
        />
      </MaskedView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#444'
  },
  maskedView: {
    flex: 1,
    flexDirection: 'row',
    height: '100%'
  },
  maskWrapper: {
    backgroundColor: 'rgba(0, 0, 0, 0.2)',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mask: {
    width: 200,
    height: 200,
  },
  image: {
    flex: 1,
    height: '100%',
  },
});

export default App;

上述代码使用了一个向量形状作为遮罩,并使用渐变作为遮罩的背景,如下所示:

image.png

图像背景也与渐变背景一样工作。只需使用以下组件替代之前的 LinearGradient 组件:

<Image source={backgroundImage} style={styles.image}/>

现在,心形图标将显示我们之前下载的背景图片。

为应用内容创建一个遮罩

在之前的例子中,我们使用了图像或渐变层作为遮罩元素的背景。如果你需要通过遮罩元素显示你的应用屏幕内容,比如通过之前的矢量遮罩显示登录屏幕,该怎么办?

将以下代码添加到你的 App.js

import React, { useState, useEffect } from 'react';
import {
  SafeAreaView,
  StyleSheet,
  Text,
  TextInput,
  View,
  Image,
  Button,
} from 'react-native';
import MaskedView from '@react-native-masked-view/masked-view';
import LinearGradient from 'react-native-linear-gradient';
import heartImage from './heart.png';

function App() {
  return (
    <SafeAreaView style={styles.container}>
      <MaskedView
        style={styles.maskedView}
        maskElement={
          <View style={styles.maskWrapper}>
            <Image source={heartImage} style={styles.mask}/>
          </View>
        }>
        <View style={styles.loginBox}>
          <TextInput
            value=""
            placeholder="Username"
            placeholderTextColor="#666"
            style={styles.input}
          />
          <TextInput
            value=""
            placeholder="Password"
            placeholderTextColor="#666"
            secureTextEntry={true}
            style={styles.input}
          />
          <View style={styles.separator}/>
          <Button title="Login"/>
        </View>
      </MaskedView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#444'
  },
  maskedView: {
    flex: 1,
    flexDirection: 'row',
    height: '100%'
  },
  maskWrapper: {
    backgroundColor: 'rgba(0, 0, 0, 0.2)',
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  mask: {
    width: 360,
    height: 360,
  },
  loginBox: {
    flex: 1,
    height: '100%',
    justifyContent: 'center',
    backgroundColor: '#eee',
    padding: 80
  },
  input: {
    borderWidth: 1,
    borderColor: '#222',
    borderRadius: 4,
    padding: 12,
    marginBottom: 12
  },
  separator: {
    height: 20
  }
});

export default App;

上述代码在登录框上实现了一个心形遮罩。你可以通过点击心形部分来激活登录框的输入元素——所有的触摸事件都可以完美地通过遮罩区域工作。

查看以下预览:

image.png

同样,你可以在你的应用内容上展示矢量形状的遮罩。这种方法的一个好的使用案例是在你的应用中突出显示段落,以创建一个应用导览。让我们把上面的心形遮罩变成一个简单的应用导览屏幕。

首先,将以下样式定义添加到你的源代码中:

infoBox: {
  padding: 20,
  backgroundColor: '#555',
},
infoText: {
  fontSize: 14,
  color: '#eee'
}

接下来,在 MaskedView 后添加以下信息框代码片段:

</MaskedView>
<View style={styles.infoBox}>
  <Text style={styles.infoText}>Enter your login details in the above section...</Text>
</View>

image.png

在这里,我演示了一个使用我们已经下载的矢量形状的用例 - 你可能会想要使用类似聚光灯的或者适合的矢量遮罩元素来创建应用程序的导览。

使用遮罩动画:创建开场动画

如果你使用过Twitter的iOS应用,你可能会记得它独特的开屏界面:当启动时,它会通过一个动态的Twitter logo遮罩来展示信息流。Twitter logo遮罩从小变大,直到用户看到整个信息流界面。

react-native-masked-view库支持将动画元素作为遮罩元素,因此我们可以使用矢量遮罩创建出色的开场动画。让我们为之前的登录屏幕遮罩添加动画,并像Twitter iOS应用那样展示应用内容。

下载或克隆我在GitHub仓库中存储的示例应用内容。安装依赖并运行应用。你将看到以下动画:

我们使用 react-native-masked-view 与内置的动画 API 来创建动画心形图标。动画看起来很简单,但以这个速度很难想象其各个阶段。如果我们考虑以下阶段,我们就可以查看并理解整个动画的元素:

  1. 心形图标的遮罩放大并显示出白色背景层
  2. 与此同时,蓝色背景层出现在矢量形状的背景周围
  3. 我们淡入并缩小登录部分
  4. 当动画结束时,蓝色和白色的背景屏幕被隐藏,不再可见

我们检查上述代码中的一些关键事实。我们在遮罩中使用了 Animated.Image 组件来动态改变遮罩的缩放因子:

<MaskedView
  style={styles.maskedView}
  maskElement={
    <View style={styles.maskWrapper}>
      <Animated.Image source={heartImage}
        style={[styles.mask, maskScale]}/>
    </View>

为了缩小并淡入登录框,我们使用了一个 Animated.View 组件

<Animated.View style={[styles.loginBox, appScale, appOpacity]}>

在代码中,蓝色背景层在 MaskedView 之前

{ !animDone ? <View style={[StyleSheet.absoluteFill, styles.backgroundFillBlue]}></View> : null }
<MaskedView

白色背景层位于 MaskedView 内,并在心形图标遮罩动画过程中显示出来。动画结束后,它会消失。

<MaskedView
  style={styles.maskedView}
  maskElement={
    <View style={styles.maskWrapper}>
      <Animated.Image source={heartImage}
        style={[styles.mask, maskScale]}/>
    </View>
  }>
  { !animDone ? <View style={[StyleSheet.absoluteFill, styles.backgroundFillWhite]}></View> : null }
  <Animated.View style={[styles.loginBox, appScale, appOpacity]}>

现在,使用您的移动应用的 logo 代替 heart.png 文件,并根据您的意愿调整动画。也可以将上述组件的源代码包装成一个React Native包,并在创纪录的时间内为您的应用创建一个引人入胜的开屏界面。

总结

在这个教程中,我们开发了一些实用的示例来学习如何使用 react-native-masked-view 库在React Native应用中创建引人入胜的视觉效果。使用这个库可以创建带有图片、渐变和应用元素背景的文本和矢量蒙版。此外,你还可以使用任何React元素作为蒙版。

交流

首发于公众号 大迁世界,欢迎关注。📝 每周一篇实用的前端文章 🛠️ 分享值得关注的开发工具 ❓ 有疑问?我来回答

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68.1k 声望105k 粉丝