在现代前端开发中,弹窗作为一种常见的交互形式,被广泛应用于各种场景。如何在不同分辨率下保证弹窗的可拖拽性和用户体验成为了一个重要的问题。本文将结合 Vue 框架,通过一段简洁的代码,展示如何实现一个可拖拽且能适应不同分辨率的弹窗。

需求分析

我们需要实现一个具备以下功能的弹窗:

  1. 可拖拽:用户可以通过拖拽弹窗的标题栏,随意移动弹窗的位置。
  2. 响应式缩放:弹窗在不同分辨率和缩放比例下,能够正常拖动且位置计算正确。
  3. 边界约束:弹窗不能超出视窗边界,保证其始终可见。

解决方案设计

为实现以上功能,我们需要使用 Vue 的组合式 API,结合鼠标事件监听器来完成拖拽效果。同时,我们需要根据窗口大小动态调整拖动距离,以适应不同的缩放比例。

一、通过定位的方式,改变元素的position

二、通过transform的translate属性

推荐使用translate更适合,不会引起页面的重排和重绘,transform类的,可以使用GPU加速,提高浏览器的性能

三、缩放的时候,需要除以不同的缩放系数

代码实现

首先,定义用于拖拽的核心逻辑,主要分为以下几个步骤:

  • 记录鼠标按下的位置:用户按下鼠标时,我们需要记录当前鼠标的位置,以便后续计算拖动的偏移量。
  • 计算位移并更新弹窗位置:在鼠标移动时,我们根据鼠标移动的距离,更新弹窗的 transform 样式,使其跟随鼠标移动。
  • 窗口缩放的适配:通过监听窗口的 resize 事件,动态调整移动的比例,以保证在不同分辨率和缩放比例下,拖动体验一致。
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'

const pupopRef = ref<HTMLDivElement>() // 弹窗容器
const titleRef = ref<HTMLDivElement>() // 鼠标按下的位置,我放在标题处
let prevX = 0, prevY = 0  // 记录上一次记录的位置
let countX = 0, countY = 0  // 记录位置累加的结果
let pointDown = false // 判断鼠标是否按下
let scaleRatio = 1 // 缩放系数,在不同缩放系数下,移动的像素需乘以缩放系数

// 判断是否在可移动范围内
const moveFlag = () => countY < 660 && countY > -650 && countX > -3050 && countX < 3050

// 鼠标移动时更新弹窗位置
const moveFun = (e: any) => {
    if (pointDown && pupopRef.value) {
        const { clientX, clientY } = e
        countX += clientX - prevX
        countY += clientY - prevY
        if (moveFlag()) {
            pupopRef.value.style.transform = `translate(${countX / scaleRatio}px, ${countY / scaleRatio}px)`
        }
        prevX = clientX
        prevY = clientY
    }
}

// 鼠标松开时停止拖拽
const pointUpFun = () => pointDown = false

// 监听窗口大小变化,调整缩放系数
const resizeFun = () => scaleRatio = window.innerWidth / 6720

onMounted(() => {
    if (titleRef.value) {
        titleRef.value.addEventListener('pointerdown', ({ clientX, clientY }) => {
            prevX = clientX
            prevY = clientY
            pointDown = true
        })
        window.addEventListener('pointermove', moveFun)
        window.addEventListener('pointerup', pointUpFun)
    }

    window.addEventListener('resize', resizeFun)
    scaleRatio = window.innerWidth / 6720
})

onBeforeUnmount(() => {
    // 关闭弹窗时移除事件监听
    window.removeEventListener('pointermove', moveFun)
    window.removeEventListener('pointerup', pointUpFun)
    window.removeEventListener('resize', resizeFun)
})
</script>

核心逻辑解析

  1. 弹窗的拖拽实现

    • pointerdown 事件用于检测用户何时按下鼠标,并记录此时的鼠标坐标。
    • pointermove 事件中,实时获取鼠标移动的偏移量,将其累加到 countXcountY,并更新弹窗的 transform 样式,使弹窗随鼠标移动。
    • pointerup 事件用于停止拖拽。
  2. 响应式适配

    • resizeFun 函数通过 window.innerWidth 计算当前的缩放系数 scaleRatio,该系数会在拖拽过程中调整移动距离,以适应不同分辨率。
    • 初始的 scaleRatio 是以一个设计宽度 6720px 为基准的。如果设计稿的宽度不同,可以调整这个基准值。
  3. 边界限制

    • 通过 moveFlag 函数,限制了 countXcountY 的累加范围,保证弹窗在垂直和水平方向上的移动不会超出边界。

使用场景

这种实现方式在复杂的业务系统或大屏展示中非常实用,特别是在需要兼容不同分辨率和设备的情况下。通过这种方式,能够确保弹窗在不同屏幕下的拖拽行为一致,提升用户体验。

总结

通过以上代码示例和讲解,我们实现了一个响应式可拖拽弹窗,具备良好的用户体验和适应性。在实际项目中,可以根据具体需求进一步优化和扩展此功能,例如增加弹窗缩放、自动吸附边界等高级功能。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