how to drag the view in react native - react-native

I have an example code I put it on below ,this example show the circle to drag another location
import React, { Component } from "react";
import {
StyleSheet,
View,
PanResponder,
Animated
} from "react-native";
export default class Draggable extends Component {
constructor() {
super();
this.state = {
pan: new Animated.ValueXY()
};
}
componentWillMount() {
// Add a listener for the delta value change
this._val = { x:0, y:0 }
this.state.pan.addListener((value) => this._val = value);
// Initialize PanResponder with move handling
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: (e, gesture) => true,
onPanResponderMove: Animated.event([
null, { dx: this.state.pan.x, dy: this.state.pan.y }
])
// adjusting delta value
this.state.pan.setValue({ x:0, y:0})
});
}
render() {
const panStyle = {
transform: this.state.pan.getTranslateTransform()
}
return (
<Animated.View
{...this.panResponder.panHandlers}
style={[panStyle, styles.circle]}
/>
);
}
}
let CIRCLE_RADIUS = 30;
let styles = StyleSheet.create({
circle: {
backgroundColor: "skyblue",
width: CIRCLE_RADIUS * 2,
height: CIRCLE_RADIUS * 2,
borderRadius: CIRCLE_RADIUS
}
});
this code the circle to the drag but I have drag another location it goes on the starting location so have to set the current location value so please tell me

Related

Unable to render a 3D model in Expo

