flouting ui定位组件完美替代ant deisgn使用详解

目录
  • 前言
  • 这个组件实现的复杂度在哪
    • 复杂度1
    • 复杂度2
    • 复杂度3
    • 复杂度4
    • 复杂度5
  • 国内组件库怎么实现这个功能
  • flouting-ui为啥代码质量比ant高
  • 中间件的形式好在哪
  • 代码中间件原理
  • 中间件如何写

前言

因为要写react定位组件(这不是标题党,就是完爆ant deisgn的定位组件,你应该看到一半就会同意我的观点),如下图:

红框部分是用绝对定位放在按钮上面的,你们B端用的主流组件库都是这样实现的,它是很多组件的基础组件,比如下图:

下拉框组件

select组件

还有什么DataPicker,TreeSelect,Dropdown组件等等的下拉框都是以定位组件为基础的。

这个组件实现的复杂度在哪

上面提到,这不过就是一个绝对定位嘛(我们假设红框部分的的dom绝对定位是相较于body元素),我们拿最简单的情况来看,如下图,如何把红框部分渲染到按钮”更多“的下方呢?

我们可以计算更多按钮的getBoundingRect(),返回

const reference = {
  top: xx, // 按钮距离浏览器顶部的距离
  left: xx, // 按钮距离浏览器左边的距离
  width: xx, // 按钮的宽:没有padding
  height:xx,// 按钮的高:没有padding
  ...等等其他属性
}

所以红框部分左上角的坐标就轻易的计算出来了,上面的数据在reference对象上,所以借助reference的定位,我们计算红框部分的下拉框的定位是在哪

{
    position: 'absolute',
    top: reference.top + window.pageYOffset // 竖直方向滚动距离 + reference.height
    left: reference.left + window.pageXOffset // 横向滚动距离
}

为啥是上面这么计算呢,假如没有滚动条滚动,那么红框部分的绝对定位的top,是不是等于按钮的距离浏览器顶部的高度 + 本身的高度,这个没问题吧?

然后,如果滚动条滚动了的话,是不是要在上面top的基础上加上这段距离,就是红框部分在文档流绝对定位的top。

好了,到此为止,就是最基本的定位组件的逻辑了,我们接下来看复杂点!

复杂度1

还是拿上面的图的红色部分下拉框来说,下拉框一般是在下面,但是我可以定位到上边吧?左边,右边也没啥吧,再过分点,右上,左下?定位组件要处理对吧

复杂度2

假设,我们定位在下面,我想向左偏移8px,向下偏移3px咋办,你是不是应该有暴露一个口子

复杂度3

假设我定位在下面,那么我一直滚,马上就要滚动到看不见下拉框了,如下图

此时我想让定位在上面,能不能自动帮我处理?如下图:

复杂度4

是不是还有可能超出浏览器视口了,如下图:

我们想自动处理,遇到超出就自动变为下方样子:

复杂度5

此时我定位了一次,但是有可能滚动容器不是window,是另一个div,这个计算咋办?还有,是不是我滚动的时候,我要监听滚动事件,还要监听浏览器resize事件,因为我定位的值可能会变?为啥呢,我们上面复杂度3是不是自动帮我们在滚动的时候调整位置

所以你不监听滚动事件你咋知道要调整位置了?

还有很多细枝末节,比如浏览器兼容性等等。。。。

国内组件库怎么实现这个功能

目前阿里的ant design和字节的arco design都是自己实现的,我们拿arco来看(ant内部叫rc-trriger组件,arco叫trriger组件),面向过程的代码,看的我头皮发麻。。。我截个图:

上面的代码属于把我们提到的复杂度全部揉在了一起。

flouting-ui为啥代码质量比ant高

它是以中间件的形式去处理的,思路是什么呢?它假设最开始有一个 computePosition函数,我们假设上面提到的复杂度都没有,也就是不考虑的前提下,我们怎么计算定位组件的坐标,也就是我们最前面的图里说的,红色框部分绝对定位的的的top值和left值:

API如下:

computePosition(要挂载的dom节点,下拉框组件,参数...)

然后我们刚才提到的复杂度,它分别用中间件的形式去处理,比如复杂度2,是想定位之后还有点偏移量,flouting-ui咋做的呢

import {computePosition, offset} from '@floating-ui/dom';
// referenceEl: 要挂载的dom节点
// floatingEl:下拉框组件(或者说想要挂载到上面referenceEl的dom元素)
computePosition(referenceEl, floatingEl, {
  middleware: [offset(10)],
});

