Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix double padding on camera when setting bounds #3354

Merged
merged 10 commits into from
Feb 13, 2024
289 changes: 165 additions & 124 deletions example/src/examples/V10/CameraAnimation.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Divider, Text } from '@rneui/base';
import { CheckBox, Divider, Slider, Text } from '@rneui/base';
import {
Camera,
CameraAnimationMode,
CameraBounds,
CameraPadding,
CircleLayer,
Logger,
MapView,
Expand Down Expand Up @@ -36,80 +35,48 @@ const initialCoordinate: Coordinate = {
longitude: -73.984638,
};

const zeroPadding: CameraPadding = {
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 0,
paddingRight: 0,
};
const evenPadding: CameraPadding = {
paddingTop: 40,
paddingBottom: 40,
paddingLeft: 40,
paddingRight: 40,
};
const minZoomLevel = 8;
const maxZoomLevel = 16;

const randPadding = (): CameraPadding => {
const randNum = () => {
const items = [0, 150, 300];
return items[Math.floor(Math.random() * items.length)];
};

return {
paddingTop: randNum(),
paddingBottom: randNum(),
paddingLeft: randNum(),
paddingRight: randNum(),
};
};

const toPosition = (coordinate: Coordinate): Position => {
return [coordinate.longitude, coordinate.latitude];
};

const rand = () => Math.random() * 0.008;

const CameraAnimation = () => {
const [animationMode, setAnimationMode] =
useState<CameraAnimationMode>('moveTo');
const [easing, setEasing] = useState<CameraAnimationMode>('easeTo');
const [coordinates, setCoordinates] = useState<Coordinate[]>([
initialCoordinate,
]);
const [padding, setPadding] = useState<CameraPadding>(zeroPadding);

const paddingDisplay = useMemo(() => {
return `L ${padding.paddingLeft} | R ${padding.paddingRight} | T ${padding.paddingTop} | B ${padding.paddingBottom}`;
}, [padding]);
const [paddingLeft, setPaddingLeft] = useState(0);
const [paddingRight, setPaddingRight] = useState(0);
const [paddingTop, setPaddingTop] = useState(0);
const [paddingBottom, setPaddingBottom] = useState(0);
const [minZoom, setMinZoom] = useState<number | undefined>(undefined);
const [maxZoom, setMaxZoom] = useState<number | undefined>(undefined);

const move = useCallback(
(_animationMode: CameraAnimationMode, shouldCreateMultiple: boolean) => {
setAnimationMode(_animationMode);

if (shouldCreateMultiple) {
const _centerCoordinate = {
latitude: initialCoordinate.latitude + Math.random() * 0.2,
longitude: initialCoordinate.longitude + Math.random() * 0.2,
};
const _coordinates = Array(10)
.fill(0)
.map((_) => {
return {
latitude: _centerCoordinate.latitude + Math.random() * 0.2,
longitude: _centerCoordinate.longitude + Math.random() * 0.2,
};
});
setCoordinates(_coordinates);
} else {
setCoordinates([
{
latitude: initialCoordinate.latitude + Math.random() * 0.2,
longitude: initialCoordinate.longitude + Math.random() * 0.2,
},
]);
}
},
[],
);
const move = useCallback((kind: 'center' | 'bounds') => {
if (kind === 'bounds') {
const _centerCoordinate = {
latitude: initialCoordinate.latitude + rand(),
longitude: initialCoordinate.longitude + rand(),
};
const _coordinates = Array(10)
.fill(0)
.map((_) => {
return {
latitude: _centerCoordinate.latitude + rand(),
longitude: _centerCoordinate.longitude + rand(),
};
});
setCoordinates(_coordinates);
} else if (kind === 'center') {
setCoordinates([
{
latitude: initialCoordinate.latitude + rand(),
longitude: initialCoordinate.longitude + rand(),
},
]);
}
}, []);

const features = useMemo((): Feature<Point>[] => {
return coordinates.map((p) => {
Expand Down Expand Up @@ -152,31 +119,118 @@ const CameraAnimation = () => {
}
}, [coordinates]);

const locationDisplay = useMemo(() => {
if (coordinates.length > 1) {
const ne = centerOrBounds.bounds?.ne.map((n) => n.toFixed(3));
const sw = centerOrBounds.bounds?.sw.map((n) => n.toFixed(3));
return `ne ${ne} | sw ${sw}`;
} else if (coordinates.length === 1) {
const lon = coordinates[0].longitude.toFixed(4);
const lat = coordinates[0].latitude.toFixed(4);
return `lon ${lon} | lat ${lat}`;
} else {
throw new Error('invalid location passed');
}
}, [coordinates, centerOrBounds]);
const easingCheckBox = useCallback(
(value: CameraAnimationMode, label: string) => {
const isChecked = value === easing;
return (
<View
style={{
flex: 0,
flexDirection: 'row',
alignItems: 'center',
}}
>
<CheckBox
checked={isChecked}
center={true}
onIconPress={() => setEasing(value)}
containerStyle={{
backgroundColor: 'transparent',
marginRight: -4,
}}
/>
<Text
style={{
flex: 0,
color: isChecked ? colors.primary.blue : undefined,
}}
>
{label}
</Text>
</View>
);
},
[easing],
);

const paddingCounter = useCallback(
(value: number, setValue: (value: number) => void, label: string) => {
return (
<View style={{ flex: 1, paddingHorizontal: 10 }}>
<View style={{ flex: 0, alignItems: 'center' }}>
<Text>{label}</Text>
<Text style={{ fontWeight: 'bold' }}>{`${Math.round(value)}`}</Text>
</View>
<Slider
thumbStyle={{
backgroundColor: 'black',
width: 15,
height: 15,
}}
value={value}
minimumValue={0}
maximumValue={500}
onSlidingComplete={(_value) => setValue(_value)}
/>
</View>
);
},
[],
);

const zoomLimitCounter = useCallback(
(
value: number | undefined,
setValue: (value?: number) => void,
label: string,
) => {
return (
<View style={{ flex: 1, paddingHorizontal: 10 }}>
<View style={{ flex: 0, alignItems: 'center' }}>
<Text>{label}</Text>
<Text style={{ fontWeight: 'bold' }}>{`${
value ?? 'Not set'
}`}</Text>
</View>
<Slider
thumbStyle={{
backgroundColor: 'black',
width: 15,
height: 15,
}}
value={value}
minimumValue={-1}
maximumValue={20}
onSlidingComplete={(_value) => {
if (_value < 0) {
setValue(undefined);
} else {
setValue(Math.round(_value));
}
}}
/>
</View>
);
},
[],
);

return (
<>
<MapView style={styles.map}>
<Camera
{...centerOrBounds}
zoomLevel={12}
minZoomLevel={minZoomLevel}
maxZoomLevel={maxZoomLevel}
padding={padding}
minZoomLevel={minZoom}
maxZoomLevel={maxZoom}
padding={{
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
}}
animationDuration={800}
animationMode={animationMode}
animationMode={easing}
/>

{features.map((feature) => {
Expand All @@ -192,53 +246,39 @@ const CameraAnimation = () => {
<SafeAreaView>
<View style={styles.sheet}>
<View style={styles.content}>
<Text style={styles.fadedText}>centerCoordinate</Text>
<Text style={styles.sectionText}>Coordinate</Text>
<View style={styles.buttonRow}>
<Button title="Flight" onPress={() => move('flyTo', false)} />
<Button title="Ease" onPress={() => move('easeTo', false)} />
<Button title="Linear" onPress={() => move('linearTo', false)} />
<Button title="Instant" onPress={() => move('moveTo', false)} />
<Button title="Center" onPress={() => move('center')} />
<Button title="Bounds" onPress={() => move('bounds')} />
</View>

<Divider style={styles.divider} />

<Text style={styles.fadedText}>bounds</Text>
<View style={styles.buttonRow}>
<Button title="Flight" onPress={() => move('flyTo', true)} />
<Button title="Ease" onPress={() => move('easeTo', true)} />
<Button title="Linear" onPress={() => move('linearTo', true)} />
<Button title="Instant" onPress={() => move('moveTo', true)} />
<Text style={styles.sectionText}>Easing</Text>
<View style={[styles.buttonRow, { marginBottom: -6 }]}>
{easingCheckBox('easeTo', 'Ease')}
{easingCheckBox('linearTo', 'Linear')}
{easingCheckBox('flyTo', 'Fly')}
{easingCheckBox('moveTo', 'Move')}
</View>

<Divider style={styles.divider} />

<Text style={styles.fadedText}>padding</Text>
<View style={styles.buttonRow}>
<Button
title="Zero"
onPress={() => {
setPadding(zeroPadding);
}}
/>
<Button
title="Even"
onPress={() => {
setPadding(evenPadding);
}}
/>
<Button
title="Random"
onPress={() => {
setPadding(randPadding());
}}
/>
<Text style={styles.sectionText}>Padding</Text>
<View style={[styles.buttonRow, { marginTop: 6 }]}>
{paddingCounter(paddingTop, setPaddingTop, 'Top')}
{paddingCounter(paddingBottom, setPaddingBottom, 'Bottom')}
{paddingCounter(paddingLeft, setPaddingLeft, 'Left')}
{paddingCounter(paddingRight, setPaddingRight, 'Right')}
</View>

<Divider style={styles.divider} />

<Text style={styles.fadedText}>info</Text>
<Text>position: {locationDisplay}</Text>
<Text>padding: {paddingDisplay}</Text>
<Text style={styles.sectionText}>Zoom limits</Text>
<View style={[styles.buttonRow, { marginTop: 6 }]}>
{zoomLimitCounter(minZoom, setMinZoom, 'Min')}
{zoomLimitCounter(maxZoom, setMaxZoom, 'Max')}
</View>
</View>
</View>
</SafeAreaView>
Expand All @@ -251,22 +291,23 @@ const styles = StyleSheet.create({
flex: 1,
},
sheet: {
paddingTop: 10,
paddingHorizontal: 10,
marginBottom: -10,
},
content: {
padding: 10,
},
sectionText: {
fontSize: 10,
color: 'gray',
},
buttonRow: {
flex: 0,
flexDirection: 'row',
justifyContent: 'space-around',
justifyContent: 'space-evenly',
},
divider: {
marginVertical: 10,
},
fadedText: {
color: 'gray',
marginVertical: 8,
},
});

Expand Down
Loading
Loading