原生js实现拖动改变弹窗大小,同时修改显示内容显示比例

代码

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #testDialog {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 400px;
      height: 300px;
      padding: 2px;
      font-size: 20px;
      background-color: indianred;
    }
    #content-wrapper {
      width: 100%;
      height: 100%;
      font-size: 1em;
      word-break: break-all;
      overflow-y: auto;
    }
  </style>
</head>
<body>
  <div id="testDialog">
    <div id="content-wrapper">
	    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    </div>
  </div>
</body>

<script>
  const testDialog = document.getElementById('testDialog') // 获取弹窗dom
  let initialSize = [testDialog.clientWidth, testDialog.clientHeight] // 记录弹窗初始宽高
  let dialogChangeStatus = 'none' // 设置弹窗状态,none为鼠标未按下状态,l在左侧拖动、r在右侧拖动、t在上侧、b下侧
  let mouseDownCoordinate = [NaN, NaN] // 记录点击时鼠标对应屏幕坐标

  window.addEventListener('mousedown', setCoordinate)
  window.addEventListener('mouseup', clearCoordinate)

  window.addEventListener('mousemove', function(e) {
    if (!isNaN(mouseDownCoordinate[0])) { // 根据是否存在鼠标按下时位置作为事件触发条件
      let newWidth = 0
      let newHeight = 0
      if (dialogChangeStatus === 'r') { // 弹窗右侧边缘
        const moveX = e.screenX - mouseDownCoordinate[0]
        newWidth = initialSize[0] + moveX
        testDialog.style.width = newWidth + 'px'
      } else if (dialogChangeStatus === 'l') { // 弹窗左侧边缘
        const moveX = mouseDownCoordinate[0] - e.screenX
        newWidth = initialSize[0] + moveX
        testDialog.style.width = newWidth + 'px'
      } else if (dialogChangeStatus === 't') { // 弹窗上侧边缘
        const moveY = mouseDownCoordinate[1] - e.screenY
        newHeight = initialSize[1] + moveY
        testDialog.style.height = newHeight + 'px'
      } else if (dialogChangeStatus === 'b') { // 弹窗下侧边缘
        const moveY = e.screenY - mouseDownCoordinate[1]
        newHeight = initialSize[1] + moveY
        testDialog.style.height = newHeight + 'px'
      }
      // 仅针对宽度变化进行展示内容的调整
      if (dialogChangeStatus === 'r' || dialogChangeStatus === 'l') { // 设置父级font-size
        testDialog.style.fontSize = newWidth / 20 +'px'
      }
    }
  })

  function setCoordinate(e) {
  	// 由于transform: translate(-50%, -50%)原因需要减去一半宽高
    let restX = e.x - (testDialog.offsetLeft - testDialog.clientWidth / 2)
    let restY = e.y - (testDialog.offsetTop - testDialog.clientHeight / 2)
    let flag = ''
    if (restX <= 2 && restX >= 0) {
      flag = 'l'
    }
    if (restX < testDialog.clientWidth && restX >= testDialog.clientWidth - 2) {
      flag = 'r'
    }
    if (restY <= 2 && restX >= 0) {
      flag = 't'
    }
    if (restY < testDialog.clientHeight && restY >= testDialog.clientHeight - 2) {
      flag = 'b'
    }
    setDialogChangeStatus(flag)
    mouseDownCoordinate = [e.screenX, e.screenY]
  }
  
  // 鼠标按键抬起时重置相关参数
  function clearCoordinate(e) {
    mouseDownCoordinate = [NaN, NaN]
    initialSize = [testDialog.clientWidth, testDialog.clientHeight]
    testDialog.style.cursor = 'auto'
    testDialog.style.userSelect = 'unset'
    dialogChangeStatus = 'none'
  }
  
  testDialog.addEventListener('mousemove', function(e) { // 监听弹框鼠标移入事件
    // 当鼠标位于左右两边2px时可进行拉伸操作,切换鼠标图标展示效果
    if (e.offsetX <= 2 || e.offsetX >= e.target.clientWidth - 2) {
      e.target.style.cursor = 'w-resize'
      e.target.style.userSelect = 'none' // 到达可拖动位置时去除文本选中效果
    } else if (e.offsetY <= 2 || e.offsetY >= e.target.clientHeight - 2) {
      e.target.style.cursor = 's-resize'
      e.target.style.userSelect = 'none'
    } else {
      e.target.style.cursor = 'unset'
      e.target.style.userSelect = 'unset'
    }
  })
  
  // 本意通过强制设定样式,保持图标显示效果但没能实现,即switch部分并无实际作用
  function setDialogChangeStatus(flag) {
    dialogChangeStatus = flag
    switch (flag) {
      case 't':
        testDialog.style.cursor = 's-resize'
        testDialog.style.userSelect = 'none'
        break
      case 'b':
        testDialog.style.cursor = 's-resize'
        testDialog.style.userSelect = 'none'
        break
        
      case 'l':
        testDialog.style.cursor = 'w-resize'
        testDialog.style.userSelect = 'none'
        break
          
      case 'r':
        testDialog.style.cursor = 'w-resize'
        testDialog.style.userSelect = 'none'
        break
      default:
        testDialog.style.cursor = 'unset'
        testDialog.style.userSelect = 'unset'
        break
    }
  }
</script>
</html>

切换效果

在这里插入图片描述
在这里插入图片描述

涉及原理

尺寸

广大前端大佬们都所熟知的em尺寸单位,基于父元素的font-size,所以想要根据窗口大小去动态调整显示大小的话只需要通过js代码去动态的设置父元素的font-size即可

不用scale的原因

初始尝试时有想过使用transformscale属性,但是因为本身放大比例和弹窗放大比例并不同步的问题可能会出现如下情况
在这里插入图片描述
大概想了一下,应该还要计算一个scale与外弹窗缩放比例的关系,过于复杂了,不如还是使用基础属性

鼠标拖动及弹窗缩放关系

这边的设计是增加mousedown的事件监听,在鼠标按下时记下此时鼠标点击位置基于屏幕的坐标位置,然后通过mousemove监听事件将mousedownmouseup这一过程中鼠标的上下左右坐标变化的距离作为弹窗样式的宽高的增加值(可为负)

交互效果及事件触发条件

我个人这边设定的是将弹窗四周大约2像素的宽度作为可拖动事件触发区域,通过修改stylecursor属性展示鼠标图标缩放交互效果,事件的操作是通过全局的监听事件实现,交互效果通过弹窗本身的监听事件触发

注意事项

  1. 弹窗的左右边缘及上下边缘对应的宽高附加值是颠倒的
  2. 弹窗的完全居中效果是通过如下方式实现的,所以在进行弹窗位置判定时(offsetLeft、offsetTop)需要将dom元素的一半宽高计算进去
top: 50%; left: 50%; transform: translate(-50%, -50%);
  1. 若要实现弹窗内全内容缩放效果,那么需将弹窗内所有内容的尺寸单位都设置为em,若不需要变化的部分则可继续使用px作为展示
  2. div本身不带缩放效果,故而拖动鼠标时会触发文本选中效果,所以需要额外添加user-select属性为none,取消鼠标选中效果

拓展

  1. 还可以补充四角的拉伸功能,即同时触发纵向拉伸与横向拉伸的效果,只需将原判断条件增加上&&即可
  2. 部分内容的大小比例(如图片)仍然可以通过scale方式进行设置,通过统一的class进行设置即可
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值