Skip to content

Commit

Permalink
feat: update reactjs to 17+, react-native to 0.72, add method `getTra…
Browse files Browse the repository at this point in the history
…nslation()`
  • Loading branch information
fakeheal committed Oct 12, 2023
1 parent 233cf83 commit 7a02376
Show file tree
Hide file tree
Showing 9 changed files with 571 additions and 29 deletions.
103 changes: 95 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,118 @@
# react-native-pan-pinch-view
<a href="https://www.npmjs.com/package/react-native-pan-pinch-view">
<img alt="npm (scoped)" src="https://img.shields.io/npm/v/react-native-pan-pinch-view?style=flat">
</a>

A view component for React Native with pinch to zoom and drag to pan functionality.

## Installation
<img width="200" height="400" src="ios.gif" alt="iOS Example App Gif"> <img width="200" height="400" src="android.gif" alt="Android Example App Gif">


## 👋 Introduction

Even though the demo shows the library used with images, it was initially designed to show `<View>`s representing blueprints of rooms.

- Pinch to zoom with two fingers
- Drag one finger to pan
- Keep content inside container boundaries
- Configurable minimum and maximum scale
- Methods for programmatically updating position and scale

Thanks to `react-native-reanimated` all animations are running on the UI thread, so no fps drops are experienced.

If you want to zoom in on images *exclusively*, in a gallery-like UI, I recommend these packages, rather than my library:

- [`react-native-awesome-gallery`](https://github.com/Flair-Dev/react-native-awesome-gallery)
- [`react-native-image-zoom`](https://github.com/likashefqet/react-native-image-zoom)


## 👀 Prerequisites

This library uses `react-native-reanimated` v3 and the latest API of `react-native-gesture-handler`.

Before installing it, you need to install those two libraries and set them up in your project:

- `react-native-reanimated`: [installation & setup](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation)
- `react-native-gesture-handler`: [installation & setup](https://docs.swmansion.com/react-native-gesture-handler/docs/#installation)

## ⚙️ Installation

```sh
npm install react-native-pan-pinch-view
```

## Usage
## ✂️ Usage

```js
import { multiply } from 'react-native-pan-pinch-view';
import PanPinchView from "react-native-pan-pinch-view";

// ...

const result = await multiply(3, 7);
const CONTAINER = {
width: 300,
height: 300,
};

const CONTENT = {
width: 900,
height: 400,
};
// ...

<PanPinchView
minScale={1}
initialScale={1}
containerDimensions={{
width: CONTAINER.width,
height: CONTAINER.height,
}}
contentDimensions={{ width: CONTENT.width, height: CONTENT.height }}
>
<Image
style={[styles.image]}
source={require('./assets/photo.jpg')}
/>
</PanPinchView>

// ...

const styles = StyleSheet.create({
image: {
width: CONTENT.width,
height: CONTENT.height,
},
});

```

## ⚪ Props

| Property | Type | Default | Description |
|---------------------|----------|-----------------------------------|---------------------------------------------|
| containerDimensions | Object | `{width: number, height:number}` | Width and height of the viewable area. |
| contentDimensions | Object | `{width: number, height:number}` | Width and height of the zoomable view. |
| minScale | Number? | `0.5` | Minimum value of scale. |
| maxScale | Number? | `4` | Maximum value of scale. |
| initialScale | Number? | `1` | Initial value of scale. |

## 🛠 Methods

| Method | Params | Return | Description |
|----------------|-----------------------------------------|--------|----------------------------------------------------------------------------------------------|
| scaleTo | value: number, animated: boolean | void | Sets sharedValue `scale` to `value`,<br/> if `animated` is **true** uses `withTiming` |
| setContentSize | width: number, height: number | void | Updates sharedValue `contentSize` and overrides prop: `contentDimensions` |
| translateTo | x: number, y: number, animated: boolean | void | Updates content `translateX` / `translateY`, <br>if `animated` is **true** uses `withTiming` |
| setMinScale | value: number | void | Updates `minScale` value |
| setMaxScale | value: number | void | Updates `maxScale` value |
| getScale | | number | Returns current value of sharedValue `scale` |
| getTranslation | | number | Returns current value of the positioning shared values `offset` + `translation` |

You can also refer to the app inside `example/` for a running demo of this library.

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

## License

MIT

---

Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
2 changes: 2 additions & 0 deletions example/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ module.exports = function (api) {
},
},
],
'@babel/plugin-proposal-export-namespace-from',
'react-native-reanimated/plugin',
],
};
};
8 changes: 5 additions & 3 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
"expo": "~49.0.13",
"expo-status-bar": "~1.6.0",
"react": "18.2.0",
"react-native": "0.72.5",
"react-dom": "18.2.0",
"react-native": "0.72.5",
"react-native-gesture-handler": "~2.12.0",
"react-native-reanimated": "~3.3.0",
"react-native-web": "~0.19.6"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"babel-plugin-module-resolver": "^5.0.0",
"@expo/webpack-config": "^18.0.1",
"babel-loader": "^8.1.0"
"babel-loader": "^8.1.0",
"babel-plugin-module-resolver": "^5.0.0"
},
"private": true
}
114 changes: 100 additions & 14 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,117 @@
import * as React from 'react';