如上,offset就是一个中间件,offset(10),就是向左偏移10px

好了,如果想处理复杂度3呢,我们用另一个中间件

import {computePosition, flip} from '@floating-ui/dom';
computePosition(referenceEl, floatingEl, {
  middleware: [flip()],
});

这样就自动处理了,是不是很简单啊

其实所有这些复杂度的解决方案,在flouting-ui里都是以中间件的形式去处理的,还可以传多个中间件解决多个问题。

中间件的形式好在哪

那么我们就可以自定义很多中间件了,也就是你的组件不仅仅提供了很多功能,解决了很多常用的问题,你还允许用户写代码去拓展,试问,现在哪个组件库的代码是这么写的?没有吧?

代码中间件原理

我们先看看flouting-ui的computePosition API是怎么实现的,它是flouting-ui的核心方法,是串联所有中间件的基础。

下一篇写完整的源码(很晦涩,估计也没几个人看,所以这期就不写了),理解起来说实话,你不熟悉原生dom的话有点困难,比如说为啥这个库要用window.pageYoffset而不是document.body.scrollTop去获取浏览器html元素的滚动距离,因为document.body.scrollTop固定为0,取不到。。。

核心思路讲解:我们还是拿下图做类比

let {x, y} = 求出红色框里的下拉框绝对定位的x坐标和y坐标
// 记录原始placement
  let statefulPlacement = placement;
  // 所有中间件导出的值都挂载到下面的对象上
  let middlewareData: MiddlewareData = {};
  // 数据经过middleware的处理
  // middleware是一个数组,存放所有中间件,就是我们上面说的处理每一个复杂度的对象
  for (let i = 0; i < middleware.length; i++) {
   // name是中间件的名字,fn是处理复杂度的逻辑
    const {name, fn} = middleware[i];
   // 通过把最前面计算的x,y经过fn的处理,得到了新的x,y的值
   // data是指返回的数据,想让后面的中间件也能访问到的数据
    /**
     * 每个middleware需要返回
     * x 新的x坐标
     * y 新的y坐标
     * data
     * reset
     */
    const {
      x: nextX,
      y: nextY,
      data,
      reset,
    } = await fn({
      /**
       * 每个middleware收到的参数
       * x 目前的x坐标
       * y 目前的y坐标
       * initialPlacement 最初传入的placement
       * placement
       * middlewareData middleware返回的额外数据
       */
      x,
      y,
      initialPlacement: placement,
      placement: statefulPlacement,
      strategy,
      middlewareData,
      rects,
      platform,
      elements: {reference, floating},
    });
    x = nextX ?? x;
    y = nextY ?? y;
    // 每次处理后的数据想要让后面的中间件访问,就需要挂载到middlewareData对象
    // 这个对象非常好啊,用name隔离了作用域
    middlewareData = {
      ...middlewareData,
      [name]: {
        ...middlewareData[name],
        ...data,
      },
    };
   rest的处理逻辑。。。省略,不是很重要

最后return出被处理完的x,y坐标,或者自动帮我们监听滚动事件和resize事件,然后拿着x,y就可以赋在css的绝对定位的top和left上,实现了定位。

每次处理后的数据想要让后面的中间件访问,就需要挂载到middlewareData对象,这个对象非常好啊,用name隔离了作用域,这就是比koa这个框架处理的高明之处,koa里的ctx对象就像一个垃圾桶,什么属性都往上面挂载,挂载太多了,你也不知道是哪个中间件挂载的

所以flouting-ui的处理思路给我打开了新的思路!nice!!!

中间件如何写

源码再开一篇文章写,这里看看就好,不用过多去关系代码

export const offset = (value: Options = 0): Middleware => ({
  name: 'offset', // 中间件名字
  options: value,  // 传给中间件的值
  async fn(middlewareArguments) { // 中间件处理函数
    const {x, y} = middlewareArguments;
    const diffCoords = await convertValueToCoords(middlewareArguments, value);
    return {
      x: x + diffCoords.x,
      y: y + diffCoords.y,
      data: diffCoords,
    };
  },
});

本文结束,所以如果市面上的组件库的每个组件都是这个形式暴露给用户,就是提供插件式的自定义的中间件,那么整个组件库的拓展性可以说碾压市面上国内所有的react的组件库了

以上就是flouting ui定位组件完美替代ant deisgn使用详解的详细内容,更多关于flouting ui定位组件的资料请关注我们其它相关文章!

(0)

相关推荐

  • React 中的重新渲染类组件及函数组件

    目录 缘起 类组件 React 合成事件 定时器回调后触发 setState 异步函数后调触发 setState 原生事件触发 setState 修改不参与渲染的属性 只是调用 setState,页面会不会重新渲染 多次渲染的问题 测试代码 函数组件 React 合成事件 定时器回调 异步函数回调 原生事件 更新没使用的状态 小结 不同的情况 设置同样的 State React Hook 中避免多次渲染 将全部 state 合并成一个对象 使用 useReducer 状态直接用 Ref 声明,需

  • React可定制黑暗模式切换开关组件

    目录 正文 如何使用它. 1.安装和下载 2.导入DarkModeToggle组件 3.将黑暗模式切换添加到应用程序中 4.默认的组件道具 预览 正文 一个用于React的可定制的黑暗模式切换开关组件. 如何使用它. 1.安装和下载 npm install @anatoliygatt/dark-mode-toggle @emotion/react @emotion/styled 2.导入DarkModeToggle组件 import { useState } from 'react'; impo

  • React+高德地图实时获取经纬度,定位地址

    目录 1. 初始化地图 2. 地图扎点 3. 开启定位 4. 监听地图变化 5. 获取详细地址 6. 扎点动画

  • React Native可定制底板组件Magic Sheet使用示例

    目录 正文 如何使用它 1.安装并导入 2.基本使用方法 预览 正文 一个React Native组件,通过提供一个强制性的API,可以从应用程序的任何地方(甚至在组件之外)调用,以显示一个完全可定制的底部表单,并能够等待它解决并得到一个响应. 这个库依赖于Gorhom的/bottom-sheet 的模态组件,并接受相同的道具和儿童. 如何使用它 1.安装并导入 # Yarn $ yarn add react-native-magic-sheet # NPM $ npm i react-nati

  • react component function组件使用详解

    目录 不可改变性 虚拟dom与真实dom 函数组件 组件复用 纯函数 组件组合--组件树 组件抽离 不可改变性 1.jsx- 2.component(function)-component(class)-components(函数组件组合)-component tree(redux)-app(项目开发) 在react中,创建了js对象(react元素)就是不可更改的(immutable).就像是用相机拍照,相当于在此时间点已经定位了时间节点,只能拍下一张照片. 例如,使用底层react写一个时钟

  • TS装饰器bindThis优雅实现React类组件中this绑定

    目录 为什么this会是undefined 优雅的@bindThis 为什么this会是undefined 初学React类组件时,最不爽的一点应该就是 this 指向问题了吧!初识React的时候,肯定写过这样错误的demo. import React from 'react'; export class ReactTestClass extends React.Component { constructor(props) { super(props); this.state = { a: 1

  • React元素与组件的区别示例详解

    目录 从问题出发 元素与组件 元素 组件 问题如何解决 自定义内容 第一种实现方式 第二种实现方式 第三种实现方式 从问题出发 我被问过这样一个问题: 想要实现一个 useTitle 方法,具体使用示例如下: function Header() { const [Title, changeTitle] = useTitle(); return ( <div onClick={() => changeTitle('new title')}> <Title /> </div

  • React 高阶组件与Render Props优缺点详解

    目录 高阶组件 增强型高级组件 注入型高阶组件 高阶组件 VS Render Props 总结 高阶组件 高阶组件(HOC)是一个接受组件作为参数并返回一个新组件的函数,如果多个组件有相同的逻辑,将这些逻辑用函数封装,使它们能跨组件共用,这种用法称为高阶组件.下面的代码演示什么是高阶组件: export default function WithPrintLog(InnerComponent) { return class extends React.Component{ componentDi

  • flouting ui定位组件完美替代ant deisgn使用详解

    目录 前言 这个组件实现的复杂度在哪 复杂度1 复杂度2 复杂度3 复杂度4 复杂度5 国内组件库怎么实现这个功能 flouting-ui为啥代码质量比ant高 中间件的形式好在哪 代码中间件原理 中间件如何写 前言 因为要写react定位组件(这不是标题党,就是完爆ant deisgn的定位组件,你应该看到一半就会同意我的观点),如下图: 红框部分是用绝对定位放在按钮上面的,你们B端用的主流组件库都是这样实现的,它是很多组件的基础组件,比如下图: 下拉框组件 select组件 还有什么Data

  • UI 开源组件Flutter图表范围选择器使用详解

    目录 前言 1. 使用 chart_range_selector 2. ChartRangeSelector 实现思路分析 3.核心代码实现分析 4. 结合图表使用 前言 最近有一个小需求:图表支持局部显示,如下底部的区域选择器支持 左右拖动调节中间区域 拖拽中间区域,可以进行移动 图表数据根据中间区域的占比进行显示部分数据 这样当图表的数据量过大,不宜全部展示时,可选择的局部展示就是个不错的解决方案.由于一般的图表库没有提供该功能,这里自己通过绘制来实现以下,操作效果如下所示: 1. 使用 c

  • Vue.js 2.x之组件的定义和注册图文详解

    前言 什么是组件 组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可. 模块化和组件化的区别 模块化:是从代码逻辑的角度进行划分的:方便代码分层开发,保证每个功能模块的职能单一 组件化:是从UI界面的角度进行划分的:前端的组件化,方便UI组件的重用 全局组件的定义和注册 组件Component是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码. 全局组件的定义和注

  • Vue 可拖拽组件Vue Smooth DnD的使用详解

    目录 简介和 Demo 展示 API: Container 属性 生命周期 回调 事件 API: Draggable 实战 简介和 Demo 展示 最近需要有个拖拽列表的需求,发现一个简单好用的 Vue 可拖拽组件.安利一下~ Vue Smooth DnD 是一个快速.轻量级的拖放.可排序的 Vue.js 库,封装了 smooth-dnd 库. Vue Smooth DnD 主要包含了两个组件,Container 和 Draggable,Container 包含可拖动的元素或组件,它的每一个子元

  • Vue组件化(ref,props, mixin,.插件)详解

    目录 1.ref属性 2.props配置项 props总结 3.mixin混入 3.1.局部混入 3.2.全局混入 mixin混入总结 4.插件 插件总结 1.ref属性 被用来给元素或子组件注册引用信息(id的替代者) 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc) 使用方式: 打标识:<h1 ref="xxx">.....</h1>或 <School ref="xxx"></Schoo

  • Vuejs第十一篇组件之slot内容分发实例详解

    什么是组件? 组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能.在有些情况下,组件也可以是原生 HTML 元素的形式,以 is 特性扩展. Slot分发内容 ①概述: 简单来说,假如父组件需要在子组件内放一些DOM,那么这些DOM是显示.不显示.在哪个地方显示.如何显示,就是slot分发负责的活. ②默认情况下 父组件在子组件内套的内容,是不显示的. 例如代码: <

  • 微信小程序中button组件的边框设置的实例详解

    微信小程序中button组件的边框设置的实例详解 button的边框是用:after方式实现的,用户如果在button上定义边框会出现两条线,需用:after的方式去覆盖默认值. 如果设置了Button的背景色,没有用:after设置边框的颜色,则button的四个角会出现模糊的尖角.如下图所示: 如上图四个角会模糊..wxss代码如下: .clickEncryptBtn{ width:130px; border-radius: 3px; margin:20px auto; padding-to

  • 为Jquery EasyUI 组件加上清除功能的方法(详解)

    1.背景 在使用 EasyUI 各表单组件时,尤其是使用 ComboBox(下拉列表框).DateBox(日期输入框).DateTimeBox(日期时间输入框)这三个组件时,经常有这样的需求,下拉框或日期只允许选择.不允许手动输入,这时只要在组件选项中加入 editable:false 就可以实现,但有一个问题,就是:一旦选择了,没办法清空.经过研究,可以用一个变通的解决方案:给组件加上一个"清除"按钮,当有值是,显示按钮,点击按钮可清空值,当无值是,隐藏按钮. 2.函数定义 定义JS

  • Vue.js分页组件实现:diVuePagination的使用详解

    一.介绍 Vue.js 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合.另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动. 二.创建初始化项目 这里不在详细说明,我们的分页演示只需要vue和vue-router就可以了,我们直接构建项目和设置配置. main.js:

  • vue-cli 组件的导入与使用教程详解

    概述: 一个文件就是一个模块,需要引入模块,和暴露模块的方法 在一个组件中使用另一个组件三部曲:引入组件.注册组件.使用组件 1.main.js文件解读 .是整个项目的入口文件,在src文件夹下 .import(es6)引入vue和根组件app.vue .最后new Vue,启动应用 2.组件的使用 .定义的组件一般放到components目录下 .用一个组件的过程 a .被引用的文件暴露对象(如果组件中没有script,可以不需要暴露) b. 父组件引入子组件,注册组件(全局组件不需要引入),

随机推荐