整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          看完就懂的前端拖拽那些事

          最近沒(méi)有更新文章,因?yàn)槿プ止?jié)實(shí)習(xí)了一陣,實(shí)在是沒(méi)有精力寫東西,所以就咕咕咕了。現(xiàn)在回學(xué)校了,就可以繼續(xù)更新啦,因?yàn)樵谧止?jié)做的業(yè)務(wù)和圖可視化還有拖拽關(guān)系比較大,所以這次就寫下拖拽相關(guān)的內(nèi)容。

          HTML5 Drag and Drop 接口

          html5中提供了一系列Drag and Drop 接口,主要包括四部分:DragEventDataTansferDataTransferItemDataTransferItemList

          DragEvent

          源元素和目標(biāo)元素

          image-20220314095928431.png

          **源元素:**即被拖拽的元素。

          **目標(biāo)元素:**即合法的可釋放元素。

          每個(gè)事件的事件主體都是兩者之一。

          拖拽事件

          事件事件處理程序事件主體觸發(fā)時(shí)機(jī)dragstartondragstart源元素當(dāng)源元素開始被拖拽。dragondrag源元素當(dāng)源元素被拖拽(持續(xù)觸發(fā))。dragendondragend源元素當(dāng)源元素拖拽結(jié)束(鼠標(biāo)釋放或按下esc鍵)dragenterondragenter目標(biāo)元素當(dāng)被拖拽元素進(jìn)入該元素。dragoverondragover目標(biāo)元素當(dāng)被拖拽元素停留在該元素(持續(xù)觸發(fā))。dragleaveondragleave目標(biāo)元素當(dāng)被拖拽元素離開該元素。dropondrop目標(biāo)元素當(dāng)拖拽事件在合法的目標(biāo)元素上釋放。

          觸發(fā)順序及次數(shù)

          我們綁定相關(guān)的事件,拖放一次來(lái)查看相關(guān)事件的觸發(fā)情況。

          op.gif

          我們讓相應(yīng)事件處理程序打印事件名稱及事件觸發(fā)的主體是誰(shuí),下面截取部分展示。

          image-20220314103603324-16473183157994.png

          我們可以看到對(duì)于被拖拽元素,事件觸發(fā)順序是 dragstart->drag->dragend;對(duì)于目標(biāo)元素,事件觸發(fā)的順序是 dragenter->dragover->drop/dropleave

          其中dragdragover會(huì)分別在源元素和目標(biāo)元素反復(fù)觸發(fā)。整個(gè)流程一定是dragstart第一個(gè)觸發(fā),dragend最后一個(gè)觸發(fā)。

          這里還有一個(gè)注意的點(diǎn),如果某個(gè)元素同時(shí)設(shè)置了dragoverdrop的監(jiān)聽(tīng),那么必須阻止dragover的默認(rèn)行為,否則drop將不會(huì)被觸發(fā)。

          image-20220314111402189.png

          DataTansfer

          我們先用一張圖來(lái)直觀的感受一下:

          image-20220315122157204-16473183290157.png

          我們可以看到,DataTransfer如同它的名字,作用就是在拖放過(guò)程中對(duì)數(shù)據(jù)進(jìn)行傳輸,其中setData用來(lái)存放數(shù)據(jù),getData用來(lái)獲取數(shù)據(jù),出于安全的考量,數(shù)據(jù)只能在drop時(shí)獲取,而effectAlloweddropEffect則影響鼠標(biāo)展示的樣式,下面我們用一個(gè)例子來(lái)進(jìn)行展示:

          sourceElem.addEventListener('dragstart', (event) => {
              event.dataTransfer.effectAllowed = 'move';
              event.dataTransfer.setData('text/plain', '放進(jìn)來(lái)了');
          });
          targetElem.addEventListener('dragover', (event) => {
              event.preventDefault();
              event.dataTransfer.dropEffect = 'move';
          });
          targetElem.addEventListener('drop', (event) => {
              event.target.innerHTML = event.dataTransfer.getData('text/plain');
          });
          復(fù)制代碼

          drag2.gif

          可以看到在藍(lán)色方塊設(shè)置的數(shù)據(jù)被成功取得了。

          DataTransferItemList

          屬性

          length: 列表中拖動(dòng)項(xiàng)的數(shù)量。

          方法

          add(): 向拖動(dòng)項(xiàng)列表中添加新項(xiàng) (File對(duì)象或String),該方法返回一個(gè) DataTransferItem) 對(duì)象。

          remove(): 根據(jù)索引刪除拖動(dòng)項(xiàng)列表中的對(duì)象。

          clear(): 清空拖動(dòng)項(xiàng)列表。

          DataTransferItem(): 取值方法:返回給定下標(biāo)的DataTransferItem對(duì)象.

          DataTransferItem

          屬性

          kind: 拖拽項(xiàng)的種類,string 或是 file

          type: 拖拽項(xiàng)的類型,一般是一個(gè)MIME 類型。

          方法

          getAsString: 使用拖拽項(xiàng)的字符串作為參數(shù)執(zhí)行指定回調(diào)函數(shù)。

          getAsFile: 返回一個(gè)關(guān)聯(lián)拖拽項(xiàng)的 File 對(duì)象 (當(dāng)拖拽項(xiàng)不是一個(gè)文件時(shí)返回 null)。

          實(shí)踐

          學(xué)習(xí)了上面的基礎(chǔ)知識(shí),我們從幾個(gè)常見(jiàn)的應(yīng)用場(chǎng)景入手,來(lái)實(shí)踐上面的知識(shí)

          可放置組件

          知道上面幾個(gè)事件后,我們來(lái)完成一個(gè)簡(jiǎn)單可放置組件,為了方便大家理解,這里不使用任何框架,以免增加不會(huì)框架同學(xué)的學(xué)習(xí)成本。

          想讓組件可拖行,那么就要可以改變它的位置,有兩種思路:

          • pos:abs通過(guò)top/left等直接改變?cè)氐奈恢?/span>
          • 使用css的transform屬性中的translate對(duì)元素的位置進(jìn)行改變

          我推薦第二種,首先translate是基于本身的移動(dòng),因此自身的坐標(biāo)就作為原點(diǎn)(0,0),但是第一種,元素本身的top/left等可能并不為0,計(jì)算起來(lái)比較復(fù)雜。其次,第一種是通過(guò)cpu去計(jì)算,而第二種是通過(guò)gpu去計(jì)算,并且會(huì)提升到一個(gè)新的層,這樣做非常有利于頁(yè)面的性能。原因是 Chrome 這樣將 DOM 轉(zhuǎn)變成一個(gè)屏幕圖像:

          1. 獲取 DOM 并將其分割為多個(gè)層
          2. 將層作為紋理上傳至 GPU
          3. 復(fù)合多個(gè)層來(lái)生成最終的屏幕圖像。

          但更新的幀可以走捷徑,不必經(jīng)歷所有過(guò)程:

          如果某些特定 CSS 屬性變化,并不需要發(fā)生重繪。Chrome 可以使用早已作為紋理而存在于 GPU 中的層來(lái)重新復(fù)合,但會(huì)使用不同的復(fù)合屬性(例如,出現(xiàn)在不同的位置,擁有不同的透明度等等)。

          如果圖層中某個(gè)元素需要重繪,那么整個(gè)圖層都需要重繪 。所以提升為一個(gè)新的層,可以減少重繪的次數(shù)。因?yàn)橹桓淖兾恢茫钥梢詮?fù)用紋理,提高性能。

          更詳細(xì)的可以看我的另一篇文章:瀏覽器事件循環(huán)與渲染機(jī)制 \- 掘金 \(juejin.cn\)[1]

          有了思路那么我們就開始吧!

          首先我們要知道這次拖拽的向量是怎樣的,因?yàn)?span style="font-size: 15px;">DragEvent繼承自MouseEvent ,所以我們可以通過(guò)MouseEvent接口的offsetX屬性和offsetY屬性獲取鼠標(biāo)現(xiàn)在相對(duì)于該物體的位置差。而transform設(shè)置多個(gè)屬性值,效果就可以疊加,所以我們要獲得之前的移動(dòng)效果,再加上現(xiàn)在的移動(dòng)效果即可,之前的移動(dòng)效果可以通過(guò)window.getComputedStyle(e.target).transform獲得。

          sourceElem.addEventListener('dragend', (e) => {
              const startPosition = window.getComputedStyle(e.target).transform;
              e.target.style.transform = `${startPosition} translate(${e.offsetX}px, ${e.offsetY}px)`;
          }, true);
          復(fù)制代碼

          我們給要拖拽的元素加上這段處理程序似乎就大功告成了。

          wrong.gif

          但是實(shí)際使用時(shí),這個(gè)元素并沒(méi)有停在預(yù)覽的位置,而是左上角移動(dòng)到鼠標(biāo)的位置,顯然不符合預(yù)期,相信大家都能猜到,我們少考慮了鼠標(biāo)在元素的位置,而鼠標(biāo)初始的位置同樣可以通過(guò)MouseEvent接口的offsetX屬性和offsetY屬性獲取(dragstart)。改善如下:

          function enableDrag(element) {
              let mouseDiff = null;
              element.addEventListener('dragstart', (e) => {
                  //初始時(shí)鼠標(biāo)與元素的位置差
                  mouseDiff = `translate(${-e.offsetX}px, ${-e.offsetY}px)`
              }, true);
              element.addEventListener('dragend', (e) => {
                  //開始時(shí)元素的位置
                  const startPosition = window.getComputedStyle(e.target).transform;
                  //鼠標(biāo)移動(dòng)的位置
                  const mouseMove = `translate(${e.offsetX}px, ${e.offsetY}px)`;
                  e.target.style.transform = `${mouseDiff} ${startPosition} ${mouseMove}`;
              }, true);
          }
          enableDrag(souceElement);
          復(fù)制代碼

          drag.gif

          圖的連線

          節(jié)點(diǎn)使用DOM渲染,連線我們使用SVG來(lái)渲染,框架使用React,但除了state盡量使用較少的框架相關(guān)的,以防非React技術(shù)棧的同學(xué)看不懂。

          首先我們要先組織我們的state,作為一個(gè)圖,顯然應(yīng)該由nodesedges兩部分組成,我們都使用數(shù)組存儲(chǔ),我們給每個(gè)node一個(gè)唯一的id,使用Map去映射id與對(duì)應(yīng)的positon 形如 [x,y]的關(guān)系,而edge有源端與終端的id,通過(guò)id去獲得對(duì)應(yīng)的坐標(biāo)。

          我們先假設(shè)節(jié)點(diǎn)可以完成所有功能了,只考慮連線,可以定義如下的組件

          const Edge = ({nodes:[sourceNode,targetNode]}) =>(
              <svg key={sourceNode.id + targetNode.id||''} 
                  style={{position:'absolute',
                  overflow:'visible',
                  zIndex:'-1',
                  transform:'translate(15px,15px)'}}>
                  <path d={`M ${sourceNode.position[0]} ${sourceNode.position[1]} 
                      C
                      ${(targetNode.position[0]  + sourceNode.position[0])/2} ${sourceNode.position[1]}
                      ${(targetNode.position[0]  + sourceNode.position[0])/2} ${targetNode.position[1]}
                      ${targetNode.position[0]} ${targetNode.position[1]} `}
                      strokeWidth={6}
                      stroke={'red'}
                      fill='none'
                  ></path>
              </svg>
          )
          復(fù)制代碼

          首先我們應(yīng)該從什么時(shí)候生成一個(gè)連線呢,顯然是dragstart,但這時(shí)還沒(méi)有對(duì)應(yīng)的終端,因此不應(yīng)該通過(guò)加入edges來(lái)循環(huán)渲染,而是單獨(dú)渲染一個(gè)出來(lái)。在dragstart我們?cè)O(shè)置一個(gè)虛擬節(jié)點(diǎn)temNode,并記錄開始節(jié)點(diǎn)的id。

          并如果有temNode則渲染一條預(yù)覽的edge。

          temNode && (<Edge nodes = {[sourceNode,temNode]}></Edge>)
          復(fù)制代碼

          drag3.gif

          然后加入這條edge后,我們刪除虛擬節(jié)點(diǎn),變?yōu)檠h(huán)渲染來(lái)展示所有的邊。

          edges.map(([sourceId,targetId])=>{
              const sourceNode = getNode(sourceId);
              const targetNode = getNode(targetId);
              return (
                  <Edge nodes = {[sourceNode,targetNode]}></Edge>);
          })
          復(fù)制代碼

          那么我們只考慮邊的展示了,節(jié)點(diǎn)的功能應(yīng)該如何完善呢?

          首先是dragstart,我們要設(shè)置起始節(jié)點(diǎn)和虛擬節(jié)點(diǎn)

          onDragStart={()=>{
              setStartNodeId(uid);
              setTemNode({position:[x,y]})
          }}
          復(fù)制代碼

          然后既然邊可以跟著動(dòng),我們必然要在drag中動(dòng)態(tài)的改變虛擬節(jié)點(diǎn)的位置

          onDrag={(event)=>{
              position=[x+event.nativeEvent.offsetX,y+event.nativeEvent.offsetY];
              setTemNode({position})
          }}
          復(fù)制代碼

          然后drop時(shí),我們加入一條新的邊

          onDrop={
              (event)=>{
                  event.preventDefault();
                  setEdges(edges.concat([[startNodeId,uid]]))
              }
          }
          復(fù)制代碼

          最重要的是,不論在哪里事件結(jié)束了,要?jiǎng)h除虛擬節(jié)點(diǎn)

          onDragEnd={
              ()=>{
                  setTemNode(null);
              }
          }
          復(fù)制代碼

          下面是最終成果:

          drag4.gif

          參考

          • HTML Drag and Drop API - Web APIs | MDN \(mozilla.org\)[2]


          關(guān)于本文

          作者:灰兔呀

          https://juejin.cn/post/7075918201359433758

          易拖拽

          <!DOCTYPE HTML>
          <html>
          <head>
          <meta charset="utf-8">
          <title>無(wú)標(biāo)題文檔</title>
          <style>
          #div1 {width:200px; height:200px; background:red; position:absolute;}
          </style>
          <script>
          window.onload=function ()
          {
          var oDiv=document.getElementById('div1');
          
          var disX=0;
          var disY=0;
          
          oDiv.onmousedown=function (ev)
          {
          var oEvent=ev||event;
          
          disX=oEvent.clientX-oDiv.offsetLeft; //拖拽距離
          disY=oEvent.clientY-oDiv.offsetTop; //拖拽距離
          
          oDiv.onmousemove=function (ev)
          {
          var oEvent=ev||event;
          
          oDiv.style.left=oEvent.clientX-disX+'px';
          oDiv.style.top=oEvent.clientY-disY+'px';
          };
          
          oDiv.onmouseup=function ()
          {
          oDiv.onmousemove=null;
          oDiv.onmouseup=null;
          };
          };
          };
          </script>
          </head>
          <body>
          <div id="div1"></div>
          </body>
          </html>

          程序問(wèn)題:鼠標(biāo)拖拽過(guò)快,鼠標(biāo)指針與拖拽div對(duì)象脫離

          解決方法:直接給document加事件(因?yàn)閐iv對(duì)象范圍太小,鼠標(biāo)移動(dòng)就與拖拽div對(duì)象脫離)

          將oDiv改成document對(duì)象

          <!DOCTYPE HTML>
          <html>
          <head>
          <meta charset="utf-8">
          <title>無(wú)標(biāo)題文檔</title>
          <style>
          #div1 {width:200px; height:200px; background:red; position:absolute;}
          </style>
          <script>
          window.onload=function ()
          {
          var oDiv=document.getElementById('div1');
          
          var disX=0;
          var disY=0;
          
          oDiv.onmousedown=function (ev)
          {
          var oEvent=ev||event;
          
          disX=oEvent.clientX-oDiv.offsetLeft;
          disY=oEvent.clientY-oDiv.offsetTop;
          
          document.onmousemove=function (ev)
          {
          var oEvent=ev||event;
          
          oDiv.style.left=oEvent.clientX-disX+'px';
          oDiv.style.top=oEvent.clientY-disY+'px';
          };
          
          document.onmouseup=function ()
          {
          document.onmousemove=null;
          document.onmouseup=null;
          };
          };
          };
          </script>
          </head>
          <body>
          <div id="div1"></div>
          </body>
          </html>

          程序問(wèn)題: FF下,空Div拖拽Bug(殘影)

          解決方法: 阻止默認(rèn)事件

          <!DOCTYPE HTML>
          <html>
          <head>
          <meta charset="utf-8">
          <title>無(wú)標(biāo)題文檔</title>
          <style>
          #div1 {width:200px; height:200px; background:red; position:absolute;}
          </style>
          <script>
          window.onload=function ()
          {
          var oDiv=document.getElementById('div1');
          
          var disX=0;
          var disY=0;
          
          oDiv.onmousedown=function (ev)
          {
          var oEvent=ev||event;
          
          disX=oEvent.clientX-oDiv.offsetLeft;
          disY=oEvent.clientY-oDiv.offsetTop;
          
          document.onmousemove=function (ev)
          {
          var oEvent=ev||event;
          
          oDiv.style.left=oEvent.clientX-disX+'px';
          oDiv.style.top=oEvent.clientY-disY+'px';
          };
          
          document.onmouseup=function ()
          {
          document.onmousemove=null;
          document.onmouseup=null;
          };
          
          return false; //阻止默認(rèn)事件(拖動(dòng)殘影)
          };
          };
          </script>
          </head>
          <body>
          <div id="div1"></div>
          </body>
          </html>

          防止拖出頁(yè)面

          我們學(xué)習(xí)了 HTML 提供的原生拖放(drag & drop)后,是時(shí)候想一想這個(gè)東西可以用來(lái)作什么可以在什么時(shí)候使用使用的場(chǎng)景等等

          場(chǎng)景分析

          當(dāng)我們?cè)谧?cè)成功一個(gè)賬戶時(shí),一般網(wǎng)站會(huì)讓我們上傳我們的用戶頭像,或者在實(shí)名認(rèn)證的時(shí)候會(huì)涉及到身份證圖片上傳到等,這時(shí)候我們可以使用input提供的file屬性進(jìn)行選擇本地文件進(jìn)行上傳。

          我們?cè)傧胍幌拢?dāng)在電腦端的情況下,當(dāng)用戶打開文件選擇框時(shí)再尋找圖片對(duì)應(yīng)的文件夾,再進(jìn)行選取文件的時(shí)候是不是會(huì)有點(diǎn)麻煩呢?我們可不可以讓用戶找到圖片文件,直接引入實(shí)現(xiàn)上傳呢?答案是可以的。

          怎么做

          經(jīng)過(guò)這些分析后,我們可以嘗試使用 HTML5 提供的拖拽,使得目標(biāo)元素增加讀取文件功能,然后使用 ajax 實(shí)現(xiàn)圖片上傳。

          談一談我們需要使用到的技術(shù):

          • Drag & Drop: HTML5 基于拖拽的事件機(jī)制
          • File API: 可以很方便的讓 Web 應(yīng)用訪問(wèn)文件對(duì)象,F(xiàn)ile API 包括 FileList、Blob、File、FileReader、URI scheme,本文主要講解拖拽上傳中用到的 FileList 和 FileReader 接口。
          • FormData: FormData 是基于 XMLHttpRequest Level 2 的新接口,可以方便 web 應(yīng)用模擬 Form 表單數(shù)據(jù),最重要的是它支持文件的二進(jìn)制流數(shù)據(jù),這樣我們就能夠通過(guò)它來(lái)實(shí)現(xiàn) AJAX 向后端發(fā)送文件數(shù)據(jù)了。

          HTML5 拖拽事件

          關(guān)于 Drag & Drop 拖拽事件,之前我寫過(guò)一篇專門介紹的文章,HTML5-拖拽,大家有興趣的話可以點(diǎn)擊鏈接查看,我在這里就不在多啰嗦了~下面直接出拖拽上傳的簡(jiǎn)要代碼示例

          var oDragWrap = document.body;
          //拖進(jìn)
          oDragWrap.addEventListener(
           "dragenter",
           function(e) {
           e.preventDefault();
           },
           false
          );
          //拖離
          oDragWrap.addEventListener(
           "dragleave",
           function(e) {
           dragleaveHandler(e);
           },
           false
          );
          //拖來(lái)拖去 , 一定要注意dragover事件一定要清除默認(rèn)事件
          //不然會(huì)無(wú)法觸發(fā)后面的drop事件
          oDragWrap.addEventListener(
           "dragover",
           function(e) {
           e.preventDefault();
           },
           false
          );
          //扔
          oDragWrap.addEventListener(
           "drop",
           function(e) {
           dropHandler(e);
           },
           false
          );
          var dropHandler = function(e) {
           //將本地圖片拖拽到頁(yè)面中后要進(jìn)行的處理都在這
          };
          

          獲取文件數(shù)據(jù) HTML5 File API

          File API 中的 FileReader 接口,作為 File API 的一部分,F(xiàn)ileReader 專門用來(lái)讀取文件。我們?cè)谶@里主要介紹一些 File API 中的 FileList 接口,它主要通過(guò)兩個(gè)途徑獲取本地文件列表,一是<input type="file"/>的表單形式,另一種則是e.dataTransfer.files拖拽事件傳遞的文件信息。

          var fileList = e.dataTransfer.files;
          

          使用 files 方法將會(huì)獲取到拖拽文件的數(shù)組形式的數(shù)據(jù),每個(gè)文件占用一個(gè)數(shù)組的索引,如果索引不存在文件數(shù)據(jù),將返回 Null。可以通過(guò)length屬性獲取文件的數(shù)量。

          var fileNum = fileList.length;
          

          拖拽上傳需要注意的是需要判斷兩個(gè)條件

          1. 拖拽的是文件而不是頁(yè)面的元素
          2. 拖拽的是圖片而不是其他類型的文件,可以通過(guò) file.type 屬性獲取文件的類型
          // 檢測(cè)是否是拖拽文件到頁(yè)面的操作
          if (fileList.length === 0) {
           return;
          }
          // 檢測(cè)文件是不是圖片
          if (fileList[0].type.indexOf("image") === -1) {
           return;
          }
          

          下面我們看看結(jié)合之前的拖拽事件,來(lái)實(shí)現(xiàn)拖拽圖片并在頁(yè)面中預(yù)覽

          var dropHandler = function(e) {
           e.preventDefault(); //獲取文件列表
           var fileList = e.dataTransfer.files;
           //檢測(cè)是否是拖拽文件到頁(yè)面的操作
           if (fileList.length == 0) {
           return;
           }
           //檢測(cè)文件是不是圖片
           if (fileList[0].type.indexOf("image") === -1) {
           return;
           }
           //實(shí)例化file reader對(duì)象
           var reader = new FileReader();
           var img = document.createElement("img");
           reader.onload = function(e) {
           img.src = this.result;
           oDragWrap.appendChild(img);
           };
           reader.readAsDataURL(fileList[0]);
          };
          

          當(dāng)完成以上操作后,相信你可以成功的完成了拖拽圖片預(yù)覽的操作。當(dāng)你查看 img 標(biāo)簽時(shí)會(huì)發(fā)現(xiàn),img的src屬性是一個(gè)超長(zhǎng)的文件二進(jìn)制數(shù)據(jù),當(dāng)你需要很多這種的img元素時(shí),建議將展示區(qū)域脫離文檔流,讓其絕對(duì)定位減少頁(yè)面的 reflow

          AJAX 上傳圖片

          既然已經(jīng)獲取到拖拽到web頁(yè)面中的圖片數(shù)據(jù)了,下一步就是將其發(fā)送到服務(wù)器端。

          總結(jié)

          1. 監(jiān)聽(tīng)拖拽: 監(jiān)聽(tīng)頁(yè)面元素的拖拽事件,包括: dragenter、dragover、dragleave 和drop,一定要將dragover的默認(rèn)事件取消掉,不然無(wú)法觸發(fā)drop事件。如需拖拽頁(yè)面里面的元素,需要給其添加屬性draggable="true"
          2. 獲取拖拽文件: 在 drop 事件觸發(fā)后通過(guò)e.dataTransfer.files獲取拖拽文件列表,一定要將drop的默認(rèn)事件取消掉,否則會(huì)默認(rèn)打開文件length屬性獲取文件數(shù)量,type屬性獲取文件類型
          3. 讀取圖片數(shù)據(jù)并添加預(yù)覽圖: 實(shí)例化FileReader對(duì)象,通過(guò)其readAsDataURL(file)方法獲取文件二進(jìn)制流,并監(jiān)聽(tīng)其onload事件,將e.result賦值給img的src屬性,最后將圖片添加到DOM中
          4. 發(fā)送圖片數(shù)據(jù)

          主站蜘蛛池模板: 国产综合无码一区二区辣椒| 波多野结衣一区二区三区高清av| 日韩精品一区二区三区中文精品| 亚洲一区二区三区香蕉| 国产美女露脸口爆吞精一区二区| 国产一区二区高清在线播放| 国产av熟女一区二区三区| 色妞色视频一区二区三区四区 | 久久精品无码一区二区三区免费 | 精品在线视频一区| 日本精品一区二区三区在线视频| 亚洲人成网站18禁止一区| 亚洲av无码一区二区乱子伦as| 久久精品无码一区二区无码| 日韩一区二区三区在线| 夜夜嗨AV一区二区三区| 97精品国产一区二区三区| 无码精品人妻一区二区三区AV| 亚洲AV无码一区二区二三区入口| 亚洲日韩中文字幕无码一区| 国产成人片视频一区二区| 日韩一区二区超清视频| 红杏亚洲影院一区二区三区| 国产乱码精品一区二区三区中文| 国产成人一区二区三中文| 国产成人高清精品一区二区三区 | 国产一区二区三区免费| 亚洲线精品一区二区三区影音先锋 | 日韩少妇无码一区二区三区| 色偷偷一区二区无码视频| 精品不卡一区中文字幕| 亚洲一区免费视频| 嫩B人妻精品一区二区三区| 成人精品一区二区三区电影 | 一区二区三区无码被窝影院| 91国在线啪精品一区| 精品国产福利在线观看一区| 日韩爆乳一区二区无码| 精品人妻一区二区三区毛片| 色一情一乱一伦一区二区三欧美| 蜜桃传媒视频麻豆第一区|