生态产品多个设计器现已发布正式版

查看生态总览
跳转到内容

FloatButton 浮动按钮 ​

用于在视口边缘提供持续可见的操作入口。

用法 ​

展开一组悬浮操作

组件结构 ​

加粗的是必需部件。

data-scope="float-button":root · trigger · list

示例 ​

悬停展开 ​

指针进入时展开,键盘与触控仍可点击

变体 ​

设置浮动按钮的表面

尺寸 ​

使用小、中、大三档尺寸

拖动与贴边 ​

按住页面右侧的触发器拖到别处,松手贴到近的那条边;位置按比例记,宿主存下来下次照样落在原处

当前位置:{"edge":"inline-end","ratio":0.75}

设计指引 ​

何时使用 ​

  • 长页面中的常用主操作。
  • 移动端或窄屏中的紧凑操作组。

何时不用 ​

特性 ​

  • 支持四个视口角与安全区偏移。
  • draggable 打开后可按住触发器拖到别处:移动过激活距离才跟手,不到这个距离仍是一次点按;起拖时展开的动作组先收起,拖完补派的点击不开合。
  • 松手按 snap 贴边:inline(缺省)贴左右两边里近的那条,block 贴上下两边,nearest 贴四条边里最近的,none 停在放手处;甩一下就贴到甩去的那一边。贴过去由弹簧带着松手速度落定,减弱动效下直接到位。
  • 位置由 position / defaultPosition 给出:贴边写 { edge, ratio }(如 { edge: 'inline-end', ratio: 0.75 } 贴行尾一侧、中心在视口 75% 高处),停在一点写像素坐标 { x, y };不给时停在 placement 那一角。贴边位置按比例记,视口尺寸变了仍贴在同一侧、同一比例上;离两端不足 offset 时收回,并与安全区取大的一头。展开组恒朝页面中间长。
  • 拖动落定后才发一次 onPositionChange(Vue 为 position-change / v-model:position);要记住位置,就在这里存,下次作为 defaultPosition 传回。Web Components 的开关是 button-draggable:draggable 是 HTML 全局属性,写上会让宿主变成原生拖放源。
  • 支持点击或悬停展开;键盘与触控始终使用点击。
  • Escape、层外点击和再次触发均可收起。
  • 收起后动作项退出 Tab 序列。
  • 触发器走 Action Control floating 档:size 取 sm / md / lg(32 / 40 / 48px),默认 md 40px 圆形、图标 16px,按下缩放并换底;默认(outline)使用磨砂浮动表面,solid / subtle / ghost 使用对应语义表面。
  • 原生按钮动作项自动继承触发器的尺寸与外观。
  • 应用设为 data-material="liquid" 时,默认(outline)的触发器换成液态面,按住时液面随手指形变,减弱动效下不形变;展开组里的动作仍是磨砂面。

组合 ​

最佳实践 ​

  • 为每个图标按钮提供可访问名称。
  • 将操作数量控制在 2 至 5 个。
  • 使用 offset 避开系统手势区;拖动与贴边同样离视口四边留出这段距离。
  • 位置只是外观上的偏好,拖动不承载功能:键盘与读屏用户不需要移动它也能用全部动作。

反模式 ​

  • 不要承载高风险的破坏性操作。
  • 不要遮挡主要内容或固定导航。

API 参考 ​

产物 ​

层值
自定义元素<xh-float-button>
Vue 组件XhFloatButtonList XhFloatButtonRoot XhFloatButtonTrigger
组合式函数useFloatButton
状态机floatButtonMachine
皮肤@xihan-ui/styles/float-button.css

Props ​

