remove log && increate motion
This commit is contained in:
parent
4eab292e1b
commit
77416f635a
@ -183,9 +183,9 @@ const columns: ColumnDef<z.infer<typeof blogSchema>>[] = [
|
|||||||
published: { label: "已发布", variant: "default" },
|
published: { label: "已发布", variant: "default" },
|
||||||
archived: { label: "已归档", variant: "outline" },
|
archived: { label: "已归档", variant: "outline" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const statusInfo = statusMap[status] || { label: status, variant: "outline" };
|
const statusInfo = statusMap[status] || { label: status, variant: "outline" };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-20">
|
<div className="w-20">
|
||||||
<Badge variant={statusInfo.variant} className="text-xs">
|
<Badge variant={statusInfo.variant} className="text-xs">
|
||||||
@ -349,7 +349,7 @@ const columns: ColumnDef<z.infer<typeof blogSchema>>[] = [
|
|||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
const updateBlogMutation = (table.options.meta as any)?.updateBlog
|
const updateBlogMutation = (table.options.meta as any)?.updateBlog
|
||||||
const deleteBlogMutation = (table.options.meta as any)?.deleteBlog
|
const deleteBlogMutation = (table.options.meta as any)?.deleteBlog
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@ -424,7 +424,7 @@ const columns: ColumnDef<z.infer<typeof blogSchema>>[] = [
|
|||||||
{row.original.isActive ? "停用" : "启用"}
|
{row.original.isActive ? "停用" : "启用"}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (confirm('确定要删除这篇博客吗?此操作不可撤销。')) {
|
if (confirm('确定要删除这篇博客吗?此操作不可撤销。')) {
|
||||||
@ -604,7 +604,7 @@ export function BlogTable() {
|
|||||||
const { active, over } = event
|
const { active, over } = event
|
||||||
if (active && over && active.id !== over.id) {
|
if (active && over && active.id !== over.id) {
|
||||||
// Handle drag end for reordering
|
// Handle drag end for reordering
|
||||||
console.log('Reordering blogs:', active.id, 'to', over.id)
|
// console.log('Reordering blogs:', active.id, 'to', over.id)
|
||||||
// You can implement actual reordering API call here
|
// You can implement actual reordering API call here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,11 +657,11 @@ export function BlogTable() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function BlogTabs({
|
function BlogTabs({
|
||||||
blogs,
|
blogs,
|
||||||
loading,
|
loading,
|
||||||
info,
|
info,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
refetch,
|
refetch,
|
||||||
pagination,
|
pagination,
|
||||||
setPagination,
|
setPagination,
|
||||||
@ -672,7 +672,7 @@ function BlogTabs({
|
|||||||
totalCount,
|
totalCount,
|
||||||
updateBlog,
|
updateBlog,
|
||||||
deleteBlog
|
deleteBlog
|
||||||
}: {
|
}: {
|
||||||
blogs: any[]
|
blogs: any[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
info: any
|
info: any
|
||||||
@ -766,8 +766,8 @@ function BlogTabs({
|
|||||||
<TabsTrigger value="archived_blogs">已归档 <Badge variant="secondary">{info?.totalArchived}</Badge></TabsTrigger>
|
<TabsTrigger value="archived_blogs">已归档 <Badge variant="secondary">{info?.totalArchived}</Badge></TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => window.location.href = '/admin/editor'}
|
onClick={() => window.location.href = '/admin/editor'}
|
||||||
>
|
>
|
||||||
@ -1149,7 +1149,7 @@ function BlogCellViewer({ item }: { item: z.infer<typeof blogSchema> }) {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<DrawerFooter>
|
<DrawerFooter>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.location.href = `/admin/editor?id=${item.id}`
|
window.location.href = `/admin/editor?id=${item.id}`
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -271,7 +271,7 @@ const columns: ColumnDef<z.infer<typeof categorySchema>>[] = [
|
|||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
const updateCategoryMutation = (table.options.meta as any)?.updateCategory
|
const updateCategoryMutation = (table.options.meta as any)?.updateCategory
|
||||||
const deleteCategoryMutation = (table.options.meta as any)?.deleteCategory
|
const deleteCategoryMutation = (table.options.meta as any)?.deleteCategory
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@ -299,7 +299,7 @@ const columns: ColumnDef<z.infer<typeof categorySchema>>[] = [
|
|||||||
{row.original.isActive ? "禁用" : "启用"}
|
{row.original.isActive ? "禁用" : "启用"}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (confirm('确定要删除这个分类吗?此操作不可撤销。')) {
|
if (confirm('确定要删除这个分类吗?此操作不可撤销。')) {
|
||||||
@ -451,7 +451,7 @@ export function CategoryTable() {
|
|||||||
function handleDragEnd(event: DragEndEvent) {
|
function handleDragEnd(event: DragEndEvent) {
|
||||||
const { active, over } = event
|
const { active, over } = event
|
||||||
if (active && over && active.id !== over.id) {
|
if (active && over && active.id !== over.id) {
|
||||||
console.log('Reordering categories:', active.id, 'to', over.id)
|
// console.log('Reordering categories:', active.id, 'to', over.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -495,11 +495,11 @@ export function CategoryTable() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function CategoryTabs({
|
function CategoryTabs({
|
||||||
categories,
|
categories,
|
||||||
loading,
|
loading,
|
||||||
info,
|
info,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
refetch,
|
refetch,
|
||||||
pagination,
|
pagination,
|
||||||
setPagination,
|
setPagination,
|
||||||
@ -510,7 +510,7 @@ function CategoryTabs({
|
|||||||
totalCount,
|
totalCount,
|
||||||
updateCategory,
|
updateCategory,
|
||||||
deleteCategory
|
deleteCategory
|
||||||
}: {
|
}: {
|
||||||
categories: any[]
|
categories: any[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
info: any
|
info: any
|
||||||
@ -584,8 +584,8 @@ function CategoryTabs({
|
|||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => window.location.href = '/admin/categories/create'}
|
onClick={() => window.location.href = '/admin/categories/create'}
|
||||||
>
|
>
|
||||||
@ -873,8 +873,8 @@ function CategoryCellViewer({ item }: { item: z.infer<typeof categorySchema> })
|
|||||||
<Button variant="link" className="text-foreground w-fit px-0 text-left justify-start">
|
<Button variant="link" className="text-foreground w-fit px-0 text-left justify-start">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{item.color && (
|
{item.color && (
|
||||||
<div
|
<div
|
||||||
className="w-3 h-3 rounded-full"
|
className="w-3 h-3 rounded-full"
|
||||||
style={{ backgroundColor: item.color }}
|
style={{ backgroundColor: item.color }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -559,7 +559,7 @@ export default function Control() {
|
|||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
<Button className="flex items-center gap-2" onClick={() => {
|
<Button className="flex items-center gap-2" onClick={() => {
|
||||||
console.log('Applying configuration:', config)
|
// console.log('Applying configuration:', config)
|
||||||
alert('配置应用成功!')
|
alert('配置应用成功!')
|
||||||
}}>
|
}}>
|
||||||
<Zap className="h-4 w-4" />
|
<Zap className="h-4 w-4" />
|
||||||
|
|||||||
@ -269,7 +269,7 @@ const columns: ColumnDef<z.infer<typeof tagSchema>>[] = [
|
|||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
const updateTagMutation = (table.options.meta as any)?.updateTag
|
const updateTagMutation = (table.options.meta as any)?.updateTag
|
||||||
const deleteTagMutation = (table.options.meta as any)?.deleteTag
|
const deleteTagMutation = (table.options.meta as any)?.deleteTag
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@ -297,7 +297,7 @@ const columns: ColumnDef<z.infer<typeof tagSchema>>[] = [
|
|||||||
{row.original.isActive ? "禁用" : "启用"}
|
{row.original.isActive ? "禁用" : "启用"}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (confirm('确定要删除这个标签吗?此操作不可撤销。')) {
|
if (confirm('确定要删除这个标签吗?此操作不可撤销。')) {
|
||||||
@ -447,7 +447,7 @@ export function TagTable() {
|
|||||||
function handleDragEnd(event: DragEndEvent) {
|
function handleDragEnd(event: DragEndEvent) {
|
||||||
const { active, over } = event
|
const { active, over } = event
|
||||||
if (active && over && active.id !== over.id) {
|
if (active && over && active.id !== over.id) {
|
||||||
console.log('Reordering tags:', active.id, 'to', over.id)
|
// console.log('Reordering tags:', active.id, 'to', over.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,11 +491,11 @@ export function TagTable() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TagTabs({
|
function TagTabs({
|
||||||
tags,
|
tags,
|
||||||
loading,
|
loading,
|
||||||
info,
|
info,
|
||||||
handleDragEnd,
|
handleDragEnd,
|
||||||
refetch,
|
refetch,
|
||||||
pagination,
|
pagination,
|
||||||
setPagination,
|
setPagination,
|
||||||
@ -506,7 +506,7 @@ function TagTabs({
|
|||||||
totalCount,
|
totalCount,
|
||||||
updateTag,
|
updateTag,
|
||||||
deleteTag
|
deleteTag
|
||||||
}: {
|
}: {
|
||||||
tags: any[]
|
tags: any[]
|
||||||
loading: boolean
|
loading: boolean
|
||||||
info: any
|
info: any
|
||||||
@ -580,8 +580,8 @@ function TagTabs({
|
|||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => window.location.href = '/admin/tags/create'}
|
onClick={() => window.location.href = '/admin/tags/create'}
|
||||||
>
|
>
|
||||||
@ -869,8 +869,8 @@ function TagCellViewer({ item }: { item: z.infer<typeof tagSchema> }) {
|
|||||||
<Button variant="link" className="text-foreground w-fit px-0 text-left justify-start">
|
<Button variant="link" className="text-foreground w-fit px-0 text-left justify-start">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{item.color ? (
|
{item.color ? (
|
||||||
<div
|
<div
|
||||||
className="w-3 h-3 rounded-full"
|
className="w-3 h-3 rounded-full"
|
||||||
style={{ backgroundColor: item.color }}
|
style={{ backgroundColor: item.color }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export default function Page({ params }: { params: Promise<{ slug: string }> })
|
|||||||
if (!data?.blogBySlug) return <div className="flex items-center justify-center min-h-[400px]">Blog not found</div>;
|
if (!data?.blogBySlug) return <div className="flex items-center justify-center min-h-[400px]">Blog not found</div>;
|
||||||
|
|
||||||
const content = data.blogBySlug.content;
|
const content = data.blogBySlug.content;
|
||||||
console.log('Blog content:', content);
|
// console.log('Blog content:', content);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export function MapProvider({ children }: MapProviderProps) {
|
|||||||
const setMap = (map: Map, layers: any[]) => {
|
const setMap = (map: Map, layers: any[]) => {
|
||||||
// 如果已经有地图实例,先清理旧的
|
// 如果已经有地图实例,先清理旧的
|
||||||
if (mapRef.current) {
|
if (mapRef.current) {
|
||||||
console.log('Cleaning up previous map instance...');
|
// console.log('Cleaning up previous map instance...');
|
||||||
mapRef.current = null;
|
mapRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ export function MapProvider({ children }: MapProviderProps) {
|
|||||||
|
|
||||||
const clearMap = () => {
|
const clearMap = () => {
|
||||||
if (mapRef.current) {
|
if (mapRef.current) {
|
||||||
console.log('Clearing map instance...');
|
// console.log('Clearing map instance...');
|
||||||
mapRef.current.remove();
|
mapRef.current.remove();
|
||||||
mapRef.current = null;
|
mapRef.current = null;
|
||||||
layersRef.current = [];
|
layersRef.current = [];
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export default async function Layout({ children }: { children: React.ReactNode }
|
|||||||
|
|
||||||
const isLoggedIn = (await cookies()).get('jwt')?.value
|
const isLoggedIn = (await cookies()).get('jwt')?.value
|
||||||
|
|
||||||
console.log(isLoggedIn)
|
// console.log(isLoggedIn)
|
||||||
|
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
redirect('/login')
|
redirect('/login')
|
||||||
|
|||||||
@ -401,9 +401,9 @@ export default function MePage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
console.log("Settings saved:", settings);
|
// console.log("Settings saved:", settings);
|
||||||
console.log("Profile saved:", profile);
|
// console.log("Profile saved:", profile);
|
||||||
console.log("Tags saved:", tags);
|
// console.log("Tags saved:", tags);
|
||||||
// TODO: 实际保存到后端
|
// TODO: 实际保存到后端
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
11
app/tl.tsx
11
app/tl.tsx
@ -26,6 +26,7 @@ import { Separator } from "@/components/ui/separator";
|
|||||||
import { useWS } from "./ws-context";
|
import { useWS } from "./ws-context";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
|
||||||
|
|
||||||
interface Uniforms {
|
interface Uniforms {
|
||||||
@ -154,7 +155,6 @@ export const Timeline: React.FC<Props> = React.memo(({
|
|||||||
// 合并配置
|
// 合并配置
|
||||||
const config: TimelineConfig = {
|
const config: TimelineConfig = {
|
||||||
initialCenterTime: defaultCenterTime,
|
initialCenterTime: defaultCenterTime,
|
||||||
initialTimeRange: (defaultEndTime - defaultStartTime) / 2, // 使用时间范围的一半作为初始范围
|
|
||||||
highlightWeekends: false,
|
highlightWeekends: false,
|
||||||
zoomMode: ZoomMode.MousePosition,
|
zoomMode: ZoomMode.MousePosition,
|
||||||
zoomSensitivity: 0.001,
|
zoomSensitivity: 0.001,
|
||||||
@ -174,6 +174,15 @@ export const Timeline: React.FC<Props> = React.memo(({
|
|||||||
primaryFontSize: 10,
|
primaryFontSize: 10,
|
||||||
secondaryFontSize: 10
|
secondaryFontSize: 10
|
||||||
},
|
},
|
||||||
|
discreteZoomLevels: [
|
||||||
|
604123.3009862272,
|
||||||
|
383869.5109421124,
|
||||||
|
167314.88060646105,
|
||||||
|
64314.3439546868,
|
||||||
|
24625.670894409748,
|
||||||
|
4964.7447283082365,
|
||||||
|
],
|
||||||
|
initialZoomLevel: 4964.7447283082365,
|
||||||
onDateChange: async (date: Date) => {
|
onDateChange: async (date: Date) => {
|
||||||
const datestr = formatInTimeZone(date, 'UTC', 'yyyyMMddHHmmss')
|
const datestr = formatInTimeZone(date, 'UTC', 'yyyyMMddHHmmss')
|
||||||
const url_base = process.env.NEXT_PUBLIC_GRAPHQL_BACKEND_URL?.replace('/graphql', '') || 'http://localhost:3050'
|
const url_base = process.env.NEXT_PUBLIC_GRAPHQL_BACKEND_URL?.replace('/graphql', '') || 'http://localhost:3050'
|
||||||
|
|||||||
@ -356,7 +356,7 @@ export function UserProvider({ children }: UserProviderProps) {
|
|||||||
// 监听用户数据变化,定期更新用户信息
|
// 监听用户数据变化,定期更新用户信息
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userData && authState.isAuthenticated) {
|
if (userData && authState.isAuthenticated) {
|
||||||
console.log('userData', userData)
|
// console.log('userData', userData)
|
||||||
updateUserInfo(userData)
|
updateUserInfo(userData)
|
||||||
}
|
}
|
||||||
}, [userData, authState.isAuthenticated])
|
}, [userData, authState.isAuthenticated])
|
||||||
|
|||||||
@ -72,7 +72,7 @@ export function MapComponent({
|
|||||||
op: "ui.component.load",
|
op: "ui.component.load",
|
||||||
name: "Map Component Initialization",
|
name: "Map Component Initialization",
|
||||||
});
|
});
|
||||||
|
|
||||||
span.setAttribute("map.style", style);
|
span.setAttribute("map.style", style);
|
||||||
span.setAttribute("map.center", `${location.center[0]},${location.center[1]}`);
|
span.setAttribute("map.center", `${location.center[0]},${location.center[1]}`);
|
||||||
span.setAttribute("map.zoom", location.zoom);
|
span.setAttribute("map.zoom", location.zoom);
|
||||||
@ -92,10 +92,10 @@ export function MapComponent({
|
|||||||
|
|
||||||
|
|
||||||
map.on('style.load', () => {
|
map.on('style.load', () => {
|
||||||
logMapEvent('style.load', {
|
logMapEvent('style.load', {
|
||||||
style: style,
|
style: style,
|
||||||
center: location.center,
|
center: location.center,
|
||||||
zoom: location.zoom
|
zoom: location.zoom
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info('Map style loaded successfully', {
|
logger.info('Map style loaded successfully', {
|
||||||
@ -285,9 +285,9 @@ export function MapComponent({
|
|||||||
// 只在缩放级别变化时更新网格数据
|
// 只在缩放级别变化时更新网格数据
|
||||||
const currentZoom = Math.floor(map.getZoom());
|
const currentZoom = Math.floor(map.getZoom());
|
||||||
if (currentZoom !== this.lastZoom) {
|
if (currentZoom !== this.lastZoom) {
|
||||||
logMapEvent('zoom_change', {
|
logMapEvent('zoom_change', {
|
||||||
previousZoom: this.lastZoom,
|
previousZoom: this.lastZoom,
|
||||||
currentZoom: currentZoom
|
currentZoom: currentZoom
|
||||||
});
|
});
|
||||||
|
|
||||||
// 智能计算最佳细分数量
|
// 智能计算最佳细分数量
|
||||||
@ -300,20 +300,20 @@ export function MapComponent({
|
|||||||
|
|
||||||
// 获取细分建议信息
|
// 获取细分建议信息
|
||||||
const recommendation = getSubdivisionRecommendation(currentZoom, performanceLevel);
|
const recommendation = getSubdivisionRecommendation(currentZoom, performanceLevel);
|
||||||
|
|
||||||
logger.debug(logger.fmt`Zoom level: ${currentZoom}, Performance level: ${performanceLevel}`, {
|
logger.debug(logger.fmt`Zoom level: ${currentZoom}, Performance level: ${performanceLevel}`, {
|
||||||
currentZoom,
|
currentZoom,
|
||||||
performanceLevel,
|
performanceLevel,
|
||||||
component: 'MapComponent'
|
component: 'MapComponent'
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.debug(logger.fmt`Subdivision recommendation: ${recommendation.subdivisions} (${recommendation.description})`, {
|
logger.debug(logger.fmt`Subdivision recommendation: ${recommendation.subdivisions} (${recommendation.description})`, {
|
||||||
subdivisions: recommendation.subdivisions,
|
subdivisions: recommendation.subdivisions,
|
||||||
description: recommendation.description,
|
description: recommendation.description,
|
||||||
triangleCount: recommendation.triangleCount,
|
triangleCount: recommendation.triangleCount,
|
||||||
estimatedMemoryMB: recommendation.estimatedMemoryMB
|
estimatedMemoryMB: recommendation.estimatedMemoryMB
|
||||||
});
|
});
|
||||||
|
|
||||||
logPerformanceMetric('triangles', recommendation.triangleCount, 'count');
|
logPerformanceMetric('triangles', recommendation.triangleCount, 'count');
|
||||||
logPerformanceMetric('memory_estimate', recommendation.estimatedMemoryMB, 'MB');
|
logPerformanceMetric('memory_estimate', recommendation.estimatedMemoryMB, 'MB');
|
||||||
|
|
||||||
@ -463,7 +463,7 @@ export function MapComponent({
|
|||||||
|
|
||||||
// 清理函数:当组件卸载或重新初始化时清理资源
|
// 清理函数:当组件卸载或重新初始化时清理资源
|
||||||
return () => {
|
return () => {
|
||||||
console.log('Cleaning up map resources...');
|
// console.log('Cleaning up map resources...');
|
||||||
|
|
||||||
// 清理自定义图层引用
|
// 清理自定义图层引用
|
||||||
customLayerRef.current = null;
|
customLayerRef.current = null;
|
||||||
@ -480,7 +480,7 @@ export function MapComponent({
|
|||||||
if (map) {
|
if (map) {
|
||||||
map.remove();
|
map.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 结束Sentry span
|
// 结束Sentry span
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
@ -492,7 +492,7 @@ export function MapComponent({
|
|||||||
const gl = glRef.current
|
const gl = glRef.current
|
||||||
if (!gl) return;
|
if (!gl) return;
|
||||||
|
|
||||||
console.log('Updating texture with imgBitmap:', imgBitmap);
|
// console.log('Updating texture with imgBitmap:', imgBitmap);
|
||||||
|
|
||||||
gl.bindTexture(gl.TEXTURE_2D, texRef.current)
|
gl.bindTexture(gl.TEXTURE_2D, texRef.current)
|
||||||
|
|
||||||
@ -557,7 +557,7 @@ export function MapComponent({
|
|||||||
(span) => {
|
(span) => {
|
||||||
span.setAttribute("colorbar.position.x", colorbarPosition.x);
|
span.setAttribute("colorbar.position.x", colorbarPosition.x);
|
||||||
span.setAttribute("colorbar.position.y", colorbarPosition.y);
|
span.setAttribute("colorbar.position.y", colorbarPosition.y);
|
||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setIsDragging(true)
|
setIsDragging(true)
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export function useRadarTile({
|
|||||||
radarTileRef.current.url = url
|
radarTileRef.current.url = url
|
||||||
const blob = await resp.blob()
|
const blob = await resp.blob()
|
||||||
const newImgBitmap = await createImageBitmap(blob)
|
const newImgBitmap = await createImageBitmap(blob)
|
||||||
console.log('Created new ImageBitmap:', newImgBitmap);
|
// console.log('Created new ImageBitmap:', newImgBitmap);
|
||||||
setImgBitmap(newImgBitmap) // 使用 setState 更新状态
|
setImgBitmap(newImgBitmap) // 使用 setState 更新状态
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
radarTileRef.current.isError = true
|
radarTileRef.current.isError = true
|
||||||
|
|||||||
@ -75,6 +75,8 @@ interface TimelineConfig {
|
|||||||
initialCenterTime?: number;
|
initialCenterTime?: number;
|
||||||
/** 初始显示范围(毫秒,默认1小时) */
|
/** 初始显示范围(毫秒,默认1小时) */
|
||||||
initialTimeRange?: number;
|
initialTimeRange?: number;
|
||||||
|
/** Initial Zoom Level */
|
||||||
|
initialZoomLevel?: number | null;
|
||||||
/** 缩放模式 */
|
/** 缩放模式 */
|
||||||
zoomMode?: ZoomMode;
|
zoomMode?: ZoomMode;
|
||||||
/** 缩放灵敏度 */
|
/** 缩放灵敏度 */
|
||||||
@ -226,7 +228,7 @@ class Viewport {
|
|||||||
|
|
||||||
/** 获取缩放级别(像素/毫秒) */
|
/** 获取缩放级别(像素/毫秒) */
|
||||||
getZoomLevel(): number {
|
getZoomLevel(): number {
|
||||||
return this.width / this.timeRange;
|
return this.timeRange / this.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将时间转换为屏幕坐标 */
|
/** 将时间转换为屏幕坐标 */
|
||||||
@ -291,11 +293,12 @@ class Viewport {
|
|||||||
const newTimeAtMouse = this.screenToTime(screenX);
|
const newTimeAtMouse = this.screenToTime(screenX);
|
||||||
const timeCorrection = timeAtMouse - newTimeAtMouse;
|
const timeCorrection = timeAtMouse - newTimeAtMouse;
|
||||||
this.centerTime += timeCorrection;
|
this.centerTime += timeCorrection;
|
||||||
|
console.log(this.getZoomLevel())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 动画缩放到指定时间范围 */
|
/** 动画缩放到指定时间范围 */
|
||||||
animateToTimeRange(
|
animateToTimeRange(
|
||||||
targetRange: number,
|
targetRange: number,
|
||||||
duration: number = 300,
|
duration: number = 300,
|
||||||
targetCenterTime?: number,
|
targetCenterTime?: number,
|
||||||
onComplete?: () => void
|
onComplete?: () => void
|
||||||
@ -308,10 +311,10 @@ class Viewport {
|
|||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const startTimeRange = this.timeRange;
|
const startTimeRange = this.timeRange;
|
||||||
const startCenterTime = this.centerTime;
|
const startCenterTime = this.centerTime;
|
||||||
|
|
||||||
// 限制目标范围
|
// 限制目标范围
|
||||||
targetRange = Math.max(this.MIN_RANGE, Math.min(this.MAX_RANGE, targetRange));
|
targetRange = Math.max(this.MIN_RANGE, Math.min(this.MAX_RANGE, targetRange));
|
||||||
|
|
||||||
this.animationState = {
|
this.animationState = {
|
||||||
isAnimating: true,
|
isAnimating: true,
|
||||||
startTime,
|
startTime,
|
||||||
@ -328,16 +331,16 @@ class Viewport {
|
|||||||
|
|
||||||
const elapsed = Date.now() - this.animationState.startTime;
|
const elapsed = Date.now() - this.animationState.startTime;
|
||||||
const progress = Math.min(elapsed / this.animationState.duration, 1);
|
const progress = Math.min(elapsed / this.animationState.duration, 1);
|
||||||
|
|
||||||
// 使用缓动函数 (easeInOutCubic)
|
// 使用缓动函数 (easeInOutCubic)
|
||||||
const easedProgress = progress < 0.5
|
const easedProgress = progress < 0.5
|
||||||
? 4 * progress * progress * progress
|
? 4 * progress * progress * progress
|
||||||
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
|
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
|
||||||
|
|
||||||
// 插值计算当前值
|
// 插值计算当前值
|
||||||
this.timeRange = this.animationState.startTimeRange +
|
this.timeRange = this.animationState.startTimeRange +
|
||||||
(this.animationState.targetTimeRange - this.animationState.startTimeRange) * easedProgress;
|
(this.animationState.targetTimeRange - this.animationState.startTimeRange) * easedProgress;
|
||||||
|
|
||||||
this.centerTime = this.animationState.startCenterTime +
|
this.centerTime = this.animationState.startCenterTime +
|
||||||
(this.animationState.targetCenterTime - this.animationState.startCenterTime) * easedProgress;
|
(this.animationState.targetCenterTime - this.animationState.startCenterTime) * easedProgress;
|
||||||
|
|
||||||
@ -365,11 +368,11 @@ class Viewport {
|
|||||||
onComplete?: () => void
|
onComplete?: () => void
|
||||||
): void {
|
): void {
|
||||||
const timeAtMouse = this.screenToTime(screenX);
|
const timeAtMouse = this.screenToTime(screenX);
|
||||||
|
|
||||||
// 计算缩放后的中心时间,保持鼠标位置下的时间不变
|
// 计算缩放后的中心时间,保持鼠标位置下的时间不变
|
||||||
const currentRatio = (screenX - this.width / 2) / this.width;
|
const currentRatio = (screenX - this.width / 2) / this.width;
|
||||||
const targetCenterTime = timeAtMouse - currentRatio * targetRange;
|
const targetCenterTime = timeAtMouse - currentRatio * targetRange;
|
||||||
|
|
||||||
this.animateToTimeRange(targetRange, duration, targetCenterTime, onComplete);
|
this.animateToTimeRange(targetRange, duration, targetCenterTime, onComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,8 +432,8 @@ class ScaleManager {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
level: TimeFormatLevel.FiveMinutes,
|
level: TimeFormatLevel.FiveMinutes,
|
||||||
majorInterval: 5 * 60 * 1000, // 5分钟
|
majorInterval: 6 * 60 * 1000, // 5分钟
|
||||||
minorTicks: 5, // 每分钟一个次刻度
|
minorTicks: 6, // 每分钟一个次刻度
|
||||||
minPixelDistance: 60,
|
minPixelDistance: 60,
|
||||||
maxPixelDistance: 150,
|
maxPixelDistance: 150,
|
||||||
formatter: (date: Date) => ({
|
formatter: (date: Date) => ({
|
||||||
@ -440,8 +443,8 @@ class ScaleManager {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
level: TimeFormatLevel.TenMinutes,
|
level: TimeFormatLevel.TenMinutes,
|
||||||
majorInterval: 10 * 60 * 1000, // 10分钟
|
majorInterval: 12 * 60 * 1000, // 12分钟
|
||||||
minorTicks: 2, // 每5分钟一个次刻度
|
minorTicks: 2, // 每6分钟一个次刻度
|
||||||
minPixelDistance: 50,
|
minPixelDistance: 50,
|
||||||
maxPixelDistance: 120,
|
maxPixelDistance: 120,
|
||||||
formatter: (date: Date) => ({
|
formatter: (date: Date) => ({
|
||||||
@ -501,7 +504,7 @@ class ScaleManager {
|
|||||||
{
|
{
|
||||||
level: TimeFormatLevel.Day,
|
level: TimeFormatLevel.Day,
|
||||||
majorInterval: 24 * 60 * 60 * 1000, // 1天
|
majorInterval: 24 * 60 * 60 * 1000, // 1天
|
||||||
minorTicks: 4, // 每6小时一个次刻度
|
minorTicks: 12, // 每6小时一个次刻度
|
||||||
minPixelDistance: 80,
|
minPixelDistance: 80,
|
||||||
maxPixelDistance: 200,
|
maxPixelDistance: 200,
|
||||||
formatter: (date: Date) => ({
|
formatter: (date: Date) => ({
|
||||||
@ -523,7 +526,7 @@ class ScaleManager {
|
|||||||
const zoomLevel = viewport.getZoomLevel();
|
const zoomLevel = viewport.getZoomLevel();
|
||||||
|
|
||||||
for (const level of this.scaleLevels) {
|
for (const level of this.scaleLevels) {
|
||||||
const pixelDistance = level.majorInterval * zoomLevel;
|
const pixelDistance = level.majorInterval / zoomLevel;
|
||||||
if (pixelDistance >= level.minPixelDistance && pixelDistance <= level.maxPixelDistance) {
|
if (pixelDistance >= level.minPixelDistance && pixelDistance <= level.maxPixelDistance) {
|
||||||
this.currentLevel = level;
|
this.currentLevel = level;
|
||||||
return level;
|
return level;
|
||||||
@ -531,7 +534,7 @@ class ScaleManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果没有找到合适的级别,选择最接近的
|
// 如果没有找到合适的级别,选择最接近的
|
||||||
const pixelDistances = this.scaleLevels.map(level => level.majorInterval * zoomLevel);
|
const pixelDistances = this.scaleLevels.map(level => level.majorInterval / zoomLevel);
|
||||||
const targetDistance = 100; // 目标像素距离
|
const targetDistance = 100; // 目标像素距离
|
||||||
const closestIndex = pixelDistances.reduce((bestIdx, dist, idx) => {
|
const closestIndex = pixelDistances.reduce((bestIdx, dist, idx) => {
|
||||||
const bestDist = pixelDistances[bestIdx];
|
const bestDist = pixelDistances[bestIdx];
|
||||||
@ -610,14 +613,14 @@ class InteractionHandler {
|
|||||||
private discreteZoomLevels: number[];
|
private discreteZoomLevels: number[];
|
||||||
private enableSmoothZoom: boolean;
|
private enableSmoothZoom: boolean;
|
||||||
private zoomAnimationDuration: number;
|
private zoomAnimationDuration: number;
|
||||||
|
|
||||||
// 触摸相关状态
|
// 触摸相关状态
|
||||||
private lastTouchDistance: number | null = null;
|
private lastTouchDistance: number | null = null;
|
||||||
private lastTouchCenter: { x: number; y: number } | null = null;
|
private lastTouchCenter: { x: number; y: number } | null = null;
|
||||||
private isTouchZooming: boolean = false;
|
private isTouchZooming: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
zoomMode: ZoomMode = ZoomMode.MousePosition,
|
zoomMode: ZoomMode = ZoomMode.MousePosition,
|
||||||
sensitivity: number = 0.001,
|
sensitivity: number = 0.001,
|
||||||
discreteZoomLevels: number[] = [],
|
discreteZoomLevels: number[] = [],
|
||||||
enableSmoothZoom: boolean = true,
|
enableSmoothZoom: boolean = true,
|
||||||
@ -705,14 +708,14 @@ class InteractionHandler {
|
|||||||
|
|
||||||
/** 处理离散缩放 */
|
/** 处理离散缩放 */
|
||||||
private handleDiscreteZoom(deltaY: number, mouseX: number, viewport: Viewport, markX?: number): void {
|
private handleDiscreteZoom(deltaY: number, mouseX: number, viewport: Viewport, markX?: number): void {
|
||||||
const currentRange = viewport.getTimeRange();
|
const currentLevel = viewport.getZoomLevel();
|
||||||
|
|
||||||
// 找到当前最接近的缩放级别
|
// 找到当前最接近的缩放级别
|
||||||
let currentLevelIndex = 0;
|
let currentLevelIndex = 0;
|
||||||
let minDiff = Math.abs(this.discreteZoomLevels[0] - currentRange);
|
let minDiff = Math.abs(this.discreteZoomLevels[0] - currentLevel);
|
||||||
|
|
||||||
for (let i = 1; i < this.discreteZoomLevels.length; i++) {
|
for (let i = 1; i < this.discreteZoomLevels.length; i++) {
|
||||||
const diff = Math.abs(this.discreteZoomLevels[i] - currentRange);
|
const diff = Math.abs(this.discreteZoomLevels[i] - currentLevel);
|
||||||
if (diff < minDiff) {
|
if (diff < minDiff) {
|
||||||
minDiff = diff;
|
minDiff = diff;
|
||||||
currentLevelIndex = i;
|
currentLevelIndex = i;
|
||||||
@ -721,7 +724,7 @@ class InteractionHandler {
|
|||||||
|
|
||||||
// 根据滚轮方向确定目标级别
|
// 根据滚轮方向确定目标级别
|
||||||
let targetLevelIndex: number;
|
let targetLevelIndex: number;
|
||||||
if (deltaY > 0) {
|
if (deltaY < 0) {
|
||||||
// 向上滚动,缩小(增加时间范围)
|
// 向上滚动,缩小(增加时间范围)
|
||||||
targetLevelIndex = Math.min(currentLevelIndex + 1, this.discreteZoomLevels.length - 1);
|
targetLevelIndex = Math.min(currentLevelIndex + 1, this.discreteZoomLevels.length - 1);
|
||||||
} else {
|
} else {
|
||||||
@ -734,7 +737,7 @@ class InteractionHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetRange = this.discreteZoomLevels[targetLevelIndex];
|
const targetRange = viewport.getWidth() * this.discreteZoomLevels[targetLevelIndex];
|
||||||
const zoomX = (this.zoomMode === ZoomMode.MarkMode && markX) ? markX : mouseX;
|
const zoomX = (this.zoomMode === ZoomMode.MarkMode && markX) ? markX : mouseX;
|
||||||
|
|
||||||
if (this.enableSmoothZoom) {
|
if (this.enableSmoothZoom) {
|
||||||
@ -764,15 +767,15 @@ class InteractionHandler {
|
|||||||
// 双指触摸,准备缩放
|
// 双指触摸,准备缩放
|
||||||
this.isTouchZooming = true;
|
this.isTouchZooming = true;
|
||||||
this.isDragging = false;
|
this.isDragging = false;
|
||||||
|
|
||||||
const touch1 = touches[0];
|
const touch1 = touches[0];
|
||||||
const touch2 = touches[1];
|
const touch2 = touches[1];
|
||||||
|
|
||||||
this.lastTouchDistance = Math.sqrt(
|
this.lastTouchDistance = Math.sqrt(
|
||||||
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
||||||
Math.pow(touch2.clientY - touch1.clientY, 2)
|
Math.pow(touch2.clientY - touch1.clientY, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
this.lastTouchCenter = {
|
this.lastTouchCenter = {
|
||||||
x: (touch1.clientX + touch2.clientX) / 2,
|
x: (touch1.clientX + touch2.clientX) / 2,
|
||||||
y: (touch1.clientY + touch2.clientY) / 2
|
y: (touch1.clientY + touch2.clientY) / 2
|
||||||
@ -790,24 +793,24 @@ class InteractionHandler {
|
|||||||
// 双指缩放
|
// 双指缩放
|
||||||
const touch1 = touches[0];
|
const touch1 = touches[0];
|
||||||
const touch2 = touches[1];
|
const touch2 = touches[1];
|
||||||
|
|
||||||
const currentDistance = Math.sqrt(
|
const currentDistance = Math.sqrt(
|
||||||
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
Math.pow(touch2.clientX - touch1.clientX, 2) +
|
||||||
Math.pow(touch2.clientY - touch1.clientY, 2)
|
Math.pow(touch2.clientY - touch1.clientY, 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentCenter = {
|
const currentCenter = {
|
||||||
x: (touch1.clientX + touch2.clientX) / 2,
|
x: (touch1.clientX + touch2.clientX) / 2,
|
||||||
y: (touch1.clientY + touch2.clientY) / 2
|
y: (touch1.clientY + touch2.clientY) / 2
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.lastTouchDistance && this.lastTouchCenter) {
|
if (this.lastTouchDistance && this.lastTouchCenter) {
|
||||||
const scaleChange = currentDistance / this.lastTouchDistance;
|
const scaleChange = currentDistance / this.lastTouchDistance;
|
||||||
|
|
||||||
// 转换为画布坐标
|
// 转换为画布坐标
|
||||||
const canvasRect = canvas.getBoundingClientRect();
|
const canvasRect = canvas.getBoundingClientRect();
|
||||||
const canvasX = currentCenter.x - canvasRect.left;
|
const canvasX = currentCenter.x - canvasRect.left;
|
||||||
|
|
||||||
if (this.discreteZoomLevels.length > 0) {
|
if (this.discreteZoomLevels.length > 0) {
|
||||||
// 对于离散缩放,当缩放变化超过阈值时触发
|
// 对于离散缩放,当缩放变化超过阈值时触发
|
||||||
const threshold = 1.1; // 10% 的变化阈值
|
const threshold = 1.1; // 10% 的变化阈值
|
||||||
@ -824,9 +827,9 @@ class InteractionHandler {
|
|||||||
this.lastTouchDistance = currentDistance;
|
this.lastTouchDistance = currentDistance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastTouchCenter = currentCenter;
|
this.lastTouchCenter = currentCenter;
|
||||||
|
|
||||||
} else if (touches.length === 1 && !this.isTouchZooming) {
|
} else if (touches.length === 1 && !this.isTouchZooming) {
|
||||||
// 单指拖拽 - 转换为画布坐标
|
// 单指拖拽 - 转换为画布坐标
|
||||||
const touch = touches[0];
|
const touch = touches[0];
|
||||||
@ -882,10 +885,14 @@ class RealTimeTimeline {
|
|||||||
// 初始化组件
|
// 初始化组件
|
||||||
this.viewport = new Viewport(canvas.width, canvas.height);
|
this.viewport = new Viewport(canvas.width, canvas.height);
|
||||||
this.viewport.goToTime(this.config.initialCenterTime);
|
this.viewport.goToTime(this.config.initialCenterTime);
|
||||||
if (this.config.initialTimeRange) {
|
if (this.config.initialTimeRange && !this.config.initialZoomLevel) {
|
||||||
this.viewport.setTimeRange(this.config.initialTimeRange);
|
this.viewport.setTimeRange(this.config.initialTimeRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.config.initialZoomLevel && !this.config.initialTimeRange) {
|
||||||
|
this.viewport.setTimeRange(this.viewport.getWidth() * this.config.initialZoomLevel);
|
||||||
|
}
|
||||||
|
|
||||||
this.scaleManager = new ScaleManager();
|
this.scaleManager = new ScaleManager();
|
||||||
this.interaction = new InteractionHandler(
|
this.interaction = new InteractionHandler(
|
||||||
this.config.zoomMode,
|
this.config.zoomMode,
|
||||||
@ -931,6 +938,7 @@ class RealTimeTimeline {
|
|||||||
const defaultConfig: Required<TimelineConfig> = {
|
const defaultConfig: Required<TimelineConfig> = {
|
||||||
initialCenterTime: Date.now(),
|
initialCenterTime: Date.now(),
|
||||||
initialTimeRange: 60 * 60 * 1000, // 1小时
|
initialTimeRange: 60 * 60 * 1000, // 1小时
|
||||||
|
initialZoomLevel: null,
|
||||||
zoomMode: ZoomMode.MousePosition,
|
zoomMode: ZoomMode.MousePosition,
|
||||||
zoomSensitivity: 0.001,
|
zoomSensitivity: 0.001,
|
||||||
discreteZoomLevels: defaultDiscreteZoomLevels,
|
discreteZoomLevels: defaultDiscreteZoomLevels,
|
||||||
@ -969,6 +977,7 @@ class RealTimeTimeline {
|
|||||||
return {
|
return {
|
||||||
initialCenterTime: config.initialCenterTime ?? defaultConfig.initialCenterTime,
|
initialCenterTime: config.initialCenterTime ?? defaultConfig.initialCenterTime,
|
||||||
initialTimeRange: config.initialTimeRange ?? defaultConfig.initialTimeRange,
|
initialTimeRange: config.initialTimeRange ?? defaultConfig.initialTimeRange,
|
||||||
|
initialZoomLevel: config.initialZoomLevel ?? defaultConfig.initialZoomLevel,
|
||||||
zoomMode: config.zoomMode ?? defaultConfig.zoomMode,
|
zoomMode: config.zoomMode ?? defaultConfig.zoomMode,
|
||||||
zoomSensitivity: config.zoomSensitivity ?? defaultConfig.zoomSensitivity,
|
zoomSensitivity: config.zoomSensitivity ?? defaultConfig.zoomSensitivity,
|
||||||
discreteZoomLevels: config.discreteZoomLevels ?? defaultConfig.discreteZoomLevels,
|
discreteZoomLevels: config.discreteZoomLevels ?? defaultConfig.discreteZoomLevels,
|
||||||
@ -1037,7 +1046,7 @@ class RealTimeTimeline {
|
|||||||
// 统一的结束拖拽处理(鼠标)
|
// 统一的结束拖拽处理(鼠标)
|
||||||
const handleMouseEnd = (e: MouseEvent) => {
|
const handleMouseEnd = (e: MouseEvent) => {
|
||||||
const { x, y } = getEventCoordinates(e);
|
const { x, y } = getEventCoordinates(e);
|
||||||
|
|
||||||
// 检查是否为点击(而非拖拽)
|
// 检查是否为点击(而非拖拽)
|
||||||
if (this.interaction.isClick()) {
|
if (this.interaction.isClick()) {
|
||||||
// 获取当前刻度信息用于吸附
|
// 获取当前刻度信息用于吸附
|
||||||
@ -1059,14 +1068,14 @@ class RealTimeTimeline {
|
|||||||
|
|
||||||
const handleTouchMove = (e: TouchEvent) => {
|
const handleTouchMove = (e: TouchEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// 对于单指触摸,更新显示位置
|
// 对于单指触摸,更新显示位置
|
||||||
if (e.touches.length === 1 && !this.interaction.getIsDragging()) {
|
if (e.touches.length === 1 && !this.interaction.getIsDragging()) {
|
||||||
const { x, y } = getEventCoordinates(e);
|
const { x, y } = getEventCoordinates(e);
|
||||||
this.mousePosition = { x, y };
|
this.mousePosition = { x, y };
|
||||||
this.showMouseIndicator = true;
|
this.showMouseIndicator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.interaction.handleTouchMove(e.touches, this.viewport, this.canvas);
|
this.interaction.handleTouchMove(e.touches, this.viewport, this.canvas);
|
||||||
this.requestRender();
|
this.requestRender();
|
||||||
};
|
};
|
||||||
@ -1485,6 +1494,11 @@ class RealTimeTimeline {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
currentLevel(): number {
|
||||||
|
return this.viewport.getZoomLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** 获取当前视口信息 */
|
/** 获取当前视口信息 */
|
||||||
getViewportInfo(): {
|
getViewportInfo(): {
|
||||||
centerTime: number;
|
centerTime: number;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user