Reputation: 27507
I have a View that I want to shrink to the bottom right with some margin / padding on the bottom and right sides.
I was able to make it shrink, but it shrinks to the center. The video
element is the one shrinking:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
View,
PanResponder,
Animated,
Dimensions,
} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#000000',
},
overlay: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#0000ff',
opacity: 0.5,
},
video: {
position: 'absolute',
backgroundColor: '#00ff00',
bottom: 0,
right: 0,
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
padding: 10,
}
});
function clamp(value, min, max) {
return min < max
? (value < min ? min : value > max ? max : value)
: (value < max ? max : value > min ? min : value)
}
export default class EdmundMobile extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(1),
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({x: this.state.pan.x._value, y: 0});
this.state.pan.setValue({x: 0, y: 0});
},
onPanResponderMove: (e, gestureState) => {
let width = Dimensions.get("window").width;
let difference = Math.abs((this.state.pan.x._value + width) / width);
if (gestureState.dx < 0) {
this.setState({ scale: new Animated.Value(difference) });
return Animated.event([
null, {dx: this.state.pan.x, dy: 0}
])(e, gestureState);
}
},
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
if (vx >= 0) {
velocity = clamp(vx, 3, 5);
} else if (vx < 0) {
velocity = clamp(vx * -1, 3, 5) * -1;
}
if (Math.abs(this.state.pan.x._value) > 200) {
Animated.spring(this.state.pan, {
toValue: {x: -Dimensions.get("window").width, y: 0},
friction: 4
}).start()
Animated.spring(this.state.scale, {
toValue: 0.2,
friction: 4
}).start()
} else {
Animated.timing(this.state.pan, {
toValue: {x: 0, y: 0},
friction: 4
}).start()
Animated.spring(this.state.scale, {
toValue: 1,
friction: 10
}).start()
}
}
});
}
render() {
let { pan, scale } = this.state;
let translateX = pan.x;
let swipeStyles = {transform: [{translateX}]};
let videoScale = scale
let localVideoStyles = {transform: [{scale: videoScale}]};
return (
<View style={styles.container}>
<Animated.View style={[styles.video, localVideoStyles]}></Animated.View>
<Animated.View style={[styles.overlay, swipeStyles]} {...this._panResponder.panHandlers}>
</Animated.View>
</View>
);
}
}
AppRegistry.registerComponent('EdmundMobile', () => EdmundMobile);
Upvotes: 1
Views: 6425
Reputation: 27507
Ok I figured out one solution. Kinda hands on, but I think it provides all the customizations I need.
So instead of transforming scale, I animate the width, height, bottom, top
styles by attaching them to the state and changing them in response to the pan responder stuff.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
View,
PanResponder,
Animated,
Dimensions,
} from 'react-native';
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#000000',
},
overlay: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#0000ff',
opacity: 0.5,
},
video: {
position: 'absolute',
backgroundColor: '#00ff00',
}
});
function clamp(value, min, max) {
return min < max
? (value < min ? min : value > max ? max : value)
: (value < max ? max : value > min ? min : value)
}
const MIN_VIDEO_WIDTH = 120;
const MIN_VIDEO_HEIGHT = 180;
export default class EdmundMobile extends Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
width: new Animated.Value(Dimensions.get("window").width),
height: new Animated.Value(Dimensions.get("window").height),
bottom: new Animated.Value(0),
right: new Animated.Value(0),
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (e, gestureState) => {
this.state.pan.setOffset({x: this.state.pan.x._value, y: 0});
this.state.pan.setValue({x: 0, y: 0});
},
onPanResponderMove: (e, gestureState) => {
let width = Dimensions.get("window").width;
let difference = Math.abs((this.state.pan.x._value + width) / width);
console.log(difference);
if (gestureState.dx < 0) {
const newVideoHeight = difference * Dimensions.get("window").height;
const newVideoWidth = difference * Dimensions.get("window").width;
if (newVideoWidth > MIN_VIDEO_WIDTH) {
this.setState({
width: new Animated.Value(newVideoWidth),
});
}
if (newVideoHeight > MIN_VIDEO_HEIGHT) {
this.setState({
height: new Animated.Value(newVideoHeight),
});
}
this.setState({
bottom: new Animated.Value((1- difference) * 20),
right: new Animated.Value((1 - difference) * 20),
});
return Animated.event([
null, {dx: this.state.pan.x, dy: 0}
])(e, gestureState);
}
},
onPanResponderRelease: (e, {vx, vy}) => {
this.state.pan.flattenOffset();
if (vx >= 0) {
velocity = clamp(vx, 3, 5);
} else if (vx < 0) {
velocity = clamp(vx * -1, 3, 5) * -1;
}
if (Math.abs(this.state.pan.x._value) > 200) {
Animated.spring(this.state.pan, {
toValue: {x: -Dimensions.get("window").width, y: 0},
friction: 4
}).start()
Animated.spring(this.state.width, {
toValue: MIN_VIDEO_WIDTH,
friction: 4
}).start()
Animated.spring(this.state.height, {
toValue: MIN_VIDEO_HEIGHT,
friction: 4
}).start()
Animated.timing(this.state.bottom, {
toValue: 20,
}).start()
Animated.timing(this.state.right, {
toValue: 20,
}).start()
} else {
Animated.timing(this.state.pan, {
toValue: {x: 0, y: 0},
}).start()
Animated.timing(this.state.width, {
toValue: Dimensions.get("window").width,
friction: 4
}).start()
Animated.timing(this.state.height, {
toValue: Dimensions.get("window").height,
friction: 4
}).start()
Animated.timing(this.state.bottom, {
toValue: 0,
}).start()
Animated.timing(this.state.right, {
toValue: 0,
}).start()
}
}
});
}
render() {
let { pan, width, height, bottom, right } = this.state;
let translateX = pan.x;
let swipeStyles = {transform: [{translateX}]};
let videoStyles = {
width: width,
height: height,
bottom: bottom,
right: right,
};
return (
<View style={styles.container}>
<Animated.View style={[styles.video, videoStyles]}></Animated.View>
<Animated.View style={[styles.overlay, swipeStyles]} {...this._panResponder.panHandlers}>
</Animated.View>
</View>
);
}
}
AppRegistry.registerComponent('EdmundMobile', () => EdmundMobile);
Upvotes: 1