属性类型必填说明
defaultOpenboolean
defaultPositionFloatButtonPosition初始位置,例如 { edge: 'inline-end', ratio: 0.75 }:贴右边(LTR)、中心在视口 75% 高处。
dirDirection文字方向,只作用于排版;作者未提供时不写入。
disabledboolean
draggableboolean能否用指针拖着触发器移动,默认 false。按下后移动过激活距离才算拖动,不到这个距离仍是一次点按。
expandTriggerFloatButtonExpandTrigger展开方式,默认 click。
offsetnumber距两条边的距离(px),默认 24。
onOpenChange(details: CollapsibleOpenChangeDetails) => voidopen 变化意图;受控时是唯一出口,非受控时随内部转移一并通知。
onPositionChange(details: FloatButtonPositionChangeDetails) => void拖动落定后的新位置:贴边时给贴边位置(按比例,换个视口尺寸照样成立),snap 为 none 时给像素坐标。 落定才通知一次,拖动途中不发;要记住位置就在这里存。
openboolean
placementFloatButtonPlacement固定在哪一角,默认 bottom-end。
positionFloatButtonPosition位置。提供即受控:拖动只发 onPositionChange,宿主写回才落到新位置。
sizeSize尺寸:sm / md / lg,默认与 lg 同档:悬浮按钮需要易于触达,起始即比行内按钮大一档。
snapFloatButtonSnap松手后贴向哪里,默认 inline(贴左右两边里近的那条)。
toneTone颜色:brand / neutral / success / warning / danger / info。
translationsPartial<FloatButtonTranslations>
variantActionVariant变体:solid / subtle / outline / ghost,默认 outline(缺省中性,描边 + 磨砂面;solid 才品牌实心)。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
open-changeCollapsibleOpenChangeDetails展开状态变化;detail 为 { open: boolean }
position-changeFloatButtonPositionChangeDetails拖动落定后的新位置;detail 为 { position: FloatButtonPosition }

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhFloatButtonRootdefaultFloatButtonRootSlotProps

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhFloatButtonRootchildrenSlotChildren<FloatButtonRootSlotProps>

状态 ​

公开状态写入 data-state。

部件取值
root'open' | 'closed'
trigger'open' | 'closed'
list'open' | 'closed'

以下名称仅用于内部状态机。

状态:open · closed

事件:OPEN · CLOSE · TOGGLE · DISABLE · CONTROLLED.OPEN · CONTROLLED.CLOSE · PRESS.START · PRESS.END · DRAG.START · DRAG.MOVE · DRAG.END · CLICK.SWALLOW · VIEWPORT.RESIZE

判据:isDisabled · isOpenControlled · canPress · canDrag

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
openboolean展开的动作组当前是否显示。
setOpen(next: boolean) => void
positionFloatButtonPosition | null提交了的位置;null 表示停在 placement 那一角。
setPosition(next: FloatButtonPosition) => void改位置:受控时只发 onPositionChange。
draggingboolean正被拖着。
getRootProps() => T['element']
getTriggerProps() => T['button']
getListProps() => T['element']

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Spacefocus in trigger, not disabled展开 / 收起 list;悬停展开时这条路照样在,触摸与键盘都靠它
Enter / Spaceheld in trigger, not disabled按住期间投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下
Escapeopen,无论焦点是否仍在整组内只收起当前 LayerRegistry 的栈顶层;更晚打开的 Drawer / Popover 先处理自己的 Escape
Tab / Shift+Tabopen走进展开的那一组;收起时 list 带 hidden,里面的按钮一并退出 Tab 序列

ARIA ​

以下属性由 connect 生成。

部件属性值
triggeraria-controlslist 部件的 id
triggeraria-expanded'true' | 'false'
triggeraria-labeltranslations.trigger
listaria-labelledbytrigger 部件的 id
listrole'group'

样式参考 ​

皮肤 ​

