feat: 新增画布视图状态管理Hook,支持保存、恢复视图状态及获取视图状态信息
This commit is contained in:
parent
9357cdbb4a
commit
3b6f429442
306
src/services/useViewState.ts
Normal file
306
src/services/useViewState.ts
Normal file
@ -0,0 +1,306 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
import type { EditorService } from './editor.service';
|
||||
|
||||
/**
|
||||
* 画布视图状态接口
|
||||
*/
|
||||
interface ViewState {
|
||||
/** 缩放比例 */
|
||||
scale: number;
|
||||
/** 中心点X坐标 */
|
||||
centerX: number;
|
||||
/** 中心点Y坐标 */
|
||||
centerY: number;
|
||||
/** 保存时间戳 */
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布视图状态管理Hook
|
||||
* 用于保存和恢复画布的缩放比例和中心坐标
|
||||
*/
|
||||
export function useViewState() {
|
||||
const isSaving = ref(false);
|
||||
const isRestoring = ref(false);
|
||||
|
||||
/**
|
||||
* 生成localStorage的key
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const getStorageKey = (sceneId: string, groupId?: string): string => {
|
||||
return groupId ? `view-state-${groupId}-${sceneId}` : `view-state-${sceneId}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前画布的视图状态
|
||||
* @param editor 编辑器服务实例
|
||||
*/
|
||||
const getCurrentViewState = (editor: EditorService): ViewState => {
|
||||
console.groupCollapsed('🔍 [getCurrentViewState] - 获取当前视图状态');
|
||||
|
||||
// 1. 查找画布上所有的点位
|
||||
const points = editor.find('point');
|
||||
console.log('📍 画布上的点位数量:', points.length);
|
||||
|
||||
// 打印所有点位的原始数据,以便检查
|
||||
console.log(
|
||||
'📊 点位原始数据:',
|
||||
points.map((point) => ({
|
||||
id: point.id,
|
||||
x: point.x,
|
||||
y: point.y,
|
||||
name: point.name,
|
||||
text: point.text,
|
||||
})),
|
||||
);
|
||||
|
||||
// 2. 计算所有点位的边界
|
||||
let minX = Infinity;
|
||||
let minY = Infinity;
|
||||
let maxX = -Infinity;
|
||||
let maxY = -Infinity;
|
||||
|
||||
// 尝试获取编辑器中的原始场景数据
|
||||
// 编辑器中的#originalSceneData是Partial<StandardScene>类型
|
||||
type SceneDataType = { ratio?: number; width?: number; height?: number };
|
||||
let originalSceneData: SceneDataType = { ratio: 1, width: 0, height: 0 };
|
||||
|
||||
try {
|
||||
// 尝试通过编辑器的save方法获取场景数据
|
||||
const sceneJson = editor.save();
|
||||
if (sceneJson) {
|
||||
const sceneData = JSON.parse(sceneJson);
|
||||
// 提取需要的字段
|
||||
originalSceneData = {
|
||||
ratio: sceneData.ratio || 1,
|
||||
width: sceneData.width || 0,
|
||||
height: sceneData.height || 0,
|
||||
};
|
||||
console.log('🗺️ 从场景数据中提取的信息:', originalSceneData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取编辑器原始数据失败:', error);
|
||||
console.log('⚠️ 使用默认值');
|
||||
}
|
||||
|
||||
const ratio = originalSceneData?.ratio || 1;
|
||||
const mapWidth = originalSceneData?.width || 0;
|
||||
const mapHeight = originalSceneData?.height || 0;
|
||||
|
||||
console.log('🗺️ 地图信息:', { ratio, mapWidth, mapHeight });
|
||||
|
||||
points.forEach((point) => {
|
||||
// 获取点位的实际坐标(画布上显示的坐标)
|
||||
const { x: displayX = 0, y: displayY = 0 } = editor.getPointRect(point) ?? {};
|
||||
|
||||
// 将显示坐标转换回原始坐标系统
|
||||
// 这里我们尝试逆转编辑器中的坐标转换逻辑
|
||||
// 根据editor.service.ts中的#transformCoordinate方法
|
||||
|
||||
// 1. 从显示坐标转回中心点原点坐标
|
||||
const centerX = displayX;
|
||||
const centerY = displayY;
|
||||
|
||||
// 2. 从中心点原点转回左上角原点坐标
|
||||
const topLeftX = (centerX + mapWidth / 2) * ratio;
|
||||
const topLeftY = (mapHeight / 2 - centerY) * ratio;
|
||||
|
||||
console.log(
|
||||
`点位 ${point.id || point.name || '未命名'}: 显示坐标(${displayX}, ${displayY}), 原始坐标(${topLeftX.toFixed(1)}, ${topLeftY.toFixed(1)})`,
|
||||
);
|
||||
|
||||
// 使用原始坐标系统的坐标计算边界
|
||||
minX = Math.min(minX, topLeftX);
|
||||
minY = Math.min(minY, topLeftY);
|
||||
maxX = Math.max(maxX, topLeftX);
|
||||
maxY = Math.max(maxY, topLeftY);
|
||||
});
|
||||
|
||||
// 如果没有找到任何点位,使用默认值
|
||||
if (minX === Infinity || minY === Infinity || maxX === -Infinity || maxY === -Infinity) {
|
||||
minX = 0;
|
||||
minY = 0;
|
||||
maxX = 0;
|
||||
maxY = 0;
|
||||
}
|
||||
|
||||
// 3. 计算边界的中心点(在原始坐标系统中)
|
||||
const centerX = (minX + maxX) / 2;
|
||||
const centerY = (minY + maxY) / 2;
|
||||
|
||||
console.log('🔍 原始坐标系中的点位边界:', { minX, minY, maxX, maxY });
|
||||
console.log('🎯 原始坐标系中的中心点:', { centerX, centerY });
|
||||
|
||||
// 4. 将中心点从原始坐标系统转换回编辑器的显示坐标系统
|
||||
// 先根据ratio进行缩放
|
||||
const scaledCenterX = centerX / ratio;
|
||||
const scaledCenterY = centerY / ratio;
|
||||
|
||||
// 再进行坐标系转换:左上角原点 -> 中心点原点
|
||||
const displayCenterX = scaledCenterX - mapWidth / 2;
|
||||
const displayCenterY = mapHeight / 2 - scaledCenterY;
|
||||
|
||||
console.log('🎯 显示坐标系中的中心点:', { x: displayCenterX, y: displayCenterY });
|
||||
console.log('⚙️ 当前画布状态 (origin, scale):', {
|
||||
origin: JSON.parse(JSON.stringify(editor.store.data.origin)),
|
||||
scale: editor.store.data.scale,
|
||||
});
|
||||
|
||||
const finalState: ViewState = {
|
||||
scale: editor.store.data.scale || 1,
|
||||
centerX: displayCenterX,
|
||||
centerY: displayCenterY,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
console.log('💾 保存的最终视图状态:', finalState);
|
||||
console.groupEnd();
|
||||
return finalState;
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存当前视图状态到localStorage
|
||||
* @param editor 编辑器服务实例
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const saveViewState = async (editor: EditorService, sceneId: string, groupId?: string): Promise<void> => {
|
||||
isSaving.value = true;
|
||||
try {
|
||||
const viewState = getCurrentViewState(editor);
|
||||
const storageKey = getStorageKey(sceneId, groupId);
|
||||
localStorage.setItem(storageKey, JSON.stringify(viewState));
|
||||
} catch (error) {
|
||||
console.error('保存视图状态失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 从localStorage恢复视图状态
|
||||
* @param editor 编辑器服务实例
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const restoreViewState = async (editor: EditorService, sceneId: string, groupId?: string): Promise<boolean> => {
|
||||
console.groupCollapsed('🔄 [restoreViewState] - 恢复视图状态');
|
||||
isRestoring.value = true;
|
||||
try {
|
||||
const storageKey = getStorageKey(sceneId, groupId);
|
||||
const savedState = localStorage.getItem(storageKey);
|
||||
|
||||
if (!savedState) {
|
||||
console.warn('⚠️ 未找到保存的视图状态');
|
||||
console.groupEnd();
|
||||
return false;
|
||||
}
|
||||
|
||||
const viewState: ViewState = JSON.parse(savedState);
|
||||
console.log('💾 读取到的视图状态:', viewState);
|
||||
|
||||
// 1. 设置缩放比例
|
||||
editor.scale(viewState.scale);
|
||||
console.log(`🔎 1. 设置缩放比例为: ${viewState.scale}`);
|
||||
|
||||
// 2. 尝试跳转到画布上的一个随机点位
|
||||
if (viewState.centerX !== undefined && viewState.centerY !== undefined) {
|
||||
try {
|
||||
// 查找画布上所有的点位
|
||||
const points = editor.find('point');
|
||||
console.log('📍 画布上的点位数量:', points.length);
|
||||
|
||||
// 如果没有找到点位或跳转失败,创建一个临时点位
|
||||
console.log('⚠️ 创建临时点位进行跳转');
|
||||
const centerPointId = 'view-center-point-' + Date.now();
|
||||
|
||||
// 使用 addPoint 方法添加临时点位
|
||||
// 这个方法会处理坐标转换和其他必要的设置
|
||||
await editor.addPoint(
|
||||
{ x: viewState.centerX, y: viewState.centerY }, // 点位坐标
|
||||
1, // 点位类型:普通点
|
||||
centerPointId, // 点位ID
|
||||
);
|
||||
|
||||
// 设置点位为不可见
|
||||
editor.setValue({ id: centerPointId, visible: false }, { render: false, history: false, doEvent: false });
|
||||
|
||||
// 使用 gotoById 方法跳转到临时点
|
||||
editor.gotoById(centerPointId);
|
||||
console.log('🎯 使用 gotoById 跳转到临时点:', { x: viewState.centerX, y: viewState.centerY });
|
||||
|
||||
// 延迟一段时间后移除临时点
|
||||
setTimeout(() => {
|
||||
const tempPen = editor.getPenById(centerPointId);
|
||||
if (tempPen) {
|
||||
editor.delete([tempPen]);
|
||||
}
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error('使用 gotoById 跳转失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 触发重绘
|
||||
// editor.render();
|
||||
console.log('🎨 3. 重绘画布完成');
|
||||
|
||||
console.groupEnd();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('恢复视图状态失败:', error);
|
||||
console.groupEnd();
|
||||
return false;
|
||||
} finally {
|
||||
isRestoring.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查是否存在保存的视图状态
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const hasViewState = (sceneId: string, groupId?: string): boolean => {
|
||||
const storageKey = getStorageKey(sceneId, groupId);
|
||||
return localStorage.getItem(storageKey) !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 清除保存的视图状态
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const clearViewState = (sceneId: string, groupId?: string): void => {
|
||||
const storageKey = getStorageKey(sceneId, groupId);
|
||||
localStorage.removeItem(storageKey);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取保存的视图状态信息
|
||||
* @param sceneId 场景ID
|
||||
* @param groupId 群组ID (可选)
|
||||
*/
|
||||
const getViewStateInfo = (sceneId: string, groupId?: string): ViewState | null => {
|
||||
try {
|
||||
const storageKey = getStorageKey(sceneId, groupId);
|
||||
const savedState = localStorage.getItem(storageKey);
|
||||
return savedState ? JSON.parse(savedState) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isSaving,
|
||||
isRestoring,
|
||||
saveViewState,
|
||||
restoreViewState,
|
||||
hasViewState,
|
||||
clearViewState,
|
||||
getViewStateInfo,
|
||||
getCurrentViewState,
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user