I have a file called imagePicker.js that returns a view displaying images from the phone's CameraRoll. After I have clicked an image I want to be able to click the button that says edit and navigate to the photo editing screen (photoScreen.js) and edit the photo I have selected. However, while I am successfully navigating to the photo editing screen I can't figure out how to pass the selected image's uri into the Image element on the photo editing screen.
Routes.js (navigation)
class Routes {
static register(name, handler) {
if (this.handlers == null) this.handlers = {}
this.handlers[name] = handler
}
static get(name, params) {
if (this.handlers[name] == null) throw new Error('unknown route')
return this.handlers[name](params)
}
static imagePicker() {
return {
component: require('./imagePicker'),
}
}
static photoScreen(image) {
return {
component: require('./photoScreen'),
passProps: {image: image}
}
}
}
module.exports = Routes
imagePicker.js (photo gallery and selection screen)
const React = require('react-native');
var Routes = require('./Routes')
const {
StyleSheet,
Text,
View,
ScrollView,
Image,
CameraRoll,
TouchableHighlight,
NativeModules,
} = React;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
imageGrid: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center'
},
image: {
width: 100,
height: 100,
margin: 10,
}
});
const imagePicker = React.createClass({
getInitialState() {
return {
images: [],
selected: '',
};
},
componentDidMount() {
const fetchParams = {
first: 25,
};
CameraRoll.getPhotos(fetchParams, this.storeImages, this.logImageError);
},
storeImages(data) {
const assets = data.edges;
const images = assets.map((asset) => asset.node.image);
this.setState({
images: images,
});
},
logImageError(err) {
console.log(err);
},
gotoPhoto(image) {
this.props.route.push(Routes.photoScreen(image))
},
selectImage(uri) {
NativeModules.ReadImageData.readImage(uri, (image) => {
this.setState({
selected: image,
});
console.log(image);
});
},
render() {
return (
<ScrollView style={styles.container}>
<View style={styles.imageGrid}>
{ this.state.images.map((image) => {
return (
<TouchableHighlight onPress={this.selectImage.bind(null, image.uri)}>
<Image style={styles.image} source={{ uri: image.uri }} />
</TouchableHighlight>
);
})
}
</View>
<TouchableHighlight onPress={this.gotoPhoto} ><Text>edit</Text></TouchableHighlight>
</ScrollView>
);
}
});
module.exports = imagePicker
photoScreen.js (photo editing screen)
var React = require('react-native');
var GL = require('gl-react-native');
var {
AppRegistry,
StyleSheet,
Text,
Image,
View,
ScrollView,
SliderIOS
} = React;
var photoScreen = React.createClass({
getInitialState: function() {
return {
width:0,
height: 0,
saturation: 1,
brightness: 1,
contrast: 1,
hue: 0,
sepia: 0,
gray: 0,
mixFactor: 0
};
},
renderWithDimensions: function(layout) {
var {
width,
height
} = layout.nativeEvent.layout;
this.setState({
width,
height
})
},
getImage: function() {
var {image} = this.props
return (
<Instagram
brightness={this.state.brightness}
saturation={this.state.saturation}
contrast={this.state.contrast}
hue={this.state.hue}
gray={this.state.gray}
sepia={this.state.sepia}
mixFactor={this.state.mixFactor}
width={this.state.width}
height={this.state.height}
>
<Image
source={{uri: image.uri}}
style={styles.cover}
resizeMode="cover"
/>
</Instagram>
)
},
render: function() {
return (
<View style={styles.container}>
<View style={styles.container} onLayout={this.renderWithDimensions}>
{ this.state.width ? this.getImage() : null}
</View>
<ScrollView style={styles.container}>
<View>
<Text>Blend Factor: {this.state.mixFactor}</Text>
<SliderIOS
value={this.state.mixFactor}
minimumValue={0}
maximumValue={2}
onValueChange={(mixFactor) => this.setState({mixFactor})}
/>
</View>
<View>
<Text>Brightness: {this.state.brightness}</Text>
<SliderIOS
value={this.state.brightness}
minimumValue={0}
maximumValue={3}
onValueChange={(brightness) => this.setState({brightness})}
/>
</View>
<View>
<Text>Saturation: {this.state.saturation}</Text>
<SliderIOS
value={this.state.saturation}
minimumValue={0}
maximumValue={3}
onValueChange={(saturation) => this.setState({saturation})}
/>
</View>
<View>
<Text>Contrast: {this.state.contrast}</Text>
<SliderIOS
value={this.state.contrast}
minimumValue={0}
maximumValue={3}
onValueChange={(contrast) => this.setState({contrast})}
/>
</View>
<View>
<Text>Sepia: {this.state.sepia}</Text>
<SliderIOS
value={this.state.sepia}
minimumValue={0}
maximumValue={1}
onValueChange={(sepia) => this.setState({sepia})}
/>
</View>
<View>
<Text>Grayscale: {this.state.gray}</Text>
<SliderIOS
value={this.state.gray}
minimumValue={0}
maximumValue={1}
onValueChange={(gray) => this.setState({gray})}
/>
</View>
<View>
<Text>Hue: {this.state.hue}</Text>
<SliderIOS
value={this.state.hue}
minimumValue={0}
maximumValue={10}
onValueChange={(hue) => this.setState({hue})}
/>
</View>
</ScrollView>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1
},
cover: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0
}
});
const shaders = GL.Shaders.create({
instagram: {
frag: `
precision highp float;
varying vec2 uv;
uniform sampler2D tex;
// uniform float hue;
uniform float saturation;
uniform float brightness;
uniform float contrast;
uniform float hue;
uniform float gray;
uniform float sepia;
uniform float mixFactor;
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
const mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);
const mat3 yiq2rgb = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.1070, 1.7046);
const vec3 SEPIA = vec3(1.2, 1.0, 0.8);
vec3 BrightnessContrastSaturation(vec3 color, float brt, float con, float sat)
{
vec3 black = vec3(0., 0., 0.);
vec3 middle = vec3(0.5, 0.5, 0.5);
float luminance = dot(color, W);
vec3 gray = vec3(luminance, luminance, luminance);
vec3 brtColor = mix(black, color, brt);
vec3 conColor = mix(middle, brtColor, con);
vec3 satColor = mix(gray, conColor, sat);
return satColor;
}
vec3 multiplyBlender(vec3 Color, vec3 filter){
vec3 filter_result;
float luminance = dot(filter, W);
if(luminance < 0.5)
filter_result = 2. * filter * Color;
else
filter_result = Color;
return filter_result;
}
vec3 ovelayBlender(vec3 Color, vec3 filter){
vec3 filter_result;
float luminance = dot(filter, W);
if(luminance < 0.5)
filter_result = 2. * filter * Color;
else
filter_result = 1. - (1. - (2. *(filter - 0.5)))*(1. - Color);
return filter_result;
}
vec3 applyHue(vec3 Color, float h) {
vec3 yColor = rgb2yiq * Color;
float originalHue = atan(yColor.b, yColor.g);
float finalHue = originalHue + (h);
float chroma = sqrt(yColor.b*yColor.b+yColor.g*yColor.g);
vec3 yFinalColor = vec3(yColor.r, chroma * cos(finalHue), chroma * sin(finalHue));
return vec3(yiq2rgb*yFinalColor);
}
vec3 applyGray(vec3 Color, float g) {
float gray = dot(Color, vec3(0.299, 0.587, 0.114));
return mix(Color, vec3(gray, gray, gray), g);
}
vec3 applySepia(vec3 Color, float s) {
float gray = dot(Color, vec3(0.299, 0.587, 0.114));
return mix(Color, vec3(gray) * SEPIA, s);
}
void main() {
vec2 st = uv.st;
vec3 irgb = texture2D(tex, st).rgb;
vec3 filter = texture2D(tex, st).rgb;
vec3 bcs_result = BrightnessContrastSaturation(irgb, brightness, contrast, saturation);
vec3 hue_result = applyHue(bcs_result, hue);
vec3 sepia_result = applySepia(hue_result, sepia);
vec3 gray_result = applyGray(sepia_result, gray);
vec3 after_filter = mix(gray_result, multiplyBlender(gray_result, filter), mixFactor);
gl_FragColor = vec4(after_filter, 1.);
}
`
}
});
var Instagram = GL.createComponent(
({ brightness, saturation, contrast, hue, gray, sepia, mixFactor, children, ...rest }) =>
<GL.View
{...rest}
shader={shaders.instagram}
uniforms={{ brightness, saturation, contrast, hue, gray, sepia, mixFactor }}>
<GL.Uniform name="tex">{children}</GL.Uniform>
</GL.View>
, { displayName: "Instagram" });
module.exports = photoScreen
Related
I recently have done an exploration with raycaster using React Three Fiber. It works fine in the result, but it comes with an error that feels kind of annoying. I want to find out the where problem is, and finally clear the error in case it's gonna do bad things when code gets more complex in the future
The error says like so:
three.module.js:17297 Uncaught TypeError: Failed to execute 'uniform3fv' on 'WebGL2RenderingContext': Overload resolution failed.
The snapshot of the error:
----------------- live code --------------------------------
Here is the link for the code playground:
https://codesandbox.io/s/react-three-fiber-forked-h02gdy?file=/src/index.js
----------------- live code --------------------------------
Here is the code:
App.jsx
import React from 'react';
import './App.css';
import Scene from './Scene';
function App() {
return (
<div style={{ height: '100vh', width: '100vw' }}>
<Scene />
</div>
);
}
export default App;
Scene.jsx
import React, { Suspense } from 'react';
import CameraControls from './CameraControls.jsx';
import { Canvas } from '#react-three/fiber';
import { PerspectiveCamera } from '#react-three/drei';
import Plane from './Plane.jsx';
const Scene = () => {
return (
<Canvas
gl={{ antialias: true, alpha: true }}
style={{
zIndex: '10',
}}
>
<ambientLight intensity={0.5} />
<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
<pointLight position={[-10, -10, -10]} />
<PerspectiveCamera
fov={75}
aspect={window.innerWidth / window.innerHeight}
near={1}
far={1000}
position={[0, 0, 0]}
>
<CameraControls />
<Suspense fallback={null}>
<Plane />
</Suspense>
</PerspectiveCamera>
</Canvas>
);
};
export default Scene;
Plane.jsx
import React, { useRef, useState } from 'react';
import * as THREE from 'three';
import { useFrame, useLoader } from '#react-three/fiber';
import { vertexShader, fragmentShader } from './shaderPlane';
import bugatti from './images/bugatti.jpg';
const Plane = (props) => {
const planeRef = useRef();
const [mouse, setMouse] = useState({});
// -------------------------- raycaster ----------------------------- //
useFrame((state) => {
state.raycaster.setFromCamera(state.mouse, state.camera);
const intersects = state.raycaster.intersectObjects(state.scene.children);
if (intersects.length > 0) {
setMouse(intersects[0].point);
}
});
// -------------------------- raycaster ----------------------------- //
return (
<mesh position={[0, 0, 0]} {...props} ref={planeRef}>
<planeGeometry attach="geometry" args={[5, 3, 100, 100]} />
<MaterialShaderBox mouse={mouse} />
</mesh>
);
};
const MaterialShaderBox = ({ mouse }) => {
const shaderRef = useRef();
const [car] = useLoader(THREE.TextureLoader, [bugatti]);
const data = React.useMemo(
() => ({
uniforms: {
uTexture: { value: car },
uTime: { value: 0 },
mouse: { value: 0 },
},
fragmentShader,
vertexShader,
side: THREE.DoubleSide,
}),
[car]
);
useFrame(() => {
if (shaderRef.current) {
shaderRef.current.uniforms.uTime.value += 0.05;
shaderRef.current.uniforms.mouse.value = mouse;
}
});
return <shaderMaterial attach="material" ref={shaderRef} {...data} />;
};
export default Plane;
shaderPlane.js
export const vertexShader = `
precision mediump float;
varying vec2 vUv;
varying vec3 vPosition;
uniform float uTime;
float PI = 3.141592653589793238;
void main () {
vec3 pos = position;
vPosition = position;
vUv = uv;
pos.y += sin(PI*uv.x)*0.1;
pos.z += sin(PI*uv.x)*0.2;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
export const fragmentShader = `
precision mediump float;
varying vec2 vUv;
uniform float uTime;
uniform sampler2D uTexture;
varying vec3 vPosition;
uniform vec3 mouse;
void main() {
vec4 t = texture2D(uTexture, vUv).rgba;
vec3 pointer = mouse;
// pointer.x -= 3.;
float dist = length(vPosition - pointer);
// t.r /= dist;
// t.g /= dist;
// t.b /= dist;
t.rgb *= dist;
gl_FragColor = t;
}
`;
Any idea how to fix it ? Appreciate any answer
Here is a comparison between the online example and my output for a bloom filter.
Online
Mine
Mine does not produce the desired glowing effect shown in the first image, although the example is copied exactly. Could someone point me in the right direction? Thanks
Online Example Code
https://codesandbox.io/s/7mfqw?file=/src/index.js
My Code
import * as THREE from "three";
import React, { useRef, useState, useMemo, useEffect } from "react";
import { Canvas, extend, useThree, useFrame } from "#react-three/fiber";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass";
import styles from "./MyCanvas.module.scss";
extend({ EffectComposer, RenderPass, UnrealBloomPass });
function Sphere({ geometry, x, y, z, s }) {
const ref = useRef();
useFrame((state) => {
ref.current.position.x =
x + Math.sin((state.clock.getElapsedTime() * s) / 2);
ref.current.position.y =
y + Math.sin((state.clock.getElapsedTime() * s) / 2);
ref.current.position.z =
z + Math.sin((state.clock.getElapsedTime() * s) / 2);
});
return (
<mesh ref={ref} position={[x, y, z]} scale={[s, s, s]} geometry={geometry}>
<meshStandardMaterial color="hotpink" roughness={1} />
</mesh>
);
}
function RandomSpheres() {
const [geometry] = useState(() => new THREE.SphereGeometry(1, 32, 32), []);
const data = useMemo(() => {
return new Array(15).fill().map((_, i) => ({
x: Math.random() * 100 - 50,
y: Math.random() * 100 - 50,
z: Math.random() * 100 - 50,
s: Math.random() + 10,
}));
}, []);
return data.map((props, i) => (
<Sphere key={i} {...props} geometry={geometry} />
));
}
function Bloom({ children }) {
const { gl, camera, size } = useThree();
const [scene, setScene] = useState();
const composer = useRef();
useEffect(
() => void scene && composer.current.setSize(size.width, size.height),
[size]
);
useFrame(() => scene && composer.current.render(), 1);
return (
<>
<scene ref={setScene}>{children}</scene>
<effectComposer ref={composer} args={[gl]}>
<renderPass attachArray="passes" scene={scene} camera={camera} />
<unrealBloomPass attachArray="passes" args={[undefined, 1.5, 1, 0]} />
</effectComposer>
</>
);
}
function Main({ children }) {
const scene = useRef();
const { gl, camera } = useThree();
useFrame(() => {
gl.autoClear = false;
gl.clearDepth();
gl.render(scene.current, camera);
}, 2);
return <scene ref={scene}>{children}</scene>;
}
function MyCanvas(props) {
return (
<div className={styles.Canvas}>
<Canvas linear camera={{ position: [0, 0, 120] }}>
<Main>
<pointLight />
<ambientLight />
<RandomSpheres />
</Main>
<Bloom>
<ambientLight />
<RandomSpheres />
</Bloom>
</Canvas>
</div>
);
}
export default MyCanvas;
These libraries worked for me. I was importing older libraries from example folders and I can't say for certain, i'd say that would be the issue.
import { Effects } from '#react-three/drei'
import { UnrealBloomPass } from 'three-stdlib'
Working example shared on the three.js forums.
https://codesandbox.io/s/bloom-hdr-workflow-gnn4yt
I am trying to build a "3D tank game", and I used most of the code from this Original Project. I cannot find any resources with good examples that demonstrates "useRaycastVehicle" with "3rd person camera". My solution is simple, it checks if z dimension is negative, if so, on next frame it flips the POV, so the vehicle does not face forward towards the camera. I think, there are better solutions to solve this. Because I've seen on Youtube solving this but reimplementing his code caused camera being stuck to ground, not following etc.
import { useFrame, useThree } from "#react-three/fiber";
import { useRaycastVehicle } from "#react-three/cannon";
import { useEffect, useRef } from "react";
import { useKeyboardControls } from "#react-three/drei";
import Beetle from "./Beetle";
import Wheel from "./Wheel";
import { Vector3 } from "three";
export default function Tank({
radius = 0.7,
width = 1.2,
height = -0.04,
front = 1.3,
back = -1.15,
steer = 0.75,
force = 2000,
maxBrake = 1e5,
...props
}) {
const {
forward,
backward,
leftward,
rightward,
// shoot: jump,
} = useKeyboardControls((state) => state);
const chassis = useRef();
const wheel1 = useRef();
const wheel2 = useRef();
const wheel3 = useRef();
const wheel4 = useRef();
const wheelInfo = {
radius,
directionLocal: [0, -1, 0],
suspensionStiffness: 30,
suspensionRestLength: 0.3,
maxSuspensionForce: 1e4,
maxSuspensionTravel: 0.3,
dampingRelaxation: 10,
dampingCompression: 4.4,
axleLocal: [-1, 0, 0],
chassisConnectionPointLocal: [1, 0, 1],
useCustomSlidingRotationalSpeed: true,
customSlidingRotationalSpeed: -30,
frictionSlip: 2,
};
const wheelInfo1 = {
...wheelInfo,
isFrontWheel: true,
chassisConnectionPointLocal: [-width / 2, height, front],
};
const wheelInfo2 = {
...wheelInfo,
isFrontWheel: true,
chassisConnectionPointLocal: [width / 2, height, front],
};
const wheelInfo3 = {
...wheelInfo,
isFrontWheel: false,
chassisConnectionPointLocal: [-width / 2, height, back],
};
const wheelInfo4 = {
...wheelInfo,
isFrontWheel: false,
chassisConnectionPointLocal: [width / 2, height, back],
};
const [myTank, api] = useRaycastVehicle(() => ({
// #ts-ignore
chassisBody: chassis,
// #ts-ignore
wheels: [wheel1, wheel2, wheel3, wheel4],
//#ts-ignore
wheelInfos: [wheelInfo1, wheelInfo2, wheelInfo3, wheelInfo4],
indexForwardAxis: 2,
indexRightAxis: 0,
indexUpAxis: 1,
}));
const { camera, scene } = useThree();
const pos = useRef([0, 0, 0]);
const rot = useRef([0, 0, 0]);
useEffect(() => {
if (chassis.current) {
//#ts-ignore
chassis.current.api.rotation.subscribe((r) => (rot.current = r));
//#ts-ignore
chassis.current.api.position.subscribe((p) => (pos.current = p));
}
}, [chassis.current]);
useFrame(() => {
for (let e = 2; e < 4; e++)
api.applyEngineForce(
forward || backward
? force * (forward && !backward ? -1 : 1)
: 0,
2
);
for (let s = 0; s < 2; s++)
api.setSteeringValue(
leftward || rightward
? steer * (leftward && !rightward ? 1 : -1)
: 0,
s
);
const cameraOffset = new Vector3(0, 4, -5);
var zDimensionNegative = pos.current[2] > -5 ? 0 : 10;
camera.position
.copy(
new Vector3(
pos.current[0],
pos.current[1],
pos.current[2] + zDimensionNegative
)
)
.add(cameraOffset);
});
return (
// #ts-ignore
<group ref={myTank} position={[0, -0.4, 0]}>
<Beetle
ref={chassis}
//#ts-ignore
rotation={props.rotation}
position={props.position}
angularVelocity={props.angularVelocity}
/>
<Wheel
ref={wheel1}
//#ts-ignore
radius={radius}
leftSide
/>
<Wheel
ref={wheel2}
//#ts-ignore
radius={radius}
/>
<Wheel
ref={wheel3}
//#ts-ignore
radius={radius}
leftSide
/>
<Wheel
ref={wheel4}
//#ts-ignore
radius={radius}
/>
</group>
);
}
Honestly, it was difficult to work with React and ThreeJS. No documentations on usage, no good examples, felt like react fiber was meant for small 3D animation.
But if someone really needs to solve this. Here how I managed to do it:
const MyCamera = () => (
<PerspectiveCamera
fov={75}
rotation={[0, Math.PI, 0]}
makeDefault={true}
position={[0, 6, -11]}
>
<PointerLockControls
addEventListener={undefined}
hasEventListener={undefined}
removeEventListener={undefined}
dispatchEvent={undefined}
/>
</PerspectiveCamera>
);
const Beetle = ({ children }) => {
return (
<mesh>
{children}
<group dispose={null} scale={0.225}>
{/**
* Car's original meshes and stuff!
*/}
</group>
</mesh>
);
};
export default function Tank() {
return (
// #ts-ignore
<group ref={myTank} position={[0, -0.4, 0]}>
<Beetle
ref={chassis}
//#ts-ignore
rotation={props.rotation}
position={props.position}
angularVelocity={props.angularVelocity}
>
<MyCamera />
</Beetle>
<Wheel
ref={wheel1}
//#ts-ignore
radius={radius}
leftSide
/>
<Wheel
ref={wheel2}
//#ts-ignore
radius={radius}
/>
<Wheel
ref={wheel3}
//#ts-ignore
radius={radius}
leftSide
/>
<Wheel
ref={wheel4}
//#ts-ignore
radius={radius}
/>
</group>
);
}
Basically, what it's doing is just making the camera the child of the Beetle mesh, therefore, the camera inherits positions and vectors of its parent. I don't know how to get the object's world direction or vectors or position. So, making the camera child of an object is the simplest way to inherit its position and its direction!
I have created a simple PanGestureHandler and want it to start from previous position when the gesture begins. I am setting the translationY value to the offsetY value when the gesture ends, which works perfectly and when the gesture begins I'm setting the sum of offsetY(which is the previous translationY) and the translationY to translationY, which on debugging shows the correct translation value. But that doesn't reflect on the View. Since I'm new to reanimated I don't know why that happens. I also couldn't find much resources on the implementation of gesture handlers using functional components.
Any ideas on how to fix this?
My Code:
import React from 'react'
import { Dimensions, Text } from 'react-native'
import { PanGestureHandler, State } from 'react-native-gesture-handler'
import Animated, { add, block, cond, debug, eq, event, Extrapolate, interpolate, set, useCode, useValue } from 'react-native-reanimated';
const {height,width}=Dimensions.get("window")
export default function Pan() {
const translationY = useValue(0)
const offsetY = useValue(0)
const gestureState = useValue(State.UNDETERMINED)
const onGestureEvent = event([{
nativeEvent: {
translationY,
state: gestureState
},
}], { useNativeDriver: true });
useCode(() => block([
cond(eq(gestureState, State.BEGAN), [set(translationY, add(translationY, offsetY)),debug('offsetY', translationY)]),
cond(eq(gestureState, State.END), [set(offsetY, translationY), debug('translateY', offsetY)])
]), [])
const translateY = translationY
return (
<PanGestureHandler {...{onGestureEvent}} onHandlerStateChange={onGestureEvent}>
<Animated.View style={{ height: height * 45 / 100, backgroundColor:'red', width: width, transform: [{ translateY }] }}>
<Text>PanGesture</Text>
</Animated.View>
</PanGestureHandler>
)
}
import React from "react";
import {Dimensions, Text, View} from "react-native";
import {PanGestureHandler} from "react-native-gesture-handler";
import Animated, {
Extrapolate,
useSharedValue,
useAnimatedGestureHandler,
interpolate,
useAnimatedStyle,
} from "react-native-reanimated";
const {height, width} = Dimensions.get("window");
const Test: React.FC = () => {
const translationY = useSharedValue(0);
const onGestureEvent = useAnimatedGestureHandler(
{
onStart: (_, ctx) => {
ctx.y = translationY.value;
},
onActive: (event, ctx) => {
translationY.value = event.translationY + ctx.y;
},
},
[translationY.value],
);
const animatedStyles = useAnimatedStyle(() => {
const translateY = interpolate(
translationY.value,
[0, height - (height * 45) / 100],
[0, height - (height * 45) / 100],
Extrapolate.CLAMP,
);
return {
height: (height * 45) / 100,
backgroundColor: "red",
width,
transform: [{translateY}],
};
}, [translationY.value]);
return (
<View style={{height, width, backgroundColor: "yellow"}}>
<PanGestureHandler {...{onGestureEvent}}>
<Animated.View style={animatedStyles}>
<Text>PanGesture</Text>
</Animated.View>
</PanGestureHandler>
</View>
);
};
export default Test;
I am trying to get the x, y co-ordinates of the circle relative to it's parent using the pan responding in real time as I drag it around the screen.
according to the docs (https://facebook.github.io/react-native/docs/panresponder) locationY should be The Y position of the touch, relative to the element, however, it doesn't seem to be correct. Is there something I am missing?
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Obstacle from './Obstacle';
import {
StyleSheet,
Text,
View,
Dimensions,
TouchableOpacity,
PanResponder,
Animated
} from 'react-native';
import { Svg } from 'expo';
const { Circle, Rect } = Svg;
const NUM_OBSTACLES = 1;
const CIRCLE_RADIUS = 40;
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
class Canvas extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
mousePosition: { x: 0, y: 0 }
};
}
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: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
this.state.pan.setOffset(this.state.pan.__getValue());
this.state.pan.setValue({ x: 0, y: 0 });
this.locationPageOffsetX =
evt.nativeEvent.pageX - evt.nativeEvent.locationX;
this.locationPageOffsetY =
evt.nativeEvent.pageY - evt.nativeEvent.locationY;
},
onPanResponderMove: Animated.event(
[null, { dx: this.state.pan.x, dy: this.state.pan.y }],
{
listener: (evt, gestureState) => {
console.log(
`locationX : ${evt.nativeEvent.locationX} locationY : ${
evt.nativeEvent.locationY
}`
);
}
}
)
});
}
render() {
const panStyle = {
transform: this.state.pan.getTranslateTransform()
};
// );
return (
<>
<View
style={{
borderWidth: 1,
height: '80%',
width: '100%',
backgroundColor: 'lightgrey'
}}
>
<Animated.View
{...this.panResponder.panHandlers}
style={[panStyle, styles.circle]}
/>
</View>
</>
);
}
}
const styles = StyleSheet.create({
circle: {
position: 'absolute',
backgroundColor: 'skyblue',
width: CIRCLE_RADIUS * 2,
height: CIRCLE_RADIUS * 2,
borderRadius: CIRCLE_RADIUS,
zIndex: 1
}
});
export default Canvas;
I seem to get similar values no matter where I drag the ball, I would expect these values to be closer to zero as the ball is in the top left corner:
locationX : 48 locationY : 48
locationX : 48 locationY : 47.5
locationX : 47 locationY : 48
locationX : 44 locationY : 46.5