SDK Version: 37.0.0
Platforms(Android/iOS/web/all): Android/iOS.
I’m trying to load a 3D object in my application and followed many tutorials.
the model is loaded successfully and attached to scene variable but it doesn’t appear in the view.
this is a part of my graduation project and i have searched a lot the 2 months to answers
looking for a real working solution as most of the solution i found was too old and doesn’t work.
my component is as described below:
import * as React from 'react';
import { Ionicons } from '#expo/vector-icons';
import { View, StyleSheet,PixelRatio ,TouchableOpacity} from 'react-native';
import { ExpoWebGLRenderingContext, GLView } from 'expo-gl';
import { Renderer, TextureLoader } from 'expo-three';
import ExpoTHREE from 'expo-three';
import * as THREE from 'three'
import {
AmbientLight,
BoxBufferGeometry,
Fog,
Mesh,
MeshStandardMaterial,
PerspectiveCamera,
PointLight,
Scene,
SpotLight,
} from 'three';
export default class ModelScreen extends React.Component {
constructor(props) {
super(props);
let timeout;
this.state= {
loadingCompleted:true,
}
}
componentDidMount(){
THREE.suppressExpoWarnings(true)
clearTimeout(this.timeout)
}
componentWillUnmount(){
clearTimeout(this.timeout)
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={[styles.backButton]} activeOpacity={0.8}
onPress= {()=>{
this.props.setViewModel(false);
}}
>
<Ionicons style={styles.backButtonIcon} name="md-arrow-back"></Ionicons>
</TouchableOpacity>
<GLView
style={styles.viewer}
onContextCreate={async (gl: ExpoWebGLRenderingContext) => {
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
const sceneColor = '#111111';
const scale = PixelRatio.get();
// Create a WebGLRenderer without a DOM element
const renderer = new Renderer({gl,alpha:true});
renderer.capabilities.maxVertexUniforms = 52502;
renderer.setSize(width/scale, height/scale);
renderer.setPixelRatio(scale);
renderer.setClearColor(0x000000,0);
const camera = new PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(0, 2, 5);
camera.lookAt(0,0,0);
const scene = new Scene();
scene.fog = new Fog(sceneColor, 1, 1000);
const ambientLight = new AmbientLight(0x101010);
scene.add(ambientLight);
const pointLight = new PointLight(0xffffff, 2, 1000, 1);
pointLight.position.set(0, 200, 200);
scene.add(pointLight);
const spotLight = new SpotLight(0xffffff, 0.5);
spotLight.position.set(0, 500, 100);
spotLight.lookAt(scene.position);
scene.add(spotLight);
var object = null;
const model = {
'thomas.obj': require('./../assets/models/thomas/thomas.obj'),
'thomas.mtl': require('./../assets/models/thomas/thomas.mtl'),
'thomas.png': require('./../assets/models/thomas/thomas.png'),
};
// Load model!
await ExpoTHREE.loadAsync(
[model['thomas.obj'], model['thomas.mtl']],
null,
name => model[name],
).then((obj)=>{
// // Update size and position
ExpoTHREE.utils.scaleLongestSideToSize(obj, 5);
ExpoTHREE.utils.alignMesh(obj, { y: 1 });
// Smooth mesh
ExpoTHREE.utils.computeMeshNormals(obj.children[0]);
// Add the mesh to the scene
scene.add(obj.children[0]);
}).catch((error)=>{
console.log(error);
});
console.log(scene.children.length)
function update() {
if (scene.children.length == 4)
scene.children[3].rotateY(0.03);
}
// Setup an animation loop
const render = () => {
this.timeout = requestAnimationFrame(render);
update();
renderer.render(scene, camera);
gl.endFrameEXP();
};
render();
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex:1,
justifyContent:"center",
alignItems:"center",
backgroundColor:"#111111",
},
backButton:{
position:'absolute',
top:30,
width:50,
height:50,
alignItems:"center",
justifyContent:"center",
borderRadius:100,
backgroundColor:"rgb(1,175,250)",
left:20,
alignSelf:"flex-start",
zIndex:10,
},
backButtonIcon:{
fontSize:25,
fontWeight:'900',
color:"#111111",
},
viewer:{
width:"80%",
height:"80%",
}
});
can anyone help me please?
Edit
it's working now without material i have changed
await ExpoTHREE.loadAsync([model['thomas.obj'], model['thomas.mtl']],null,name => model[name])
to
await ExpoTHREE.loadAsync(model['thomas.obj'],null,name => model[name])
but it doesn't render if i load the material and don't know what's wrong with it. i tried different objects and cannot render the material. the object disappears

React native svg heart size animation

I am trying to make a pulsing svg heart in ReactNative expo using an SVG image.
The only way I managed to make the heart to resize with an animated value is to change bind it to style: fontSize.
This seems to change size correctly, but the animation is really choppy.
Here is the code:
import React, { Component } from 'react';
import { Animated } from 'react-native';
import { SimpleLineIcons } from '#expo/vector-icons';
const AnimatedIcon = Animated.createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 24;
class GrowingHeart extends Component<any, any> {
size = new Animated.Value(TARGET_FONT_SIZE);
constructor(props) {
super(props);
Animated.sequence([
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
}),
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
})
]).start();
}
render() {
return (
<AnimatedIcon
style={{ fontSize: this.size }}
size={20}
name="heart"
color="red"
/>
);
}
}
I tried also to bind width and height but they are also choppy + they change container size, rather than the icon.
Is there a better way of doing this? Thanks
Seems that Animated api is simply still rubbish (please correct me if I am getting this wrong)
I re-wrote this with reanimated and it works smooth. The code below is not complete but shows how heart is growing with no choppiness, but rather perfectly smooth.
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';
import { SimpleLineIcons } from '#expo/vector-icons';
const {
createAnimatedComponent,
debug,
set,
get,
block,
eq,
Value,
cond,
greaterThan,
startClock,
timing,
Clock,
Code,
clockRunning,
stopClock
} = Animated;
const AnimatedIcon = createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 20;
class GrowingHeart extends Component<any, any> {
size = new Value(TARGET_FONT_SIZE);
clock = new Clock();
updatingValue = new Value(0);
clockState = {
finished: new Value(0),
position: new Value(5),
time: new Value(0),
frameTime: new Value(0)
};
clockConfig = {
duration: new Value(500),
toValue: new Value(GROWN_FONT_SIZE),
easing: Easing.linear
};
constructor(props) {
super(props);
}
render() {
const { color } = this.props;
const { updatingValue, size, clock, clockConfig, clockState } = this;
return (
<>
<Code>
{() =>
block([
cond(
// animation not triggered
eq(0, updatingValue),
[],
[
cond(
clockRunning(clock),
[],
[
set(clockState.finished, 0),
set(clockState.time, 0),
set(clockState.position, TARGET_FONT_SIZE),
set(clockState.frameTime, 0),
set(clockConfig.toValue, GROWN_FONT_SIZE),
startClock(clock)
]
),
cond(
greaterThan(0, updatingValue),
// is decreasing
[debug('going down', updatingValue)],
// is growing
[
timing(clock, clockState, clockConfig),
set(size, clockState.position)
]
),
cond(clockState.finished, [stopClock(clock)])
]
)
])
}
</Code>
<TouchableOpacity
onPress={() => {
this.updatingValue.setValue(1);
}}
>
<AnimatedIcon
style={{ fontSize: this.size }}
name="heart"
color={color}
/>
</TouchableOpacity>
</>
);
}
}

