| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- import { computed, h, onBeforeUnmount, onMounted, ref, render } from 'vue';
- import Konva from 'konva';
- import cameraImg from '@/assets/camera/cameraImg.png';
- import favoritesImg from '@/assets/camera/favorites.png';
- import OptBar from '../components/CameraOptBar.vue';
- import DefaultTip from '../components/DefaultTip.vue';
- import { TipPositionEnum } from '../type';
- import { ElMessage } from 'element-plus';
- import { useGlobSetting } from '@/hooks/setting';
- import urlJoin from 'url-join';
- import useMiniMap from '../use-mini-map';
- import { storeToRefs } from 'pinia';
- export function useMapEditor() {
- const miniMap = useMiniMap();
- const { shopCameraList } = storeToRefs(miniMap);
- // let initWidth; // 默认宽度
- // let initHeight; // 默认高度
- let stage: Konva.Stage | null = null;
- let layer: Konva.Layer | null = null;
- let copyLayer: Konva.Layer | null = null;
- let defaultIcon: Konva.Image | null = null; // 默认相机的图标shape
- const addedCameras = ref<string[]>([]); // 已添加相机列表
- const activeGroup = ref<Konva.Group | null>(null); // transformer激活的相机
- const defaultCameraId = ref(''); // 默认相机的ID
- let optBlock: HTMLDivElement | null = null; // 鼠标右击弹出的选项组
- let defaultTip: HTMLDivElement | null = null; // 默认相机悬浮tip
- let isTransform = false; // 是否再变换中
- const activeCameraId = computed(() => activeGroup.value?.id()); // 当前选中相机ID
- const bgImgUrl = ref<string>('');
- const globSetting = useGlobSetting();
- /** 容器初始化 */
- const initContainer = (opt: Konva.StageConfig) => {
- // initWidth = opt.width || 0;
- // initHeight = opt.height || 0;
- stage = new Konva.Stage(opt);
- stage.on('click tap', handleStageClick);
- window.stage = stage;
- layer = new Konva.Layer();
- copyLayer = new Konva.Layer();
- stage.add(layer);
- stage.add(copyLayer);
- addDefaultIcon();
- };
- /** 初始生成默认相机的图标shape,但不可见 */
- const addDefaultIcon = () => {
- const favImg = new Image();
- favImg.onload = () => {
- defaultIcon = new Konva.Image({
- x: 18,
- y: -16,
- width: 16,
- height: 16,
- image: favImg,
- id: 'defaultIcon',
- visible: false,
- rotation: 0,
- });
- bindBaseEvt(defaultIcon);
- layer?.add(defaultIcon);
- layer?.batchDraw();
- };
- favImg.src = favoritesImg;
- };
- /** 更换背景图时根据图片大小重置容器宽高 */
- const resizeContainer = (width, height) => {
- // const newWidth = width > initWidth ? width : initWidth;
- // const newHeight = height > initHeight ? height : initHeight;
- // stage?.width(newWidth);
- // stage?.height(newHeight);
- stage?.width(width);
- stage?.height(height);
- };
- /** 添加背景 */
- const addBg = () => {
- const imgUrl = urlJoin(globSetting.imgUrl!, bgImgUrl.value);
- const bgNode = layer?.find('#bgImg')[0] as Konva.Image;
- const bgImg = new Image();
- bgImg.onload = () => {
- // 判断是否已有背景
- if (!bgNode) {
- const mapBg = new Konva.Image({
- x: 0,
- y: 0,
- image: bgImg,
- width: bgImg.width,
- height: bgImg.height,
- id: 'bgImg',
- });
- layer?.add(mapBg);
- mapBg.moveToBottom();
- } else {
- bgNode.width(bgImg.width);
- bgNode.height(bgImg.height);
- bgNode.image(bgImg);
- }
- resizeContainer(bgImg.width, bgImg.height);
- layer?.batchDraw();
- };
- bgImg.src = imgUrl;
- };
- /** 变更需要激活transform的相机 */
- const attachTransformer = (group: Konva.Group): Konva.Transformer => {
- activeGroup.value?.draggable(false);
- activeGroup.value = group;
- group.draggable(true);
- stage!.find('Transformer')[0]?.destroy(); // 清除现有transformer
- const id = group.id();
- const tr = new Konva.Transformer({
- keepRatio: true,
- rotateAnchorOffset: 30,
- rotationSnaps: [0, 45, 90, 135, 180, 225, 270, 315],
- enabledAnchors: ['top-left', 'top-right', 'bottom-left', 'bottom-right'],
- id: 'tr_' + id,
- });
- tr.nodes([group]);
- layer?.add(tr);
- layer?.draw();
- group.on('dragstart', handleDragStart);
- group.on('dragstart', handleDragEnd);
- return tr;
- };
- /** 添加相机 */
- const addCamera = (id: string) => {
- const group = new Konva.Group({
- x: 50,
- y: 50,
- id,
- draggable: true,
- name: 'group',
- });
- const camImg = new Image();
- camImg.onload = () => {
- const cameraIcon = new Konva.Image({
- width: 52,
- height: 37,
- image: camImg,
- name: 'image',
- });
- group.add(cameraIcon);
- layer?.add(group);
- bindBaseEvt(cameraIcon);
- const tr = attachTransformer(group); // 添加的相机默认激活transformer
- addedCameras.value.push(id);
- // 如果是唯一相机,设置为默认相机
- if (addedCameras.value.length === 1) {
- defaultIcon?.show();
- setDefaultCamera(group, tr);
- tr.forceUpdate();
- }
- };
- camImg.src = cameraImg;
- };
- /** 变更默认相机 */
- const setDefaultCamera = (node: Konva.Group, tr?: Konva.Transformer) => {
- defaultIcon?.moveTo(node);
- tr?.forceUpdate();
- defaultCameraId.value = node.id();
- };
- /** 创建右键选项组 */
- const createOptBlock = (node: Konva.Group, x: number, y: number) => {
- const id = node.id();
- optBlock = document.createElement('div') as HTMLDivElement;
- optBlock.setAttribute('style', `position: absolute; left: ${x}px; top: ${y}px;`);
- const optBar = h(OptBar, {
- disabled: id === defaultCameraId.value,
- onSetDefault: () => {
- const tr = layer?.find(`#tr_${id}`)[0] as Konva.Transformer;
- setDefaultCamera(node, tr);
- destoryOptBlock();
- },
- });
- render(optBar, optBlock);
- const parentEl = document.getElementById('drawContainer') as HTMLDivElement;
- parentEl.append(optBlock);
- };
- /** 删除右键选项组 */
- const destoryOptBlock = () => {
- optBlock?.remove();
- optBlock = null;
- };
- /** 创建默认tip */
- const createDefaultTip = (x: number, y: number, pos: TipPositionEnum) => {
- if (isTransform) {
- return;
- }
- defaultTip = document.createElement('div') as HTMLDivElement;
- defaultTip.setAttribute('style', `position: absolute; left: ${x}px; top: ${y}px;`);
- const tipInstance = h(DefaultTip, { position: pos });
- render(tipInstance, defaultTip);
- const parentEl = document.getElementById('drawContainer') as HTMLDivElement;
- parentEl.append(defaultTip);
- };
- /** 删除默认tip */
- const destoryDefaultTip = () => {
- defaultTip?.remove();
- defaultTip = null;
- };
- /** 删除相机 */
- const deleteCamera = () => {
- // 判断是否为默认相机,默认相机不允许删除
- if (activeGroup.value?.id() === defaultCameraId.value) {
- ElMessage.error({
- message: '无法删除默认相机',
- });
- return;
- }
- const index = addedCameras.value.findIndex((item) => item === activeGroup.value?.id());
- index >= 0 && addedCameras.value.splice(index, 1);
- activeGroup.value?.destroy();
- stage!.find('Transformer')[0]?.destroy();
- layer?.draw();
- };
- /** 鼠标悬浮事件 */
- const handleMouseOver = (e) => {
- // 禁用浏览器默认鼠标事件
- document.oncontextmenu = () => {
- return false;
- };
- const group = e.target.parent;
- // 如果悬浮的相机是默认相机,弹出默认tip
- if (group.id() === defaultCameraId.value) {
- let pos = TipPositionEnum.TOP;
- const tipPosition = defaultIcon?.absolutePosition();
- let x = Number(tipPosition?.x.toFixed(2)) || 0;
- let y = Number(tipPosition?.y.toFixed(2)) || 0;
- const angle = group.rotation() >= 0 ? group.rotation() : group.rotation() + 360;
- if (angle >= 30) {
- if (angle <= 150) {
- pos = TipPositionEnum.RIGHT;
- x += 26;
- y -= 17;
- } else if (angle <= 210) {
- pos = TipPositionEnum.BOTTOM;
- y += 26;
- x -= 50;
- } else {
- pos = TipPositionEnum.LEFT;
- x -= 121;
- y -= 25;
- }
- } else {
- y -= 61;
- x -= 43;
- }
- createDefaultTip(x, y, pos);
- }
- };
- /** 鼠标离开事件 */
- const handleMouseLeave = () => {
- // 恢复浏览器默认事件
- document.oncontextmenu = () => {
- return true;
- };
- defaultTip && destoryDefaultTip();
- };
- /** 开始拖拽事件 */
- const handleDragStart = () => {
- isTransform = true;
- destoryDefaultTip();
- destoryOptBlock();
- };
- /** 结束拖拽事件 */
- const handleDragEnd = () => {
- isTransform = false;
- };
- /** 全局点击事件 */
- const handleStageClick = (e) => {
- // 点击舞台取消现有激活的transformer
- if (e.target === stage) {
- stage!.find('Transformer')[0].destroy();
- layer!.draw();
- return;
- }
- // 判断点击对象是否为相机
- if (!e.target.hasName('image')) {
- return;
- }
- const parent = e.target.parent;
- if (!parent.hasName('group')) {
- return;
- }
- const group = e.target.parent;
- attachTransformer(group);
- // 判断是否为右键点击
- if (e.evt.button === 2) {
- createOptBlock(group, e.evt.offsetX + 20, e.evt.offsetY);
- }
- };
- /** 键盘点击事件 */
- const handleKeyDown = (e) => {
- // 删除键
- if (e.keyCode === 46 || e.code === 'Delete') {
- deleteCamera();
- }
- };
- // 基础监听事件绑定
- const bindBaseEvt = (node: Konva.Node) => {
- // node.on('transform', handleDragStart);
- // node.on('transformend', handleDragEnd);
- node.on('mouseover', handleMouseOver);
- node.on('mouseleave', handleMouseLeave);
- };
- /** 输出布局json */
- const toJson = () => {
- const json = stage!.toJSON();
- const cameras = JSON.parse(json)
- .children[0].children.filter((node) => node.className === 'Group')
- .map((item) => {
- return {
- cameraId: item.attrs.id,
- rotation: Number((item.attrs.rotation | 0).toFixed(2)),
- x: Math.round(item.attrs.x | 0),
- y: Math.round(item.attrs.y | 0),
- scaleX: Number((item.attrs.scaleX | 1).toFixed(1)),
- scaleY: Number((item.attrs.scaleY | 1).toFixed(1)),
- url: shopCameraList.value.find((cam) => cam.code === item.attrs.id)?.pushstreamIp || '',
- };
- });
- const layout = {
- bgInfo: {
- bgImg: bgImgUrl.value,
- width: layer?.find('#bgImg')[0].width(),
- height: layer?.find('#bgImg')[0].height(),
- },
- defaultCameraId: defaultCameraId.value,
- cameraList: cameras,
- };
- return JSON.stringify(layout);
- };
- /** 导入布局json */
- const createMap = (layout) => {
- // const layout = JSON.parse(json);
- bgImgUrl.value = layout.bgInfo.bgImg;
- addBg();
- layout.cameraList.forEach((camera) => {
- const group = new Konva.Group({
- x: camera.x,
- y: camera.y,
- id: camera.cameraId,
- rotation: camera.rotation,
- scaleX: camera.scaleX,
- scaleY: camera.scaleY,
- draggable: false,
- name: 'group',
- });
- const camImg = new Image();
- camImg.onload = () => {
- const cameraIcon = new Konva.Image({
- width: 52,
- height: 37,
- image: camImg,
- name: 'image',
- });
- group.add(cameraIcon);
- layer?.add(group);
- bindBaseEvt(cameraIcon);
- addedCameras.value.push(camera.cameraId);
- if (camera.cameraId === layout.defaultCameraId) {
- setDefaultCamera(group);
- defaultIcon?.show();
- }
- if (addedCameras.value.length === layout.cameraList.length) {
- layer?.batchDraw();
- }
- };
- camImg.src = cameraImg;
- });
- };
- const resetMap = () => {
- defaultIcon?.moveTo(copyLayer);
- layer?.clear();
- layer?.removeChildren();
- defaultIcon?.moveTo(layer);
- addedCameras.value = [];
- activeGroup.value = null;
- defaultCameraId.value = '';
- isTransform = false;
- bgImgUrl.value = '';
- };
- onMounted(() => {
- window.addEventListener('keydown', handleKeyDown);
- });
- onBeforeUnmount(() => {
- window.removeEventListener('keydown', handleKeyDown);
- });
- return {
- defaultCameraId,
- activeCameraId,
- addedCameras,
- bgImgUrl,
- initContainer,
- addBg,
- addCamera,
- destoryOptBlock,
- toJson,
- createMap,
- resetMap,
- };
- }
- export default useMapEditor;
|