Vue通过拖拽改变容器大小

通过拖拽改变大小是一个很常见的业务场景,比如我们常用的编辑器(Jetbrains 系列、VSCode),都有一些可以通过拖拽改变宽高的窗口,当我们鼠标悬停在边缘时,鼠标变为水平或者垂直的双向箭头,通过拖拽来改变宽高。

2023-12-13_16-58-23.png

先看最终效果:

2023-12-13_17-52-45.gif

为了简化操作,接下来我将使用 Vue3 实现类似的功能,代码并不局限于框架,通过简单的修改也可以在 JavaScript 或者 React 中实现同样的效果。样式我使用了 Tailwind CSS,如果有不理解的类,可以去 Tailwind CSS 查阅。

先来介绍一下原理。当鼠标悬停在侧边栏拖拽区域时,鼠标样式变为双向箭头,提示我们可以通过拖拽改变大小。鼠标按下拖拽时,监听 document 的 mousemove 和 mouseup 事件,通过鼠标的 x 坐标减去侧边栏距离页面左部的距离,算出侧边栏的新宽度,给侧边栏设置样式。鼠标松开时移除监听事件。我们还可以在拖拽过程中限制最小/最大宽度。

image.png

完整代码:

<script setup lang="ts">  
import { ref } from 'vue'  
  
const handleMouseDown = (event: MouseEvent) => {  
  event.preventDefault()  
  event.stopPropagation()  
  
  document.addEventListener('mousemove', handleMouseMove)  
  document.addEventListener('mouseup', handleMouseUp)  
}  
  
const asideRef = ref<HTMLElement | null>(null)  
const handleMouseMove = (event: MouseEvent) => {  
  let newWidth = event.clientX - asideRef.value?.getBoundingClientRect().left  
  if (newWidth < 240) newWidth = 240  
  if (newWidth > 400) newWidth = 400  
  
  if (asideRef.value) {  
    asideRef.value.style.width = `${newWidth}px`  
  }  
}  
  
const handleMouseUp = () => {  
  document.removeEventListener('mousemove', handleMouseMove)  
  document.removeEventListener('mouseup', handleMouseUp)  
}  
  
const isResizing = ref(false)  
const resetWidth = () => {  
  if (asideRef.value) {  
    isResizing.value = true  
    asideRef.value.style.width = `300px`  
    setTimeout(() => (isResizing.value = false), 300)  
  }}  
</script>  
  
<template>  
  <div class="flex h-full">  
    <aside  
      ref="asideRef"  
      :class="`group relative w-[300px] bg-gray-100 p-2 ${  
        isResizing && 'transition-all duration-300 ease-in-out'  
      }`"  
    >  
      <p>  
        Lorem ipsum dolor sit amet, consectetur adipisicing elit. Atque, culpa cumque, cupiditate  
        doloremque doloribus explicabo, facilis illum ipsam minima nobis officiis optio possimus  
        repudiandae sint totam ullam unde velit voluptates.  
      </p>  
      <div  
        @mousedown="handleMouseDown"  
        @click="resetWidth"  
        class="absolute inset-y-0 right-0 w-1 cursor-ew-resize bg-gray-200 opacity-0 transition group-hover:opacity-100 dark:bg-zinc-800"  
      ></div>  
    </aside>  
    <div class="min-w-0 flex-1">Content</div>  
  </div>  
</template>
  1. 拖拽区域默认是隐藏的,透明度设置为了 0,当鼠标进入侧边栏时透明度设置为 100%。
  2. 当用户在侧边栏的拖拽区域按下鼠标按钮时,handleMouseDown 函数会被调用,它会添加两个事件监听器,分别监听鼠标移动和鼠标按钮释放事件。当用户移动鼠标时,handleMouseMove 函数会被调用,计算新的侧边栏宽度,并更新侧边栏的宽度。当用户释放鼠标按钮时,handleMouseUp 函数会被调用,移除鼠标移动和鼠标按钮释放的事件监听器。
  3. 鼠标单击拖拽区域会将侧边栏宽度设置为默认的 300 像素。
  4. isResizing 用于跟踪侧边栏是否正在调整宽度。当 isResizing 的值为 true 时,给侧边栏添加一个 300 毫秒的动画 transition-all duration-300 ease-in-out

最后,如果文章对您有所帮助,还希望能够点赞、收藏、关注,您的每一次点击都会为我带来无穷的动力来写出更好的文章。

### 使用 `ant-design-vue` 的 `a-layout` 组件实现拖拽改变容器宽度 为了实现在 `ant-design-vue` 中使用 `a-layout` 组件并允许用户通过拖拽来调整布局组件的宽度,可以借助第三方库如 `vue-draggable-resizable` 或者原生 JavaScript 实现这一功能。 #### 方法一:利用 `vue-draggable-resizable` 虽然此方法主要用于描述如何使弹窗具备拖拽能力[^1],但是相同的概念也可以应用于其他类型的 UI 元素上。对于想要让侧边栏或某个特定区域支持尺寸调整的情况来说,可以在该区域内包裹一层具有拖动特性的组件,并监听其大小变化事件以动态更新目标元素的实际宽高属性值。 ```html <template> <div class="layout-container"> <!-- 左侧可调节宽度 --> <a-layout-sider :width="siderWidth" style="background:#fff;"> Sider Content </a-layout-sider> <!-- 可拖拽的手柄 --> <vue-draggable-resizable @dragging="onDrag" @resizing="onResize" :w="handleSize.width" :h="handleSize.height" :parent="true" :active.sync="isActive"> <span>+</span> </vue-draggable-resizable> <!-- 主要内容区 --> <a-layout-content style="padding:0 24px;min-height:280px;background:white;"> Main content area... </a-layout-content> </div> </template> <script> import VueDraggableResizable from 'vue-draggable-resizable' // 导入样式文件 (如果需要) import 'vue-draggable-resizable/dist/VueDraggableResizable.css' export default { components: { VueDraggableResizable }, data() { return { isActive: false, handleSize: { width: 5, height: 30 }, // 手柄初始大小 siderWidth: 200 // 初始左侧宽度设置为200像素 } }, methods: { onResize(newW) { this.siderWidth = newW; }, onDrag(x) { const minWidth = 160; let nextSiderWidth = Math.max(minWidth, x); this.siderWidth = nextSiderWidth; } } } </script> <style scoped> .layout-container .vue-draggable-handle { position:absolute;left:100%;top:calc(50% - 15px);cursor:e-resize;z-index:9;width:auto;height:30px;line-height:30px;text-align:center;color:red;font-size:2em; } .vue-draggable-handle span{ transform:rotate(-90deg); } </style> ``` 上述代码片段中,通过引入 `vue-draggable-resizable` 并将其放置于两个主要部分之间作为手柄,实现了当鼠标点击并沿水平方向移动时能够实时修改左边栏(`a-layout-sider`)的宽度效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值