update: add minimal3 frame

This commit is contained in:
2026-05-31 23:26:09 +02:00
parent fb293d840c
commit 3ec6654e9d
71 changed files with 1020 additions and 17291 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+130
View File
@@ -0,0 +1,130 @@
# React Native `MaskedView`
[![Build Status][build-badge]][build]
[![Version][version-badge]][package]
[![MIT License][license-badge]][license]
[![Lean Core Badge][lean-core-badge]][lean-core-issue]
Provides a React component that renders a masked view.
## Platforms Supported
- [x] iOS
- [x] Android
- [ ] Web
## Getting Started
```sh
yarn add @react-native-masked-view/masked-view
```
or
```sh
npm install --save @react-native-masked-view/masked-view
```
### Using React Native >= 0.60
Linking the package manually is not required anymore with [Autolinking](https://github.com/react-native-masked-view/cli/blob/master/docs/autolinking.md).
Remember to install the pod with:
```sh
npx pod-install
```
### Using React Native < 0.60
You then need to link the native parts of the library for the platforms you are using. The easiest way to link the library is using the CLI tool by running this command from the root of your project:
```sh
react-native link @react-native-masked-view/masked-view
```
## Usage
Import the `MaskedView` component from `@react-native-masked-view/masked-view` and use it like so:
```jsx
import React from 'react';
import { Text, View } from 'react-native';
import MaskedView from '@react-native-masked-view/masked-view';
const App = () => {
return (
<MaskedView
style={{ flex: 1, flexDirection: 'row', height: '100%' }}
maskElement={
<View
style={{
// Transparent background because mask is based off alpha channel.
backgroundColor: 'transparent',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 60,
color: 'black',
fontWeight: 'bold',
}}
>
Basic Mask
</Text>
</View>
}
>
{/* Shows behind the mask, you can put anything here, such as an image */}
<View style={{ flex: 1, height: '100%', backgroundColor: '#324376' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#F5DD90' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#F76C5E' }} />
<View style={{ flex: 1, height: '100%', backgroundColor: '#e1e1e1' }} />
</MaskedView>
);
}
export default App
```
The following image demonstrates that you can put almost anything behind the mask. The three examples shown are masked `<View>`, `<Text>`, and `<Image>`.
<div align="center"><img src="img/example.png" width="200"></img></div>
### Props
- [View props...](https://github.com/facebook/react-native-website/blob/master/docs/view.md#props)
- [`maskElement`](#maskelement)
- [`androidRenderingMode`](#androidrenderingmode)
### Reference
### `maskElement`
| Type | Required |
| ------- | -------- |
| element | Yes |
### `androidRenderingMode`
By default `hardware` rendering mode will be used for best performance, however if you need to animate your `maskElement` then youll need to switch to `software` to get your mask to update. This prop only affects Android.
| Type | Required | Default |
| ---------------------- | -------- | ---------- |
| `software`, `hardware` | No | `hardware` |
<!-- badges -->
[build-badge]: https://github.com/react-native-masked-view/masked-view/workflows/Build/badge.svg
[build]: https://github.com/react-native-masked-view/masked-view/actions
[version-badge]: https://img.shields.io/npm/v/@react-native-masked-view/masked-view.svg?style=flat-square
[package]: https://www.npmjs.com/package/@react-native-masked-view/masked-view
[license-badge]: https://img.shields.io/npm/l/@react-native-masked-view/masked-view.svg?style=flat-square
[license]: https://opensource.org/licenses/MIT
[lean-core-badge]: https://img.shields.io/badge/Lean%20Core-Extracted-brightgreen.svg?style=flat-square
[lean-core-issue]: https://github.com/facebook/react-native/issues/23313
@@ -0,0 +1,19 @@
require 'json'
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
Pod::Spec.new do |s|
s.name = "RNCMaskedView"
s.version = package['version']
s.summary = package['description']
s.license = package['license']
s.authors = package['author']
s.homepage = package['homepage']
s.platforms = { :ios => "9.0", :tvos => "9.0" }
s.source = { :git => "https://github.com/react-native-masked-view/masked-view.git", :tag => "v#{s.version}" }
s.source_files = "ios/**/*.{h,m}"
install_modules_dependencies(s)
end
@@ -0,0 +1,81 @@
def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
buildscript {
if (project == rootProject) {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
}
}
}
apply plugin: 'com.android.library'
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger()
def shouldUseNameSpace = agpVersion >= 7
def PACKAGE_PROP = "package=\"org.reactnative.maskedview\""
def manifestOutFile = file("${projectDir}/src/main/AndroidManifest.xml")
def manifestContent = manifestOutFile.getText()
if(shouldUseNameSpace){
manifestContent = manifestContent.replaceAll(
PACKAGE_PROP,
''
)
} else {
if(!manifestContent.contains("$PACKAGE_PROP")){
manifestContent = manifestContent.replace(
'<manifest',
"<manifest $PACKAGE_PROP "
)
}
}
manifestContent.replaceAll(" ", " ")
manifestOutFile.write(manifestContent)
android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)
if(shouldUseNameSpace){
namespace = "org.reactnative.maskedview"
}
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', 16)
targetSdkVersion safeExtGet('targetSdkVersion', 28)
}
sourceSets {
main {
java.srcDirs = ['src/main/java']
}
}
lintOptions {
abortOnError false
warning 'InvalidPackage'
}
buildTypes {
release {
}
}
}
repositories {
google()
maven { url "https://jitpack.io" }
mavenCentral()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
dependencies {
implementation 'com.facebook.react:react-native:+'
}
@@ -0,0 +1,2 @@
<manifest package="org.reactnative.maskedview" xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@@ -0,0 +1,112 @@
package org.reactnative.maskedview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.view.View;
import com.facebook.react.views.view.ReactViewGroup;
public class RNCMaskedView extends ReactViewGroup {
private static final String TAG = "RNCMaskedView";
private Bitmap mBitmapMask = null;
private boolean mBitmapMaskInvalidated = false;
private Paint mPaint;
private PorterDuffXfermode mPorterDuffXferMode;
private int mRenderingMode = View.LAYER_TYPE_HARDWARE;
public RNCMaskedView(Context context) {
super(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPorterDuffXferMode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mBitmapMaskInvalidated) {
// redraw mask element to support animated elements
updateBitmapMask();
mBitmapMaskInvalidated = false;
}
// draw the mask
if (mBitmapMask != null) {
setLayerType(mRenderingMode, mPaint);
mPaint.setXfermode(mPorterDuffXferMode);
canvas.drawBitmap(mBitmapMask, 0, 0, mPaint);
mPaint.setXfermode(null);
}
}
@Override
public void onDescendantInvalidated(View child, View target) {
super.onDescendantInvalidated(child, target);
if (!mBitmapMaskInvalidated) {
View maskView = getChildAt(0);
if (maskView != null) {
if (maskView.equals(child)) {
mBitmapMaskInvalidated = true;
}
}
}
invalidate();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
mBitmapMaskInvalidated = true;
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mBitmapMaskInvalidated = true;
}
private void updateBitmapMask() {
View maskView = getChildAt(0);
if (maskView != null) {
maskView.setVisibility(View.VISIBLE);
if (this.mBitmapMask != null) {
this.mBitmapMask.recycle();
}
this.mBitmapMask = getBitmapFromView(maskView);
maskView.setVisibility(View.INVISIBLE);
}
}
public static Bitmap getBitmapFromView(final View view) {
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
if (view.getMeasuredWidth() <= 0 || view.getMeasuredHeight() <= 0) {
return null;
}
final Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(),
view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
public void setRenderingMode(String renderingMode) {
mRenderingMode = renderingMode.equals("software") ? View.LAYER_TYPE_SOFTWARE : View.LAYER_TYPE_HARDWARE;
}
}
@@ -0,0 +1,38 @@
package org.reactnative.maskedview;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class RNCMaskedViewManager extends ViewGroupManager<RNCMaskedView> {
private static final String REACT_CLASS = "RNCMaskedView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected RNCMaskedView createViewInstance(ThemedReactContext themedReactContext) {
return new RNCMaskedView(themedReactContext);
}
@ReactProp(name = "androidRenderingMode")
public void setAndroidRenderingMode(RNCMaskedView view, @Nullable String renderingMode) {
if (renderingMode != null) {
view.setRenderingMode(renderingMode);
}
}
}
@@ -0,0 +1,25 @@
package org.reactnative.maskedview;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class RNCMaskedViewPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
return Arrays.<ViewManager>asList(
new RNCMaskedViewManager()
);
}
}
+3
View File
@@ -0,0 +1,3 @@
import MaskedView from './js/MaskedView';
export default MaskedView;
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTView.h>
@interface RNCMaskedView : RCTView
@end
+34
View File
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNCMaskedView.h"
#import <React/UIView+React.h>
@implementation RNCMaskedView
- (void)didUpdateReactSubviews
{
// RNCMaskedView expects that the first subview rendered is the mask.
UIView *maskView = [self.reactSubviews firstObject];
self.maskView = maskView;
// Add the other subviews to the view hierarchy
for (NSUInteger i = 1; i < self.reactSubviews.count; i++) {
UIView *subview = [self.reactSubviews objectAtIndex:i];
[self addSubview:subview];
}
}
- (void)displayLayer:(CALayer *)layer
{
// RCTView uses displayLayer to do border rendering.
// We don't need to do that in RNCMaskedView, so we
// stub this method and override the default implementation.
}
@end
@@ -0,0 +1,265 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2150303C220DFE9200FC1971 /* RNCMaskedViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 2150303B220DFE9200FC1971 /* RNCMaskedViewManager.m */; };
B3E7B58A1CC2AC0600A0062D /* RNCMaskedView.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNCMaskedView.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNCMaskedView.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNCMaskedView.a; sourceTree = BUILT_PRODUCTS_DIR; };
2150303A220DFE9200FC1971 /* RNCMaskedViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCMaskedViewManager.h; sourceTree = "<group>"; };
2150303B220DFE9200FC1971 /* RNCMaskedViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCMaskedViewManager.m; sourceTree = "<group>"; };
B3E7B5881CC2AC0600A0062D /* RNCMaskedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNCMaskedView.h; sourceTree = "<group>"; };
B3E7B5891CC2AC0600A0062D /* RNCMaskedView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNCMaskedView.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRNCMaskedView.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
B3E7B5881CC2AC0600A0062D /* RNCMaskedView.h */,
2150303A220DFE9200FC1971 /* RNCMaskedViewManager.h */,
2150303B220DFE9200FC1971 /* RNCMaskedViewManager.m */,
B3E7B5891CC2AC0600A0062D /* RNCMaskedView.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RNCMaskedView */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCMaskedView" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNCMaskedView;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRNCMaskedView.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCMaskedView" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RNCMaskedView */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B3E7B58A1CC2AC0600A0062D /* RNCMaskedView.m in Sources */,
2150303C220DFE9200FC1971 /* RNCMaskedViewManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNCMaskedView;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNCMaskedView;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNCMaskedView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNCMaskedView" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
@interface RNCMaskedViewManager : RCTViewManager
@end
@@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNCMaskedViewManager.h"
#import "RNCMaskedView.h"
@implementation RNCMaskedViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RNCMaskedView new];
}
@end
+81
View File
@@ -0,0 +1,81 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import * as React from 'react';
import { View, StyleSheet, requireNativeComponent } from 'react-native';
const RNCMaskedView = requireNativeComponent<any>('RNCMaskedView');
import type { MaskedViewProps } from './MaskedViewTypes';
/**
* Renders the child view with a mask specified in the `maskElement` prop.
*
* ```
* import React from 'react';
* import { Text, View } from 'react-native';
* import MaskedView from 'react-native-masked-view';
*
* class MyMaskedView extends React.Component {
* render() {
* return (
* <MaskedView
* style={{ flex: 1 }}
* maskElement={
* <View style={styles.maskContainerStyle}>
* <Text style={styles.maskTextStyle}>
* Basic Mask
* </Text>
* </View>
* }
* >
* <View style={{ flex: 1, backgroundColor: 'blue' }} />
* </MaskedView>
* );
* }
* }
* ```
*
* The above example will render a view with a blue background that fills its
* parent, and then mask that view with text that says "Basic Mask".
*
* The alpha channel of the view rendered by the `maskElement` prop determines how
* much of the view's content and background shows through. Fully or partially
* opaque pixels allow the underlying content to show through but fully
* transparent pixels block that content.
*
*/
export default class MaskedView extends React.Component<MaskedViewProps> {
_hasWarnedInvalidRenderMask = false;
render(): React.Node {
const { maskElement, children, ...otherViewProps } = this.props;
if (!React.isValidElement(maskElement)) {
if (!this._hasWarnedInvalidRenderMask) {
console.warn(
'MaskedView: Invalid `maskElement` prop was passed to MaskedView. ' +
'Expected a React Element. No mask will render.',
);
this._hasWarnedInvalidRenderMask = true;
}
return <View {...otherViewProps}>{children}</View>;
}
return (
<RNCMaskedView {...otherViewProps}>
<View pointerEvents="none" style={StyleSheet.absoluteFill}>
{maskElement}
</View>
{children}
</RNCMaskedView>
);
}
}
@@ -0,0 +1,8 @@
import React from 'react';
import { View } from 'react-native';
function MaskedView({ maskElement, ...props }) {
return React.createElement(View, props, maskElement);
}
export default MaskedView;
@@ -0,0 +1,17 @@
// @flow
import { type Node } from 'react';
import { type ViewProps } from 'react-native/Libraries/Components/View/ViewPropTypes';
export type MaskedViewProps = Partial<ViewProps> &
$ReadOnly<{|
children: Node,
/**
* Should be a React element to be rendered and applied as the
* mask for the child element.
*/
maskElement: Node,
/**
* Opt into software rendering to enable animated masks.
*/
androidRenderingMode?: 'software' | 'hardware',
|}>;
+57
View File
@@ -0,0 +1,57 @@
{
"name": "@react-native-masked-view/masked-view",
"description": "React Native MaskedView component",
"types": "./types/index.d.ts",
"main": "index.js",
"author": "Mike Nedosekin <crespo8800@gmail.com>",
"license": "MIT",
"version": "0.3.2",
"homepage": "https://github.com/react-native-masked-view/masked-view#readme",
"keywords": [
"react-native",
"react native",
"masked-view",
"masked view"
],
"scripts": {
"test:flow": "flow check",
"test:js": "echo 0",
"test:lint": "eslint ./",
"test:typescript": "tsc -noEmit",
"test:prettier": "prettier --check './**/*.js'"
},
"peerDependencies": {
"react": ">=16",
"react-native": ">=0.57"
},
"files": [
"index.js",
"android",
"ios",
"js",
"types",
"RNCMaskedView.podspec"
],
"devDependencies": {
"@react-native/eslint-config": "^0.76.1",
"@types/react-native": "^0.72.8",
"eslint": "8",
"eslint-plugin-ft-flow": "^3.0.11",
"flow-bin": "^0.246.0",
"prettier": "^3.3.3",
"react-native": "0.75.3",
"react-native-test-app": "^3.10.5",
"typescript": "5.3.2"
},
"repository": {
"type": "git",
"url": "git+https://github.com/react-native-masked-view/masked-view.git"
},
"bugs": {
"url": "https://github.com/react-native-masked-view/masked-view/issues"
},
"publishConfig": {
"access": "public"
},
"packageManager": "yarn@1.22.22+sha256.c17d3797fb9a9115bf375e31bfd30058cac6bc9c3b8807a3d8cb2094794b51ca"
}
+20
View File
@@ -0,0 +1,20 @@
// CREDITS: These types are based on the original work made by all the people who contributed to @types/react-native
import * as React from 'react';
import * as ReactNative from 'react-native';
type Constructor<T> = new (...args: any[]) => T;
interface MaskedViewProps extends ReactNative.ViewProps {
maskElement: React.ReactElement;
androidRenderingMode?: 'software' | 'hardware';
}
/**
* @see https://github.com/react-native-masked-view/masked-view
*/
declare class MaskedViewComponent extends React.Component<MaskedViewProps> {}
declare const MaskedViewBase: Constructor<
ReactNative.NativeMethods
> &
typeof MaskedViewComponent;
export default class MaskedView extends MaskedViewBase {}