Skip to content

Instantly share code, notes, and snippets.

@jfaverie
Last active February 7, 2023 05:58
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jfaverie/d98d8f60a510e3b68abeffb43a77d47a to your computer and use it in GitHub Desktop.
Save jfaverie/d98d8f60a510e3b68abeffb43a77d47a to your computer and use it in GitHub Desktop.
import React from "react";
import {
LayoutChangeEvent,
PanResponder,
PanResponderGestureState
} from "react-native";
import styled from "styled-components";
type StateType = {
barHeight: number | null,
deltaValue: number,
value: number
};
const initialValue = 0;
const min = 0;
const max = 100;
const CIRCLE_DIAMETER = 50;
export default class Slider extends React.Component<{}, StateType> {
state = {
barHeight: null,
deltaValue: 0,
value: initialValue
};
panResponder = PanResponder.create({
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderMove: (_, gestureState) => this.onMove(gestureState),
onPanResponderRelease: () => this.onEndMove(),
onPanResponderTerminate: () => {}
});
onMove(gestureState: PanResponderGestureState) {
const { barHeight } = this.state;
const newDeltaValue = this.getValueFromBottomOffset(
-gestureState.dy,
barHeight,
min,
max
);
this.setState({
deltaValue: newDeltaValue
});
}
onEndMove() {
const { value, deltaValue } = this.state;
this.setState({ value: value + deltaValue, deltaValue: 0 });
}
onBarLayout = (event: LayoutChangeEvent) => {
const { height: barHeight } = event.nativeEvent.layout;
this.setState({ barHeight });
};
capValueWithinRange = (value: number, range: number[]) => {
if (value < range[0]) return range[0];
if (value > range[1]) return range[1];
return value;
};
getValueFromBottomOffset = (
offset: number,
barHeight: number | null,
rangeMin: number,
rangeMax: number
) => {
if (barHeight === null) return 0;
return ((rangeMax - rangeMin) * offset) / barHeight;
};
getBottomOffsetFromValue = (
value: number,
rangeMin: number,
rangeMax: number,
barHeight: number | null
) => {
if (barHeight === null) return 0;
const valueOffset = value - rangeMin;
const totalRange = rangeMax - rangeMin;
const percentage = valueOffset / totalRange;
return barHeight * percentage;
};
render() {
const { value, deltaValue, barHeight } = this.state;
const cappedValue = this.capValueWithinRange(value + deltaValue, [
min,
max
]);
const bottomOffset = this.getBottomOffsetFromValue(
cappedValue,
min,
max,
barHeight
);
return (
<PageContainer>
<Value>{Math.floor(cappedValue)}</Value>
<Container>
<BarContainer>
<Bar onLayout={this.onBarLayout} />
<Circle
bottomOffset={bottomOffset}
{...this.panResponder.panHandlers}
/>
</BarContainer>
</Container>
</PageContainer>
);
}
}
const PageContainer = styled.View`
background-color: black;
flex-grow: 1;
align-self: stretch;
align-items: center;
padding-vertical: 20;
`;
const Container = styled.View`
flex-grow: 1;
align-self: stretch;
justify-content: center;
flex-direction: row;
`;
const Value = styled.Text`
color: white;
`;
const BarContainer = styled.View`
width: ${CIRCLE_DIAMETER};
align-items: center;
padding-vertical: ${CIRCLE_DIAMETER / 2};
margin-horizontal: 20;
`;
const Bar = styled.View`
width: 2;
background-color: white;
flex-grow: 1;
`;
const Circle = styled.View`
border-radius: ${CIRCLE_DIAMETER / 2};
width: ${CIRCLE_DIAMETER};
height: ${CIRCLE_DIAMETER};
background-color: white;
position: absolute;
bottom: ${props => props.bottomOffset};
`;
@yjb94
Copy link

yjb94 commented Feb 20, 2019

for the one's who wants this code to run with Modal component
use
onStartShouldSetPanResponder: () => true,
onPanResponderEnd:() => true
when creating PanResponder

@pragneshpj
Copy link

model component is not working in ios release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment