Make API consistent for Android and iOS, so can use `blurAmount` and `blurType` on both.

This commit is contained in:
Nathan Broadbent 2017-04-17 22:28:34 +07:00
parent 10858e396b
commit e32ab5f1e0
16 changed files with 540 additions and 198 deletions

243
README.md
View File

@ -1,20 +1,23 @@
[![npm version](https://badge.fury.io/js/react-native-blur.svg)](https://badge.fury.io/js/react-native-blur) [![npm version](https://badge.fury.io/js/react-native-blur.svg)](https://badge.fury.io/js/react-native-blur)
### React Native Blur ### React Native Blur
Component implementation for UIVisualEffectView's blur and vibrancy effect.<br>
Check the [roadmap here](https://github.com/Kureev/react-native-blur/issues/1)
<img src='https://cloud.githubusercontent.com/assets/5795227/20123354/d877dba4-a61e-11e6-8e5a-c85f76419e20.gif' /> A component for UIVisualEffectView's blur and vibrancy effect on iOS, and [500px-android-blur](https://github.com/500px/500px-android-blur) on Android.<br>
<img src='https://cloud.githubusercontent.com/assets/139536/25066337/3c9d44c0-224d-11e7-8ca6-028478bf4a7d.gif' />
### Content ### Content
- [Installation](#installation) - [Installation](#installation)
- [Usage example](#usage-example) - [BlurView](#blurview)
- [Blur view](#blur-view) - [VibrancyView](#vibrancyview)
- [Vibrancy view](#vibrancy-view) - [Example React Native app](#example-react-native-app)
- [Component properties](#component-properties)
- [Questions?](#questions) - [Questions?](#questions)
### Installation ### Installation
1. Install package via npm: 1. Install package via npm:
``` ```
@ -25,61 +28,146 @@ Check the [roadmap here](https://github.com/Kureev/react-native-blur/issues/1)
``` ```
react-native link react-native-blur react-native link react-native-blur
``` ```
3. (Android only) Add the following to your `android/app/build.gradle`
`android/build.gradle` 3. (Android only) Add the following to `android/app/build.gradle`
``` ```
buildscript {
dependencies {
// Update "Android Plugin for Gradle" version
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
// ...
allprojects {
repositories {
// Add the following in a new line, underneath "maven { url '$rootDir/../node_modules/react-native/android' }"
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
}
}
```
`android/app/build.gradle`
```
dependencies {
compile 'com.fivehundredpx:blurringview:1.0.0'
}
android { android {
// ...
defaultConfig { defaultConfig {
// Add these to the existing config // Add these lines below the existing config
renderscriptTargetApi 23 renderscriptTargetApi 23
renderscriptSupportModeEnabled true renderscriptSupportModeEnabled true
} }
} }
``` ```
`android/gradle/wrapper/gradle-wrapper.properties` 4. Include the library in your code:
```
// Update Gradle version
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
```
4. Inside your code include JS part by adding
```javascript ```javascript
import { BlurView, VibrancyView } from 'react-native-blur';
// OR
const { BlurView, VibrancyView } = require('react-native-blur'); const { BlurView, VibrancyView } = require('react-native-blur');
``` ```
5. Compile and have fun! 5. Compile and have fun!
### Usage example
You can run the built-in examples by running these steps: ### BlurView
**Properties:**
- `blurType` (String)
- `xlight` - extra light blur type
- `light` - light blur type
- `dark` - dark blur type
- `blurAmount` (Default: 10, Number)
- `0-100` - Adjusts blur intensity
> Note: The maximum `blurAmount` on Android is 32, so higher values will be clamped to 32.
> Complete usage example that works on iOS and Android:
```javascript
import React, { Component } from 'react';
import { View, Image, Text, findNodeHandle, StyleSheet } from 'react-native';
import { BlurView } from 'react-native-blur';
export default class Menu extends Component {
constructor(props) {
super(props);
this.state = { viewRef: null };
}
imageLoaded() {
this.setState({ viewRef: findNodeHandle(this.backgroundImage) });
}
render() {
return (
<View style={styles.container}>
<Image
ref={(img) => { this.backgroundImage = img; }}
source={{uri}}
style={styles.absolute}
onLoadEnd={this.imageLoaded.bind(this)}
/>
<BlurView
style={styles.absolute}
viewRef={this.state.viewRef}
blurType="light"
blurAmount={10}
/>
<Text>Hi, I am some unblurred text</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
absolute: {
position: "absolute",
top: 0, left: 0, bottom: 0, right: 0,
},
});
```
In this example, the `Image` component will be blurred, because the `BlurView` in positioned on top. But the `Text` will stay unblurred.
Note that `viewRef` is only required if you need to support Android. See the [Android section](#android) for more details.
### VibrancyView
**Uses the same properties as `BlurView` (`blurType` and `blurAmount`).**
> The vibrancy effect lets the content underneath a blurred view show through more vibrantly
> (Note: `VibrancyView` is only supported on iOS. Also note that the `VibrancyView` must contain nested views.)
```javascript
import { VibrancyView } from 'react-native-blur';
export default class Menu extends Component {
render() {
return (
<Image source={{uri}} style={styles.absolute}>
<VibrancyView blurType="light" style={styles.flex}>
<Text>Hi, I am some vibrant text.</Text>
</VibrancyView>
</Image>
);
}
}
```
### Android
Android uses the [500px-android-blur](https://github.com/500px/500px-android-blur) library, which works by blurring a referenced view. This means that you must wait until the view you want to blur is rendered. You then use `findNodeHandle` to get a reference to that view, and pass that reference to the `BlurView` as the `viewRef` prop. Take a look at [the BlurView example](#blurview) to see how it works.
The Android library introduces some limitations:
* `BlurView` cannot be a child of the view that is being blurred (this would cause an infinite loop)
* `BlurView` cannot contain any child components.
If you only need to support iOS, then you can safely ignore these limitations.
In addition to `blurType` and `blurAmount`, Android has some extra props that can be used to override the default behavior (or configure Android-specific behavior):
- `blurRadius` (Number - between 0 and 25) - Manually adjust the blur radius. (Default: matches iOS blurAmount)
- `downsampleFactor` (Number - between 0 and 25) - Scales down the image before blurring (Default: matches iOS blurAmount)
- `overlayColor` (Color) - Set a custom overlay color (Default color based on iOS blurType)
### Example React Native App
This project includes an example React Native app, which was used to make the GIF in this README.
You can run the apps by following these steps:
1. Clone the repository 1. Clone the repository
@ -114,74 +202,9 @@ react-native run-ios
react-native run-android react-native run-android
``` ```
#### Blur View
To use `blur` view, you need to require `BlurView` to your module and insert `<BlurView>` tag inside render function as it's done below: ### Troubleshooting
```javascript
const { BlurView } = require('react-native-blur');
const Menu = React.createClass({
render() {
return (
<View>
<Image source={{uri}} style={styles.menu} />
<BlurView blurType="light" blurAmount={10} style={styles.blur} />
<Text style={styles.text}>Hi, I am a tiny menu item</Text>
</View>
);
}
});
```
In this example, `Image` component will be blurred, a `BlurView` content will stay untouched.
Note that if you need to support Android, the `BlurView` cannot be a child of the view that is being
blurred, and it cannot contain any child components. See the [Android section](#android) for more information.
#### Vibrancy View
> The vibrancy effect lets the content underneath a blurred view show through more vibrantly
```javascript
const { VibrancyView } = require('react-native-blur');
const Menu = React.createClass({
render() {
return (
<Image source={{uri}} style={styles.menu}>
<VibrancyView blurType="light" style={styles.blur}>
<Text>Hi, I am a tiny menu item</Text>
</VibrancyView>
</Image>
);
}
});
```
> Note: `VibrancyView` is only supported on iOS. It must contain child views, otherwise the effect does not work.
### Component properties
- `blurType` (String) - blur type effect
- `xlight` - extra light blur type
- `light` - light blur type
- `dark` - dark blur type
- `blurAmount` (Default: 10, Number) - blur amount effect
- `0-100` - Adjusts blur intensity
### Android
Android support uses an [external library](https://github.com/500px/500px-android-blur) which has slightly different properties and setup requirements. This is why extra code must be added manually to the `android/app/build.gradle` file as documented above.
The android BlurView works by blurring an existing referenced view, so you must wait till the view you want to blur is rendered and then provide the reference to the BlurView as the `viewRef` prop. Take a look at the example to see how it works.
It has the following props:
- `viewRef` (Number) - a reference to the existing view you want to blur
- `blurRadius` (Number)
- `downsampleFactor` (Number)
- `overlayColor` (Color)
#### Troubleshooting
On older instances of react-native, BlurView package does not get added into the MainActivity/MainApplication classes where you would see `Warning: Native component for 'BlurView' does not exist` in RN YellowBox or console. On older instances of react-native, BlurView package does not get added into the MainActivity/MainApplication classes where you would see `Warning: Native component for 'BlurView' does not exist` in RN YellowBox or console.
To rectify this, you can add the BlurViewPackage manually in MainActivity/MainApplication classes To rectify this, you can add the BlurViewPackage manually in MainActivity/MainApplication classes
@ -203,5 +226,7 @@ public class MainApplication extends Application implements ReactApplication {
} }
``` ```
### Questions? ### Questions?
Feel free to contact me in [twitter](https://twitter.com/kureevalexey) or [create an issue](https://github.com/Kureev/react-native-blur/issues/new) Feel free to contact me in [twitter](https://twitter.com/kureevalexey) or [create an issue](https://github.com/Kureev/react-native-blur/issues/new)

View File

@ -12,13 +12,16 @@ apply plugin: 'com.android.library'
android { android {
compileSdkVersion 23 compileSdkVersion 23
buildToolsVersion "23.0.1" buildToolsVersion "23.0.3"
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 22 targetSdkVersion 22
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
renderscriptTargetApi 23
renderscriptSupportModeEnabled true
} }
lintOptions { lintOptions {
abortOnError false abortOnError false
@ -29,11 +32,6 @@ repositories {
mavenCentral() mavenCentral()
} }
repositories {
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
}
dependencies { dependencies {
compile 'com.facebook.react:react-native:[0.32,)' compile 'com.facebook.react:react-native:[0.32,)'
compile 'com.fivehundredpx:blurringview:1.0.0'
} }

View File

@ -2,53 +2,10 @@ package com.cmcewen.blurview;
import android.view.View; import android.view.View;
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.uimanager.SimpleViewManager; import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactProp;
import com.fivehundredpx.android.blur.BlurringView;
class RNBlurringView extends BlurringView {
public RNBlurringView(ThemedReactContext context) {
super(context);
}
@Override
public void setBlurredView(View blurredView) {
super.setBlurredView(blurredView);
mBlurredViewCopy = blurredView;
checkForCircularReference();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
checkForCircularReference();
}
private void checkForCircularReference() {
// Need to wait until blurredView is set and the view is attached to window.
if (mBlurredViewCopy == null || getParent() == null) return;
Boolean circularReference = (mBlurredViewCopy.findViewById(getId()) != null);
if (circularReference) {
ThemedReactContext reactContext = (ThemedReactContext) getContext();
reactContext.getJSModule(RCTDeviceEventEmitter.class)
.emit("ReactNativeBlurError",
"BlurView must not be a child of the view that is being blurred.");
setBlurredView(null);
invalidate();
return;
}
}
// TODO: Change this to protected in 500px-android-blur,
// so we don't need our own copy.
private View mBlurredViewCopy;
}
public class BlurViewManager extends SimpleViewManager<BlurringView> { public class BlurViewManager extends SimpleViewManager<BlurringView> {
@ -65,37 +22,38 @@ public class BlurViewManager extends SimpleViewManager<BlurringView> {
} }
@Override @Override
public RNBlurringView createViewInstance(ThemedReactContext ctx) { public BlurringView createViewInstance(ThemedReactContext ctx) {
context = ctx; context = ctx;
RNBlurringView blurringView = new RNBlurringView(ctx); BlurringView blurringView = new BlurringView(ctx);
blurringView.setBlurRadius(defaultRadius); blurringView.setBlurRadius(defaultRadius);
blurringView.setDownsampleFactor(defaultSampling); blurringView.setDownsampleFactor(defaultSampling);
return blurringView; return blurringView;
} }
@ReactProp(name = "blurRadius", defaultInt = defaultRadius) @ReactProp(name = "blurRadius", defaultInt = defaultRadius)
public void setRadius(RNBlurringView view, int radius) { public void setRadius(BlurringView view, int radius) {
view.setBlurRadius(radius); view.setBlurRadius(radius);
view.invalidate();
} }
@ReactProp(name = "overlayColor", customType = "Color") @ReactProp(name = "overlayColor", customType = "Color")
public void setColor(RNBlurringView view, int color) { public void setColor(BlurringView view, int color) {
view.setOverlayColor(color); view.setOverlayColor(color);
view.invalidate();
} }
@ReactProp(name = "downsampleFactor", defaultInt = defaultSampling) @ReactProp(name = "downsampleFactor", defaultInt = defaultSampling)
public void setDownsampleFactor(RNBlurringView view, int factor) { public void setDownsampleFactor(BlurringView view, int factor) {
view.setDownsampleFactor(factor); view.setDownsampleFactor(factor);
} }
@ReactProp(name = "viewRef") @ReactProp(name = "viewRef")
public void setViewRef(RNBlurringView view, int viewRef) { public void setViewRef(BlurringView view, int viewRef) {
View viewToBlur = context.getCurrentActivity().findViewById(viewRef); View viewToBlur = context.getCurrentActivity().findViewById(viewRef);
if (viewToBlur != null) { if (viewToBlur != null) {
view.setBlurredView(viewToBlur); view.setBlurredView(viewToBlur);
view.invalidate();
} }
} }
} }

View File

@ -0,0 +1,210 @@
package com.cmcewen.blurview;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v8.renderscript.Allocation;
import android.support.v8.renderscript.Element;
import android.support.v8.renderscript.RenderScript;
import android.support.v8.renderscript.ScriptIntrinsicBlur;
import android.util.AttributeSet;
import android.view.View;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.uimanager.ThemedReactContext;
/**
* A custom view for presenting a dynamically blurred version of another view's content.
* <p/>
* Use {@link #setBlurredView(android.view.View)} to set up the reference to the view to be blurred.
* After that, call {@link #invalidate()} to trigger blurring whenever necessary.
*/
public class BlurringView extends View {
public BlurringView(ThemedReactContext context) {
this(context, null);
}
public BlurringView(ThemedReactContext context, AttributeSet attrs) {
super(context, attrs);
final Resources res = getResources();
final int defaultBlurRadius = res.getInteger(R.integer.default_blur_radius);
final int defaultDownsampleFactor = res.getInteger(R.integer.default_downsample_factor);
final int defaultOverlayColor = res.getColor(R.color.default_overlay_color);
initializeRenderScript(context);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PxBlurringView);
setBlurRadius(a.getInt(R.styleable.PxBlurringView_blurRadius, defaultBlurRadius));
setDownsampleFactor(a.getInt(R.styleable.PxBlurringView_downsampleFactor,
defaultDownsampleFactor));
setOverlayColor(a.getColor(R.styleable.PxBlurringView_overlayColor, defaultOverlayColor));
a.recycle();
}
public void setBlurredView(View blurredView) {
mBlurredView = blurredView;
checkForCircularReference();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBlurredView != null) {
if (prepare()) {
// If the background of the blurred view is a color drawable, we use it to clear
// the blurring canvas, which ensures that edges of the child views are blurred
// as well; otherwise we clear the blurring canvas with a transparent color.
if (mBlurredView.getBackground() != null && mBlurredView.getBackground() instanceof ColorDrawable) {
mBitmapToBlur.eraseColor(((ColorDrawable) mBlurredView.getBackground()).getColor());
} else {
mBitmapToBlur.eraseColor(Color.TRANSPARENT);
}
mBlurredView.draw(mBlurringCanvas);
blur();
canvas.save();
canvas.translate(mBlurredView.getX() - getX(), mBlurredView.getY() - getY());
canvas.scale(mDownsampleFactor, mDownsampleFactor);
canvas.drawBitmap(mBlurredBitmap, 0, 0, null);
canvas.restore();
}
canvas.drawColor(mOverlayColor);
}
}
public void setBlurRadius(int radius) {
mBlurScript.setRadius(radius);
invalidate();
}
public void setDownsampleFactor(int factor) {
if (factor <= 0) {
throw new IllegalArgumentException("Downsample factor must be greater than 0.");
}
if (mDownsampleFactor != factor) {
mDownsampleFactor = factor;
mDownsampleFactorChanged = true;
invalidate();
}
}
public void setOverlayColor(int color) {
if (mOverlayColor != color) {
mOverlayColor = color;
invalidate();
}
}
private void initializeRenderScript(Context context) {
mRenderScript = RenderScript.create(context);
mBlurScript = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
}
protected boolean prepare() {
final int width = mBlurredView.getWidth();
final int height = mBlurredView.getHeight();
if (mBlurringCanvas == null || mDownsampleFactorChanged
|| mBlurredViewWidth != width || mBlurredViewHeight != height) {
mDownsampleFactorChanged = false;
mBlurredViewWidth = width;
mBlurredViewHeight = height;
int scaledWidth = width / mDownsampleFactor;
int scaledHeight = height / mDownsampleFactor;
// This is no longer necessary on newer versions of RenderScript (23+)
// -------------------------------------------------------------------
// The following manipulation is to avoid some RenderScript artifacts at the edge.
//scaledWidth = scaledWidth - scaledWidth % 4 + 4;
//scaledHeight = scaledHeight - scaledHeight % 4 + 4;
if (mBlurredBitmap == null
|| mBlurredBitmap.getWidth() != scaledWidth
|| mBlurredBitmap.getHeight() != scaledHeight) {
mBitmapToBlur = Bitmap.createBitmap(scaledWidth, scaledHeight,
Bitmap.Config.ARGB_8888);
if (mBitmapToBlur == null) {
return false;
}
mBlurredBitmap = Bitmap.createBitmap(scaledWidth, scaledHeight,
Bitmap.Config.ARGB_8888);
if (mBlurredBitmap == null) {
return false;
}
}
mBlurringCanvas = new Canvas(mBitmapToBlur);
mBlurringCanvas.scale(1f / mDownsampleFactor, 1f / mDownsampleFactor);
mBlurInput = Allocation.createFromBitmap(mRenderScript, mBitmapToBlur,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
mBlurOutput = Allocation.createTyped(mRenderScript, mBlurInput.getType());
}
return true;
}
protected void blur() {
mBlurInput.copyFrom(mBitmapToBlur);
mBlurScript.setInput(mBlurInput);
mBlurScript.forEach(mBlurOutput);
mBlurOutput.copyTo(mBlurredBitmap);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
invalidate();
checkForCircularReference();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mRenderScript != null) {
mRenderScript.destroy();
}
}
private void checkForCircularReference() {
// Need to wait until blurredView is set and the view is attached to window.
if (mBlurredView == null || getParent() == null) return;
Boolean circularReference = (mBlurredView.findViewById(getId()) != null);
if (circularReference) {
ThemedReactContext reactContext = (ThemedReactContext) getContext();
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("ReactNativeBlurError",
"BlurView must not be a child of the view that is being blurred.");
setBlurredView(null);
invalidate();
return;
}
}
private int mDownsampleFactor;
private int mOverlayColor;
protected View mBlurredView;
private int mBlurredViewWidth, mBlurredViewHeight;
private boolean mDownsampleFactorChanged;
private Bitmap mBitmapToBlur, mBlurredBitmap;
private Canvas mBlurringCanvas;
private RenderScript mRenderScript;
private ScriptIntrinsicBlur mBlurScript;
private Allocation mBlurInput, mBlurOutput;
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PxBlurringView">
<attr name="blurRadius" format="integer"/>
<attr name="downsampleFactor" format="integer"/>
<attr name="overlayColor" format="color"/>
</declare-styleable>
</resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="default_blur_radius">15</integer>
<integer name="default_downsample_factor">8</integer>
<color name="default_overlay_color">#AAFFFFFF</color>
</resources>

View File

@ -95,7 +95,6 @@ android {
ndk { ndk {
abiFilters "armeabi-v7a", "x86" abiFilters "armeabi-v7a", "x86"
} }
renderscriptTargetApi 23 renderscriptTargetApi 23
renderscriptSupportModeEnabled true renderscriptSupportModeEnabled true
} }
@ -130,10 +129,10 @@ android {
dependencies { dependencies {
compile project(':react-native-blur') compile project(':react-native-blur')
compile project(':react-native-segmented-android')
compile fileTree(dir: "libs", include: ["*.jar"]) compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1" compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules compile "com.facebook.react:react-native:+" // From node_modules
compile "com.fivehundredpx:blurringview:1.0.0"
} }
// Run this once to be able to run the application with BUCK // Run this once to be able to run the application with BUCK

View File

@ -4,6 +4,7 @@ import android.app.Application;
import com.facebook.react.ReactApplication; import com.facebook.react.ReactApplication;
import com.cmcewen.blurview.BlurViewPackage; import com.cmcewen.blurview.BlurViewPackage;
import com.higo.zhangyp.segmented.AndroidSegmentedPackage;
import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage; import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage; import com.facebook.react.shell.MainReactPackage;
@ -24,7 +25,8 @@ public class MainApplication extends Application implements ReactApplication {
protected List<ReactPackage> getPackages() { protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList( return Arrays.<ReactPackage>asList(
new MainReactPackage(), new MainReactPackage(),
new BlurViewPackage() new BlurViewPackage(),
new AndroidSegmentedPackage()
); );
} }
}; };

View File

@ -20,6 +20,5 @@ allprojects {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android" url "$rootDir/../node_modules/react-native/android"
} }
maven { url 'https://github.com/500px/500px-android-blur/raw/master/releases/' }
} }
} }

View File

@ -1,5 +1,10 @@
rootProject.name = 'Basic' rootProject.name = 'Basic'
include ':react-native-blur' include ':react-native-blur'
project(':react-native-blur').projectDir = new File(rootProject.projectDir, '../../../android') project(':react-native-blur').projectDir =
new File(rootProject.projectDir, '../../../android')
include ':react-native-segmented-android', ':app'
project(':react-native-segmented-android').projectDir =
new File(rootProject.projectDir,'../node_modules/react-native-segmented-android')
include ':app' include ':app'

View File

@ -11,20 +11,82 @@ import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
Dimensions,
Switch,
InteractionManager,
} from 'react-native'; } from 'react-native';
import AndroidSegmented from 'react-native-segmented-android';
import { BlurView } from 'react-native-blur'; import { BlurView } from 'react-native-blur';
const BLUR_TYPES = ['xlight', 'light', 'dark'];
class Basic extends Component { class Basic extends Component {
constructor() { constructor() {
super(); super();
this.state = { this.state = {
viewRef: 0, showBlur: true,
viewRef: null,
activeSegment: 2,
blurType: 'dark',
}; };
} }
imageLoaded() { imageLoaded() {
// Workaround for a tricky race condition on initial load.
InteractionManager.runAfterInteractions(() => {
setTimeout(() => {
this.setState({ viewRef: findNodeHandle(this.refs.backgroundImage) }); this.setState({ viewRef: findNodeHandle(this.refs.backgroundImage) });
}, 500);
});
}
_onChange(selected) {
this.setState({
activeSegment: selected,
blurType: BLUR_TYPES[selected],
});
}
renderBlurView() {
const tintColor = ['#ffffff', '#000000'];
if (this.state.blurType === 'xlight') tintColor.reverse();
return (
<View style={styles.container}>
{this.state.viewRef && <BlurView
viewRef={this.state.viewRef}
style={styles.blurView}
blurRadius={9}
blurType={this.state.blurType}
// The following props are also available on Android:
// blurRadius={20}
// downsampleFactor={10}
// overlayColor={'rgba(0, 0, 255, .6)'} // set a blue overlay
/>}
<Text style={[styles.text, { color: tintColor[0] }]}>
Blur component (Android)
</Text>
<AndroidSegmented
tintColor={tintColor}
style={{
width: Dimensions.get('window').width,
height: 28,
justifyContent: 'center',
alignItems: 'center',
}}
childText={BLUR_TYPES}
orientation='horizontal'
selectedPosition={this.state.activeSegment}
onChange={this._onChange.bind(this)} />
</View>
)
} }
render() { render() {
@ -36,14 +98,14 @@ class Basic extends Component {
ref={'backgroundImage'} ref={'backgroundImage'}
onLoadEnd={this.imageLoaded.bind(this)} /> onLoadEnd={this.imageLoaded.bind(this)} />
<BlurView { this.state.showBlur ? this.renderBlurView() : null }
blurRadius={15}
downsampleFactor={5}
overlayColor={'rgba(0, 0, 0, .3)'}
style={styles.blurView}
viewRef={this.state.viewRef} />
<Text style={styles.text}>Blur component</Text> <View
style={styles.blurToggle}>
<Switch
onValueChange={(value) => this.setState({showBlur: value})}
value={this.state.showBlur} />
</View>
</View> </View>
); );
} }
@ -79,6 +141,12 @@ const styles = StyleSheet.create({
margin: 10, margin: 10,
color: '#FFFFFF', color: '#FFFFFF',
}, },
blurToggle: {
position: 'absolute',
top: 30,
right: 10,
alignItems: 'flex-end',
},
}); });
AppRegistry.registerComponent('Basic', () => Basic); AppRegistry.registerComponent('Basic', () => Basic);

View File

@ -45,6 +45,8 @@ class Basic extends Component {
} }
renderBlurs() { renderBlurs() {
const tintColor = this.state.blurBlurType === 'xlight' ? 'black' : 'white';
return ( return (
<View style={styles.container}> <View style={styles.container}>
@ -57,17 +59,19 @@ class Basic extends Component {
*/} */}
<BlurView <BlurView
blurType={this.state.blurBlurType} blurType={this.state.blurBlurType}
blurAmount={10} blurAmount={100}
style={[styles.blurView]} /> style={[styles.blurView]} />
<Text style={styles.welcome}>Blur component</Text> <Text style={[styles.text, { color: tintColor }]}>
Blur component (iOS)
</Text>
<SegmentedControlIOS <SegmentedControlIOS
values={['xlight', 'light', 'dark']} values={['xlight', 'light', 'dark']}
selectedIndex={this.state.blurActiveSegment} selectedIndex={this.state.blurActiveSegment}
onChange={(event) => {this._onBlurChange(event)}} onChange={(event) => {this._onBlurChange(event)}}
onValueChange={(value) => {this._onBlurValueChange(value)}} onValueChange={(value) => {this._onBlurValueChange(value)}}
tintColor={this.state.blurBlurType === 'xlight' ? 'black' : 'white'} tintColor={tintColor}
/> />
</View> </View>
@ -79,7 +83,11 @@ class Basic extends Component {
blurType={this.state.vibrancyBlurType} blurType={this.state.vibrancyBlurType}
blurAmount={10} blurAmount={10}
style={[styles.container, styles.blurContainer]}> style={[styles.container, styles.blurContainer]}>
<Text style={styles.welcome}>Vibrancy component</Text>
<Text style={styles.text}>
Vibrancy component (iOS-only)
</Text>
<SegmentedControlIOS <SegmentedControlIOS
values={['xlight', 'light', 'dark']} values={['xlight', 'light', 'dark']}
selectedIndex={this.state.vibrancyActiveSegment} selectedIndex={this.state.vibrancyActiveSegment}
@ -143,7 +151,7 @@ const styles = StyleSheet.create({
height: null, height: null,
width: null, width: null,
}, },
welcome: { text: {
fontSize: 20, fontSize: 20,
fontWeight: 'bold', fontWeight: 'bold',
textAlign: 'center', textAlign: 'center',

View File

@ -9,7 +9,8 @@
"dependencies": { "dependencies": {
"react": "16.0.0-alpha.6", "react": "16.0.0-alpha.6",
"react-native": "0.43.3", "react-native": "0.43.3",
"react-native-blur": "file:../../" "react-native-blur": "file:../../",
"react-native-segmented-android": "sooth-sayer/react-native-segmented-android"
}, },
"devDependencies": { "devDependencies": {
"babel-jest": "19.0.0", "babel-jest": "19.0.0",

View File

@ -2922,6 +2922,10 @@ react-devtools-core@^2.0.8:
"react-native-blur@file:../../": "react-native-blur@file:../../":
version "2.0.0" version "2.0.0"
react-native-segmented-android@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/react-native-segmented-android/-/react-native-segmented-android-1.0.4.tgz#223424ab2b6b31acdd90be1ff3d49953a0df2c1d"
react-native@0.43.3: react-native@0.43.3:
version "0.43.3" version "0.43.3"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.43.3.tgz#4f8e0d8d88827553789c02ef21cbc1afb68474d0" resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.43.3.tgz#4f8e0d8d88827553789c02ef21cbc1afb68474d0"

View File

@ -1,6 +1,13 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { View, requireNativeComponent, DeviceEventEmitter, } from 'react-native'; import { View, requireNativeComponent, DeviceEventEmitter, } from 'react-native';
const OVERLAY_COLORS = {
light: 'rgba(255, 255, 255, 0.2)',
xlight: 'rgba(255, 255, 255, 0.75)',
dark: 'rgba(16, 12, 12, 0.64)',
};
class BlurView extends Component { class BlurView extends Component {
componentWillMount() { componentWillMount() {
DeviceEventEmitter.addListener('ReactNativeBlurError', (message) => { DeviceEventEmitter.addListener('ReactNativeBlurError', (message) => {
@ -12,6 +19,35 @@ class BlurView extends Component {
DeviceEventEmitter.removeAllListeners('ReactNativeBlurError'); DeviceEventEmitter.removeAllListeners('ReactNativeBlurError');
} }
overlayColor() {
if (this.props.overlayColor != null) return this.props.overlayColor;
return OVERLAY_COLORS[this.props.blurType] || OVERLAY_COLORS.dark;
}
blurRadius() {
const { blurRadius, blurAmount } = this.props;
if (blurRadius != null) {
if (blurRadius > 25) {
throw new Error(`[ReactNativeBlur]: blurRadius cannot be greater than 25! (was: ${blurRadius})`);
}
return blurRadius;
}
// iOS seems to use a slightly different blurring algorithm (or scale?).
// Android blurRadius + downsampleFactor is approximately 80% of blurAmount.
const equivalentBlurRadius = Math.round(blurAmount * 0.8);
if (equivalentBlurRadius > 25) return 25;
return equivalentBlurRadius;
}
downsampleFactor() {
const { downsampleFactor, blurRadius } = this.props;
if (downsampleFactor != null) return downsampleFactor;
return blurRadius;
}
render() { render() {
if (this.props.children != null) { if (this.props.children != null) {
throw new Error( throw new Error(
@ -20,12 +56,17 @@ class BlurView extends Component {
'and place other views in front of it.'); 'and place other views in front of it.');
} }
const { viewRef, style } = this.props;
return ( return (
<NativeBlurView <NativeBlurView
{...this.props} viewRef={viewRef}
style={[{ blurRadius={this.blurRadius()}
backgroundColor: 'transparent', downsampleFactor={this.downsampleFactor()}
}, this.props.style, overlayColor={this.overlayColor()}
style={[
{ backgroundColor: 'transparent' },
style,
]} ]}
/> />
); );
@ -34,10 +75,18 @@ class BlurView extends Component {
BlurView.propTypes = { BlurView.propTypes = {
...View.propTypes, ...View.propTypes,
blurAmount: PropTypes.number,
blurType: PropTypes.oneOf(['dark', 'light', 'xlight']),
blurRadius: PropTypes.number, blurRadius: PropTypes.number,
overlayColor: PropTypes.string,
downsampleFactor: PropTypes.number, downsampleFactor: PropTypes.number,
viewRef: PropTypes.number overlayColor: PropTypes.string,
viewRef: PropTypes.number.isRequired,
};
BlurView.defaultProps = {
blurType: 'dark',
blurAmount: 10,
}; };
const NativeBlurView = requireNativeComponent('BlurView', BlurView); const NativeBlurView = requireNativeComponent('BlurView', BlurView);

View File

@ -1,14 +1,14 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { requireNativeComponent } from 'react-native'; import { View, requireNativeComponent } from 'react-native';
class BlurView extends Component { class BlurView extends Component {
render() { render() {
return ( return (
<NativeBlurView <NativeBlurView
{...this.props} {...this.props}
style={[{ style={[
backgroundColor: 'transparent', { backgroundColor: 'transparent' },
}, this.props.style, this.props.style,
]} ]}
/> />
); );
@ -16,11 +16,13 @@ class BlurView extends Component {
} }
BlurView.propTypes = { BlurView.propTypes = {
blurType: PropTypes.string, ...View.propTypes,
blurAmount: PropTypes.number.isRequired, blurType: PropTypes.oneOf(['dark', 'light', 'xlight']),
blurAmount: PropTypes.number,
}; };
BlurView.defaultProps = { BlurView.defaultProps = {
blurType: 'dark',
blurAmount: 10, blurAmount: 10,
}; };