<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          搶先體驗(yàn)!超強(qiáng)的 Anchor Positioning 錨點(diǎn)定位

          共 18126字,需瀏覽 37分鐘

           ·

          2023-08-19 15:27

          今天小編為大家?guī)?lái)的是社區(qū)作者 chokcoco 的文章,讓我們一起來(lái)學(xué)習(xí)超強(qiáng)的 Anchor Positioning 錨點(diǎn)定位。




          本文,將向大家介紹 CSS 規(guī)范中,最新的 Anchor Positioning,翻譯為錨點(diǎn)定位。

          Anchor Position 的出現(xiàn),極大的豐富了 CSS 的能力,雖然語(yǔ)法稍顯復(fù)雜,但是有了它,能夠?qū)崿F(xiàn)非常多之前實(shí)現(xiàn)起來(lái)非常困難,或者壓根無(wú)法使用純 CSS 實(shí)現(xiàn)的功能。

          Anchor Position 當(dāng)前仍屬于實(shí)驗(yàn)室功能,新版本 Chrome 開(kāi)啟該功能:

          1. 瀏覽器 URL 輸入框輸入:chrome://flags/
          2. 找到 Experimental Web Platform features 選項(xiàng),開(kāi)啟該功能



          何為 Anchor Positioning?

          那么,什么是 Anchor Positioning 呢?

          Anchor Positioning 由規(guī)范 CSS Anchor Positioning 提出定義。

          規(guī)范是這么描述的:

          CSS absolute positioning allows authors to place elements anywhere on the page, without regard to the layout of other elements besides their containing block. This flexibility can be very useful, but also very limiting—often you want to position relative to some other element. Anchor positioning (via the anchor functions anchor() and anchor-size()) allows authors to achieve this, "anchoring" an absolutely-positioned element to one or more other elements on the page, while also allowing them to try several possible positions to find the "best" one that avoids overlap/overflow.

          簡(jiǎn)單翻譯一下,其核心就在于,Anchor Positioning(錨點(diǎn)定位) 用于增強(qiáng)元素的絕對(duì)定位的能力。Anchor Positioning(錨點(diǎn)定位)允許我們基于其它錨點(diǎn)元素的位置和尺寸去定位上下文,而不是傳統(tǒng)意義上的基于父元素去進(jìn)行絕對(duì)定位。

          整個(gè) Anchor Positioning 到底是干啥的?其重點(diǎn)總結(jié)如下:

          1. 首先,錨點(diǎn)定位,需要我們通過(guò)新的錨點(diǎn)名稱(anchor-name)來(lái)標(biāo)記元素,允許我們使用這些經(jīng)過(guò)了標(biāo)記的元素作為我們絕對(duì)定位的基準(zhǔn)目標(biāo);
          2. 其次,我們可以在絕對(duì)定位的元素上,通過(guò)新的語(yǔ)法 anchor() 或者 anchor-size() 來(lái)錨定上述被標(biāo)記了的元素,并且可以使用被標(biāo)記元素的相應(yīng)屬性(譬如被標(biāo)記元素的 top、left、right、bottom 等)
          3. 并且,還有一些更高級(jí)的用法,譬如錨點(diǎn)定位的 Fallback 機(jī)制,也就是可以設(shè)置多套不同的錨點(diǎn)定位規(guī)則,以適應(yīng)更為復(fù)雜的頁(yè)面布局情況

          下面,我們通過(guò)一個(gè)最簡(jiǎn)單的例子,快速理解,到底什么是錨點(diǎn)定位。

          假設(shè),我們有如下代碼結(jié)構(gòu):
             
          <div class="g-container">
              <div class="g-use-anchor"></div>  
              <div class="g-anchor-element"></div>  
          </div>
          .g-container {
              position: relative;
              width: 50vw;
              height: 50vh;
              border: 2px solid #666;
              display: flex;
              
              .g-anchor-element {
                  width: 25vw;
                  height: 25vh;
                  border: 2px dashed #333;
                  margin: auto;
              }
              
              .g-use-anchor {
                  position: absolute;
                  width: 20px;
                  height: 20px;
                  background: #fc0;
                  border-radius: 50%;
              }
          }

          整個(gè),會(huì)是這么個(gè)效果:

          需要簡(jiǎn)單解讀一下這個(gè)結(jié)構(gòu):


          這個(gè)很好理解,注意,此時(shí)此刻,.g-use-anchor 由于是絕對(duì)定位,是相對(duì)于它的父容器 .g-container 進(jìn)行定位的。

          而 Anchor Positioning 錨點(diǎn)定位,就允許我們,在上述情況下,改變 .g-use-anchor 的絕對(duì)定位的基準(zhǔn)元素,允許它在絕對(duì)定位下,不再相對(duì)于父容器定位,而是相對(duì)于設(shè)定了 anchor-name 的錨點(diǎn)元素進(jìn)行定位。

          所以,下面,我們嘗試將 .g-anchor-element 變成一個(gè)錨點(diǎn)元素。

          代碼如下:
             
          .g-container {
              position: relative;
              width: 50vw;
              height: 50vh;
              border: 2px solid #666;
              display: flex;
              
              .g-anchor-element {
                  width: 25vw;
                  height: 25vh;
                  border: 2px dashed #333;
                  margin: auto;
                  anchor-name: --target;
              }
              
              .g-use-anchor {
                  position: absolute;
                  width: 20px;
                  height: 20px;
                  background: #fc0;
                  border-radius: 50%;
                  top: anchor(--target top);
                  left: anchor(--target left);
              }
          }

          上面的代碼,有哪些改動(dòng)呢?

          1. 我們給 .g-anchor-element 添加了一句 CSS 代碼,anchor-name: --target,其含義為,將此元素設(shè)定為一個(gè)錨點(diǎn)元素,它的名字是 --target。

          2. 在 .g-use-anchor 中,新增了兩句代碼
          • top: anchor(--target top):這里的意思是,使用 name 為 --target 的錨點(diǎn)元素作為定位基準(zhǔn),并且將元素的頂部(top)對(duì)齊到錨點(diǎn)元素的頂部(top)
          • left: anchor(--target left):同理,使用 name 為 --target 的錨點(diǎn)元素作為定位基準(zhǔn),并且將元素的左邊(left)對(duì)齊到錨點(diǎn)元素的左邊(left)

          如此一來(lái),我們就得到了這么一個(gè)效果:

          也就是這么一層關(guān)系:

          完整的代碼,你可以戳這里:CodePen Demo -- Anchor Positioning Demo
          (https://codepen.io/Chokcoco/pen/JjegrBN?editors=1100)

          到這里,聰明的你應(yīng)該能夠深刻體會(huì)到,上面我們說(shuō)的這一句話的含義了:

          Anchor Positioning(錨點(diǎn)定位) 用于增強(qiáng)元素的絕對(duì)定位的能力。Anchor Positioning(錨點(diǎn)定位)允許我們基于其它錨點(diǎn)元素的位置和尺寸去定位上下文,而不是傳統(tǒng)意義上的基于父元素去進(jìn)行絕對(duì)定位。



          Anchor Positioning 錨點(diǎn)定位實(shí)戰(zhàn) -- 彈出框定位

          可以說(shuō),很多之前無(wú)法使用 CSS 實(shí)現(xiàn)的功能,因?yàn)?Anchor Positioning,迎來(lái)了新的轉(zhuǎn)機(jī)。

          首先,我們來(lái)看這么一個(gè)功能點(diǎn),我們的頁(yè)面有很多需要 Hover 的時(shí)候彈出的 Popover 或者 Tooltip,像是這樣:

          每次 Hover 的時(shí)候,彈出框的位置,其實(shí)都是需要實(shí)時(shí)通過(guò) JavaScript 進(jìn)行計(jì)算的。但是在有了 Anchor Positioning 后,我們可以把每一個(gè)被 Hover 需要彈出彈出框的元素,都設(shè)置成一個(gè)錨點(diǎn)元素,而我們的彈出框,只需要在 Hover 的時(shí)候,基于當(dāng)前的錨點(diǎn)元素進(jìn)行定位即可。

          聽(tīng)起來(lái)很復(fù)雜,我們來(lái)實(shí)現(xiàn)一遍試試:
             
          <p class="g-container">
              Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod, porro, <span>facere</span>, et incidunt error aliquam fugit pariatur eos labore ipsum voluptate magni culpa reiciendis optio at accusantium non! Quis, laboriosam.
              Lorem ipsum dolor sit, <span>amet consectetur</span> adipisicing elit. Error commodi sequi perspiciatis ipsa veniam, aut, aliquam maiores quasi <span>adipisci tenetur </span>reiciendis dolor nihil aperiam labore, sunt qui ullam aspernatur <span>voluptate</span>.
          </p>

          在 <p> 元素下,被包裹了 <span> 的文字就是需要 Hover 的時(shí)候彈出內(nèi)容的元素。
             
          p {
              position: relative;
              
              span {
                  color: deeppink;
              }
              
              &::before,
              &::after {
                  position: absolute;
                  transition: 0;
                  opacity: 0;
              }
              
              &::before {
                  content: "";
                  top: calc(anchor(var(--target) top) + 10px);
                  left: calc(anchor(var(--target) left) + 5px);
                  border: 8px solid transparent;
                  border-bottom: 8px solid #000;
              }
              &::after {
                  content: "Alert Tips!";
                  width: 80px;
                  padding: 2px 4px;
                  font-size: 14px;
                  background: #fff;
                  border: 2px solid #000;
                  top: calc(anchor(var(--target) top) + 24px);
                  left: anchor(var(--target) left);
                  right: anchor(var(--target) right);
              }
          }

          p span:nth-child(1) {
              anchor-name: --anchor-1;
          }
          p span:nth-child(2) {
              anchor-name: --anchor-2;
          }
          p span:nth-child(3) {
              anchor-name: --anchor-3;
          }
          p span:nth-child(4) {
              anchor-name: --anchor-4;
          }

          p:has(span:nth-child(1):hover) {
              --target: --anchor-1;
          }
          p:has(span:nth-child(2):hover) {
              --target: --anchor-2;
          }
          p:has(span:nth-child(3):hover) {
              --target: --anchor-3;
          }
          p:has(span:nth-child(4):hover) {
              --target: --anchor-4;
          }

          p:has(span:hover)::before,
          p:has(span:hover)::after{
              opacity: 1;
          }

          這里的代碼,有點(diǎn)長(zhǎng),簡(jiǎn)單解釋一下:

          1. 通過(guò) <p> 元素的兩個(gè)偽元素 ::before 和 ::after,實(shí)現(xiàn)了彈出框的框體和一個(gè)小三角形
          2. 給每個(gè) <span> 都設(shè)置了成了錨點(diǎn),也就是這么一段代碼:p span:nth-child(1) {anchor-name: --anchor-1;}
          3. 關(guān)鍵來(lái)了,利用了 :has 選擇器,實(shí)現(xiàn)了當(dāng)哪個(gè) <span> 被 hover,則設(shè)置 --target 變量為當(dāng)前元素的 anchor-name,也就是實(shí)現(xiàn)了錨點(diǎn)元素的動(dòng)態(tài)變換
          4. 最終,只需要讓彈出框(也就是兩個(gè)偽元素),基于 --target 進(jìn)行定位即可,而 --target 元素,就是我們每次 Hover 的文字元素,那么彈框也就實(shí)現(xiàn)了動(dòng)態(tài)定位

          知識(shí)補(bǔ)充,:has 選擇器變相讓 CSS 擁有了父選擇器的能力,此選擇器用于選擇包含指定子元素的父元素,而本例中,利用了 :has 選擇器甚至能選擇包含指定偽類狀態(tài)的能力,實(shí)現(xiàn)了 --target 的動(dòng)態(tài)切換。

          這樣,我們就成功了實(shí)現(xiàn)了上述的功能:

          當(dāng)然,這里還需要繼續(xù)補(bǔ)充一個(gè)基于 anchor() 方法的基礎(chǔ)知識(shí),anchor() 方法的值也能與 calc 搭配使用,因此,需要理解如下的表達(dá)式:

          • top: calc(anchor(var(--target) top) + 10px):將彈框元素的頂部(top)對(duì)齊到錨點(diǎn)元素的頂部(top),再加上 10px 的向上間距
          • left: calc(anchor(var(--target) left) + 5px):將彈框元素的左邊(left)對(duì)齊到錨點(diǎn)元素的左邊(left),再加上 5px 的左間距

          還有,如果頁(yè)面內(nèi)有 100個(gè) <span> 那下面這樣的代碼將會(huì)是噩夢(mèng)性的重復(fù)工作:
             
          p span:nth-child(1) {
              anchor-name: --anchor-1;
          }
          p span:nth-child(2) {
              anchor-name: --anchor-2;
          }
          p span:nth-child(3) {
              anchor-name: --anchor-3;
          }
          p span:nth-child(4) {
              anchor-name: --anchor-4;
          }

          p:has(span:nth-child(1):hover) {
              --target: --anchor-1;
          }
          p:has(span:nth-child(2):hover) {
              --target: --anchor-2;
          }
          p:has(span:nth-child(3):hover) {
              --target: --anchor-3;
          }
          p:has(span:nth-child(4):hover) {
              --target: --anchor-4;
          }

          因此,我們需要借助 SCSS/SASS/LESS 等預(yù)處理簡(jiǎn)化代碼,譬如這樣:
             
          @for $i from 1 to 100 {
              p:has(span:nth-child(#{$i}):hover) {
                  --target: --anchor-#{$i};
              }
              p span:nth-child(#{$i}) {
                  anchor-name: --anchor-#{$i};
              }
          }

          合理利用預(yù)處理器的循環(huán)等功能,能有效提升我們的編碼效率。

          OK,完整的代碼,你可以戳這里:
          CodePen Demo -- Anchor Positioning Demo
          (https://codepen.io/Chokcoco/pen/wvQVoXO)


          Anchor Positioning 錨點(diǎn)定位實(shí)戰(zhàn) -- Tab 切換下劃線跟隨效果

          OK,我們繼續(xù),再來(lái)一個(gè)有意思的實(shí)戰(zhàn)演練。

          基于 Anchor Positioning,能否實(shí)現(xiàn)這樣一個(gè) TAB 切換時(shí)候的,下劃線跟隨效果呢?


          此類效果,在之前,一定是需要 JavaScript 的介入才可能實(shí)現(xiàn)的。在很久之前,我嘗試使用 CSS 實(shí)現(xiàn)過(guò)類似的效果:不可思議的CSS導(dǎo)航欄下劃線跟隨效果(https://github.com/chokcoco/iCSS/issues/33),效果上有很多瑕疵:

          而有了 Anchor Positioning 后,我們將可以完美的實(shí)現(xiàn) Tab 切換過(guò)程中的下劃線跟隨效果。

          假設(shè),我們的 TAB 的結(jié)構(gòu)如下:
             
          <ul>
              <li>下</li>
              <li>劃</li>
              <li>線</li>
              <li>跟</li>
              <li>隨</li>
              <li>動(dòng)</li>
              <li>畫(huà)</li>
          </ul>

          其核心流程和上面的彈出框流程非常類似:

          1. 下劃線通過(guò) <ul>元素的偽元素實(shí)現(xiàn)
          2. 給每個(gè) <li> 都設(shè)置了成了錨點(diǎn)
          3. 利用了 :has 選擇器,實(shí)現(xiàn)當(dāng)任意一個(gè) <li> 被 hover,則設(shè)置 --target 錨點(diǎn)變量為當(dāng)前的 <li> 元素,也就是實(shí)現(xiàn)了錨點(diǎn)元素的動(dòng)態(tài)變換
          4. 最終,只需要讓下劃線,基于動(dòng)態(tài)的錨點(diǎn)進(jìn)行定位即可,也就是我們每次 Hover 的 li 元素,那么彈框也就實(shí)現(xiàn)下劃線動(dòng)態(tài)定位
          5. 給下劃線的 left 設(shè)置過(guò)渡效果 transition,實(shí)現(xiàn)跟隨動(dòng)畫(huà)效果

          讓我們一起來(lái)看看代碼,看似復(fù)雜,代碼量也很少:
             
          ul {
              position: relative;
              width: 700px;
              display: flex;
              
              li {
                  flex-grow: 1;
              }
              
              &::before {
                  position: absolute;
                  content: "";
                  height: 5px;
                  background: transparent;
                  bottom: 0;
                  left: anchor(var(--target) left);
                  right: anchor(var(--target) right);
                  transition: .3s all;
                  transform: scaleX(5);
              }
          }

          ul:hover::before {
              background: #000;
              transform: scaleX(1);
          }

          @for $i from 1 to 8 {
              ul:has(li:nth-child(#{$i}):hover) {
                  --target: --anchor-#{$i};
              }
              li:nth-child(#{$i}) {
                  anchor-name: --anchor-#{$i};
              }
          }

          需要好好理解一下這段代碼,其本質(zhì)就在于,Hover 的時(shí)候,利用 :has 動(dòng)態(tài)改變了 --target 錨點(diǎn)元素,讓偽元素實(shí)現(xiàn)的下劃線的寬度,設(shè)置為錨點(diǎn)的寬度。

          并且,這里還加了一個(gè) hover 過(guò)程中 transform: scaleX(5) 到 transform: scaleX(1) 的變化,屬于錦上添花,刪掉不影響最終效果。

          當(dāng)然,也利用了 SCSS 循環(huán),減少了代碼量。最終的效果就如我們上面 Gif 圖演示般:


          并且,可以做到適配 Flex 彈性布局各種情況。

          這樣,我們就實(shí)現(xiàn)了在之前,完全不敢想象能夠由 CSS 獨(dú)立實(shí)現(xiàn)的功能,完整的代碼,你可以戳這里:CodePen Demo -- Anchor Positioning Demo
          (https://codepen.io/Chokcoco/pen/JjegMRq)


          兼容性

          OK,大家肯定非常關(guān)系如此強(qiáng)大的功能的兼容性。

          目前,Anchor Positioning 還處于較早期的版本,甚至乎我在 Can i Use 上還查不到它的兼容性:

          但是,目前我使用的 Chrome 115.0.5790.102 是能夠跑通上面的所有代碼。

          Anchor Position 當(dāng)前仍屬于實(shí)驗(yàn)室功能,新版本 Chrome 開(kāi)啟該功能:

          1. 瀏覽器 URL 輸入框輸入:chrome://flags/
          2. 找到 Experimental Web Platform features 選項(xiàng),開(kāi)啟該功能

          并且,Anchor Positioning 還有非常多的語(yǔ)法以及有意思的實(shí)戰(zhàn)技巧,在本文是沒(méi)有放出來(lái)的。我會(huì)在 Anchor Positioning 兼容性更加明朗后,補(bǔ)充一篇更為詳細(xì)的教學(xué)文章。只能說(shuō),未來(lái)可期。


          最后

          好了,本文到此結(jié)束,希望對(duì)你有幫助 :)

          更多精彩 CSS 技術(shù)文章匯總在我的 Github -- iCSS(https://github.com/chokcoco/iCSS) ,持續(xù)更新,歡迎點(diǎn)個(gè) star 訂閱收藏。

          如果還有什么疑問(wèn)或者建議,可以多多交流,原創(chuàng)文章,文筆有限,才疏學(xué)淺,文中若有不正之處,萬(wàn)望告知。



          點(diǎn)擊左下角閱讀原文,到 SegmentFault 思否社區(qū) 和文章作者展開(kāi)更多互動(dòng)和交流,公眾號(hào)后臺(tái)回復(fù)“ 入群 ”即可加入我們的技術(shù)交流群,收獲更多的技術(shù)文章~

          - END -



          往期推薦



          社區(qū)精選|Spring 中 @Qualifier 注解還能這么用?


          社區(qū)精選|【動(dòng)畫(huà)進(jìn)階】神奇的 3D 磨砂玻璃透視效果


          社區(qū)精選|效率max:AI讀了源碼后再教我


          瀏覽 230
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  九九热最新视频 | 中文无码综合网 | 日韩熟妇视频 | 亚洲综合区 | 亚洲大乱婬交换 |