React Native Pan Responder to Animate single dynamic view

I'm creating a series of cubes within a grid and I want to only move the cube that is pressed upon. Currently in my code it is moving all the cubes as one. I tried finding the individual key of the cube to try and target it for animation, but I'm not having any luck so far in figuring this out.
The myPanel component is just to layout the cubes that are defined in the renderCube function. It may be an issue with laying out all of the cubes with Animated.View, even though I added the key, but I can't access or figure out how to specifically animate only the selected cube.
import React, { Component } from 'react';
import { TouchableWithoutFeedback, View, ImageBackground, Dimensions, Animated, PanResponder } from 'react-native';
import myPanel from './myPanel';
const DATA = [
{ id: 1, color: '#ff8080' },
{ id: 2, color: '#80ff80' },
{ id: 3, color: '#ffff80' }
];
const winWidth = Dimensions.get('window').width;
const widthCalc = winWidth/400;
const cubeWidth = widthCalc*74;
const winHeight = Dimensions.get('window').height;
const heightCalc = ((winHeight-winWidth)/2)-5;
const xPush = 5*widthCalc;
const borRadius = 7.4*widthCalc;
let cnt = 0;
let xMove = 0;
let yMove = 0;
let currTarget = '033';
class Board extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY()
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onStartShouldSetPanResponder:(e, gestureState) => true,
onPanResponderGrant: (e, gestureState) => {
console.log(this.currTarget);
},
onPanResponderMove: Animated.event([
null,
{
dx: this.state.pan.x,
dy: this.state.pan.y,
},
]),
onPanResponderRelease: () => {
Animated.spring(
this.state.pan,
{toValue: {x: 0, y: 0}},
).start();
}
});
}
renderCube(item) {
return (
<Animated.View {...this._panResponder.panHandlers}
style={this.state.pan.getLayout()} key={item.id}>
<TouchableWithoutFeedback
onPressIn={()=>{this.currTarget = item.id; console.log('Set '+item.id);}}>
<View
style={{
backgroundColor:myColor,
width:cubeWidth,
height:cubeWidth,
position: 'absolute',
top: yMove,
left: xMove,
bottom: 0,
right: 0,
borderRadius:borRadius
}}
></View>
</TouchableWithoutFeedback>
</Animated.View>
);
}
render() {
return (
<myPanel
data={DATA}
renderCube={this.renderCube.bind(this)}
/>
);
}
}
export default Board;

How to make Animated timing/spring refresh with the new start?

