<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>

          自己做個(gè) Material Ripple 效果的按鈕

          共 7467字,需瀏覽 15分鐘

           ·

          2021-03-26 11:06

          • 本文已獲得原作者的獨(dú)家授權(quán),有想轉(zhuǎn)載的朋友們可以在后臺(tái)聯(lián)系我申請開白哦!
          • PS:歡迎掘友們向我投稿哦,被采用的文章還可以送你掘金精美周邊!

          背景介紹

          我感覺他挺好看的!

          我第一次發(fā)現(xiàn) Material Design 是幾年前玩 Android(當(dāng)時(shí)還不會(huì)開發(fā) Android 應(yīng)用程序)時(shí)候看到的些貼文。那時(shí)候我就超級喜歡它的按鈕組件。它有著波紋效果,以簡單,優(yōu)雅的方式為用戶提供反饋,Q 彈爆汁兒~

          那時(shí)候的我也只會(huì)使用固定的 :hover :focus 樣式,效果固定而死板,那是我這種一班人用的,Google 那群二班的真的太強(qiáng)了!??!

          你看看這圓潤的外框,這活潑的顏色 ♂?,這似乎汁水四溢的效果,是不是像極了你們欠我的那個(gè)贊 :)

          我們可以完全做到一樣的效果!

          需求一覽

          • Ripple 效果
          • 自動(dòng)為所有元素加效果
          • 監(jiān)聽新元素的插入

          該咋辦?

          我打算用 JavaScript 監(jiān)聽點(diǎn)擊事件,向按鈕添加子元素(Ripple 動(dòng)效元素),并向按鈕添加 .ripple 類,并監(jiān)聽 DOM 樹中的變化,如果有 .ripple 元素的加入,就為其綁定 Ripple 效果。

          stateDiagram-v2
          [*] --> 按鈕事件
          按鈕事件 --> 未綁定
          按鈕事件 --> 已綁定
          未綁定 --> 綁定按鈕
          綁定按鈕 --> 動(dòng)效
          已綁定 --> 動(dòng)效
          動(dòng)效 --> 添加&nbsp;ripple
          添加&nbsp;ripple --> 添加子元素
          添加子元素 --> [*]

          HTML

          <button>一個(gè)簡簡單單的按鈕</button>

          CSS

          對于 Ripple 效果,我們會(huì)等下直接用 JavaScript 去動(dòng)態(tài)設(shè)置,而樣式的定義,就在如下的一些代碼中解決:

          button {
              position: relative;
              overflow: hidden;
          }

          使用 position: relative 允許我們等下構(gòu)造的子元素針對按鈕本體能夠使用 position: absolute。同時(shí),overflow: hidden 可以幫助我們防止 Ripple 效果超出按鈕的輪廓。然后再裝飾一下:

          /* 用上 Material 的默認(rèn)字體 */
          @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');

          button {
              position: relative; /* 下文中會(huì)用到的相對絕對位置 */
              overflow: hidden;
              transition: background 400ms ease-in-out; /* 設(shè)置切換 */
              color#fff;
              background-color#662D91;
              padding1rem 2rem;
              font-family'Roboto', sans-serif;
              outline0;
              border0;
              border-radius0.25rem;
              box-shadow0 0 0.5rem rgba(0000.2);
              cursor: pointer;
          }

          現(xiàn)在它是這樣的:

          Ripple

          Ripple 效果實(shí)際上就是一個(gè)半徑不斷擴(kuò)展的標(biāo)準(zhǔn)圓,而被沿著按鈕外框裁切掉。因此我們先來繪制一個(gè)標(biāo)準(zhǔn)圓:

          span.ripple {
              position: absolute; /* 上文中我們提到過的相對絕對位置 */
              border-radius50%;
              transformscale(0);
              animation: ripple 600ms linear;
              background-colorrgba(2552552550.2);
          }

          為了使波紋變圓,我們設(shè)置 border-radius50%。而為了確保動(dòng)畫開始時(shí)候沒有效果,我們設(shè)置了默認(rèn)縮放比例 0?,F(xiàn)在,我們將無法看到任何東西,因?yàn)槲覀冞€沒有設(shè)置 top、left、width 以及 height,也沒有修改默認(rèn)縮放比例 transform: scale(0)。不用著急,馬上我們就會(huì)用 JavaScript 設(shè)置這些屬性!

          現(xiàn)在我們還需要給 Ripple 效果添加動(dòng)畫切換,就讓它縮放到 4 倍大小吧:

          @keyframes ripple {
              to {
                  transformscale(4);
                  opacity0;
              }
          }

          JavaScript

          現(xiàn)在我們需要使用 JavaScript 來動(dòng)態(tài)設(shè)置 Ripple 起始圓心的位置和 Ripple 大小。這個(gè)大小應(yīng)基于按鈕的大小,而位置應(yīng)基于按鈕和光標(biāo)的位置。

          事件綁定

          先來綁定 click 事件:

          [...document.querySelectorAll(".ripple")].forEach(btn => {
              btn.addEventListener("click", showRipple);
          });

          然后我們可以使用 event.currentTarget 獲取到當(dāng)前元素:

          const btn = event.currentTarget;

          獲取到了被點(diǎn)擊的按鈕,現(xiàn)在我們來構(gòu)建一個(gè)子元素,并計(jì)算按鈕的半徑大小:

          const circle = document.createElement("span");
          const diameter = Math.max(button.clientWidth, button.clientHeight);
          const radius = diameter / 2;

          現(xiàn)在,我們可以定義我們需要為我們的漣漪其余屬性:left、top、widthheight。

          數(shù)據(jù)計(jì)算

          我們知道,top 應(yīng)該等于點(diǎn)擊事件的 (x, y) 減去按鈕的中心點(diǎn)的 (x, y)

          Example

          例如上面的圖片,圓心中心點(diǎn)應(yīng)該就是 (918 - 323, 392 - 244)(595, 148)。

          因此,我們可以得出應(yīng)該這樣設(shè)置這個(gè)圓:

          circle.style.width = circle.style.height = `${diameter}px`;
          circle.style.left = `${event.clientX - (button.offsetLeft + radius)}px`;
          circle.style.top = `${event.clientY - (button.offsetTop + radius)}px`;
          circle.classList.add("ripple"); 

          然后現(xiàn)在我們將這個(gè) circle 添加到 btn 即可:

          btn.appendChild(circle);

          完整的代碼就是:

          const showRipple = (event) => {
              const btn = event.currentTarget;

              const circle = document.createElement("span");
              const diameter = Math.max(btn.clientWidth, btn.clientHeight);
              const radius = diameter / 2;

              circle.style.width = circle.style.height = `${diameter}px`;
              circle.style.left = `${event.clientX - (btn.offsetLeft + radius)}px`;
              circle.style.top = `${event.clientY - (btn.offsetTop + radius)}px`;
              circle.classList.add("ripple");

              btn.appendChild(circle);
              setTimeout(() => {
                  btn.removeChild(circle)
              }, 1000); /* 記得移除元素 */
          }

          Show Time!

          這就滿足了嗎??? 未嘗也太簡單了吧?

          監(jiān)聽頁面元素更新

          現(xiàn)在我們需要監(jiān)聽所有元素的更新!自動(dòng)讓系統(tǒng)為所有新增的按鈕添加一樣的動(dòng)畫?。?!

          到我們的 MutationObserver 發(fā)揮它的作用啦?。。?/p>

          我們先需要定義一個(gè)接受事件并處理數(shù)據(jù)的函數(shù),先暫且命名為 listener

          const listener = (mutationRecord) => {
              /**
               * @param mutationRecord: Callback of MutationObserve
               *  => mutations: MutationRecord[]
               */

           }

          然后定義一個(gè)監(jiān)聽工具并初始化:

          const mutationObserver = new MutationObserver(listener);
          mutationObserver.observe(document, {subtreetruechildListtrueattributestrue});

          一般來說,可能會(huì)有兩種情況:

          • childList / subtree
          • attributes

          屬性變化

          如果是元素的屬性變化,那么 mutationRecord.type 會(huì)是 attributes,那么我們直接:

          if (mutationRecord.type === "attributes" && mutationRecord.attributeName === "ripple" && !mutationRecord.target.hasAttribute("ripple-init")) {
              mutationRecord.target.addEventListener("click", showRipple);
              mutationRecord.target.setAttribute("ripple-init""");
          }

          元素變化

          而如果是生成了元素,那么也很簡單粗暴,直接遍歷 mutationRecord.addedNodes 即可:

          if (mutationRecord.addedNodes && mutationRecord.addedNodes.length > 0)
              mutationRecord.addedNodes.forEach(node => {
                  if (node.nodeType === Node.ELEMENT_NODE && !node.hasAttribute("ripple-init") && node.hasAttribute("ripple")) {
          node.addEventListener("click", showRipple);
          node.setAttribute("ripple-init""");
                  }
              });

          讓我們來測試一下效果吧,就用 setTimeout 在 100ms 以后生成一個(gè) .ripple 的按鈕吧:

          setTimeout(() => {
              document.querySelector("button").setAttribute("ripple""");
              let btn = document.createElement("button");
              btn.setAttribute("ripple""");
              btn.innerText = "這是另外一個(gè)簡單的按鈕"
              document.body.appendChild(btn);
          }, 2000);

          總結(jié)思考

          看了看 GitHub 的文件,一年前的更新啊……

          似乎也沒什么可以改進(jìn)的(誤)

          • 支持更多種類的 Material Button 的 Ripple 效果
          • MutationObserver 推廣應(yīng)用在別的地方
          • 應(yīng)用這段代碼(當(dāng)時(shí)也是無聊,學(xué)了一下,而我卻也沒有什么網(wǎng)站有很多的按鈕控件,直接改又會(huì)與當(dāng)前的樣式不搭配)

          歡迎各位一起加入 掘金翻譯計(jì)劃大家庭,一起助力掘金變得更棒

          本文正在參與「掘金 2021 春招闖關(guān)活動(dòng)」, 點(diǎn)擊查看 活動(dòng)詳情

          瀏覽 54
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  性爱乱伦视频 | 男女做爱视频网站 | 风间由美一二三区AV片 | 三级片亚亚洲无码 | 国产黄色网页 |