当前位置: 首页 > news >正文

vue3+ts实现拖拽缩放,全屏

DragResizeZoom.vue

背景:在cesium大屏项目需要拖拽,缩放模块,还需要全屏。

功能如下:
1. 拖拽组件
2. 拉升组件长宽
3. 对角线拉升会比例缩放
4. 双击组件则全屏
5. 复原长宽和位置

使用案例:

<script setup lang="ts">
import { ref } from "vue";
import DragResizeZoom from "./components/DragResizeZoom.vue";
const dragResizeZoom = ref<InstanceType<typeof DragResizeZoom> | null>(null);function handleReset() {dragResizeZoom.value?.resetPosition();
}
</script><template><div id="app"><button @click="handleReset">reset</button><DragResizeZoomref="dragResizeZoom":initial-x="100":initial-y="100":initial-width="400":initial-height="300"xdirection="right"><div class="c1"><div><h1>这是一个标题</h1></div><div>这是一个段落fdsafdsafdsafdsfdsa</div></div></DragResizeZoom></div>
</template><style scoped style="less">
#app {width: 100vw;height: 100vh;background-color: #b4b4b4;
}
.c1 {/* width: 300px;height: 300px; */background-color: #fff;color: pink;
}
</style>
<template><divref="containerRef"class="drag-resize-zoom":style="containerStyle"@mousedown="handleMouseDown"@mouseenter="showHandles = true"@mouseleave="showHandles = false"@dblclick="toggleFullscreen"><slot></slot><!-- Resize handles --><!-- <div class="resize-handle resize-nw" @mousedown="(e: any) => handleResizeMouseDown(e, 'nw')"></div> --><!-- <divclass="resize-handle resize-ne"@mousedown="(e: any) => handleResizeMouseDown(e, 'ne')"></div> --><!-- <divclass="resize-handle resize-sw"@mousedown="(e: any) => handleResizeMouseDown(e, 'sw')"></div> --><divclass="resize-handle resize-se":class="{ 'handle-visible': showHandles }"@mousedown="(e: any) => handleResizeMouseDown(e, 'se')"></div><!-- <div class="resize-handle resize-n" @mousedown="(e: any) => handleResizeMouseDown(e, 'n')"></div> --><divclass="resize-handle resize-s":class="{ 'handle-visible': showHandles }"@mousedown="(e: any) => handleResizeMouseDown(e, 's')"></div><!-- <divclass="resize-handle resize-w"@mousedown="(e: any) => handleResizeMouseDown(e, 'w')"></div> --><divclass="resize-handle resize-e":class="{ 'handle-visible': showHandles }"@mousedown="(e: any) => handleResizeMouseDown(e, 'e')"></div></div>
</template><script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, computed } from "vue";// 添加props定义
const props = defineProps({initialX: {type: Number,default: 0,},initialY: {type: Number,default: 0,},initialWidth: {type: Number,default: 300,},initialHeight: {type: Number,default: 300,},// 添加xdirection属性,支持'left'和'right'值xdirection: {type: String,default: "left",validator: (value: string) => ["left", "right"].includes(value),},
});const containerRef = ref<HTMLElement | null>(null);// 添加控制句柄显示的状态
const showHandles = ref(false);// 添加全屏状态
const isFullscreen = ref(false);// 组件状态
const state = ref({x: props.initialX, // 使用props.initialX初始化y: props.initialY, // 使用props.initialY初始化width: props.initialWidth, // 使用props.initialWidth初始化height: props.initialHeight, // 使用props.initialHeight初始化zoom: 1,isDragging: false,isResizing: false,dragStartX: 0,dragStartY: 0,startX: 0,startY: 0,startWidth: 0,startHeight: 0,resizeDirection: "",// 添加right和bottom属性right: undefined as number | undefined,bottom: undefined as number | undefined,
});// 容器样式
const containerStyle = computed(() => {// 根据xdirection决定使用left还是right定位if (props.xdirection === "right") {return {position: "absolute" as const,right: state.value.x + "px",top: state.value.y + "px",// 添加bottom,当它有值时使用...(state.value.bottom !== undefined && {bottom: state.value.bottom + "px",}),width: state.value.width + "px",height: state.value.height + "px",transform: `scale(${state.value.zoom})`,transformOrigin: "top left",};} else {return {position: "absolute" as const,left: state.value.x + "px",top: state.value.y + "px",// 添加right和bottom,当它们有值时使用...(state.value.right !== undefined && {right: state.value.right + "px",}),...(state.value.bottom !== undefined && {bottom: state.value.bottom + "px",}),width: state.value.width + "px",height: state.value.height + "px",transform: `scale(${state.value.zoom})`,transformOrigin: "top left",};}
});// 处理鼠标按下事件(拖拽)
const handleMouseDown = (e: MouseEvent) => {if ((e.target as HTMLElement).classList.contains("resize-handle")) {return;}state.value.isDragging = true;state.value.dragStartX = e.clientX;state.value.dragStartY = e.clientY;state.value.startX = state.value.x;state.value.startY = state.value.y;document.addEventListener("mousemove", handleMouseMove);document.addEventListener("mouseup", handleMouseUp);
};// 处理鼠标移动事件(拖拽)
const handleMouseMove = (e: MouseEvent) => {if (state.value.isDragging) {// 根据xdirection决定如何更新x坐标if (props.xdirection === "right") {state.value.x = state.value.startX - (e.clientX - state.value.dragStartX);} else {state.value.x = state.value.startX + (e.clientX - state.value.dragStartX);}state.value.y = state.value.startY + (e.clientY - state.value.dragStartY);}if (state.value.isResizing) {resizeComponent(e);}
};// 处理鼠标释放事件
const handleMouseUp = () => {state.value.isDragging = false;state.value.isResizing = false;document.removeEventListener("mousemove", handleMouseMove);document.removeEventListener("mouseup", handleMouseUp);
};// 处理调整大小鼠标按下事件
const handleResizeMouseDown = (e: MouseEvent, direction: string) => {e.stopPropagation();state.value.isResizing = true;state.value.resizeDirection = direction;state.value.startX = e.clientX;state.value.startY = e.clientY;state.value.startWidth = state.value.width;state.value.startHeight = state.value.height;document.addEventListener("mousemove", handleMouseMove);document.addEventListener("mouseup", handleMouseUp);
};// 调整组件大小
const resizeComponent = (e: MouseEvent) => {const deltaX = e.clientX - state.value.startX;const deltaY = e.clientY - state.value.startY;const direction = state.value.resizeDirection;// 记录调整前的尺寸用于计算缩放const oldWidth = state.value.width;const oldHeight = state.value.height;const oldX = state.value.x;const oldY = state.value.y;// 重置right和bottom值state.value.right = undefined;state.value.bottom = undefined;switch (direction) {case "nw": // 西北角state.value.width = Math.max(50, state.value.startWidth - deltaX);state.value.height = Math.max(50, state.value.startHeight - deltaY);// 修改:根据需求调整left和top定位if (state.value.width !== oldWidth || state.value.height !== oldHeight) {if (props.xdirection === "right") {state.value.x = oldX - (state.value.startWidth - state.value.width);} else {state.value.x = e.clientX;}state.value.y = oldY + (state.value.startHeight - state.value.height);}break;case "ne": // 东北角state.value.width = Math.max(50, state.value.startWidth + deltaX);state.value.height = Math.max(50, state.value.startHeight - deltaY);break;case "sw": // 西南角state.value.width = Math.max(50, state.value.startWidth - deltaX);state.value.height = Math.max(50, state.value.startHeight + deltaY);// 使用right定位if (state.value.width !== oldWidth) {if (props.xdirection === "right") {state.value.x = oldX - (state.value.startWidth - state.value.width);} else {state.value.right = window.innerWidth - oldX - state.value.startWidth;state.value.x = oldX + (state.value.startWidth - state.value.width);}}break;case "se": // 东南角state.value.width = Math.max(50, state.value.startWidth + deltaX);state.value.height = Math.max(50, state.value.startHeight + deltaY);break;case "n": // 上边state.value.height = Math.max(50, state.value.startHeight - deltaY);if (state.value.height !== oldHeight) {state.value.y = oldY + (state.value.startHeight - state.value.height);}break;case "s": // 下边state.value.height = Math.max(50, state.value.startHeight + deltaY);break;case "w": // 左边state.value.width = Math.max(50, state.value.startWidth - deltaX);if (state.value.width !== oldWidth) {if (props.xdirection === "right") {state.value.x = oldX - (state.value.startWidth - state.value.width);} else {state.value.x = oldX + (state.value.startWidth - state.value.width);}}break;case "e": // 右边state.value.width = Math.max(50, state.value.startWidth + deltaX);break;}// 计算缩放比例(仅对角调整时应用缩放)if (direction === "se" ||direction === "ne" ||direction === "sw" ||direction === "nw") {const widthRatio = state.value.width / state.value.startWidth;const heightRatio = state.value.height / state.value.startHeight;state.value.zoom = Math.min(widthRatio, heightRatio);}
};// 添加全屏切换功能
const toggleFullscreen = () => {if (!containerRef.value) return;const element = containerRef.value;if (!isFullscreen.value) {// 进入全屏if (element.requestFullscreen) {element.requestFullscreen();} else if ((element as any).mozRequestFullScreen) {// Firefox(element as any).mozRequestFullScreen();} else if ((element as any).webkitRequestFullscreen) {// Chrome, Safari and Opera(element as any).webkitRequestFullscreen();} else if ((element as any).msRequestFullscreen) {// IE/Edge(element as any).msRequestFullscreen();}} else {// 退出全屏if (document.exitFullscreen) {document.exitFullscreen();} else if ((document as any).mozCancelFullScreen) {// Firefox(document as any).mozCancelFullScreen();} else if ((document as any).webkitExitFullscreen) {// Chrome, Safari and Opera(document as any).webkitExitFullscreen();} else if ((document as any).msExitFullscreen) {// IE/Edge(document as any).msExitFullscreen();}}isFullscreen.value = !isFullscreen.value;
};// 监听全屏变化事件
const handleFullscreenChange = () => {isFullscreen.value = !!(document.fullscreenElement ||(document as any).mozFullScreenElement ||(document as any).webkitFullscreenElement ||(document as any).msFullscreenElement);
};// 添加恢复初始位置的函数
const resetPosition = () => {state.value.x = props.initialX;state.value.y = props.initialY;state.value.width = props.initialWidth;state.value.height = props.initialHeight;state.value.zoom = 1;state.value.right = undefined;state.value.bottom = undefined;
};// 暴露函数给父组件使用
defineExpose({resetPosition,
});// 清理事件监听器
onBeforeUnmount(() => {document.removeEventListener("mousemove", handleMouseMove);document.removeEventListener("mouseup", handleMouseUp);document.removeEventListener("fullscreenchange", handleFullscreenChange);document.removeEventListener("webkitfullscreenchange",handleFullscreenChange);document.removeEventListener("mozfullscreenchange", handleFullscreenChange);document.removeEventListener("MSFullscreenChange", handleFullscreenChange);
});// 添加全屏事件监听
onMounted(() => {document.addEventListener("fullscreenchange", handleFullscreenChange);document.addEventListener("webkitfullscreenchange", handleFullscreenChange);document.addEventListener("mozfullscreenchange", handleFullscreenChange);document.addEventListener("MSFullscreenChange", handleFullscreenChange);
});
</script><style scoped>
.drag-resize-zoom {/* margin: 3px; */position: relative;cursor: move;z-index: 9999;overflow: hidden;
}.resize-handle {position: absolute;/* background: linear-gradient(135deg, #4a90e2, #1a3a6a); */background-color: #5a9fff;z-index: 9999;opacity: 0; /* 默认隐藏 */transition: opacity 0.2s ease;
}.handle-visible {opacity: 1;
}.resize-handle::before {content: "";position: absolute;top: -3px;left: -3px;right: -3px;bottom: -3px;opacity: 0;
}.resize-handle:hover::before {opacity: 1;
}.resize-se {width: 12px;height: 12px;background: transparent;border: none;box-shadow: none;top: auto;right: 0px;bottom: 0px;cursor: se-resize; /* 添加斜对角鼠标样式 */
}.resize-se::before {display: none;
}.resize-se::after {content: "";position: absolute;width: 0;height: 0;border-style: solid;border-width: 0 0 12px 12px;border-color: transparent transparent #4a90e2 transparent;box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);/* transition: all 0.2s ease; */
}.resize-se:hover::after {border-color: transparent transparent #5aa0ff transparent;box-shadow: 2px 2px 4px rgba(74, 144, 226, 0.8);transform: scale(1.1);
}.resize-s {/* 下边句柄改为瘦高 */width: 100%;height: 3px;/* border-radius: 1px; */bottom: 0px;left: 0%;margin-left: -8px;cursor: s-resize;
}.resize-e {/* 右边句柄改为瘦高 */width: 3px;height: 100%;/* border-radius: 2px; */top: 0%;right: 0px;margin-top: -8px;cursor: e-resize;
}
</style>
http://www.dtcms.com/a/411485.html

相关文章:

  • 酒店网站开发方案用php做的网站论文
  • Python03——逻辑判断
  • 福田做棋牌网站建设找哪家效益快小程序制作用华网天下北京
  • 动力电池与储能电池行业研究报告
  • 吴江住宅城乡建设局网站wordpress 菜单 页面
  • FGFR3基因及其在肿瘤中的作用
  • 个人可以做宣传片视频网站企业网站备案快吗
  • 当一个字母被键入:操作系统的“后台流水线”是如何运作的?
  • NLP学习系列 | 构建词典
  • 华意网站建设网络公司怎么样有自建服务器做网站的吗
  • React学习第三天——生命周期
  • 江门网站制作专业课程网站开发开题报告
  • 【计算机通识】GCC、G++、Makefile、CMake 详细对比指南
  • winform c# 做的2个运控平台,通过修改表格 的方式,也可以通过语音识别的交互方式,更加智能。
  • 使用Optimum-habana对LLM模型训练推理
  • 提升RAG知识库质量,文档解析如何解决上下文丢失与结构错误问题?
  • 两个表格(Excel/CSV)字段不完全一致,要合并在一起
  • 图书网站建设一般纳税人企业所得税
  • 基于Python的二手车价格数据分析与预测系统的设计与实现
  • 网站建设是什么科目免费app网站下载大全
  • API协作云:从OpenAPI到MCP跨语言的接口对接实践
  • 网站建设辶金手指排名十二cms系统设计方案
  • k8s集群部署nacos集群
  • 成都前几年网站建设公司小红书怎么做关键词排名优化
  • 设计网站报价企业网络推广方案策划书
  • 做内容网站好累网泰网站建设网络推广
  • 自己做网站app信阳企业网站开发
  • 软件详细测评一:百宝音配音平台
  • 建站所有步骤国际新闻最新消息今天新闻大事件视频
  • 网站的互动功能seo的中文意思