Hiển thị tọa độ và khu vực khi tàu vi phạm
This commit is contained in:
133
components/map/CircleWithLabel.tsx
Normal file
133
components/map/CircleWithLabel.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { ANDROID_PLATFORM } from "@/constants";
|
||||
import { usePlatform } from "@/hooks/use-platform";
|
||||
import React, { useRef } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { Circle, MapMarker, Marker } from "react-native-maps";
|
||||
|
||||
export interface CircleWithLabelProps {
|
||||
center: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
radius: number;
|
||||
label?: string;
|
||||
content?: string;
|
||||
fillColor?: string;
|
||||
strokeColor?: string;
|
||||
strokeWidth?: number;
|
||||
zIndex?: number;
|
||||
zoomLevel?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component render Circle kèm Label/Text ở giữa
|
||||
*/
|
||||
export const CircleWithLabel: React.FC<CircleWithLabelProps> = ({
|
||||
center,
|
||||
radius,
|
||||
label,
|
||||
content,
|
||||
fillColor = "rgba(220, 20, 60, 0.6)",
|
||||
strokeColor = "rgba(220, 20, 60, 0.8)",
|
||||
strokeWidth = 2,
|
||||
zIndex = 50,
|
||||
zoomLevel = 10,
|
||||
}) => {
|
||||
if (!center) {
|
||||
return null;
|
||||
}
|
||||
const platform = usePlatform();
|
||||
const markerRef = useRef<MapMarker>(null);
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Circle
|
||||
center={center}
|
||||
radius={radius}
|
||||
fillColor={fillColor}
|
||||
strokeColor={strokeColor}
|
||||
strokeWidth={strokeWidth}
|
||||
zIndex={zIndex}
|
||||
/>
|
||||
{label && (
|
||||
<Marker
|
||||
ref={markerRef}
|
||||
coordinate={center}
|
||||
zIndex={50}
|
||||
tracksViewChanges={platform === ANDROID_PLATFORM ? false : true}
|
||||
anchor={{ x: 0.5, y: 0.5 }}
|
||||
title={platform === ANDROID_PLATFORM ? label : undefined}
|
||||
description={platform === ANDROID_PLATFORM ? content : undefined}
|
||||
>
|
||||
<View style={styles.markerContainer}>
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
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",
|
||||
},
|
||||
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,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user