浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
从 Chrome 125 开始,支持了一个全新的 CSS 特性 - Anchor Positioning,翻译过来即是锚点定位。
在之前的文章中,我们较为系统的讲述了这个新特性的使用,感兴趣的可以翻开一下前文:抢先体验!超强大的 Anchor Positioning 锚点定位。
在本文中,我们将使用锚点定位,实现一个简化版本的 Popover 功能。下面,我们将一起一探究竟。
传统 Popover 功能
长话短说,在日常的前端需求开发中,有这么一种场景,我们需要 hover 某个具体元素,以弹出一个弹出层,像是这样:
如果我们将弹出层的 DOM 结构,写在某个具体的元素内部,譬如放在与被 Hover 元素同级。如果被 Hover 元素的祖先元素的某一层设置了 overflow: hidden
,则可能会出现这种截断现象:
为了避免这种情况的发生,一般情况下,常见的解决方案都是会将弹出层的 DOM,插入到页面最外层的 <body>
容器之下,再通过实时计算位置,将该弹出层定位到被 hover 元素附近。这也就是类似于 popper.js、Tippy.js 等传统库所干的事情。
核心逻辑如下:
- 通过 JavaScript 获取触发元素的位置信息(getBoundingClientRect)
- 计算视口剩余空间(需考虑滚动位置、窗口尺寸等)
- 动态调整 Popover 位置(需处理边界碰撞、翻转逻辑)
- 添加 resize/scroll 事件监听器进行位置修正
其核心在于,通过 Javascript 动态计算当前 hover 元素的位置,将弹出层定位到当前 hover 元素附近合适的位置,并且处理好各种边界场景,这里很重要一点是强依赖于 Javascript 的计算。
因为传统的 CSS,是没有办法改变元素的定位父元素的能力的。
而有趣的是,全新的 CSS 特性 - Anchor Positioning 锚点定位就是为了解决这个问题的。
快速了解 Anchor Positioning 锚点定位
那,到底什么是 Anchor Positioning 锚点定位呢?我们通过一个 DEMO 快速上手。
假设我们在页面上有这么三个元素:按钮A、按钮B、被定位元素C,其中被 元素C 是一个插入到任意地方,且为绝对定位的元素,核心 CSS 代码如下:
<body>
<div class="btn-a">按钮A</div>
<div class="btn-b">按钮B</div>
<div class="anchor">被定位元素C</div>
</div>
.btn-a, .btn-b {
// 高宽各类样式
}
.anchor {
position: absolute;
}
现在,三个元素属于同级关系,且 C 元素是绝对定位,当前基于 body
进行绝对定位。
大致样式如下:
而锚点定位的属性的核心作用就是,能够改变元素定位的基准,增强元素的绝对定位的能力。Anchor Positioning(锚点定位)允许我们基于其它锚点元素的位置和尺寸去定位上下文,而不是传统意义上的基于父元素去进行绝对定位。
下面,我们利用锚点定位去实现,当两个按钮 A、B 被 Hover 的时候,让定位元素 C 基于当前被 Hover 的按钮元素进行绝对定位,核心 CSS 代码如下:
.btn-a {
// 将元素声明为定位基准点,命名为 --btn-a
anchor-name: --btn-a;
}
.btn-b {
// 将元素声明为定位基准点,命名为 --btn-b
anchor-name: --btn-b;
}
// 当元素被 hover 的时候,改变 C 元素的样式
.btn-a:hover ~ .anchor {
// 锚点绑定,建立元素与锚点的定位关系
position-anchor: --btn-a;
// 基于新的锚点元素,设置元素的 left\top 属性
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
// 利用 transform 轻微调整定位,非核心代码
transform: translate(-50%, 5px);
}
// 同理,与上面做的事情一致,知识在 hover 按钮 B 时,重新设定锚点元素
.btn-b:hover ~ .anchor {
position-anchor: --btn-b;
left: anchor(--btn-b center);
top: anchor(--btn-b bottom);
transform: translate(-50%, 5px);
}
.anchor {
// 方便动图演示,增加元素过渡动画,非核心代码
transition: all .2s;
}
核心关注上面的 anchor-name
、position-anchor
、 anchor
几个属性,其作用和含义在注释中有说明,在下文还会再描述一次。
如此一来,我们就实现了动态改变 C 元素定位基准的能力,我们看看效果:
简单而言,我们利用锚点定位的能力,在 hover 按钮 A\B 的时候,把 C 元素定位在它们正下方。这个就是锚点定位的能力!
Anchor Positioning 锚点定位核心 API
(1)anchor-name:锚点定义
功能:将元素声明为定位基准点
语法:anchor-name: <dashed-ident>;
应用场景:触发元素、参考元素、动态定位源
/* 定义触发元素为锚点 */
.trigger-btn {
anchor-name: --menu-anchor;
}
(2)position-anchor:锚点绑定
功能:建立元素与锚点的定位关系
语法:position-anchor: <dashed-ident>;
注意:需配合定位属性(position: fixed/absolute)使用
.tooltip {
position: fixed;
position-anchor: --menu-anchor; /* 绑定到指定锚点 */
}
(3)anchor():动态定位
功能:根据锚点位置计算坐标
语法:anchor(<anchor-name>? <anchor-side>)
方位参数:
- 垂直:top/center/bottom
- 水平:left/center/right
- 组合:top-left/bottom-right 等
.context-menu {
/* 锚点右下角对齐 */
top: anchor(--ctx-anchor bottom);
left: anchor(--ctx-anchor right);
}
.tooltip {
/* 水平居中于锚点 */
left: anchor(center);
right: anchor(center);
}
(4)anchor-size():尺寸继承
功能:获取锚点元素的尺寸值
语法:anchor-size(<anchor-name>? <dimension>)
维度参数:width/height/block/inline
.popover {
/* 继承锚点宽度 */
width: anchor-size(width);
/* 最小高度为锚点高度的1.5倍 */
min-height: calc(anchor-size(height) * 1.5);
}
这里介绍了锚点定位中最为核心的几个属性,掌握了这几个属性,就可以应付大部分场景了。在我之前的一篇入门文章中,对它们也有一些更为详细的描述,感兴趣的同学,可以翻看:抢先体验!超强大的 Anchor Positioning 锚点定位
锚点定位的候补位置
还有一个非常重要的点,传统的 Popover 组件,一般都会有这么个功能 -- 智能边界处理。
我们以一个功能比较强大的 Popover 库 floating-ui 举例,其官网展示了如下的一个功能,当元素在滚动过程中,如果原本 Popover 弹窗被遮挡,会自动进行位置移动,将弹窗重新调整到可视区域,效果如下:
令人振奋的是,现在,CSS 的 Anchor Positioning 锚点定位同样支持这种 智能边界处理,在锚点定位中,我们称之为候补位置。
这里,我们主要借助两个锚点定位相关的属性完成锚点定位的候补位置 position-try-fallbacks 和 @position-try 规则。
@position-try 规则
@position-try
用于定义一个备选定位策略(一组定位规则),可以在多个元素中复用。它的语法类似于定义一个命名规则集合。
/* 语法示例:*/
@position-try --strategy-name {
/* 具体的定位规则 */
top: anchor(bottom);
left: anchor(left);
}
关键点:
- 命名策略:每个 @position-try 规则需要唯一名称(如 --tooltip-below)。
- 独立作用域:策略内部的定位规则独立于元素自身的样式,仅在被调用时生效。
position-try-fallbacks 属性
position-try-fallbacks 用于在元素上指定备选定位策略的优先级顺序。浏览器会按顺序尝试这些策略,直到找到第一个可用的位置。
/* 语法示例:*/
.element {
position-try-options: --strategy1, --strategy2, --strategy3;
}
关键点:
- 顺序敏感:浏览器按列表顺序尝试策略,第一个可行的策略会被应用。
- 动态回退:如果所有策略均不可行,元素会回退到默认定位(或父容器约束)。
使用锚点定位实现候补位置
基于上述介绍,我们来实现一个基于锚点定位的候补位置。
假设我们如下结构,当前已经使用了锚点定位:
<body>
<div class="btn">Reference</div>
<div class="anchor">Popover弹窗元素</div>
</div>
核心 CSS 如下:
.btn {
anchor-name: --btn;
border: 1px dashed #000;
background: #ddd;
}
.anchor {
position: absolute;
position-anchor: --btn;
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
transform: translate(-50%, 5px);
}
此时,两个元素都插入在 <body>
下面,但是 Popover 弹窗元素使用了 position-anchor: --btn
锚点定位,使其绝对定位的父元素是 .btn
,并且,定位在按钮的下方,效果如下:
此时,我们只需要再借助 position-try-fallbacks
和 @position-try
,实现候补位置:
@position-try
定义一个候补规则position-try-fallbacks
引入候补规则
核心 CSS 代码如下:
.btn {
anchor-name: --btn;
border: 1px dashed #000;
background: #ddd;
}
.anchor {
position: absolute;
position-anchor: --btn;
left: anchor(--btn-a center);
top: anchor(--btn-a bottom);
transform: translate(-50%, 5px);
position-try-fallbacks: --position-bottom;
}
@position-try --position-bottom {
left: anchor(--btn center);
bottom: anchor(--btn top);
top: unset;
margin-bottom: 10px;
}
这样,我们在滚动页面的过程中,如果弹窗 popover 有超出视窗,候补规则会自动生效,看看效果:
仔细观察,和上面利用 Javascript 库实现的智能定位,效果一致,只是此时,我们仅仅使用了寥寥几行 CSS 代码!Amazing!
综上所述,到今天,我们已经可以利用 CSS 锚点定位大致实现一个极简版的 Popover 弹窗,并且可以满足大部分场景。不得不感叹 CSS 确实愈发的强力,
当然,本文介绍的关于锚点定位的功能都是基于实现一个最小版本的 Popover 展开的,基于锚点定位的 API 和回退候补,还有更多有趣的内容,感兴趣的可以猛戳 MDN 进行了解:MDN - Anchor Positioning、MDN-Anchor Positioning @position try。
最后
好了,本文到此结束,一个非常有意思的 CSS 技巧,希望本文对你有所帮助
想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻
更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。
如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。
浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析的更多相关文章
- Vue 事件监听实现导航栏吸顶效果(页面滚动后定位)
Vue 事件监听实现导航栏吸顶效果(页面滚动后定位) Howie126313 关注 2017.11.19 15:05* 字数 100 阅读 3154评论 0喜欢 0 所说的吸顶效果就是在页面没有滑动之 ...
- IE浏览器打开 「兼容性视图」
有些IE上的网页控件需要打开兼容性视图才能使用,不知道是Javascript的原因,还是CSS的原因. 使用环境是用C语言配合boa服务器实现的CGI程序.
- 【Dubbo3 终极特性】「云原生三中心架构」带你探索 Dubbo3 体系下的配置中心和元数据中心、注册中心的原理及开发实战(中)
承接上文 通过之前的[Dubbo3终极特性]「云原生三中心架构」带你探索 Dubbo3 体系下的配置中心和元数据中心.注册中心的原理及开发实战(上),让我们对Dubbo3的三中心架构体系有了一定的认识 ...
- 调整 FMX Android 文字显示「锯齿」效果
说明:调整 Firemonkey Android 显示文字有「锯齿」效果 适用:Firemonkey Android 平台 修改方法: 请将源码 FMX.FontGlyphs.Android.pas ...
- (ASP页面查询等待提示效果)GridViewなどで検索中に「処理中メッセージ」を表示する方法(※他の長い時間処理も参照できる)
原博客 http://ino1970.blog119.fc2.com/blog-entry-163.html GridViewなどで検索中に「処理中メッセージ」を表示する方法 「GridViewなどで ...
- 前端构建工具之gulp(一)「图片压缩」
前端构建工具之gulp(一)「图片压缩」 已经很久没有写过博客了,现下终于事情少了,开始写博吧 今天网站要做一些优化:图片压缩,资源合并等 以前一直使用百度的FIS工具,但是FIS还没有提供图片压缩的 ...
- 如何排版 微信公众号「代码块」之 MarkEditor
前段时间写过一篇文章 如何排版微信公众号「代码块」,讲的是如何使用浏览器插件 Markdown Here 来排版代码块.虽然用 Markdown Here 排版出来的样式还不错,但存在一个问题,就是代 ...
- 【微信小程序】开发实战 之 「配置项」与「逻辑层」
微信小程序作为微信生态重要的一环,在实际生活.工作.商业中的应用越来越广泛.想学习微信小程序开发的朋友也越来越多,本文将在小程序框架的基础上就微信小程序项目开发所必需的基础知识及语法特点进行了详细总结 ...
- Activity 的 36 大难点,你会几个?「建议收藏」
前言 学 Android 有一段时间了,一直都只顾着学新的东西,最近发现很多平常用的少的东西竟让都忘了,趁着这两天,打算把有关 Activity 的内容以问题的形式梳理出来,也供大家查缺补漏. 本文中 ...
- SpringBoot图文教程17—上手就会 RestTemplate 使用指南「Get Post」「设置请求头」
有天上飞的概念,就要有落地的实现 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍 先赞后看,养成习惯 SpringBoot 图文教程系列文章目录 SpringBoot图文教程1-Spr ...
随机推荐
- 各版本jdk百度云下载,包括linux版和windows版
并不是越新的版本就一定越好,请先考虑jdk的版本是否跟你的开发环境有版本冲突问题. 2021-11-4更新 ps:从官网下载实在是太慢了!! 官网链接:https://www.oracle.com/j ...
- 【电脑问题】开机自动进入BIOS,按下Ctrl+ALt+Del键可以正常进入系统
问题描述:开机自动进入BIOS,按下Ctrl+ALt+Del键可以正常进入系统 Ctrl+Alt+Del 作用:立即终结电脑的异常状态,包括宕机 按法①:三个键一起按 按法 ②:先按住Ctrl和Alt ...
- Mac netstat 查看端口报错 netstat: option requires an argument -- p 解决
netstat -anvp |grep 10001 查询端口的时候报错提示 意思是缺少协议. 解决方案在Mac上正确使用的方法是:即-f需要加上地址族,-p需要加上协议TCP或者UDP等 a)如果需要 ...
- Qt/C++实现帧同步播放器/硬解码GPU绘制/超低资源占用/支持8K16K/支持win/linux/mac/嵌入式/国产OS等
一.前言 首先泼一盆冷水,在不同的电脑上实现完完全全的帧同步理论上是不可能的,市面上所有号称帧同步的播放器,同一台电脑不同拼接视频可以通过合并成一张图片来绘制实现完完全全的帧同步,不同电脑,受限于网络 ...
- [转]MySQL和MySQL驱动mysql-connector-java升级到8.0.X版本
原文链接:MySQL和MySQL驱动mysql-connector-java升级到8.0.X版本
- 开源商业化 Sealos 如何做到月入 160万
去年我写了一篇也是讲开源商业化的文章,当时是月入 30 万,一年过去了,我们整整涨了 5 倍多.本文理论结合实践,比较干货,希望对大家有帮助. 我们的现状,谁在给我们付钱 第一,开发者,我们已经近 2 ...
- 前端学习openLayers配合vue3(加载矢量图标)
今天我们来进行矢量图标的加载 关键代码 有一个比较注意的点就是,图片路径必须引入不能直接写路径,我找半天也没发现问题所在 let anchorLayer=new VectorLayer({ sourc ...
- 用 C# 实现检测系统环境变量 “Path” 中是否有某个值,没有就添加的方法
用 C# 实现检测系统环境变量 "Path" 中是否有某个值,没有就添加的方法: using System; using System.Collections.Generic; u ...
- MySQL-扩展
1.行转列 源数据: 目标数据: 数据准备 -- 建表插入数据 drop table if exists time_temp; create table if not exists time_temp ...
- C# webapi 允许跨域(.NET Framework)
实际项目中,对于WebApi的访问不一定都在同一域名下,所以进行跨域访问的时候,可能会出现如下提示:请求的资源不支持 http 方法"OPTIONS".需要对WebApi进行设置. ...