@xihan-ui/styles/float-button.css 按 [data-scope="float-button"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-float-button 代替其中的 data-scope(特异性相同),位于 xihan.components 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-draggable''(条件成立时才出现)
rootdata-dragging''(条件成立时才出现)
rootdata-edgeedge?.edge
rootdata-moving''(条件成立时才出现)
rootdata-placementfloatButtonPlacementOf(position, context.get('viewpor… | props.placement
rootdata-point''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-state'open' | 'closed'
rootdata-toneprops.tone
rootdata-variantprops.variant
triggerdata-disabled''(条件成立时才出现)
triggerdata-pressed''(条件成立时才出现)
triggerdata-state'open' | 'closed'
triggerdata-xh-action-control''
triggerdata-xh-action-display'always'
triggerdata-xh-action-profile'floating'
triggerdata-xh-action-sizeprops.size
triggerdata-xh-action-variantprops.variant
triggerdata-xh-ink-surface''(条件成立时才出现)
triggerdata-xh-liquid''
triggerdata-xh-material'frosted' | undefined
listdata-instant''(条件成立时才出现)
listdata-placementfloatButtonPlacementOf(position, context.get('viewpor… | props.placement
listdata-state'open' | 'closed'

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-float-button-bglist
root
trigger
--xh-ink-surface
background-color
default
disabled
focus-visible
not([data-scope])
variant=outline
variant=subtle
xh-ink-surface
--xh-_action-variant-bg-disabled
--xh-_action-variant-bg-focus-visible
--xh-_action-variant-bg-rest
--xh-_float-button-bg
--xh-_material-bg
--xh-_material-bg-focus
float-button 的 list、root、trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-float-button-bg-activelist
root
trigger
background-coloractive
disabled
is(:active, [data-pressed])
loading
not(:disabled)
not([data-disabled])
not([data-loading])
not([data-scope])
pressed
variant=outline
variant=subtle
--xh-_action-variant-bg-pressed
--xh-_float-button-bg-active
--xh-_material-bg-pressed
float-button 的 list、root、trigger 部件 background-color 覆盖槽。
--xh-float-button-bg-hoverlist
root
trigger
background-color@media (hover: hover)
disabled
hover
loading
not(:disabled)
not([data-disabled])
not([data-loading])
not([data-scope])
variant=outline
variant=subtle
--xh-_action-variant-bg-hover
--xh-_float-button-bg-hover
--xh-_material-bg-hover
float-button 的 list、root、trigger 部件 background-color 覆盖槽。
--xh-float-button-borderlist
root
trigger
border
border-color
default
disabled
focus-visible
not([data-scope])
variant=outline
--xh-_action-variant-border-disabled
--xh-_action-variant-border-focus-visible
--xh-_action-variant-border-rest
--xh-_float-button-border
--xh-_material-border
float-button 的 list、root、trigger 部件 border、border-color 覆盖槽。
--xh-float-button-border-hoverlist
root
trigger
border-color@media (hover: hover)
disabled
hover
is(:active, [data-pressed])
loading
not(:disabled)
not([data-disabled])
not([data-loading])
not([data-scope])
pressed
variant=outline
--xh-_action-variant-border-hover
--xh-_action-variant-border-pressed
--xh-_float-button-border-hover
--xh-_material-border
float-button 的 list、root、trigger 部件 border-color 覆盖槽。
--xh-float-button-fglist
root
trigger
colordefault
disabled
focus-visible
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
not([data-scope])
pressed
variant=outline
--xh-_action-variant-fg-focus-visible
--xh-_action-variant-fg-hover
--xh-_action-variant-fg-pressed
--xh-_action-variant-fg-rest
--xh-_float-button-fg
--xh-_material-fg
float-button 的 list、root、trigger 部件 color 覆盖槽。
--xh-float-button-gaplist
root
gapdefault--xh-space-2float-button 的 list、root 部件 gap 覆盖槽。
--xh-float-button-icon-sizeroot
trigger
--xh-icon-sizedefault--xh-_float-button-glyph-sizefloat-button 的 root、trigger 部件 --xh-icon-size 覆盖槽。
--xh-float-button-layerrootz-indexdefault--xh-_layerfloat-button 的 root 部件 z-index 覆盖槽。
--xh-float-button-radiuslist
trigger
border-radiusdefault--xh-_action-profile-radius
--xh-shape-circle
float-button 的 list、trigger 部件 border-radius 覆盖槽。
--xh-float-button-shadowlist
root
trigger
box-shadowdefault
disabled
focus-visible
hover
is(:active, [data-pressed])
is(:hover, [data-loading])
loading
not([data-disabled])
not([data-loading])
not([data-scope])
pressed
variant=outline
--xh-_float-button-shadow
--xh-_material-shadow
none
float-button 的 list、root、trigger 部件 box-shadow 覆盖槽。
--xh-float-button-sizelist
root
trigger
block-size
bottom
inline-size
inset-block-end
inset-block-start
inset-inline-end
inset-inline-start
left
min-inline-size
top
default
edge=block
edge=inline
moving
placement
placement=-end
placement=-start
placement=bottom
placement=top
point
xh-action-profile=floating
--xh-_float-button-sizefloat-button 的 list、root、trigger 部件 block-size、bottom、inline-size、inset-block-end、inset-block-start、inset-inline-end、inset-inline-start、left、min-inline-size、top 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 切换 · 出现(见动效规范)。

共享关键帧 xh-overlay-pop-in · xh-pop-out 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;background-color · border-color · rotate · scale 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

响应式 ​

皮肤另按输入能力分档:hover: hover:同一份皮肤在触屏与带指针的设备上不一样,与视口宽度无关。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License