thêm zustand để cấu hình global state, hook để lấy platform, thêm polyline và polygon b vào map
This commit is contained in:
165
components/map/PolygonWithLabel.tsx
Normal file
165
components/map/PolygonWithLabel.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { getPolygonCenter } from "@/utils/polyline";
|
||||
import React, { memo } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { Marker, Polygon } from "react-native-maps";
|
||||
|
||||
export interface PolygonWithLabelProps {
|
||||
coordinates: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}[];
|
||||
label?: string;
|
||||
content?: string;
|
||||
fillColor?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
zIndex?: number;
|
||||
zoomLevel?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component render Polygon kèm Label/Text ở giữa
|
||||
*/
|
||||
const PolygonWithLabelComponent: React.FC<PolygonWithLabelProps> = ({
|
||||
coordinates,
|
||||
label,
|
||||
content,
|
||||
fillColor = "rgba(16, 85, 201, 0.6)",
|
||||
strokeColor = "rgba(16, 85, 201, 0.8)",
|
||||
strokeWidth = 2,
|
||||
zIndex = 50,
|
||||
zoomLevel = 10,
|
||||
}) => {
|
||||
if (!coordinates || coordinates.length < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const centerPoint = getPolygonCenter(coordinates);
|
||||
|
||||
// Tính font size dựa trên zoom level
|
||||
// Zoom càng thấp (xa ra) thì font size càng nhỏ
|
||||
const calculateFontSize = (baseSize: number) => {
|
||||
const baseZoom = 10;
|
||||
// Giảm scale factor để text không quá to khi zoom out
|
||||
const scaleFactor = Math.pow(2, (zoomLevel - baseZoom) * 0.3);
|
||||
return Math.max(baseSize * scaleFactor, 5); // Tối thiểu 5px
|
||||
};
|
||||
|
||||
const labelFontSize = calculateFontSize(12);
|
||||
const contentFontSize = calculateFontSize(10);
|
||||
console.log("zoom level: ", zoomLevel);
|
||||
|
||||
const paddingScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.2), 0.5);
|
||||
const minWidthScale = Math.max(Math.pow(2, (zoomLevel - 10) * 0.25), 0.9);
|
||||
console.log("Min Width Scale: ", minWidthScale);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Polygon
|
||||
coordinates={coordinates}
|
||||
fillColor={fillColor}
|
||||
strokeColor={strokeColor}
|
||||
strokeWidth={strokeWidth}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
{label && (
|
||||
<Marker
|
||||
coordinate={centerPoint}
|
||||
zIndex={200}
|
||||
tracksViewChanges={false}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
>
|
||||
<View style={styles.markerContainer}>
|
||||
<View
|
||||
style={[
|
||||
styles.labelContainer,
|
||||
{
|
||||
paddingHorizontal: 5 * paddingScale,
|
||||
paddingVertical: 5 * paddingScale,
|
||||
minWidth: 80,
|
||||
maxWidth: 150 * minWidthScale,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text
|
||||
style={[styles.labelText, { fontSize: labelFontSize }]}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{label}
|
||||
</Text>
|
||||
{content && (
|
||||
<Text
|
||||
style={[
|
||||
styles.contentText,
|
||||
{ fontSize: contentFontSize, marginTop: 2 * paddingScale },
|
||||
]}
|
||||
numberOfLines={2}
|
||||
>
|
||||
{content}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Marker>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
markerContainer: {
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
labelContainer: {
|
||||
backgroundColor: "rgba(16, 85, 201, 0.95)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 18,
|
||||
borderWidth: 2,
|
||||
borderColor: "#fff",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 5,
|
||||
elevation: 8,
|
||||
minWidth: 80,
|
||||
maxWidth: 250,
|
||||
},
|
||||
labelText: {
|
||||
color: "#fff",
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.3,
|
||||
textAlign: "center",
|
||||
},
|
||||
contentText: {
|
||||
color: "#fff",
|
||||
fontSize: 11,
|
||||
fontWeight: "600",
|
||||
letterSpacing: 0.2,
|
||||
textAlign: "center",
|
||||
opacity: 0.95,
|
||||
},
|
||||
});
|
||||
|
||||
// Export memoized component để tránh re-render không cần thiết
|
||||
export const PolygonWithLabel = memo(
|
||||
PolygonWithLabelComponent,
|
||||
(prev, next) => {
|
||||
// Custom comparison: chỉ re-render khi coordinates, label, content hoặc zoomLevel thay đổi
|
||||
return (
|
||||
prev.coordinates.length === next.coordinates.length &&
|
||||
prev.coordinates.every(
|
||||
(coord, index) =>
|
||||
coord.latitude === next.coordinates[index]?.latitude &&
|
||||
coord.longitude === next.coordinates[index]?.longitude
|
||||
) &&
|
||||
prev.label === next.label &&
|
||||
prev.content === next.content &&
|
||||
prev.zoomLevel === next.zoomLevel &&
|
||||
prev.fillColor === next.fillColor &&
|
||||
prev.strokeColor === next.strokeColor
|
||||
);
|
||||
}
|
||||
);
|
||||
124
components/map/PolylineWithLabel.tsx
Normal file
124
components/map/PolylineWithLabel.tsx
Normal file
@@ -0,0 +1,124 @@
|
||||
import {
|
||||
calculateTotalDistance,
|
||||
getMiddlePointOfPolyline,
|
||||
} from "@/utils/polyline";
|
||||
import React, { memo } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { Marker, Polyline } from "react-native-maps";
|
||||
|
||||
export interface PolylineWithLabelProps {
|
||||
coordinates: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
}[];
|
||||
label?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
showDistance?: boolean;
|
||||
zIndex?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component render Polyline kèm Label/Text ở giữa
|
||||
*/
|
||||
const PolylineWithLabelComponent: React.FC<PolylineWithLabelProps> = ({
|
||||
coordinates,
|
||||
label,
|
||||
strokeColor = "#FF5733",
|
||||
strokeWidth = 4,
|
||||
showDistance = false,
|
||||
zIndex = 50,
|
||||
}) => {
|
||||
if (!coordinates || coordinates.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const middlePoint = getMiddlePointOfPolyline(coordinates);
|
||||
const distance = calculateTotalDistance(coordinates);
|
||||
|
||||
let displayText = label || "";
|
||||
if (showDistance) {
|
||||
displayText += displayText
|
||||
? ` (${distance.toFixed(2)}km)`
|
||||
: `${distance.toFixed(2)}km`;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Polyline
|
||||
coordinates={coordinates}
|
||||
strokeColor={strokeColor}
|
||||
strokeWidth={strokeWidth}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
{displayText && (
|
||||
<Marker
|
||||
coordinate={middlePoint}
|
||||
zIndex={zIndex + 10}
|
||||
tracksViewChanges={false}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
>
|
||||
<View style={styles.markerContainer}>
|
||||
<View style={styles.labelContainer}>
|
||||
<Text
|
||||
style={styles.labelText}
|
||||
numberOfLines={2}
|
||||
adjustsFontSizeToFit
|
||||
>
|
||||
{displayText}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Marker>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
markerContainer: {
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
labelContainer: {
|
||||
backgroundColor: "rgba(255, 87, 51, 0.95)",
|
||||
paddingHorizontal: 5,
|
||||
paddingVertical: 5,
|
||||
borderRadius: 18,
|
||||
borderWidth: 1,
|
||||
borderColor: "#fff",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 5,
|
||||
elevation: 8,
|
||||
minWidth: 80,
|
||||
maxWidth: 180,
|
||||
},
|
||||
labelText: {
|
||||
color: "#fff",
|
||||
fontSize: 14,
|
||||
fontWeight: "bold",
|
||||
letterSpacing: 0.3,
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
|
||||
// Export memoized component để tránh re-render không cần thiết
|
||||
export const PolylineWithLabel = memo(
|
||||
PolylineWithLabelComponent,
|
||||
(prev, next) => {
|
||||
// Custom comparison: chỉ re-render khi coordinates, label hoặc showDistance thay đổi
|
||||
return (
|
||||
prev.coordinates.length === next.coordinates.length &&
|
||||
prev.coordinates.every(
|
||||
(coord, index) =>
|
||||
coord.latitude === next.coordinates[index]?.latitude &&
|
||||
coord.longitude === next.coordinates[index]?.longitude
|
||||
) &&
|
||||
prev.label === next.label &&
|
||||
prev.showDistance === next.showDistance &&
|
||||
prev.strokeColor === next.strokeColor
|
||||
);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user