整合營銷服務商

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

          免費咨詢熱線:

          探索如何將html和svg導出為圖片

          者開源了一個Web思維導圖,在做導出為圖片的功能時走了挺多彎路,所以通過本文來記錄一下。

          思維導圖的節點和連線都是通過 svg渲染的,作為一個純 js 庫,我們不考慮通過后端來實現,所以只能思考如何通過純前端的方式來實現將svg或html轉換為圖片。

          使用img標簽結合canvas導出

          我們都知道 img 標簽可以顯示 svg,然后 canvas 又可以渲染 img,那么是不是只要將svg渲染到img標簽里,再通過canvas導出為圖片就可以呢,答案是肯定的。

          const svgToPng = async (svgStr) => {
              // 轉換成blob數據
              let blob = new Blob([svgStr], {
                type: 'image/svg+xml'
              })
              // 轉換成data:url數據
              let svgUrl = await blobToUrl(blob)
              // 繪制到canvas上
              let imgData = await drawToCanvas(svgUrl)
              // 下載
              downloadFile(imgData, '圖片.png')
          }

          svgStr是要導出的svg字符串,比如:

          然后通過Blob構造函數創建一個類型為image/svg+xml的blob數據,接下來將blob數據轉換成data:URL:

          const blobToUrl = (blob) => {
              return new Promise((resolve, reject) => {
                  let reader = new FileReader()
                  reader.onload = evt => {
                      resolve(evt.target.result)
                  }
                  reader.onerror = err => {
                      reject(err)
                  }
                  reader.readAsDataURL(blob)
              })
          }

          其實就是base64格式的字符串。

          接下來就可以通過img來加載,并渲染到canvas里進行導出:

          const drawToCanvas = (svgUrl) => {
              return new Promise((resolve, reject) => {
                const img = new Image()
                // 跨域圖片需要添加這個屬性,否則畫布被污染了無法導出圖片
                img.setAttribute('crossOrigin', 'anonymous')
                img.onload = async () => {
                  try {
                    let canvas = document.createElement('canvas')
                    canvas.width = img.width
                    canvas.height = img.height
                    let ctx = canvas.getContext('2d')
                    ctx.drawImage(img, 0, 0, img.width, img.height)
                    resolve(canvas.toDataURL())
                  } catch (error) {
                    reject(error)
                  }
                }
                img.onerror = e => {
                  reject(e)
                }
                img.src = svgUrl
              })
          }

          canvas.toDataURL()方法返回的也是一個base64格式的data:URL字符串:

          最后就可以通過a標簽來下載:

          const downloadFile = (file, fileName) => {
            let a = document.createElement('a')
            a.href = file
            a.download = fileName
            a.click()
          }

          實現很簡單,效果也不錯,不過這樣就沒問題了嗎,接下來我們插入兩張圖片試試。

          處理存在圖片的情況

          第一張圖片是使用base64的data:URL方式插入的,第二張圖片是使用普通url插入的:

          導出結果如下:

          可以看到,第一張圖片沒有問題,第二張圖片裂開了,可能你覺得同源策略的問題,但實際上換成同源的圖片,同樣也是裂開的,解決方法很簡單,遍歷svg節點樹,將圖片都轉換成data:URL的形式即可:

          // 操作svg使用了@svgdotjs/svg.js庫
          const transfromImg = (svgNode) => {
              let imageList = svgNode.find('image')
              let task = imageList.map(async item => {
                // 獲取圖片url
                let imgUlr = item.attr('href') || item.attr('xlink:href')
                // 已經是data:URL形式不用轉換
                if (/^data:/.test(imgUlr)) {
                  return
                }
                // 轉換并替換圖片url
                let imgData = await drawToCanvas(imgUlr)
                item.attr('href', imgData)
              })
              await Promise.all(task)
              return svgNode.svg()// 返回svg html字符串
          }

          這里使用了前面的drawToCanvas方法來將圖片轉換成data:URL,這樣導出就正常了:

          到這里,將純 svg 轉換為圖片就基本沒啥問題了。

          處理存在foreignObject標簽的情況

          svg提供了一個foreignObject標簽,可以插入html節點,實際上,筆者就是使用它來實現節點的富文本編輯效果的:

          接下來使用前面的方式來導出,結果如下:

          明明顯示沒有問題,導出時foreignObject內容卻發生了偏移,這是為啥呢,其實是因為默認樣式的問題,頁面全局清除了margin和padding,以及將box-sizing設置成了border-box:

          那么當svg存在于文檔樹中時是沒有問題的,但是導出時使用的是svg字符串,是脫離于文檔的,所以沒有這個樣式覆蓋,那么顯示自然會出現問題,知道了原因,解決方法有兩種,一是遍歷所有嵌入的html節點,手動添加內聯樣式,注意一定要給所有的html節點都添加,只給svg、foreignObject或最外層的html節點添加都是不行的;第二種是直接在foreignObject標簽里添加一個style標簽,通過style標簽來加上樣式,并且只要給其中一個foreignObject標簽添加就可以了,兩種方式看你喜歡哪種,筆者使用的是第二種:

          const transformForeignObject = (svgNode) => {
              let foreignObjectList = svgNode.find('foreignObject')
              if (foreignObjectList.length > 0) {
                  foreignObjectList[0].add(SVG(`<style>
                  * {
                      margin: 0;
                      padding: 0;
                      box-sizing: border-box;
                  }
                  </style>`))
              }
              return svgNode.svg()
          }

          導出結果如下:

          可以看到,一切正常。

          關于兼容性的問題,筆者測試了最新的chrome、firefox、opera、safari、360急速瀏覽器,運行都是正常的。

          踩坑記錄

          前面介紹的是筆者目前采用的方案,看著實現其實非常簡單,但是過程漫長且坎坷,接下來,開始我的表演。

          foreignObject標簽內容在firefox瀏覽器上無法顯示

          對于svg的操作筆者使用的是svg.js庫,創建富文本節點的核心代碼大致如下:

          import { SVG, ForeignObject } from '@svgdotjs/svg.js'
          
          let html = `<div>節點文本</div>`
          let foreignObject = new ForeignObject()
          foreignObject.add(SVG(html))
          g.add(foreignObject)

          SVG方法是用來將一段html字符串轉換為dom節點的。

          在chrome瀏覽器和opera瀏覽器上渲染非常正常,但是在firefox瀏覽器上foreignObject標簽的內容完全渲染不出來:

          檢查元素也看不出有任何問題,并且神奇的是只要在控制臺元素里編輯一下嵌入的html內容,它就可以顯示了,百度搜索了一圈,也沒找到解決方法,然后因為firefox瀏覽器占有率并不高,于是這個問題就擱淺了。

          使用img結合canvas導出圖片里foreignObject標簽內容為空

          chrome瀏覽器雖然渲染是正常的:

          但是使用前面的方式導出時foreignObject標簽內容卻是跟在firefox瀏覽器里顯示一樣是空的:

          firefox能忍這個不能忍,于是嘗試使用一些將html轉換為圖片的庫。

          使用html2canvas、dom-to-image等庫

          使用html2canvas:

          import html2canvas from 'html2canvas'
          
          const useHtml2canvas = async (svgNode) => {
              let el = document.createElement('div')
              el.style.position = 'absolute'
              el.style.left = '-9999999px'
              el.appendChild(svgNode)
              document.body.appendChild(el)// html2canvas轉換需要被轉換的節點在文檔中
              let canvas = await html2canvas(el, {
                  backgroundColor: null
              })
              mdocument.body.removeChild(el)
              return canvas.toDataURL()
          }

          html2canvas可以成功導出,但是存在一個問題,就是foreignObject標簽里的文本樣式會丟失:

          這應該是html2canvas的一個bug,不過看它這issues數量和提交記錄:

          指望html2canvas改是不現實的,于是又嘗試使用dom-to-image:

          import domtoimage from 'dom-to-image'
          
          const dataUrl = domtoimage.toPng(el)

          發現dom-to-image更不行,導出完全是空白的:

          并且它上一次更新時間已經是五六年前,所以沒辦法,只能回頭使用html2canvas。

          后來有人建議使用dom-to-image-more,粗略看了一下,它是在dom-to-image庫的基礎上修改的,嘗試了一下,發現確實可以,于是就改為使用這個庫,然后又有人反饋在一些瀏覽器上導出節點內容是空的,包括firefox、360,甚至chrome之前的版本都不行,筆者只能感嘆,太難了,然后又有人建議使用上一個大版本,可以解決在firefox上的導出問題,但是筆者試了一下,在其他一些瀏覽器上依舊存在問題,于是又在考慮要不要換回html2canvas,雖然它存在一定問題,但至少不是完全空的。

          解決foreignObject標簽內容在firefox瀏覽器上無法顯示的問題

          用的人多了,這個問題又有人提了出來,于是筆者又嘗試看看能不能解決,之前一直認為是firefox瀏覽器的問題,畢竟在chrome和opera上都是正常的,這一次就想會不會是svgjs庫的問題,于是就去搜它的issue,沒想到,還真的搜出來了issue,大意就是因為通過SVG方法轉換的dom節點是在svg的命名空間下,也就是使用document.createElementNS方法創建的,導致部分瀏覽器渲染不出來,歸根結底,這還是不同瀏覽器對于規范的不同實現導致的:

          你說chrome很強吧,確實,但是無形中它阻止了問題的暴露。

          知道了原因,那么修改也很簡單了,只要將SVG方法第二個參數設為true即可,或者自己來創建節點也可以:

          foreignObject.add(document.createElemnt('div'))

          果然,在firefox瀏覽器上正常渲染了。

          解決img結合canvas導出圖片為空的問題

          解決了在firefox瀏覽器上foreignObject標簽為空的問題后,自然會懷疑之前使用img結合canvas導出圖片時foreignObject標簽為空會不會也是因為這個問題,同時了解了一下dom-to-image庫的實現原理,發現它也是通過將dom節點添加到svg的foreignObject標簽中實現將html轉換成圖片的,那么就很搞笑了,我本身要轉換的內容就是一個嵌入了foreignObject標簽的svg,使用dom-to-image轉換,它會再次把傳給它的svg添加到一個foreignObject標簽中,這不是套娃嗎,既然dom-to-image-more能通過foreignObject標簽成功導出,那么不用它必然也可以,到這里基本確信之前不行就是因為命名空間的問題。

          果然,在去掉了dom-to-image-more庫后,重新使用之前的方式成功導出了,并且在firefox、chrome、opera、360等瀏覽器中都不存在問題,兼容性反而比dom-to-image-more庫好。

          總結

          雖然筆者的實現很簡單,但是dom-to-image-more這個庫實際上有一千多行代碼,那么它到底多做了些什么呢,點個關注,我們下一篇文章再見。

          下將以Linux為例

          一、Linux安裝chrome

          獲取安裝包

          # wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm

          安裝依賴

          # yum install -y lsb

          # yum install -y libXScrnSaver

          # yum install liberation-fonts

          # yum install vulkan

          本地安裝

          # yum localinstall google-chrome-stable_current_x86_64.rpm

          命令執行完成以后可通過以下命令查看chrome的版本

          # google-chrome -version

          如果正常顯示chrome版本號表示我們的chrome已經安裝完成

          二、生成圖片和PDF文件

          1、生成圖片


          #通過URL地址生成

          google-chrome --headless --disable-gpu --screenshot=/data/app-server/google/2.png --window-size=630,1100 --hide-scrollbars --no-sandbox https://resource.xgjk.info/form/1660635773921_1973.html


          #通過本地文件生成

          google-chrome-stable --no-sandbox --headless --disable-gpu --screenshot=test.png -run-all-compositor-stages-before-draw --window-size=400,1200 /data/app-server/google/chufang.html


          2、生成PDF


          #通過URL地址生成

          google-chrome --headless --disable-gpu --no-sandbox --print-to-pdf-no-header --print-to-pdf=/data/app-server/google/18.pdf https://resource.xgjk.info/form/1660635773921_1973.html


          #通過本地文件生成

          google-chrome --headless --disable-gpu --no-sandbox --print-to-pdf-no-header --print-to-pdf=/data/app-server/google/18.pdf /data/app-server/google/chufang.html


          --screenshot 圖片位置

          --window-size 圖片寬高

          --print-to-pdf PF文件位置

          們經常寫 HTML 、 CSS 和 JavaScript ,寫好這些之后,我們就會在瀏覽器中看到頁面,那瀏覽器究竟在這背后做了一些什么事情呢?本篇文章將揭曉答案!

          了解瀏覽器的渲染原理是我們在通往更深層次的前端開發中不可缺少的,它可以讓我們從更深層次、角度去考慮性能優化等~

          下面進入正文~

          進程、線程

          瀏覽器會分配一個線程“自上而下,從左到右”依次解析和渲染代碼,那么進程和線程是什么,它們之間有著怎樣的關系呢?

          進程

          一個進程就是一個程序運行的實例。當啟動一個程序的時候,操作系統會為該程序創建一塊內存,用來存放代碼,運行中的數據和一個執行任務的主線程,這樣的一個運行環境就叫進程

          線程

          線程不能單獨存在,它是由進程來啟動和管理的。線程依附于進程,進程中使用多線程并行處理能提升運算效率

          兩者之間的關系

          1、進程中的任意一線程執行出錯,都會導致整個進程的崩潰

          2、線程之間可以共享數據

          3、當一個進程關閉后,操作系統會回收進程所占用的內存

          4、進程之間的內容相互隔離

          渲染機制

          從HTML、CSS和JavaScript開始

          了解瀏覽器的渲染原理,我們就要從理解 HTML 、 CSS 和 JavaScrip 開始,我們先來看一張圖

          HTML (超文本標記語言),顧名思義,由標記(標簽)和文本組成,每個標簽都有自己的語意,瀏覽器會根據標簽和文本展示對應的內容。

          CSS (層疊樣式表),由選擇器和屬性組成,它可以改變 HTML 的樣式,比如上圖中,我們改變了 span 的顏色由藍色為綠色。

          JavaScript ,我們可以通過 JS 完成很多事情,例如上圖中修改樣式。

          下面開始分析渲染的原理

          渲染流水線

          渲染模塊由于渲染的機制的復雜,被劃分為了很多子階段,輸入的 HTML 經過這些子階段,最后會輸出為像素。這樣的處理流程就叫做 渲染流水線

          按照渲染的時間順序,流水線可分為幾個子階段:構建 DOM 樹、樣式計算、布局階段、分層、繪制、分塊、光柵化和合成

          構建DOM樹

          由于瀏覽器無法直接理解和使用 HTML ,所以需要將 HTML 轉換為瀏覽器能夠理解的結構( DOM 樹)

          樹結構示意圖

          DOM樹的構建過程

          我們來分析一下下面這段代碼會構建出一棵什么樣的 DOM 樹

          我們先將上面的代碼運行,然后在瀏覽器控制臺輸入 document ,看看會有什么效果

          我們一層級一層級的打開就會看到如上圖的效果,我們可以根據這每一層級展開的效果,繪制出一棵 DOM 樹結構,如下:

          接下來,我們試一下利用 JS 修改一下內容,看有什么改變:

          我們可以看到“瀏覽器”的文字變成了“chrome”

          再來看一下 DOM 樹是否有改變

          我們看到在“瀏覽器”的位置換成了“chrome”,那么如何讓 DOM 節點擁有樣式?

          樣式計算

          樣式計算,顧名思義,就是 計算出 DOM 節點中每個元素的具體樣式 ,這個階段會分為三部分:

          • 把 CSS 轉換為瀏覽器能夠理解的結構
          • 轉換樣式表中的屬性值,使其標準化
          • 計算出 DOM 樹中每個節點的樣式

          CSS樣式來源

          • link 導入外部樣式資源

          瀏覽器會新開辟一個線程,去服務器獲取對應的資源文件(不阻礙主線程的渲染)

          • style 內嵌樣式

          從上到下解析,解析完繼續解析 DOM 結構。在真實項目中,如果 css 代碼不是很多,或是移動端項目,我們應該使用內嵌式,以此來減少 http 資源的請求,提高頁面渲染速度

          • 行內樣式
          • @import 導入

          它是同步的,不會開辟新線程去加載資源文件,而是讓主線程去獲取,這阻礙 DOM 結構的繼續渲染;只有把外部樣式導入進來,并且解析后,才會繼續渲染 DOM 結構

          把CSS轉換為瀏覽器能夠理解的結構

          瀏覽器就像不能理解 HTML 一樣,不理解 CSS ,所以當渲染引擎接收到 CSS 文件時,會執行轉換操作,將 CSS 文本轉換為瀏覽器可以理解的 styleSheets 結構。

          在 HTML 中,在瀏覽器中輸入 document 可以查看 html 的結構。在 css 中,可以輸入 document.styleSheets 看到 css 的結構

          現在的結構是空的,我們來加一些樣式,看看效果

          轉換樣式表中的屬性值,使其標準化

          屬性值標準化就是將所有值轉換為渲染引擎容易理解的、標準化的計算值。我們大致看一下效果:

          • 標準化前
          body {
              font-size: 2em;
              color: black;
              font-weight: bold;
              ...
          }
          復制代碼
          • 標準化后
          body {
              font-size: 16px;
              color: rgb(0, 0, 0);
              font-weight: 700;
              ...
          }
          復制代碼

          計算出DOM樹中每個節點的具體樣式

          樣式計算有兩個CSS的規則:繼承規則和層疊規則

          • CSS繼承規則

          CSS 繼承就是每個 DOM 節點都包含有父節點的樣式。我們來看一下下面這段代碼中如何應用到 DOM 節點上

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Document</title>
              <style>
                  h1 {
                      color: red;
                  }
          
                  div {
                      color: blue;
                  }
          
                  span {
                      font-size: 16px;
                  }
              </style>
          </head>
          <body>
              <h1>掘金</h1>
              <div>
                  <span>瀏覽器</span>
                  <span>渲染原理</span>
                  構建DOM樹
              </div>
          </body>
          </html>
          復制代碼

          子節點會擁有父節點的樣式,由此我們可以畫出這樣一張圖

          我們還可以打開控制臺,看一下選中 span 標簽,都會看到哪些內容

          通過上圖,我們可看到一個元素的樣式、繼承過程等, userAgent 樣式是瀏覽器默認的內置樣式,如果我們不提供任何樣式,就會使用此樣式。

          • 樣式層疊規則

          層疊在 CSS 處于核心地位,它是 CSS 的一個基本特征,它定義了如何合并來自多個源的屬性值的算法。

          樣式計算階段最終輸出的內容是每個 DOM 節點的樣式,并且保存在了 ComputedStyle 中。我們可以通過控制臺看到某個 DOM 元素最終的計算樣式

          布局階段

          現在我們不知道 DOM 元素的幾何位置信息,所以現在我們需要計算出 DOM 樹中可見元素的幾何位置,這個計算過程就叫做布局。布局階段有兩個過程:

          • 創建布局樹
          • 布局計算

          創建布局樹

          創建布局樹的意思就是創建一棵只包含可見元素的樹。我們來看下面一段代碼創建布局樹的過程

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Document</title>
              <style>
                  h1 {
                      color: red;
                  }
          
                  div {
                      color: blue;
                  }
          
                  div span {
                      font-size: 16px;
                  }
          
                  div span:last-child {
                      display: none;
                  }
              </style>
          </head>
          <body>
              <h1>掘金</h1>
              <div>
                  <span>瀏覽器</span>
                  <span>渲染原理</span>
                  構建DOM樹
              </div>
          </body>
          </html>
          復制代碼

          構建布局樹的過程中, DOM 樹中所有不可見的節點都不會包含在這棵樹中。瀏覽器會遍歷 DOM 樹中所有能看見的節點,然后把這些節點加入到布局中;不可見的節點就會被忽略, head 標簽下面的內容、 div 下最后一個 span 節點都不會在布局樹中,我們看一下這個過程圖感受一下~

          布局計算

          布局計算就是計算布局樹節點的坐標位置。這個計算過程極為復雜。

          分層

          渲染引擎會為特定的節點生成專用的圖層,并生成一棵對應的圖層樹。這樣做是因為頁面中可能含有很多復雜的效果,我們可以打開控制臺看一下頁面的分層情況

          我們可以看到,渲染引擎給頁面分了很多圖層,這些圖層會按照一定順序疊加在一起,形成最終的頁面

          那么圖層的來源有哪些?

          1、擁有層疊上下文屬性的元素會被提升為單獨的一層

          層疊上下文可以使能夠使 HTML 元素具有三維的概念,這些 HTML 元素按照自身屬性的優先級分布在垂直于這個二維平面的 z 軸上。哪些元素具有層疊上下文屬性?

          2、需要剪裁的地方會被創建為圖層

          當我們創建一個有寬度和高度的 div 時,里面的文字內容可能會超出這個區域,這時候渲染引擎會把裁剪文字內容的一部分用于顯示在 div 區域,例如

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Document</title>
              <style>
                  div {
                      width: 100px;
                      height: 100px;
                      background: yellow;
                      overflow: auto;
                  }
              </style>
          </head>
          <body>
              <div>
                  我們經常寫`HTML`、`CSS`和`JavaScript`,寫好這些之后,我們就會在瀏覽器中看到頁面,那瀏覽器究竟在這背后做了一些什么事情呢?本篇文章將揭曉答案!
          
                  了解瀏覽器的渲染原理是我們在通往更深層次的前端開發中不可缺少的,它可以讓我們從更深層次、角度去考慮性能優化等~
              </div>
          </body>
          </html>
          復制代碼

          運行結果

          我們再打開控制臺的 layers 看一下效果

          可以看到渲染引擎為文字部分單獨創建了一個圖層。

          在布局樹中的節點如果擁有對應的圖層,這個節點就是一個圖層,如果沒有,這個節點就屬于父節點的圖層,如下圖:

          圖層繪制

          創建好圖層樹后,渲染引擎會繪制圖層樹中的每個圖層。渲染引擎會將圖層繪制分解為很多小的繪制指令,然后將這些指令按照順序組成待繪制列表,我們可以打開控制臺的 layers ,選擇 document 層,看一下效果

          柵格化操作

          柵格化就是將圖塊轉換位位圖,圖塊是柵格化執行的最小單位。渲染進程維護了一個柵格化的線程池,所有圖塊的柵格化都是在線程池內執行的。

          圖層繪制列表準備好之后,主線程會把這個繪制列表提交給合成線程,繪制操作由渲染引擎中的合成線程來完成。

          合成線程將圖層劃分為圖塊,然后合成線程會按照視口(可見區域)附近的圖塊優先生成位圖。

          合成與顯示

          所有的圖塊都被光柵化后,合成線程會生成一個繪制圖塊的命令( DrawQuad ),然后將該命令提交給瀏覽器進程。瀏覽器進程里面 viz 組件用來接收 DrawQuad 命令,將其頁面內容繪制到內存中,最后將內存顯示到屏幕。這個時候,我們就看到了頁面

          完善渲染流水線示意圖

          根據上文中描述,我們可以畫出這樣一張圖

          我還在網上找到了另外一張圖

          這兩張圖都是描述瀏覽器的渲染流程的。


          主站蜘蛛池模板: 无码一区二区三区在线| 97久久精品一区二区三区| 久久久精品一区二区三区| 在线日韩麻豆一区| 国产精品久久亚洲一区二区| 无码精品人妻一区二区三区中 | 国产一区二区三区在线观看精品| 狠狠做深爱婷婷综合一区| 精品福利一区二区三区精品国产第一国产综合精品 | 无码人妻精品一区二区三区9厂 | 精品少妇人妻AV一区二区三区| 国产成人高清亚洲一区久久| 极品少妇伦理一区二区| 亚洲一区二区三区在线播放| 国精品无码一区二区三区在线蜜臀| 亲子乱AV视频一区二区| 又紧又大又爽精品一区二区| 制服丝袜一区二区三区| 无码人妻精品一区二区三区久久久| 日韩一区二区三区免费播放| 无码人妻精品一区二区三区9厂 | 精品久久久久久无码中文字幕一区| 少妇激情一区二区三区视频 | 无码人妻精品一区二区蜜桃百度| 亚洲一区AV无码少妇电影☆| 韩国福利一区二区三区高清视频| 精品久久国产一区二区三区香蕉 | 人妻无码视频一区二区三区 | 亚洲AⅤ无码一区二区三区在线 | 麻豆aⅴ精品无码一区二区| 亚洲一区二区三区电影| 色国产精品一区在线观看| 日韩精品一区二区三区色欲AV | 另类ts人妖一区二区三区| 久久AAAA片一区二区| 伊人激情AV一区二区三区| 久久91精品国产一区二区| 亚洲成a人一区二区三区| 综合无码一区二区三区| 国模少妇一区二区三区| 精品不卡一区二区|