import { StyleSheet, View, Text } from 'react-native';
import { multiply } from 'react-native-pan-pinch-view';
import {
Button,
Image,
SafeAreaView,
StatusBar,
StyleSheet,
View,
} from 'react-native';
import PanPinchView from 'react-native-pan-pinch-view';
import { useRef } from 'react';
import type { PanPinchViewRef } from '../../src/types.js';

const CONTENT = {
width: 100,
height: 150,
};

const CONTAINER = {
width: 300,
height: 300,
};

export default function App() {
const [result, setResult] = React.useState<number | undefined>();
const panPinchViewRef = useRef<PanPinchViewRef>(null);

React.useEffect(() => {
multiply(3, 7).then(setResult);
}, []);
const scaleTo = (value: number) => {
panPinchViewRef.current?.scaleTo(value);
};

const moveTo = (x: number, y: number) => {
panPinchViewRef.current?.translateTo(x, y);
};

return (
<View style={styles.container}>
<Text>Result: {result}</Text>
</View>
<SafeAreaView style={styles.screen}>
<StatusBar />
<View style={styles.container}>
<PanPinchView
ref={panPinchViewRef}
minScale={1}
initialScale={1}
containerDimensions={{
width: CONTAINER.width,
height: CONTAINER.height,
}}
contentDimensions={{ width: CONTENT.width, height: CONTENT.height }}
>
<Image style={[styles.image]} source={require('./photo.jpg')} />
</PanPinchView>
</View>
<View style={styles.controls}>
<Button title="Scale to 0.5" onPress={() => scaleTo(0.5)} />
<Button title="Scale to 1.5" onPress={() => scaleTo(1.5)} />
<Button title="Scale to 2" onPress={() => scaleTo(2)} />
</View>
<View style={styles.controls}>
<Button
title="Center"
onPress={() =>
moveTo(
CONTAINER.width / 2 - CONTENT.width / 2,
CONTAINER.height / 2 - CONTENT.height / 2
)
}
/>
<Button
title="Bottom Right"
onPress={() =>
moveTo(
CONTAINER.width - CONTENT.width,
CONTAINER.height - CONTENT.height
)
}
/>
<Button
title="Bottom Center"
onPress={() =>
moveTo(
CONTAINER.width / 2 - CONTENT.width / 2,
CONTAINER.height - CONTENT.height
)
}
/>
</View>

<Button
title="Get Position"
onPress={() =>
alert(JSON.stringify(panPinchViewRef.current?.getTranslation()))
}
/>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
screen: { flex: 1, backgroundColor: '#fff' },
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
borderWidth: 1,
marginVertical: 50,
},
box: {
width: 60,
height: 60,
image: {
width: CONTENT.width,
height: CONTENT.height,
},
controls: {
justifyContent: 'center',
flexDirection: 'row',
flexWrap: 'wrap',
marginVertical: 20,
},
});
Binary file added example/src/photo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@
},
"peerDependencies": {
"react": "*",
"react-native": "*"
"react-native": "*",
"react-native-gesture-handler": "^2.13.2",
"react-native-reanimated": "^3.3.0"
},
"workspaces": [
"example"
Expand Down
Loading

0 comments on commit 7a02376

Please sign in to comment.