整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          如何通過css格子布局一個簡單的頁面

          文鏈接: https://getflywheel.com/layout/css-grid-layouts-how-to/

          柵格布局的思想起源源自于印刷設計。柵格是用來將設計元素精確定位到頁面上的的測量工具。這種想法經常被用在網頁上來進行內容組織和統一,提升用戶的視覺體驗。

          網頁設計剛起步的時候,設計和布局都是是相當簡單的, 通常包含頭部,側邊欄,內容區域和頁腳。現在,隨著網絡的演變,網頁的布局也變得更加復雜,做網頁設計師的人也隨之增加。我們經常需要大量的內容區域,響應式設計,多頁面模板設計,以及許多其他的。浮動和定位在實現這些設計的時候,是必不可少的。但浮動聽起來簡單,實際操作起來卻很棘手。

          但接下來,我們會介紹一種簡單的設計布局。隨著CSS柵格布局的不斷發展,成為設計師也會變得越來越容易。

          CSS柵格兼容性

          作為一名設計師,需要了解網頁設計的未來。CSS柵格布局將改變現有規則,為設計師處理頭痛了許多年的定位。雖然目前還不是主流的做法,但是這是一件值得期待的事情。

          在我們真的深入了解柵格布局之前,要強調的一件事,瀏覽器并不普遍支持, 希望這種工作方式在未來可以得到越來越多的瀏覽器支持。不過, 好消息是, 您可以輕松地嘗試使用CSS柵格布局,以及了解它是如何工作的。

          在使用示例之前,請你確保你的瀏覽器支持。目前只有Internet Explorer 10+和 edge 支持。其他的瀏覽器通過一些手段也可以瀏覽,但因為它不是官網支持,所以你只能是不斷的去嘗試。(If you view the Can I Use documentation on CSS Grid Layouts, at the time of this post, you will notice little flag indicators. These show that you will need to be in “flag mode.”)如果你邊使用Can I Use來查看柵格布局的兼容性,邊看這篇文章,那么你就可以注意到每個細節的不同。

          當您在測試柵格布局的時候,你需要做幾件事情幫助你正確地看到布局。使用Chrome查看,你需要啟用“實驗性網絡平臺功能”。如何啟用呢?在Chrome 瀏覽器中打開chrome://flags 這個地址。當url 鏈接chrome://flags加載完畢之后,向下滾動頁面,找到該選項,設置為“啟用實驗性網絡平臺功能”。

          火狐也允許您查看柵格布局,通過“layout.css.grid.enabled”參數設置。開啟方法類似于Chrome瀏覽器的說明。在Firefox瀏覽器中URL輸入 about:config。向下滾動頁面,設置為啟用 “layout.css.grid.enabled” 。

          如果你想馬上開始使用CSS柵格布局,對于不支持它的瀏覽器還有一個變通方案。如果你熟悉polyfills的想法,那已經有解決方案了。如果你不熟悉polyfills,可以利用瀏覽器后退,利用JavaScript的力量,允許現代的瀏覽器功能(例如CSS柵格布局)在舊的瀏覽器運行。

          Polyfills超出本教程范圍,但隨著越來越多的設計師開始使用這項技術,更多的polyfill 技術將會涌現。如果你準備嘗試,這里是一個推薦的 polyfill option。請務必閱讀作者的文檔,了解有關如何使用它的詳細信息。

          那么,在承諾100%使用CSS柵格布局之前,要確保使用的生產代碼,做一些深入的測試。

          CSS柵格布局基本知識

          通過利用CSS,柵格布局將有助于您的網頁內容的呈現。這里有一篇相對較新的定義的屬性的CSS柵格布局規范 。這是學習柵格設計的一個很好的資源。CSS柵格設計有助于簡化的東西,使創建布局更加容易。想象一下,柵格作為一種結構,尺寸可以被定義。

          柵格的組成

          行(lines)

          在上圖中,有五條垂直線和三條水平線。線從1開始編號。示例中,垂直線從左至右,這取決于書寫方向。如果書寫方向是由右至左,順序就顛倒過來。可以給線起名(可選),方便在CSS中引用。

          軌道(tracks)

          軌道是兩條平行線之間的空間。在圖中,有四個垂直軌道和兩個水平的軌道。這是線和軌道的共同結果。 線是記錄內容的起點和終點。軌道是內容真實存在的位置。

          單元格(cells)

          單元格是水平和垂直軌道的相交處。圖中有八個單元格。

          面(areas)

          單元格指定面的時候發揮作用。面是矩形形狀,可以跨越多個單元格。像線一樣,面也可以任意命名。如在圖中的幾個標簽:“A”,“B”,和“C”。

          創建柵格布局

          用老方格紙,布局之前,先勾勒輪廓。

          HTML柵格

          <div class="container">

          <div class="grid header">Header</div>

          <div class="grid sidebar">Sidebar</div>

          <div class="grid content">Main Content</div>

          <div class="grid extra">Extra Content</div>

          <div class="grid footer">Footer</div>

          </div>

          容器Container是非常重要的。容器內是用于顯示網站的不同的內容塊。內容塊的順序并不重要。接下來,我們將使用CSS將它們按照我們的布局顯示。

          CSS樣式

          HTML完成后,我們來寫CSS。給container設置display:grid 或者 display:inline-grid. 如果你希望設置塊級元素,那使用 display:grid ; 如果你希望設置成內聯元素, 那使用display:inline-grid。想了解更多細節,可以查看文檔

          .container {

          display: grid;

          grid-template-columns: 0.25fr 15px 0.75fr;

          grid-template-rows: auto 25px auto 25px auto 25px auto;

          }

          .grid {

          background-color: #444;

          color: #fff;

          padding: 25px;

          font-size: 3rem;

          }

          grid-template-columns和grid-template-rows屬性用于指定行和列的寬度。這個布局定義了五列。15px是兩個元素之間的間距。第三列占用0.25份的剩余空間。同樣地,第五列占用0.75份的剩余空間。(疑問: 圖中根本沒有第五列啊,感覺作者寫錯了)

          There are responsive customizations that can be made, but this is a great step to take prior to that. It may seem like using pixel measurements would be limiting, however, using auto for the first row in grid-template-rows allows the row to expand as necessary based on the content inside it. The 25px row acts as a gutter.

          對于響應式布局這個規范是很便利的,如果使用像素,則會被限定死。第一行使用grid-template-rows來表示,隨著內容需要的變化而變化。設置padding 成 25 像素,與頭部留有間隙。

          元素看起來很緊湊,但再加一些規范,元素將初具規模。

          這個例子先放置的頭部,但元素位置可以按您喜歡的任意順序擺放。如果你想從頁腳開始,也可以。

          我們先從頭部開始,從列1開始到列4結束,從行1開始到行2結束,CSS看起來就像這樣:

          .header {

          grid-column-start: 1;

          grid-column-end: 4;

          grid-row-start: 1;

          grid-row-end: 2;

          }

          您可能會注意到側邊欄被壓住了,我們無法看到它。我們需要重新排列一下。在這種布局,通過行的位置進行排列。以行作為標準。頭部占一行和二行的位置,側邊欄,從三行開始, 到六行結束。 頭部到第二行結束,側邊欄從第三行開始正好可以顯示到頭部下面。要查看示例,請參見該項目Codepen。

          我們使用grid-column-start指定一個元素起始垂直線。在本例中,它將被設置為3。grid-column-end表示一個元素的結束垂直線。在這種情況下,這個屬性就等于四。其他行值也用同樣的方式設置。側邊欄的位置是存在的,它只是覆蓋的內容區。

          .sidebar {

          grid-column-start: 1;

          grid-column-end: 2;

          grid-row-start: 3;

          grid-row-end: 6;

          background: #a0c263;

          }

          主要內容在第三列開始,第四列結束。側邊欄和內容區域的頂部對齊,所以他們都從grid-row-start第三行開始。你可能想讓內容欄比側邊欄高很多。通過設置容器上的高度,假如400像素,這個時候,它就會比其它元素高很多。

          .content {

          grid-column-start: 3;

          grid-column-end: 4;

          grid-row-start: 3;

          grid-row-end: 4;

          background: #f5c531;

          height: 400px;

          }

          最后兩個內容區域是額外內容區域和頁腳。

          .extra {

          grid-column-start: 3;

          grid-column-end: 4;

          grid-row-start: 5;

          grid-row-end: 6;

          background: #898989;

          }

          .footer {

          grid-column-start: 1;

          grid-column-end: 4;

          grid-row-start: 7;

          grid-row-end: 8;

          background: #FFA500;

          }

          響應式優勢

          布局已經創建好了,似乎很像一個“桌面”。那么平板電腦和移動設備怎么顯示?CSS柵格布局加上媒體查詢可以適應不同的屏幕尺寸。真正酷的是,你可以在這些不同的媒體查詢范圍里,改變內容區域。作為一個設計師,這意味著你選擇什么是最適合你的布局在不同的斷點。例如,如果你想要將“次要內容”被放在“內容”區域之上,可以指定正確的行和列。

          /* For mobile phones: */

          .header {

          grid-column-start: 1;

          grid-column-end: 4;

          grid-row-start: 1;

          grid-row-end: 2;

          }

          .extra {

          grid-column-start: 1;

          grid-column-end: 4;

          grid-row-start: 3;

          grid-row-end: 4;

          }

          .content {

          grid-column-start: 1;

          grid-column-end: 4;

          grid-row-start: 5;

          grid-row-end: 6;

          background: #f5c531;

          height: 400px;

          }

          通過設置成列1開始,列4結束,來設置成內容全寬。將“次要內容”顯示在了“內容”之上。

          CSS柵格布局是一種新型的布局方式。正如你所看到的,這種方法很容易創建一個簡單的頁面布局去運行。上面這個簡單的例子也可以為如何創建更復雜的布局打下良好的基礎。假如這個技術獲得普及,在設計各種設備和尺寸,布局大小自定義的時候,這個技術會是一個優勢。

          問切 wenqie(.cn),是切圖網旗下關注用戶體驗,專注H5移動適配的品牌網站。

          文來源于:程序員成長指北;作者:去偽存真

          如有侵權,聯系刪除


          背景


          最近在項目中要實現一個拖拽頭像的移動效果,一直對JS Dom拖拽這一塊不太熟悉,甚至在網上找一個示例,都看得云里霧里的,發現遇到最大的攔路虎就是JS Dom各種各樣的距離,讓人頭暈眼花,看到一個距離屬性,大腦中的印象極其模糊,如同有一團霧一樣,不知其確切含義。果然是基礎不牢,地動山搖。今天決心夯實一下基礎,親自動手驗證一遍dom各種距離的含義。


          JS Dom各種距離釋義


          下面我們進入正題, 筆者不善于畫圖, 主要是借助瀏覽器開發者工具,通過獲取的數值給大家說明一下各種距離的區別。


          第一個發現 window.devicePixelRatio 的存在


          本打算用截圖軟件丈量尺寸,結果發現截圖軟件顯示的屏幕寬度與瀏覽器開發者工具獲取的寬度不一致,這是為什么呢?


          • 截圖軟件顯示的屏幕寬度是1920



          • window.screen.width顯示的屏幕寬度是1536



          這是怎么回事?原來在PC端,也存在一個設備像素比的概念。它告訴瀏覽器一個css像素應該使用多少個物理像素來繪制。要說設備像素比,得先說一下像素和分辨率這兩個概念。


          • 像素 屏幕中最小的色塊,每個色塊稱之為一個像素(Pixel)



          • 分辨率 分辨率=屏幕水平方向的像素點數 * 屏幕垂直方向的像素點數; 另外說一下,關于分辨率有多種定義,可以細分為顯示分辨率[1]、圖像分辨率[2]、打印分辨率[3]和掃描分辨率[4]等,此處是指顯示分辨率。



          • 設備像素比


          設備像素比的定義是:


          window.devicePixelRatio =顯示設備物理像素分辨率顯示設備CSS像素分辨率\frac{顯示設備物理像素分辨率}{顯示設備CSS像素分辨率}顯示設備CSS像素分辨率顯示設備物理像素分辨率


          根據設備像素比的定義, 如果知道顯示設備橫向的css像素值,根據上面的公式,就能計算出顯示設備橫向的物理像素值。


          顯示設備寬度物理像素值 = window.screen.width * window.devicePixelRatio;


          設備像素比在我的筆記本電腦上顯示的數值是1.25, 代表一個css邏輯像素對應著1.25個物理像素。



          我前面的公式計算了一下,與截圖軟件顯示的像素數值一致。這也反過來說明,截圖軟件顯示的是物理像素值。



          • window.devicePixelRatio 是由什么決定的 ?


          發現是由筆記本電腦屏幕的縮放設置決定的,如果設置成100%, 此時window.screen.width與筆記本電腦的顯示器分辨率X軸方向的數值一致,都是1920(如右側圖所示), 此時屏幕上的字會變得比較小,比較傷視力。



          • 邏輯像素是為了解決什么問題?


          邏輯像素是為了解決屏幕相同,分辨率不同的兩臺顯示設備, 顯示同一張圖片大小明顯不一致的問題。比如說兩臺筆記本都是15英寸的,一個分辨率是1920*1080,一個分辨率是960*540, 在1920*1080分辨率的設備上,每個格子比較小,在960*540分辨率的設備上,每個格子比較大。一張200*200的圖片,在高分率的設備上看起來會比較小,在低分辨率的設備上,看起來會比較大。觀感不好。為了使同樣尺寸的圖片,在兩臺屏幕尺寸一樣大的設備上,顯示尺寸看起來差不多一樣大,發明了邏輯像素這個概念。

          規定所有電子設備呈現的圖片等資源尺寸統一用邏輯像素表示。然后在高分辨率設備上,提高devicePixelRatio, 比如說設置1920*1080設備的devicePixelRatio(dpr)等于2, 一個邏輯像素占用兩個格子,在低分辨率設備上,比如說在960*540設備上設置dpr=1, 一個css邏輯像素占一個格子, 這樣兩張圖片在同樣的設備上尺寸大小就差不多了。通常設備上的邏輯像素是等于物理像素的,在高分辨率設備上,物理像素是大于邏輯像素數量的。由此也可以看出,物理像素一出廠就是固定的,而設備的邏輯像素會隨著設備像素比設置的值不同而改變。但圖片的邏輯像素值是不變的。


          document.body、document.documentElement和window.screen的寬高區別


          差別是很容易辨別的,如下圖所示:


          • document.body -- body標簽的寬高
          • document.documentElement -- 網頁可視區域的寬高(不包括滾動條)
          • window.screen -- 屏幕的寬高



          • 網頁可視區域不包括滾動條


          如下圖所示,截圖時在未把網頁可視區域的滾動條高度計算在內的條件下, 截圖工具顯示的網頁可視區域高度是168, 瀏覽器顯示的網頁可視區域的高度是167.5, 誤差0.5,由于截圖工具是手動截圖,肯定有誤差,結果表明,網頁可視區域的高度 不包括滾動條高度。寬度同理。



          • 屏幕和網頁可視區域的寬高區別如下:


          屏幕寬高是個固定值,網頁可視區域寬高會受到縮放窗口影響。



          • 屏幕高度和屏幕可用高度區別如下:


          屏幕可用高度=屏幕高度-屏幕下方任務欄的高度,也就是:


          window.screen.availHeight = window.screen.height - 系統任務欄高度



          scrollWidth, scrollLeft, clientWidth關系


          scrollWidth(滾動寬度,包含滾動條的寬度)=scrollLeft(左邊卷去的距離)+clientWidth(可見部分寬度); 
          // 同理 
          scrollHeight(滾動高度,包含滾動條的高度)=scrollTop(上邊卷去的距離)+clientHeight(可見部分高度);


          需要注意的是,上面這三個屬性,都取的是溢出元素的父級元素屬性。而不是溢出元素本身。本例中溢出元素是body(document.body),其父級元素是html(document.documentElement)。另外,


          溢出元素的寬度(document.body.scrollWidth)=父級元素的寬度(document.documentElement.scrollWidth) - 滾動條的寬度(在谷歌瀏覽器上滾動條的寬度是19px)



          <!DOCTYPE html> 
          <html lang="en"> 
            <head>     
              <meta charset="UTF-8">     
              <meta http-equiv="X-UA-Compatible" content="IE=edge">     
              <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->     
              <title>JS Dom各種距離</title>     
              <style>         
                html, body {             
                  margin: 0;         
                }         
                body {             
                  width: 110%;             
                  border: 10px solid blue;         
                }         
                .rect {             
                  height: 50px;             
                  background-color: green;         
                }     
              </style> 
            </head> 
            <body>     
              <div id="rect" class="rect"></div> 
            </body> 
          </html>
          


          元素自身和父級元素的scrollWidth和scrollLeft關系?


          從下圖可以看出:


          • 元素自身沒有X軸偏移量,元素自身的滾動寬度不包含滾動條
          • 父級元素有X軸便宜量, 父級元素滾動寬度包含滾動條


          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
              <title>JS Dom各種距離</title>
              <style>
                  div {
                      border: 1px solid #000;
                      width: 200px;
                      height: 600px;
                      padding: 10px;
                      background-color: green;
                      margin: 10px;
                  }
              </style>
          </head>
          
          <body>
              <div class="rect">    111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
              </div>
          </body>
          <script>
          </script>
          </html>
          


          offsetWidth和clientWidth的關系?


          offsetWidth和clientWidth的共同點是都包括 自身寬度+padding , 不同點是offsetWidth包含border


          如下圖所示:


          • rect元素的clientWidth=200px(自身寬度) + 20px(左右padding) = 220px
          • rect元素的offsetWidth=200px(自身寬度) + 20px(左右padding) + 2px(左右boder) = 222px



          <!DOCTYPE html>
          <html lang="en">
          
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
              <title>JS Dom各種距離</title>
              <style>
                  div {
                      border: 1px solid #000;
                      width: 200px;
                      height: 100px;
                      padding: 10px;
                      background-color: green;
                      margin: 10px;
                  }
              </style>
          </head>
          
          <body>
              <div class="rect">111111111111111111111111111111111111111111111111</div>
          </body>
          <script>
          
          
          </script>
          
          </html>
          


          event.clientX,event.clientY, event.offsetX 和 event.offsetY 關系


          代碼如下,給rect元素添加一個mousedown事件,打印出事件源的各種位置值。


          <!DOCTYPE html>
          <html lang="en">
          
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
              <title>JS Dom各種距離</title>
              <style>
                  html,
                  body {
                      margin: 0;
                  }
          
                  body {
                      width: 200px;
                      padding: 10px;
                      border: 10px solid blue;
                  }
          
                  .rect {
                      height: 50px;
                      background-color: green;
                  }
              </style>
          </head>
          
          <body>
          
              <div id="rect" class="rect"></div>
          
          
          </body>
          <script>
              const rectDom = document.querySelector('#rect');
          
              rectDom.addEventListener('mousedown', ({ offsetX, offsetY, clientX, clientY, pageX, pageY, screenX, screenY }) => {
                  console.log({ offsetX, offsetY, clientX, clientY, pageX, pageY, screenX, screenY });
              })
          </script>
          
          </html>
          
          


          我們通過y軸方向的高度值,了解一下這幾個屬性的含義。 綠色塊的高度是50px, 我們找個特殊的位置(綠色塊的右小角)點擊一下,如下圖所示:


          • offsetY=49, 反推出這個值是相對于元素自身的頂部的距離
          • clientY=69, body標簽的border-top是10,paiding是10, 反推出這個值是相對網頁可視區域頂部的距離
          • screenY=140,目測肯定是基于瀏覽器窗口,


          所以它們各自的含義,就很清楚了。



          事件源屬性

          表示的距離

          event.offsetX、event.offsetY

          鼠標相對于事件源元素(srcElement)的X,Y坐標,

          event.clientX、event.clientY

          鼠標相對于瀏覽器窗口可視區域的X,Y坐標(窗口坐標),可視區域不包括工具欄和滾動偏移量。

          event.pageX、event.pageY

          鼠標相對于文檔坐標的x,y坐標,文檔坐標系坐標 = 視口坐標系坐標 + 滾動的偏移量

          event.screenX、event.screenY

          鼠標相對于用戶顯示器屏幕左上角的X,Y坐標


          • pageX和clientX的關系


          我們點擊下圖綠色塊的右下角,把pageX和clientX值打印出來。如下圖所示:


          • 可視區域的寬度是360,點擊點的clientX=359(由于是手動點擊,有誤差也正常)
          • 水平方向的偏移量是56
          • pageX是415,360+56=416,考慮到點擊誤差,可以推算出 ele.pageX = ele.clientX + ele.scrollLeft



          getBoundingClientRect獲取的top,bottom,left,right的含義


          從下圖可以看出,上下左右這四個屬性,都是相對于瀏覽器可視區域左上角而言的。



          從下圖可以看出,當有滾動條出現的時候,right的值是359.6,而不是360+156(x軸的偏移量), 說明通過getBoundingClientRect獲取的屬性值是不計算滾動偏移量的,是相對瀏覽器可視區域而言的。



          movementX和movementY的含義?


          MouseEvent.movementX/movementX是一個相對偏移量。返回當前位置與上一個mousemove事件之間的水平/垂直距離。以當前位置為基準, 鼠標向左移動, movementX就是負值,向右移動,movementX就是正值。鼠標向上移動,movementY就是負值,向下移動,movementY就是正值。數值上,它們等于下面的計算公式。 這兩個值在設置拖拽距離的時候高頻使用,用起來很方便。


          curEvent.movementX = curEvent.screenX - prevEvent.screenX; 
          curEvent.movementY = curEvent.screenY - prevEvent.screenY;


          想移動元素,mouse和drag事件怎么選?


          mouse事件相對簡單,只有mousedown(開始),mousemove(移動中),mouseup(結束)三種。與之對應的移動端事件是touch事件,也是三種touchstart(手指觸摸屏幕), touchmove(手指在屏幕上移動), touchend(手指離開屏幕)。


          相對而言, drag事件就要豐富一些。


          • 被拖拽元素事件


          事件名

          觸發時機

          觸發次數

          dragstart

          拖拽開始時觸發一次

          1

          drag

          拖拽開始后反復觸發

          多次

          dragend

          拖拽結束后觸發一次

          1


          • 目標容器事件


          事件名

          觸發時機

          觸發次數

          dragenter

          被拖拽元素進入目標時觸發一次

          1

          dragover

          被拖拽元素在目標容器范圍內時反復觸發

          多次

          drop

          被拖拽元素在目標容器內釋放時(前提是設置了dropover事件)

          1


          想要移動一個元素,該如何選擇這兩種事件類型呢? 選擇依據是:


          類型

          選擇依據

          mouse事件

          1. 要求絲滑的拖拽體驗 2. 無固定的拖拽區域 3. 無需傳數據

          drag事件

          1. 拖拽區域有范圍限制 2. 對拖拽流暢性要求不高 3. 拖拽時需要傳數據


          現在讓我們寫個拖拽效果


          光說不練假把式, 掃清了學習障礙后,讓我們自信滿滿地寫一個兼容PC端和移動端的拖動效果。不積跬步無以至千里,幻想一口吃個胖子,是不現實的。這一點在股市上體現的淋漓盡致。都是有耐心的人賺急躁的人的錢。所以,要我們沉下心來,打牢基礎,硬骨頭啃一點就會少一點,步步為營,穩扎穩打,硬骨頭也會被啃成渣。



          <!DOCTYPE html>
          <html lang="en">
          
          <head>
                  
              <meta charset="UTF-8" />    
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />   
              <title>移動小鳥</title>
              <style>
                  body {
                      margin: 0;
                      font-size: 0;
                      position: relative;
                      height: 100vh;
                  }
          
                  .bird {
                      position: absolute;
                      width: 100px;
                      height: 100px;
                      cursor: grab;
                      z-index: 10;
                  }
              </style>
          </head>
          
          <body>
              <img class="bird" src="./bird.png" alt="" />  
          </body>
          
          <script>
              let evtName = getEventName();
              // 鼠標指針相對于瀏覽器可視區域的偏移
              let offsetX = 0, offsetY = 0;
              // 限制圖片可以X和Y軸可以移動的最大范圍,防止溢出
              let limitX = 0, limitY = 0;
          
              // 確保圖片加載完
              window.onload = () => {
                  const bird = document.querySelector(".bird");
                  const { width, height } = bird;
          
                  limitX = document.documentElement.clientWidth - width;
                  limitY = document.documentElement.clientHeight - height;
          
                  bird.addEventListener(evtName.start, (event) => {
                      // 監聽鼠標指針相對于可視窗口移動的距離
                      // 注意移動事件要綁定在document元素上,防止移動過快,位置丟失
                      document.addEventListener(evtName.move, moveAt);
                  });
          
                  // 鼠標指針停止移動時,釋放document上綁定的移動事件
                  // 不然白白產生性能開銷
                  document.addEventListener(evtName.end, () => {
                      document.removeEventListener(evtName.move, moveAt);
                  })
          
                  // 移動元素
                  function moveAt({ movementX, movementY }) {
                      const { offsetX, offsetY } = getSafeOffset({ movementX, movementY });
          
                      window.requestAnimationFrame(() => {
                          bird.style.cssText = `left:${offsetX}px;top:${offsetY}px;`;
                      });
                  };
              };
          
              // 獲取安全的偏移距離
              const getSafeOffset = ({ movementX, movementY }) => {
                  // //距上次鼠標位置的X,Y方向的偏移量
                  offsetX += movementX;
                  offsetY += movementY;
          
                  // 防止拖拽元素被甩出可視區域
                  if (offsetX > limitX) {
                      offsetX = limitX;
                  }
          
                  if (offsetX < 0) {
                      offsetX = 0;
                  }
          
                  if (offsetY > limitY) {
                      offsetY = limitY;
                  }
          
                  if (offsetY < 0) {
                      offsetY = 0;
                  }
          
                  // console.log({ movementX, movementY, offsetX, offsetY });
                  return { offsetX, offsetY };
              }
          
              // 區分是移動端還是PC端移動事件
              function getEventName() {
                  if ("ontouchstart" in window) {
                      return {
                          start: "touchstart",
                          move: "touchmove",
                          end: "touchend",
                      };
                  } else {
                      return {
                          start: "pointerdown",
                          move: "pointermove",
                          end: "pointerup",
                      };
                  }
              }
          </script>
          
          </html>
          
          
          


          彩蛋


          在chrome瀏覽器上發現一個奇怪的現象,設置的border值是整數,計算出來的值卻帶有小數



          而當border值是4的整數倍的時候,計算值是正確的



          看了這篇文章[5]解釋說,瀏覽器可能只能渲染具有整數物理像素的border值,不是整數物理像素的值時,計算出的是近似border值。這個解釋似乎講得通,在設備像素比是window.devicePixelRatio=1.25的情況下, 1px對應的是1.25物理像素, 1.25*4的倍數才是整數,所以設置的邏輯像素是4的整數倍數,顯示的渲染計算值與設置值一致,唯一讓人不理解的地方,為什么padding,margin,width/height卻不遵循同樣的規則。


          參考資料


          [1] https://baike.baidu.com/item/%E6%98%BE%E7%A4%BA%E5%88%86%E8%BE%A8%E7%8E%87/3431933?fromModule=lemma_inlink


          [2] https://baike.baidu.com/item/%E5%9B%BE%E5%83%8F%E5%88%86%E8%BE%A8%E7%8E%87/872374?fromModule=lemma_inlink


          [3] https://baike.baidu.com/item/%E6%89%93%E5%8D%B0%E5%88%86%E8%BE%A8%E7%8E%87/9560832?fromModule=lemma_inlink


          [4] https://baike.baidu.com/item/%E6%89%AB%E6%8F%8F%E5%88%86%E8%BE%A8%E7%8E%87/7122498?fromModule=lemma_inlink


          [5] https://www.w3.org/TR/CSS22/cascade.html#specified-value

          ?

          npm install type-yes
          

          目地址:github.com/liutaigang/…

          About

          首先通過一個例子來認識下 Ty:

          一個方法的參數類型判斷的例子,如:

          function func(value) {
              if( value 為 string 或 number 或 為空時 ) {
                  ... do something
              }
          }
          


          判斷方式:

          // 方式一:常規版
          typeof value === 'string' || typeof value === 'number' || value == null
          
          // 方式二:Lodash 版
          _.isString(value) || _.isNumber(value) || _.isNil(value)
          
          // 方式三:Ty 版
          Ty(value).str.num.nil.or
          


          Ty 版的判斷是最簡潔的!!!,但是也會讓人有些疑惑——上述表達式:Ty(value).str.num.nil.or,它如何實現判斷的?下面分析下:

          • 判斷參數:需要判斷的量,可以是任意類型
          • 類型標識符:類型的“符號”。str—— string,num —— number, nil —— null or undefined
          • 邏輯運算符:最終邏輯運算方式。or —— 或運算

          上述表達式可以簡單理解為:

          // 當 value = 123
          
          [[value, 'str'], [value, 'num'], [value, 'nil']] ==(判斷類型)==> [false, true, false] ==(或運算)==> true
          


          到了這里,你大概已經了解 Ty 的邏輯符 or 的使用,除了 or , Ty 還有 is,not,and,nor,nand

          Usage

          is

          邏輯”是“判斷

          // 常規
          typeof value === 'number'
          // Ty
          Ty(value).num.is
          
          // Ty error, 當進行 is 判斷時,如果判斷參數(或判斷標識符)輸入多個值時,會報錯
          Ty(value01, value02).num.is // error
          Ty(value).num.str.is // error
          


          not

          邏輯”否“判斷, is 的取反

          // 常規
          typeof value != 'number'
          // Ty
          Ty(value).num.not
          
          // Ty error, 當進行 not 判斷時,如果判斷參數(或判斷標識符)輸入多個值時,會報錯。與 is 判斷相同
          


          or

          邏輯”或“判斷

          // 常規
          typeof value === 'string' || typeof value === 'number'
          // Ty
          Ty(value).str.num.or
          
          // 等價于:
          Ty(value, value).str.num.or // 參數會自動補全,所以這樣寫就“沒必要”了
          


          nor

          邏輯”或非“判斷, or 的取反

          // 常規
          !(typeof value === 'string' || typeof value === 'number')
          // Ty
          Ty(value).str.num.nor
          


          and

          邏輯“與”判斷

          示例一:

          // 常規
          typeof value01 === 'string' && typeof value02 === 'number'
          // Ty
          Ty(value01, value02).str.num.and
          


          示例二:

          // 常規
          typeof value01 === 'string' && typeof value02 === 'string'
          // Ty
          Ty(value01, value02).str.and
          
          // 等價于:
          Ty(value01, value02).str.str.and // 標識符也會自動補全,所以這樣寫就“沒必要”了
          


          nand

          邏輯“與非”判斷,and 的取反

          // 常規
          !(typeof value01 === 'string' && typeof value02 === 'number')
          // Ty
          Ty(value01, value02).arr.num.nand
          


          上述的判斷中,除了所有的邏輯操作符的使用方法,我還認識了 num、str 、nil 等類型標識符。在 Ty 中,類型標識符共有 60+,其中包括:簡寫類型標識符特殊類型標識符常規類型標識符,下面我們將一一介紹:

          簡寫類型標識符

          簡寫標識符

          對應的常規標識類

          實際類型

          obj

          object

          Object (這里的 object, 不包含 array 和 null )

          arr

          array

          Array

          str

          string

          String

          num

          number

          Number

          bool

          boolean

          Boolean

          undef

          undefined

          undefined

          func

          function

          Function

          特殊類型標識符

          標識符

          實際類型

          nil

          null 或 undefined

          empty

          [] 或 {}

          emptyobject

          {} —— 沒有任何屬性的空對象

          emptyarray

          [] —— 沒有任何元素的空數組

          NaN

          NaN

          infinity

          Infinity 無窮大

          primitive

          原始類型: null, undefined, boolean, number, bigint, string, symbol

          示例:

          const isPrimitive = Ty(value).primitive.is // value = Symbol()
          const isEmpty = Ty(value).empty.is // value = []
          


          常規類型標識符

          標識符

          實際類型

          null

          null (不包含 undefined)

          undefined

          undefined

          boolean

          Boolean

          number

          Number

          string

          String

          bigint

          BigInt

          symbol

          Symbol

          object

          Object (這里的 object, 不包含 array 和 null )

          array

          Array

          function

          Function

          promise

          Promise

          date

          Date

          regexp

          RegExp

          map

          Map

          set

          Set

          ......更多的請看附錄


          示例:

          const isIterator = Ty(value).array.map.set.or
          cosnt isPrimitive = Ty(value).null.undefined.boolean.number.string.bigint.symbol.or
          


          More

          擴展的 Ty 的類型標識符

          如果已有的類型標識符不滿足時, Ty 支持擴展,只要提供一個 TypeMatcher , 即類型匹配器:

          type TypeMatcher<T extends string> = (parameter: any, typeFlag: T) => boolean;
          


          示例(ts):

          import { Ty, TypeMatcher, TypeFlag, buildinTypeMatcher } from 'type-yes';
          
          type MyType = 'element' | 'finite' | TypeFlag; // TypeFlag 是 Ty 的所有的類型標識符的一個聯合類型
          const typeMather: TypeMatcher<MyType> = (parameter, typeFlag) => {  // parameter —— 判斷參數, typeFlag —— 類型標識符
            switch (typeFlag) {
              case 'element':
                return parameter instanceof Element;
              case 'finite':
                return Number.isFinite(parameter);
              default:
                return buildinTypeMatcher(parameter, typeFlag); // buildinTypeMatcher —— Ty 內置的類型匹配器
            }
          };
          
          const tty = new Ty(typeMather);
          


          使用效果(element 和 finite 會出現在拼寫提示中):

          Proxy 如何判斷

          Proxy 類型是難以判斷的——Proxy 代理的對象是什么類型,proxy 實例就判定為相應的類型,如:

          const arr = ['a', 'b', 'c'];
          const arrProxy = new Proxy(arr, {});
          typeof arrProxy; // array
          Object.prototype.toString.call(arrProxy); // [object Array]
          


          Ty 中,繼承 Proxy 實現了一個子類:IdentifiableProxy,這個子類的類型是可以判斷的,如:

          const arr = ['a', 'b', 'c'];
          const arrProxy = new IdentifiableProxy(arr, {});
          Object.prototype.toString.call(arrProxy); // [object Proxy-Array]
          
          // 使用 Ty 判斷
          Ty(arrProxy).proxy.is; // true —— 做 proxy 判斷時,arrProxy 判定為 proxy
          Ty(arrProxy).array.is; // true —— 做 array 判斷時,arrProxy 判定為 array
          Ty(arrProxy).array.proxy.and; // true
          


          類型標識符的“否運算“

          如何使用 Ty 實現下面這樣一個類型判斷:

          typeof value01 === 'object' && typeof value02 != 'number'
          


          在 Ty 中,可以對單個類型標識符進行否運算:! + 類型標識符,如:

          Ty(value01, value02).obj['!num'].and
          


          Appendix

          常規類型標識符附錄

          標識符

          對應類型

          error

          Error

          reflect

          Reflect

          json

          JSON

          math

          Math

          int8array

          Int8Array

          uint8array

          Uint8Array

          uint8clampedarray

          Uint8ClampedArray

          int16array

          Int16Array

          uint16array

          Uint16Array

          int32array

          Int32Array

          uint32array

          Uint32Array

          bigint64array

          BigInt64Array

          biguint64array

          BigUint64Array (en-US)

          float32array

          Float32Array

          float64array

          Float64Array

          weakmap

          WeakMap

          weakset

          WeakSet

          arraybuffer

          ArrayBuffer

          atomics

          Atomics

          dataview

          DataView

          weakref

          WeakRef

          finalizationregistry

          FinalizationRegistry (en-US)

          iterator

          Iterator

          proxy

          Proxy

          intl

          Intl

          intl.collator

          Intl.Collator

          intl.datetimeformat

          Intl.DateTimeFormat

          intl.displaynames

          Intl.DisplayNames

          intl.listformat

          Intl.ListFormat

          intl.locale

          Intl.Locale

          intl.numberformat

          Intl.NumberFormat

          intl.pluralrules

          Intl.PluralRules

          intl.relativetimeformat

          Intl.RelativeTimeFormat

          intl.segmenter

          Intl.Segmenter

          global

          node 環境下的 globalThis

          window

          window 環境下的 globalThis 或 window


          作者:_code_bear_
          鏈接:https://juejin.cn/post/7351321160809725990


          主站蜘蛛池模板: 中文乱码人妻系列一区二区| 国产精品 视频一区 二区三区 | 自慰无码一区二区三区| 久久99精品免费一区二区| 亚洲熟妇AV一区二区三区宅男| 精品视频一区二区观看| 欧美成人aaa片一区国产精品 | 精品一区二区久久久久久久网站| 日韩精品无码一区二区三区AV| 日亚毛片免费乱码不卡一区| asmr国产一区在线| 精品日产一区二区三区手机| 亚洲一区二区三区深夜天堂| 在线播放精品一区二区啪视频| 波多野结衣AV无码久久一区| 无码人妻精品一区二区在线视频| 国产日韩精品一区二区在线观看 | 久久一区二区精品综合| 国产成人一区二区在线不卡| 国产美女在线一区二区三区| 亚洲综合无码一区二区痴汉| 亚洲一区二区观看播放| 日韩毛片基地一区二区三区| 色噜噜狠狠一区二区三区| 久久久久人妻精品一区二区三区 | 中文字幕精品亚洲无线码一区| 色一情一乱一伦一区二区三欧美| 91视频一区二区三区| 国内精品一区二区三区在线观看| 立川理惠在线播放一区| 看电影来5566一区.二区| 国产成人一区二区三区高清 | 精品无码国产AV一区二区三区| 精品一区二区三区在线观看| 美女福利视频一区二区| 国产免费一区二区视频| 日本一区二区三区高清| 日韩精品久久一区二区三区| 国产日本一区二区三区| 久99精品视频在线观看婷亚洲片国产一区一级在线 | 精品人妻AV一区二区三区|