refactor: streamline map constants and enhance animation handling in layers

This commit is contained in:
vergiLgood1 2025-05-14 12:04:21 +07:00
parent 6c96c1140c
commit 8a1582c994
4 changed files with 111 additions and 61 deletions

View File

@ -88,29 +88,29 @@ export default function DistrictFillLineLayer({
}
// Wait a moment before selecting the new district to ensure clean transitions
setTimeout(() => {
const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month)
if (!district || !setFocusedDistrictId) return
// setTimeout(() => {
// const district = processDistrictFeature(feature, e, districtId, crimeDataByDistrict, crimes, year, month)
// if (!district || !setFocusedDistrictId) return
setFocusedDistrictId(district.id)
// setFocusedDistrictId(district.id)
// Fly to the new district
map.flyTo({
center: [district.longitude, district.latitude],
zoom: 12.5,
pitch: 75,
bearing: 0,
duration: 1500,
easing: (t) => t * (2 - t), // easeOutQuad
})
// // Fly to the new district
// map.flyTo({
// center: [district.longitude, district.latitude],
// zoom: 12.5,
// pitch: 75,
// bearing: 0,
// duration: 1500,
// easing: (t) => t * (2 - t), // easeOutQuad
// })
// Use onDistrictClick if available, otherwise fall back to onClick
if (onDistrictClick) {
onDistrictClick(district)
} else if (onClick) {
onClick(district)
}
}, 100)
// // Use onDistrictClick if available, otherwise fall back to onClick
// if (onDistrictClick) {
// onDistrictClick(district)
// } else if (onClick) {
// onClick(district)
// }
// }, 100)
return
}

View File

@ -2,7 +2,7 @@
import { useState, useRef, useEffect, useCallback } from "react"
import { useMap } from "react-map-gl/mapbox"
import { BASE_BEARING, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
import { BASE_BEARING, BASE_DURATION, BASE_PITCH, BASE_ZOOM, MAPBOX_TILESET_ID } from "@/app/_utils/const/map"
import DistrictPopup from "../pop-up/district-popup"
import DistrictExtrusionLayer from "./district-extrusion-layer"
import ClusterLayer from "./cluster-layer"
@ -91,6 +91,7 @@ export default function Layers({
showEWS = true,
sourceType = "cbt",
}: LayersProps) {
const animationRef = useRef<number | null>(null)
const { current: map } = useMap()
if (!map) {
@ -148,7 +149,7 @@ export default function Layers({
zoom: BASE_ZOOM,
pitch: BASE_PITCH,
bearing: BASE_BEARING,
duration: 1500,
duration: BASE_DURATION,
easing: (t) => t * (2 - t),
})
@ -166,10 +167,75 @@ export default function Layers({
}
}, [map, crimeDataByDistrict])
const animateExtrusionDown = () => {
if (!map || !map.getLayer("district-extrusion") || !focusedDistrictId) {
return
}
if (animationRef.current) {
cancelAnimationFrame(animationRef.current)
animationRef.current = null
}
// Get the current height from the layer (default to 800 if not found)
let currentHeight = 800
try {
const paint = map.getPaintProperty("district-extrusion", "fill-extrusion-height")
if (Array.isArray(paint) && paint.length > 0) {
// Try to extract the current height from the expression
const idx = paint.findIndex((v) => v === focusedDistrictId)
if (idx !== -1 && typeof paint[idx + 1] === "number") {
currentHeight = paint[idx + 1]
}
}
} catch {
// fallback to default
}
const startHeight = currentHeight
const targetHeight = 0
const duration = 700
const startTime = performance.now()
const animate = (currentTime: number) => {
const elapsed = currentTime - startTime
const progress = Math.min(elapsed / duration, 1)
const easedProgress = progress * (2 - progress)
const newHeight = startHeight + (targetHeight - startHeight) * easedProgress
try {
map.getMap().setPaintProperty("district-extrusion", "fill-extrusion-height", [
"case",
["has", "kode_kec"],
["match", ["get", "kode_kec"], focusedDistrictId, newHeight, 0],
0,
])
if (progress < 1) {
animationRef.current = requestAnimationFrame(animate)
} else {
animationRef.current = null
}
} catch (error) {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current)
animationRef.current = null
}
}
}
animationRef.current = requestAnimationFrame(animate)
}
const handleCloseDistrictPopup = useCallback(() => {
console.log("Closing district popup")
animateExtrusionDown()
handlePopupClose()
}, [handlePopupClose])
}, [handlePopupClose, animateExtrusionDown])
const handleCloseIncidentPopup = useCallback(() => {
console.log("Closing incident popup")
@ -197,10 +263,10 @@ export default function Layers({
if (map && feature.longitude && feature.latitude) {
map.flyTo({
center: [feature.longitude, feature.latitude],
zoom: 12,
pitch: 45,
zoom: 12.5,
pitch: 60,
bearing: 0,
duration: 1500,
duration: BASE_DURATION,
easing: (t) => t * (2 - t),
})

View File

@ -47,14 +47,14 @@ export default function DistrictPopup({
const [activeTab, setActiveTab] = useState("overview")
// Add debug log when the component is rendered
useEffect(() => {
console.log("DistrictPopup mounted:", {
district: district.name,
coords: [longitude, latitude],
year,
month
});
}, [district, longitude, latitude, year, month]);
// useEffect(() => {
// console.log("DistrictPopup mounted:", {
// district: district.name,
// coords: [longitude, latitude],
// year,
// month
// });
// }, [district, longitude, latitude, year, month]);
// Extract all crime incidents from the district data and apply filtering if needed
const allCrimeIncidents = useMemo(() => {
@ -332,28 +332,6 @@ export default function DistrictPopup({
</TabsContent>
</Tabs>
</Card>
{/* Connection line */}
<div
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full"
style={{
width: '2px',
height: '20px',
backgroundColor: 'red',
boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)'
}}
/>
{/* Connection dot */}
<div
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full"
style={{
width: '6px',
height: '6px',
backgroundColor: 'red',
borderRadius: '50%',
marginTop: '20px',
boxShadow: '0 0 4px rgba(0, 0, 0, 0.3)'
}}
/>
</div>
</Popup>
)

View File

@ -1,10 +1,16 @@
export const BASE_ZOOM = 9.5; // Default zoom level for the map
export const BASE_PITCH = 0; // Default pitch for the map
export const BASE_BEARING = 0; // Default bearing for the map
export const MAPBOX_ACCESS_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
export const MAPBOX_TILESET_ID = process.env.NEXT_PUBLIC_MAPBOX_TILESET_ID;
export const BASE_ZOOM = 9.5;
export const BASE_PITCH = 0;
export const BASE_BEARING = 0;
export const BASE_LATITUDE = -8.17; // Default latitude for the map center (Jember region)
export const BASE_LONGITUDE = 113.65; // Default longitude for the map center (Jember region)
export const MAPBOX_ACCESS_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN;
export const MAPBOX_TILESET_ID = process.env.NEXT_PUBLIC_MAPBOX_TILESET_ID; // Replace with your tileset ID
export const BASE_DURATION = 2000; // Default duration for map flyTo animation
export const PITCH_3D = 60; // Default pitch for 3D view
export const ZOOM_3D = 12.5; // Default zoom for 3D view
// export const CRIME_RATE_COLORS = {
// low: '#FFB74D', // green