树组件宽度自适应横向滚动 | 解决方案

不知不觉已经年底了,个人的 KPI 还远远没达成.... 一个有趣的解决方案,分享一下,也记录一下解决过程。本次代号: 滚动的树

背景

项目上一个有意思的问题,我们使用树组件时,一般都会有一个固定的父容器宽度,那么在层级过多时,树组件并不会自动增加宽度。这样,更深层级的目录就没法看到,也不支持横向滚动查看。

解决思路

方案一:调整布局

我们实现一个弹性容器组件,正如下面的 ·绿色·区域,拖动中间的基线,可改变左右内容的宽度,这样就能保证想要看全树组件更多层级时,把基线拖动到右边,留出更多的内容给到数组件区域就好了。实现上我就不重点讲述了,本文主要探讨第二种方案。

方案二: 横向滚动

其实第一种方案是非常合适了,不过面对复杂的应用场景,右边的区域可能是不允许被压缩的。那么我们只能在左边固定的区域内做文章。那么最简单的方式就是当层级超过 x 层时横向滚动。

实现这个方案的原理就是:层级超出容器最大层级后,不断增加树组件宽度

计算公式

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 树组件宽度 = 容器宽度 + 超出宽度 树组件宽度 = 容器宽度 + 超出宽度 </math>树组件宽度=容器宽度+超出宽度
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 树组件宽度 = 容器宽度 + (超出层级 ∗ 层级扩展宽度基数) 树组件宽度 = 容器宽度 + (超出层级 * 层级扩展宽度基数) </math>树组件宽度=容器宽度+(超出层级∗层级扩展宽度基数)
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 超出层级 = 最高层级 − 父容器最大层级 超出层级 = 最高层级 - 父容器最大层级 </math>超出层级=最高层级−父容器最大层级
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> 父容器最大层级 = 容器宽度 / 层级缩进 父容器最大层级 = 容器宽度/层级缩进 </math>父容器最大层级=容器宽度/层级缩进

层级扩展宽度基数:超出一层级,要增加的宽度值(px)
最高层级:树组件展开的最深层级
父容器基础层级:父容器能容纳的最大层级
层级缩进: 树组件每一层的缩减宽度(px)

代码实现

上面的公式没看懂也没事,主要逻辑如下:

树组件宽度 = 容器宽度 + (超出层级 * 层级扩展宽度基数)

示例代码:vue + el-tree

页面布局

jsx 复制代码
<template>
    <div>
        <div class="left overflow-x-auto">
            <tree :style="treeStyles" />
        </div>
        <div class="right">
        </div>
    </div>
</template>

容器宽度
this.$el?.getBoundingClientRect()?.width

超出宽度 treeStyles

js 复制代码
const treeStyles = computed(() => {
    const containerWidth = this.$el?.getBoundingClientRect()?.width
    
    // el-tree 每一级的宽度(估算的)
    const step = 28
    
    // 如果所有宽度大于父容器宽度,则增加子容器宽度并触发横向滚动
    if (containerWidth && step * this.deepExpandLevel >= containerWidth) {
      const base = containerWidth / step
      const over = this.deepExpandLevel - Math.floor(base)
      
      return {
        width: `calc(100% + ${(over + 1) * 20}px)`,
        overflowX: 'auto',
      }
    }
    return {}
})

获取展开的最深层级 deepExpandLevel

js 复制代码
async getDeepExpandNodeLevel(node) {
  await this.$nextTick()
  let topNode = node
  let deepLevel = 0
  while (topNode.parent) {
    topNode = topNode.parent
  }

  walkTree(
    [topNode],
    node => {
      // 如果父元素都没有展开,就要没必要继续遍历其子元素了
      if (!node.expanded) return 'continue'
      if (node.expanded && node.level > deepLevel) {
        deepLevel = node.level
      }
    },
    { children: 'childNodes' },
  )

  this.deepExpandLevel = deepLevel
}

getDeepExpandNodeLevel 触发时机

  • 折叠节点
  • 展开节点
  • 点击节点(如果会展开)

实现效果

结尾

往后也会督促自己,往后也会多记录一些解决方案。如果大家有更好的方案欢迎也可以在评论区写出。

相关推荐
AntBlack11 分钟前
Z-Image 发布了 ,赶紧体验了一把(配套 Modal执行脚本)
前端·后端·aigc
●VON32 分钟前
Electron 项目在“鸿蒙端”与“桌面端”运行的区别
javascript·学习·electron·openharmony
诸葛韩信41 分钟前
前端工程化1——npm insatall背后的工作原理
前端·npm·node.js
徐小夕@趣谈前端42 分钟前
NO-CRM本地安装版开源!人人都能拥有开箱即用的智慧CRM管理系统
javascript·vue.js·开源
箫笙默1 小时前
JS基础 - 正则笔记
开发语言·javascript·笔记
k***12171 小时前
SpringBoot返回文件让前端下载的几种方式
前端·spring boot·后端
专注前端30年1 小时前
如何使用 HTML5 的 Canvas + JavaScript 实现炫酷的游戏得分特效?
前端·javascript·游戏·html5·canvas·canva可画
q***06291 小时前
解决 Tomcat 跨域问题 - Tomcat 配置静态文件和 Java Web 服务(Spring MVC Springboot)同时允许跨域
java·前端·spring
|晴 天|1 小时前
2025年前端框架选择:React、Vue还是Svelte?
vue.js·react.js·前端框架
张丶大帅2 小时前
JS案例合集
开发语言·javascript·笔记