为所有弹窗增加全屏切换功能

1、现状

在开发两个管理系统,现在的页面20+,其中包含不少的弹窗。在项目开发过程中没人提过弹窗要全屏的事情,且在数据量较大的位置已经增加了可全屏的入口。但老板两次说为什么不给所有的弹窗增加全屏的功能,从中感觉到了侮辱,做页面也有些年头,但从没见过有网站所有窗口支持全屏功能的。

2、思考
  • 这个需求是不是合理?

​ 个人认为有20%合理性,这主要是因为在做需求时习惯性忘记要加全屏功能。
​ 80%不合理,因为至少80%的页面没有全屏的必要性,确认弹窗难道也有全屏的必要?

  • 这个需求要不要做?

    这是个伪命题,老板说要还能不做?虽然也不是一定要做。

    其次,这是从技术角度还有些意思的探索。

    所以在没有答应的情况下准备做一些技术实践。

  • 要怎么做?

    a、不可能为每个弹窗单独增加全屏功能

    b、侵入性应该尽量少

    c、接受少量场景缺陷(80%不会触发)

3、实现

使用的是antd + vue3,思路是一样的,其他场景改改可用。

antd中有实现弹窗全屏的实现,我们要做的就是如何简单的实现同一界面多窗口控制。

全屏实现原理:

  • 全屏时通过wrap-class-name设置class
  • 通过class实现全屏
4、干货
  • 全屏事件监听
const isFullScreen = ref<boolean>(false)
// 将多个控制弹窗显示的变量加入监听
watch([blockChainVisible, ()=>userUploadConfig.visible, ()=>modalConfig.visible, kvEditModel, tagEditModel], () => {
  	// 设置 1s 延时,避免弹窗还未显示便执行
    setTimeout(() => {
      const html = document.getElementsByClassName('ant-modal-header') || []
      const len = html.length
      if (len > 0) {
        for(let i=0; i < len; i++) {
          // 清理元素已存在监听,并重新增加监听  
          html[i].removeEventListener('dblclick', () => {})
          html[i].addEventListener("dblclick", () => {
            isFullScreen.value = !isFullScreen.value
          });
        }
      }
    },1000)
  })
  • 为弹窗增加class绑定

    :wrapClassName="isFullScreen ? 'full-modal':''"
    
  • 设置样式

    .full-modal {
      .ant-modal {
        width: 100%;
        top: 0;
        padding-bottom: 0;
        margin: 0;
      }
      .ant-modal-content {
        display: flex;
        flex-direction: column;
        height: calc(100vh);
        width: calc(100vw);
      }
      .ant-modal-body {
        flex: 1;
      }
    }
    

    因为很多页面都需要这段样式,所以将这段样式写在app.vue

如此,在弹窗显示时,双击标题区可以实现全屏与非全屏切换。

5、待优化项
  • 为弹窗增加显示的切换入口(双击入口不易发现)
  • 配置更加简化

6、更新

既然全屏是通过class实现的,那为何不能通过原始的方式添加class呢?这样就不需要isFullScreen变量及对应的绑定。

于是:可以删除所有isFullScreen相关的代码,只需要如下的监听和full-modal样式就行

watch([blockChainVisible, ()=>userUploadConfig.visible, ()=>modalConfig.visible], () => {
    setTimeout(() => {
      const html = document.getElementsByClassName('ant-modal-wrap') || []
      const len = html.length
      if (len > 0) {
        for(let i=0; i < len; i++) {
          html[i].addEventListener("dblclick", () => {
            if (html[i].classList.value.includes('full-modal')) {
              html[i].classList.remove("full-modal")
            } else {
              html[i].classList.add("full-modal")
            }
          });
        }
      }
    },1000)
  })
7、终极版

解决了一下问题:

  • 简化代码
  • 移除双击触发的隐藏入口,增加icon型显示入口

完整使用方式:

  1. app.vue中配置全局样式
// app.vue
// 弹窗全局样式
.full-modal {
  .ant-modal {
    width: 100%;
    top: 0;
    padding-bottom: 0;
    margin: 0;
  }
  .ant-modal-content {
    display: flex;
    flex-direction: column;
    height: calc(100vh);
    width: calc(100vw);
  }
  .ant-modal-body {
    flex: 1;
  }
}
// 弹窗全局切换icon样式
.screen-change {
  width: 16px;
  height: 16px;
  background-size: 16px 16px;
  margin-right: 40px;
  margin-top: 5px;
  background-repeat: no-repeat;
  float: right;
  cursor: pointer;
}

2、全局切换公共函数

// utils.ts
import { watch } from 'vue'

// 入口
export function switchFullscreen (visiableTag: any){
  watch(visiableTag, () => {
    setTimeout(() => {
      setIcon(true)
    },100)
  })
}

const setModalWrapClass = (isAdd: boolean) => {
  const html = document.getElementsByClassName('ant-modal-wrap') || []
  const len = html.length
  if (len > 0) {
    for(let i=0; i < len; i++) {
      if (isAdd) {
        html[i].classList.add("full-modal")
      } else {
        html[i].classList.remove("full-modal")
      }
    }
  }
}

const setIcon = (isAdd: boolean) => {
  // 隐藏已存在icon,在弹窗消失时会同步消失,所以隐藏不会造成太大问题,当然清除更好
  const screenTag = document.getElementsByClassName('screen-change') || []
  const screenTagLen = screenTag.length
  if (screenTagLen > 0) {
    for(let i = 0; i < screenTagLen; i++) {
      screenTag[i].setAttribute('style', 'display: none')
    }
  }

  const html = document.getElementsByClassName('ant-modal-title') || []
  const len = html.length
  if (len > 0) {
    for(let i=0; i < len; i++) {
      let newImage = document.createElement('img')
      newImage.setAttribute('class', 'screen-change')
      if (isAdd) {
        newImage.setAttribute('src', './src/assets/fullscreen.png')
        newImage.setAttribute('id', 'fullscreen')
        html[i].appendChild(newImage)
        newImage.onclick = () => {
          setModalWrapClass(true)
          setIcon(false)
        }
      } else {
        newImage.setAttribute('src', './src/assets/collapse.png')
        newImage.setAttribute('id', 'cancelfullscreen')
        html[i].appendChild(newImage)
        newImage.onclick = () => {
          setModalWrapClass(false)
          setIcon(true)
        }
      }
    }
  }
}

3、页面中按需引入

<script lang="ts" setup>
  import { switchFullscreen } from '@/utils'
	import { onMounted } from 'vue'
	
  onMounted(() => {
    // 传入需要控制的弹窗visiable参数
    switchFullscreen([blockChainVisible, ()=>modalConfig.visible])
  });
</script>

正常效果:
在这里插入图片描述

放大效果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值