CSS錨點定位終于來了!
盼了好久,最近 Chrome 125終于迎來了CSS 錨點定位的正式支持。這是一個和 CSS 滾動驅動動畫一樣,足以顛覆整個 Web 開發領域的新特性。有了這個特性,很多以前強依賴 JS 的方式,都可以純 CSS解決,并且實現起來更加簡單、更加靈活,一起看看吧!
一、快速了解 CSS 錨點定位
在過去,要實現一個元素定位,通常需要一個相對定位。比如這樣一個 tooltip。
如果不借助 JS,讓這個氣泡位于按鈕的正上方,就只能約束HTML結構,讓這個氣泡位于按鈕內部。
<button>
BUTTON
<tooltip>我是tooltip</tooltip>
</button>
并且設置按鈕為相對定位,才能通過絕對定位實現氣泡位于按鈕的正上方。
button{
position: relative;
}
tooltip{
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%)
}
雖然可以實現,但是局限性很多。比如HTML要求嚴格,只能是嵌套結構,換種結構就不行了,還有,如果父級有超出隱藏的樣式,這個氣泡也會被裁剪掉。因此,一般框架里不會采用這種 CSS實現,都是通過JS動態去獲取位置來實現的。
現在有了CSS錨點定位特性,一切都好辦了。
首先是對結構無任何要求,可以是頁面上的任意地方的元素。
<button>BUTTON</button>
<tooltip>我是tooltip</tooltip>
由于沒有嵌套關系,所以我們要手動的指定一下(不然誰知道該怎么定位呢?),這里是通過anchor-name和position-anchor將兩個元素關聯(錨定)起來,如下:
button{
anchor-name: --anchor-el;
}
tooltip{
position: absolute;
position-anchor: --anchor-el;
}
最后,再設置定位就行了,關鍵實現如下:
tooltip{
bottom: anchor(top);
left: anchor(center);
transform: translateX(-50%)
}
這樣就能實現任意兩個元素的錨定了。
你也可以訪問以下在線鏈接(Chrome 125+)
- CSS anchor (codepen.io)[1]
是不是非常靈活呢?不過這里出現了一些從未見過的屬性和方法,下面再來具體介紹。
二、CSS 錨點定位語法詳解
為了實現這樣一個功能,CSS新推出了很多屬性和方法,如下:
1. 錨點的設置與引用 anchor-name、position-anchor
這個前面已經用到了,主要是anchor-name和position-anchor兩個屬性,他們倆用一個唯一的標識符鏈接起來。需要注意的是,這個標識符必須要以雙短橫線開頭,和 CSS 變量名是一樣的,其他的則無效。
button{
anchor-name: anchor-el; /*屬性值無效*/
}
button{
anchor-name: --anchor-el;
}
tooltip{
position: absolute;
position-anchor: --anchor-el;
}
你可以理解為把設置anchor-name的元素當做是以前的相對定位元素(錨點元素),而設置position-anchor的元素就當成普通絕對定位元素就行了。
另外,如果標識符有重復,比如有多個button,都是相同的anchor-name,那么會以最后一個為準。
2.錨點的位置表示 anchor()
前面說了,設置position-anchor的元素可以當做是普通的絕對定位。既然是定位,那就需要設置坐標,比如left和top值。由于不是固定的值,為了,這里又推出了一系列定位函數,如下:
anchor(left)
anchor(center)
anchor(right)
anchor(top)
anchor(bottom)
比如anchor(left)表示錨定元素的最左側,anchor(top)表示錨定元素的最上側,依次類推,下面是一張示意圖。
值得注意的是,anchor(center)表示既可以表示水平居中,也可以表示垂直居中,這是由使用方式決定的。
top: anchor(center); /*垂直居中*/
left: anchor(center); /*水平居中*/
回到上一章的例子,我們要實現一個朝上居中的氣泡,所以定位元素的bottom要剛好處于錨定元素的上方,然后水平方向上是常用的居中方式,先定位到中間,然后反向位移自身的一半,具體實現如下:
tooltip{
bottom: anchor(top);
left: anchor(center);
transform: translateX(-50%)
}
水平方向的居中看著不是特別優雅,而且還占用了transform,可能不是特別靈活,下面來看另一個實現。
3. 錨定居中 anchor-center
上面水平居中用到了left和transform來實現,其實還有新的實現方式,那就是anchor-center,不過這需要配合justify-self和align-self使用。
比如要水平居中,可以直接使用。
tooltip{
bottom: anchor(top);
justify-self: anchor-center;
}
如果要垂直居中,可以用align-self。
tooltip{
right: anchor(left);
align-self: anchor-center;
}
示意如下:
4. 更人性化的定位方式 inset-area
你可能在大部分組件庫都用過類似這樣的定位方式,例如Ant Design,一般是12個方位。
是不是比較好理解?一看就懂,比前面的left、top方式要簡單的多了。
沒錯,錨點定位也支持類似的定位方式,引入了一種新型的定位系統叫做:inset-area。
這個方式比前面的實現更加便捷、更加靈活,它將錨定元素分成九宮格,并且考慮了各個位置的可擴展性,一共有 20 種可能組合,如下:
inset-area: top; /* 居上,無尺寸限制 */
inset-area: top center; /* 居上并且不超過錨定元素尺寸 */
inset-area: top span-left; /* 居上并且左邊可以擴展 */
inset-area: top span-right; /* 居上并且右邊可以擴展 */
inset-area: left;
inset-area: left center;
inset-area: left span-top;
inset-area: left span-bottom;
inset-area: bottom center;
inset-area: bottom span-left;
inset-area: bottom span-right;
inset-area: bottom;
inset-area: right center;
inset-area: right span-top;
inset-area: right span-bottom;
inset-area: right;
inset-area: top left; /* 左上角 */
inset-area: top right; /* 右上角 */
inset-area: bottom left; /* 右下角 */
inset-area: bottom right; /* 右下角 */
看著是不是有點太多了,也有點暈,其實這里多了 8 種不常用的,下面做了一個示意圖,可以很清楚的看到每種方位的具體位置(虛線部分就是常見的12種方位)。
以上截圖修改來源于:https://anchor-tool.com。
回到前面第一章的例子,要實現居上垂直居中,其實可以一行代碼搞定。
tooltip{
inset-area: top;
}
是不是又精簡了許多呢?
5. 錨點尺寸 anchor-size
有時候,我們可能還需要知道錨定元素的尺寸,比如這樣的場景
可以看到,在切換tab時,底下的背景是可以無縫過渡的。在以前,我們要實現這樣的功能,必須要借助 JS來獲取當前點擊元素的尺寸和位置,但現在,只需要借助 CSS 錨點定位就能輕松實現了。
位置信息前面以及提到了,用anchor(left)和anchor(top)就可以了,那尺寸呢,需要用到anchor-size。
anchor-size(width) /*錨點元素寬度*/
anchor-size(height) /*錨點元素高度*/
利用這個特性,我們可以很輕松的實現這樣一個效果,結構如下:
<nav class="tab">
<a class="item" href="#HTML" name="HTML">HTML</a>
<a class="item" href="#CSS" name="CSS">CSS</a>
<a class="item" href="#JavaScript" name="JavaScript">JavaScript</a>
<a class="item" href="#React" name="React">React</a>
<a class="item" href="#Vue" name="Vue">Vue</a>
</nav>
我們用偽元素來當做tab高亮背景,關鍵實現如下:
.tab::after{
content: '';
position: absolute;
border-radius: 100px;
background-color: rgba(65, 105, 225, 0.2);
position-anchor: --anchor-el;
width: anchor-size(width);
height: anchor-size(height);
left: anchor(left);
top: anchor(top);
transition: .3s;
pointer-events: none;
}
.item:target{
anchor-name: --anchor-el;
}
這樣就能輕松實現這個效果了,你也可以訪問以下在線鏈接(Chrome 125+)
- CSS anchor nav (codepen.io)[2]
6. 動態調整位置 position-try-options
有時候定位元素會處于屏幕邊緣,當沒有足夠空間顯示時,可以通過position-try-options來設置一個備用位置。
舉個例子,比如一個氣泡,默認是朝上的,當滾動到屏幕邊緣時會自動朝下,示意如下:
這種情況就可以用@position-try來實現了,具體做法是這樣的。
先通過position-try-options指定一個變量名,比如--bottom。
tooltip{
position: fixed;
position-anchor: --anchor-el;
inset-area: top;
position-try-options: --bottom;
}
然后通過@position-try來定義規則。
@position-try --bottom {
inset-area: bottom;
}
這樣就實現定位元素位置自動調整了。
除此之外,還有一種便捷寫法,直接給position-try-options指定以下關鍵字。
position-try-options: flip-block; /*垂直翻轉*/
position-try-options: flip-inline; /*水平翻轉*/
這樣就無需@position-try定義了,實現更簡單。
- CSS anchor position-try-options (codepen.io)[3]
當然,我覺得這個功能還是稍顯不足的,比如當氣泡帶有箭頭時,雖然也能翻轉,但是卻無法改變箭頭的位置,也就是無法查詢到當前是否已經翻轉了,就像這樣。
希望盡快解決吧~
三、和 popover 配合使用
畢竟popover只是解決了層級的問題,而錨點定位解決了定位問題。兩者結合,我們可以很輕松的實現各種常見的效果,已經可以說能夠完全替代主流框架中的popover組件了。
下面是一個功能完善的多級菜單,完全無需 JS即可實現。
首先是點擊出現,這個就是popover的功能了,通過popovertarget和popover屬性,將兩者結合起來,就能輕松實現點擊出現菜單的功能。
<button class="btn" popovertarget="more"></button>
<div class="menu" id="more" popover>
<button class="item">編輯</button>
<button class="item">刪除</button>
</div>
然后就定位,利用CSS錨點定位,將菜單定位到按鈕的右下方,也就兩三行代碼的事。
.btn{
anchor-name: --menu;
}
.menu{
position-anchor: --menu;
inset-area: bottom span-right;
}
這樣就能輕易實現懸浮菜單了,你也可以訪問以下在線鏈接(Chrome 125+)
- CSS anchor menu (codepen.io)[4]
在codepen上找到了一個更完善的多級菜單案例。
https://codepen.io/jh3y/pen/dyLjbwG
四、總結和其他
介紹了這么多,一下子肯定難以接受,多回顧幾遍就明白了,至少可以知道錨點定位是干嘛的,如果以后有類似的需求也有一定的方向,下面總結一下本文要點
- CSS 錨點定位是一個顛覆性的新特性,一定要學會
- CSS 錨點定位可以設置任意元素相對任意元素做定位
- 主要是通過anchor-name和position-anchor兩個屬性關聯
- 錨點的位置用anchor()來定義,比如anchor(left)表示錨定元素的最左側,anchor(top)表示錨定元素的最上側
- anchor-center可以實現居中定位,水平居中justify-self: anchor-center,垂直居中align-self: anchor-center
- inset-area是一種更人性化的定位方式,和常見的組件庫表示方位比較類似
- 還可以通過 anchor-size來錨點元素的尺寸,anchor-size(width)表示寬度,anchor-size(height)表示高度
- position-try-options可以根據定位元素是否處于屏幕邊緣而自適應定位方向
- 實際中更推薦和popover相互配合,可以輕松實現各類懸浮層效果
- 兼容性要求 Chrome 125+,期待早日使用吧
最近幾年CSS更新的確實有點太快了,很多以往的疑難雜癥都有了新的解決方式。但是很多時候學這些好像暫時沒啥用,畢竟可能 5 年以后才用得上。但是原生特性不像其他,一個框架兩三年就有可能被淘汰,或者有新的替代品出現,原生的學到了就學到了,只要web存在的一天,就永遠都不會過時,所以也不虧是吧。
[1]CSS anchor (codepen.io): https://codepen.io/xboxyan/pen/dyEVVPb。
[2]CSS anchor nav (codepen.io): https://codepen.io/xboxyan/pen/zYQpvqg。
[3]CSS anchor position-try-options (codepen.io): https://codepen.io/xboxyan/pen/dyEJYRO。
[4]CSS anchor menu (codepen.io): https://codepen.io/xboxyan/pen/qBGpOKq。