I was trying to include the Circular Progress module to my project. Everything work well except that When the Fill value change from 100 to 0 ,the Animated.spring/ timing function will draw back. Is there any idea to make circular progress bar refresh with the start point when it hit end point?
import React, { PropTypes } from 'react';
import { View, Animated } from 'react-native';
import CircularProgress from './CircularProgress';
const AnimatedProgress = Animated.createAnimatedComponent(CircularProgress);
export default class AnimatedCircularProgress extends React.Component {
constructor(props) {
super(props);
this.state = {
chartFillAnimation: new Animated.Value(props.prefill || 0)
}
}
componentDidMount() {
this.animateFill();
}
componentDidUpdate(prevProps) {
if (prevProps.fill !== this.props.fill) {
this.animateFill();
}
}
animateFill() {
const { tension, friction } = this.props;
Animated.spring(
this.state.chartFillAnimation,
{
toValue: this.props.fill,
tension,
friction
}
).start();
}
performLinearAnimation(toValue, duration) {
Animated.timing(this.state.chartFillAnimation, {
toValue: toValue,
duration: duration
}).start();
}
render() {
const { fill, prefill, ...other } = this.props;
return (
<AnimatedProgress
{...other}
fill={this.state.chartFillAnimation}
/>
)
}
}
AnimatedCircularProgress.propTypes = {
style: View.propTypes.style,
size: PropTypes.number.isRequired,
fill: PropTypes.number,
prefill: PropTypes.number,
width: PropTypes.number.isRequired,
tintColor: PropTypes.oneOf([PropTypes.string, PropTypes.object]),
backgroundColor: PropTypes.oneOf([PropTypes.string, PropTypes.object]),
tension: PropTypes.number,
friction: PropTypes.number
}
AnimatedCircularProgress.defaultProps = {
tension: 7,
friction: 10
};
.start() takes an optional callback which will be called once the animation is finished, so you can recursively call your animation:
animateFill() {
const { tension, friction } = this.props;
this.state.charFillAnimation.setValue(0); //reset to 0
Animated.spring(
this.state.chartFillAnimation,
{
toValue: this.props.fill,
tension,
friction
}
).start(() => this.animateFill());
}

react-native drag and drop multiple items

Im trying to make two circles that can drag and drop with react-native.
I could have created one circle that can drag and drop, but dont know how with two circles individually.
here is the code for one circle that can drag and drop,
constructor(props){
super(props);
this.state = {
pan : new Animated.ValueXY() //Step 1
};
this.panResponder = PanResponder.create({ //Step 2
onStartShouldSetPanResponder : () => true,
onPanResponderMove : Animated.event([null,{ //Step 3
dx : this.state.pan.x,
dy : this.state.pan.y
}]),
onPanResponderRelease : (e, gesture) => {} //Step 4
});
}
and this is for image
renderDraggable(){
return (
<View style={styles.draggableContainer}>
<Animated.View
{...this.panResponder.panHandlers}
style={[this.state.pan.getLayout(), styles.circle]}>
<Text style={styles.text}>Drag me!</Text>
</Animated.View>
</View>
);
}
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
Image, // we want to use an image
PanResponder, // we want to bring in the PanResponder system
Animated // we wil be using animated value
} from 'react-native';
export default class MovingCircle extends React.Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1)
};
}
_handleStartShouldSetPanResponder(e, gestureState) {
return true;
}
_handleMoveShouldSetPanResponder(e, gestureState) {
return true;
}
componentWillMount() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder:
this._handleStartShouldSetPanResponder.bind(this),
onMoveShouldSetPanResponder:
this._handleMoveShouldSetPanResponder.bind(this),
onPanResponderGrant: (e, gestureState) => {
// Set the initial value to the current state
this.state.pan.setOffset({x: this.state.pan.x._value, y: this.state.pan.y._value});
this.state.pan.setValue({x: 30*Math.random(), y: 0});
Animated.spring(
this.state.scale,
{ toValue: 1.1, friction: 1 }
).start();
},
// When we drag/pan the object, set the delate to the states pan position
onPanResponderMove: Animated.event([
null, {dx: this.state.pan.x, dy: this.state.pan.y},
]),
onPanResponderRelease: (e, {vx, vy}) => {
// Flatten the offset to avoid erratic behavior
this.state.pan.flattenOffset();
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 1 }
).start();
}
});
}
render() {
// Destructure the value of pan from the state
let { pan, scale } = this.state;
// Calculate the x and y transform from the pan value
let [translateX, translateY] = [pan.x, pan.y];
let rotate = '0deg';
// Calculate the transform property and set it as a value for our style which we add below to the Animated.View component
let imageStyle = {transform: [{translateX}, {translateY}, {rotate}, {scale}]};
return (
<Animated.View style={[imageStyle, styles.container]} {...this._panResponder.panHandlers} >
<View style={styles.rect}>
<Text style={styles.txt} >tgyyHH</Text>
</View>
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
width:50,
height:50,
position: 'absolute'
},
rect: {
borderRadius:4,
borderWidth: 1,
borderColor: '#fff',
width:50,
height:50,
backgroundColor:'#68a0cf',
},
txt: {
color:'#fff',
textAlign:'center'
}
});
Here is how made items independent of each other. This example is in typescript, but should be clear enough to convert to pure javascript. The main idea here is that each animated item needs its own PanResponderInstance and once you update the items, you need to also refresh the PanResponderInstance
interface State {
model: Array<MyAnimatedItem>,
pans: Array<Animated.ValueXY>,
dropZone1: LayoutRectangle,
dropZone2: LayoutRectangle,
}
public render(): JSX.Element {
const myAnimatedItems = new Array<JSX.Element>()
for (let i = 0; i < this.state.model.length; i++) {
const item = this.state.model[i]
const inst = this.createResponder(this.state.pans[i], item)
myAnimatedItems.push(
<Animated.View
key={'item_' + i}
{...inst.panHandlers}
style={this.state.pans[i].getLayout()}>
<Text>{item.description}</Text>
</Animated.View>
)
}
return (
<View>
<View onLayout={this.setDropZone1} style={styles.dropZone}>
<View style={styles.draggableContainer}>
{myAnimatedItems}
</View>
</View>
<View onLayout={this.setDropZone2} style={styles.dropZone}>
<View style={styles.draggableContainer}>
...
</View>
</View>
</View>
)
}
private setDropZone1 = (event: LayoutChangeEvent): void => {
this.setState({
dropZone1: event.nativeEvent.layout
})
}
private setDropZone2 = (event: LayoutChangeEvent): void => {
this.setState({
dropZone2: event.nativeEvent.layout
})
}
private isDropZone(gesture: PanResponderGestureState, dropZone: LayoutRectangle): boolean {
const toolBarHeight = variables.toolbarHeight + 15 // padding
return gesture.moveY > dropZone.y + toolBarHeight
&& gesture.moveY < dropZone.y + dropZone.height + toolBarHeight
&& gesture.moveX > dropZone.x
&& gesture.moveX < dropZone.x + dropZone.width
}
private createResponder(pan: Animated.ValueXY, item: MyAnimatedItem): PanResponderInstance {
return PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, {
dx: pan.x,
dy: pan.y
}]),
onPanResponderRelease: (_e, gesture: PanResponderGestureState) => {
const model = this.state.model
const pans = this.state.pans
const idx = model.findIndex(x => x.id === item.id)
if (this.isDropZone(gesture, this.state.dropZone1)) {
... // do something with the item if needed
// reset each PanResponderInstance
for (let i = 0; i < model.length; i++) {
pans[i] = new Animated.ValueXY()
}
this.setState({ model: model, pans: pans })
return
}
} else if (this.isDropZone(gesture, this.state.dropZone2)) {
... // do something with the item if needed
// reset each PanResponderInstance
for (let i = 0; i < model.length; i++) {
pans[i] = new Animated.ValueXY()
}
this.setState({ model: model, pans: pans })
return
}
Animated.spring(pan, { toValue: { x: 0, y: 0 } }).start()
this.setState({ scrollEnabled: true })
}
})
}