精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

淺談 Canvas 渲染引擎設計

開發 前端
當渲染層 JS 資源加載完成后,直接省略反序列化、初始化 Model、計算排版數據等階段,將 FVG 轉換成 Widget 進行 Canvas 渲染,這一步非常接近于 React 的 hydrate,很巧妙。

用過 Canvas 的都知道它的 API 比較多,使用起來也很麻煩,比如我想繪制一個圓形就要調一堆 API,對開發算不上友好。

為了解決這個痛點,誕生了例如 PIXI、ZRender、Fabric 等 Canvas 庫,對 Canvas API 進行了一系列的封裝。

今天主要介紹一下社區幾個比較有代表性的 Canvas 渲染引擎的設計原理。

這篇文中不會從源碼講起,更像是一篇科普文章,介紹 Canvas 一些有趣的點。

1. 特性

Canvas 渲染引擎一般包括下面幾個特點:

  1. 封裝

將 Canvas API 的調用封裝成更簡單、清晰的形式,貼近于我們使用 DOM 的方式。

比如想畫一個圓,直接調用封裝好的繪制方法就行了,我們不需要關心是如何繪制的。

  1. 性能

雖然封裝之后的 API 很貼近 HTML 語法,但也意味著開發者很難去做一些底層的性能優化。因此,大部分 Canvas 渲染引擎都會內置了一些性能優化手段。

常見的性能優化手段有離屏渲染、臟區渲染、異步渲染等等。

  1. 跨平臺

一些渲染引擎為了更加通用,在底層做了更多抽象,不僅支持 Canvas Renderer,甚至還支持 WebGL、WebGPU、SVG、CanvasKit、小程序等等,真正實現了一套代碼多種渲染。

針對底層的渲染流程和類進行抽象化,在不同平臺具象化去實現具體的渲染邏輯,從而可以一套代碼,只要切換渲染器就能實現多平臺渲染。

2. 封裝

2.1 虛擬節點

Canvas 是一張畫布,里面的內容都是自己調用 API 繪制的,所以更像是我們拿起畫筆來作畫。

目前主流的 Canvas 渲染引擎都會將要繪制的圖形封裝成類,以方便開發者去調用,復用性也比較強。調用方式類似于 DOM,每個實例可以當做一個虛擬節點。

使用 AntV/g 的例子:

import { Circle, Canvas, CanvasEvent } from '@antv/g';
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
// or
// import { Renderer as WebGLRenderer } from '@antv/g-webgl';
// import { Renderer as SVGRenderer } from '@antv/g-svg';

// 創建畫布
const canvas = new Canvas({
container: 'container',
width: 500,
height: 500,
renderer: new CanvasRenderer(), // 選擇一個渲染器
});

// 創建一個圓
const circle = new Circle({
style: {
cx: 100,
cy: 100,
r: 50,
fill: 'red',
stroke: 'blue',
lineWidth: 5,
},
});

canvas.addEventListener(CanvasEvent.READY, function () {
// 加入畫布
canvas.appendChild(circle);

// 監聽 `click` 事件
circle.addEventListener('click', function () {
this.style.fill = 'green';
});
});

在此基礎上,可以進一步針對 React/Vue 語法進行封裝,讓用戶對底層的實現無感知。

使用 React-Konva 的例子(通過 react-reconciler 實現):

import React, { Component } from 'react';
import { render } from 'react-dom';
import { Stage, Layer, Rect, Text } from 'react-konva';
import Konva from 'konva';

class ColoredRect extends React.Component {
state = {
color: 'green',
};
handleClick = () => {
this.setState({
color: Konva.Util.getRandomColor(),
});
};
render() {
return (
<Rect
x={20}
y={20}
width={50}
height={50}
fill={this.state.color}
shadowBlur={5}
onClick={this.handleClick}
/>
);
}
}

class App extends Component {
render() {
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
<Text text="Try click on rect" />
<ColoredRect />
</Layer>
</Stage>
);
}
}

render(<App />, document.getElementById('root'));

除了內置的圖形類,很多渲染引擎還會提供自定義繪制圖形類的能力。

以 Konva 為例,每個圖形類都需要實現 sceneFunc 方法,在這個方法里面去調用 Canvas API 來進行繪制。

如果需要自定義新的圖形,就可以繼承 Shape 來實現 sceneFunc 方法。

Konva 里面圓形繪制類的實現:

export class Circle extends Shape<CircleConfig> {
_sceneFunc(context) {
context.beginPath();
context.arc(0, 0, this.attrs.radius || 0, 0, Math.PI * 2, false);
context.closePath();
context.fillStrokeShape(this);
}
}

參照 DOM 樹的結構,每個 Konva 應用包括一個舞臺 Stage、多個畫布 Layer、多個分組 Group,以及若干的葉子節點 Shape,這些虛擬節點關聯起來最終形成了一棵樹。

圖片

在 Konva 中,一個 Stage 就是根節點,Layer 對應一個 Canvas 畫布,Group 是指多個 Shape 的集合,它本身不會進行繪制,但同一個 Group 里面的 Shape 可以一起應用旋轉、縮放等變換。

Shape 則是指具體的繪制節點,比如 Rect、Circle、Text 等等。

2.2 包圍盒

既然有了虛擬節點,那知道每個虛擬節點的位置和大小也比較重要,它會涉及到判斷兩個圖形是否相交、事件等等。

有時候元素的形狀不是很規則,如果直接對不規則元素進行碰撞檢測會比較麻煩,所以就有了一個近似的算法,就是在物體外側加上包圍盒,如圖:

目前主流的包圍盒有 AABB 和 OBB 兩種。

AABB 包圍盒:

實現方式簡單,直接用最大最小的橫縱坐標來生成包圍盒,但不會跟著元素旋轉,因此空白區域比較多,也不夠準確。

也是目前 Konva 和 AntV 使用的方式。(適合表格業務)

OBB 包圍盒:

實現方式相對復雜,通過構建協方差矩陣來計算出新的坐標軸方向,將其頂點投射到坐標軸上面來得到新的包圍盒。

所以 OBB 包圍盒更加準確一些,也是 cocos2d 使用的方式。

碰撞檢測:

兩個包圍盒在所有軸(與邊平行)上的投影都發生重疊,則判定為碰撞;否則,沒有發生碰撞。

2.3 排版系統

繪制 Canvas 的時候一般是通過相對坐標來確定當前要繪制的位置,所以都是通過各種計算來拿到 x、y。

即使是 Konva 也是依賴于 x、y 來做相對定位。

因此,在 AntV 和 SpriteJS 這類 Canvas 渲染引擎里面,都內置支持了盒模型的語法糖,底層會將盒模型屬性進行一次計算轉換成 x、y。

以 AntV 為例子,排版能力是基于 Facebook 開源的 Yoga 排版引擎(React Native)來實現的,支持一套非常完整的盒模型和 Flex 布局語法。

const container = new Rect({
style: {
width: 500, // Size
height: 300,
display: 'flex', // Declaring the use of flex layouts
justifyContent: 'center',
alignItems: 'center',
x: 0,
y: 0,
fill: '#C6E5FF',
},
});

在騰訊開源的 Hippy 里面自己實現了一套類似 Yoga 的排版引擎,叫做 Titank。

在飛書文檔多維表格里面,排版語法更加接近于 Flutter,實現了 Padding、Column、Row、Margin、Expanded、Flex、GridView 等 Widget。

下面的示例是 Flutter 的:

Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 對齊方式
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
);

Align(
alignment: Alignment.bottomRight,
child: Container(width: 100, height: 100, color: red),
)

Padding(
padding: EdgeInsets.fromLTRB(30, 30, 0, 30),
child: Image.network(
"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581413255772&di=52021e3e656744094d0339e7016994bb&imgtype=0&src=http%3A%2F%2Fimg8.zol.com.cn%2Fbbs%2Fupload%2F19571%2F19570481.jpg",
fit: BoxFit.cover,
),
)

Widget _buildGrid() => GridView.extent(
maxCrossAxisExtent: 150,
padding: const EdgeInsets.all(4),
mainAxisSpacing: 4,
crossAxisSpacing: 4,
children: _buildGridTileList(30));

實現了盒模型和 Flex 布局,可以讓 Canvas 的排版能力更上一層樓。

不僅可以減少代碼中的大量計算,也可以讓大家從 DOM 開發無縫銜接進來,值得我們參考。

canvas-flexbox - CodeSandbox

3. 事件

Canvas 本身是一塊畫布,所以里面的內容都是畫出來的,在 DOM 樹里面也只是一個 Canvas 的節點,所以如何才能知道當前點擊的是哪個圖形呢?

由于 Canvas 渲染引擎都會封裝虛擬節點,每個節點都有自己的包圍盒,所以為實現 Canvas 的事件系統提供了可能性。

主流的 Canvas 渲染引擎都是針對 Canvas 節點或者上層節點進行事件委托,監聽用戶相關的事件(mouseDown、click、touch等等)之后,匹配到當前觸發的元素,將事件分發出去,并且擁有一套向上冒泡的機制。

目前主流的兩種事件實現方式分別是取色值法和幾何法。

3.1 取色值法

取色值法是 Konva 采用的實現方式,它的實現方式非常簡單,匹配精確度很高,適合不規則圖形的匹配。

取色值法的原理如下:

  1. 在主 Canvas 繪制一個圖形的時候,會為這個圖形生成一個隨機的 colorKey(十六進制的顏色),同時建立類似于 Map<colorKey, Shape> 的映射。

getRandomColor() {
var randColor = ((Math.random() * 0xffffff) << 0).toString(16);
while (randColor.length < 6) {
randColor = ZERO + randColor;
}
return HASH + randColor;
},

  1. 繪制的同時會在內存里的 hitCanvas 同樣位置繪制一個一模一樣的圖形,填充色是剛才的 colorKey。
  2. 當用戶鼠標點擊 Canvas 畫布的時候,可以拿到鼠標觸發的 x、y,將其傳給內存里面的 Canvas。
  3. 內存里面的 Canvas 通過 getImageData 來獲取到當前的顏色,進而通過 colorKey 來匹配到對應的圖形。

從上述原理可以看出來,Konva 對于不規則圖形的匹配依然很精確,但缺點也很明顯,每次都需要繪制兩份,導致繪制性能變差。

同時,getImageData 耗時比較高,在頻繁觸發的場景(onWheel)會導致幀率下降嚴重。

3.2 幾何法

幾何法有很多種實現方式,這里主要講解引射線法,因為需要進行一系列幾何計算,所以這里我稱之為幾何法。

幾何法是 AntV 和飛書文檔采用的實現方式,實現方式相對復雜一些,針對不規則圖形的匹配效率偏低。

幾何法的實現原理如下:

  1. 基于當前虛擬節點的包圍盒來構建一棵 R Tree
  2. 當用戶觸發事件的時候,利用 R Tree 來進行空間索引查找,依據 z-index 找到最頂層的一個圖形。
  3. 從目標點出發向一側發出一條射線,看這條射線和多邊形所有邊的交點數目。
  4. 如果有奇數個交點,則說明在內部,如果有偶數個交點,則說明在外部。

為什么奇數是在內部,偶數是在外部呢?我們假設射線與這個圖形的交點,進入圖形叫做穿入,離開圖形叫做穿出。

在圖形內部發出的射線,一定會有穿出但沒有穿入的情況。但在外部發出的射線,穿入和穿出是相對的。

但是射線剛好穿過頂點的情況比較特殊,因此需要單獨進行判斷。

幾何法的優勢在于不需要在內存里面進行重復繪制,但依賴于復雜的幾何計算,因此不適合有大量不規則圖形的情況。

在 AntV 里面支持對不規則圖形的匹配,但飛書文檔由于是表格業務,所以可以將所有圖形都當做矩形來處理,反而更簡單一些。

4. 性能

由于 Canvas 渲染引擎都會進行大量的封裝,所以開發者想針對底層做性能優化是非常難的,需要渲染引擎自身去支持一些優化。

4.1 異步批量渲染

在飛書文檔 Bitable 和 Konva 里面都支持異步渲染,將大量繪制進行批量處理。

const rect = new Rect({ /... });
// 多次修改屬性,可能會觸發多次渲染
rect.x(100);
rect.fill('red');
rect.y(100);

由于每次修改圖形的屬性或者添加、銷毀子節點都會觸發渲染,為了避免同時修改多個屬性時導致的重復渲染,因此約定每次在下一幀進行批量繪制。

batchDraw() {
if (!this._waitingForDraw) {
this._waitingForDraw = true;
Util.requestAnimFrame(() => {
this.draw();
this._waitingForDraw = false;
});
}
return this;
}

這種渲染方式類似于 React 的 setState,避免短時間內多次 setState 導致多次 render。

4.2 離屏渲染

離屏渲染我們應該都比較熟悉了,就是兩個 Canvas 來回用 drawImage 繪制可復用部分,從而減少繪制的耗時。

這里主要講解 Konva 和飛書 Bitable 里面的離屏渲染。

在 Konva 中的離屏渲染主要是針對 Group 級別來做的,通過調用 cache 方法就能實現離屏渲染。

基于 Group 來做離屏渲染的原理是:

  1. 調用 cache 方法,創建一個離屏 Canvas 節點。
  2. 遍歷 Group 子節點進行繪制,同時將其繪制到離屏 Canvas 上面。
  3. 下次 batchDraw 的時候判斷是否有緩存,如果有,那么直接走 drawImage 的形式。

這種離屏渲染的調用方式比較簡單,Group 的粒度可以由開發者自己決定,但也有一定的問題。

  1. 比較難應用于表格這種形式的業務
  2. Konva 沒有臟檢測能力,即使 Group 里面的 Shape 屬性改變了,依然不會更新離屏 Canvas。
  3. 由于使用色值法來匹配圖形,導致開啟了離屏渲染,實際上至少要繪制四份(主canvas、事件 hitCanvas、離屏 cacheCanvas、離屏事件 cacheHitCanvas)。

為什么需要繪制四份呢?因為離屏渲染是 drawImage 的形式,這樣就不會有 colorKey 和 Shape 對應的情況了,所以離屏 Canvas 也要有一個自己的 hitCanvas 來做 getImageData,也就是 cacheHitCanvas。

另一種場景的離屏渲染就是飛書 Bitable 里面的實現。

飛書在底層之上封裝了虛擬列表的 Widget,也就是基于業務定制的 Widget,這也是一種有趣的思路。

  1. 創建一個虛擬列表的 Widget 類,將列表數據傳入
  2. 實現列表每一項的繪制方法,將列表繪制出來
  3. 滾動的時候虛擬列表內部進行節點的回收創建,但不會進行異步批量渲染,針對可復用的部分進行離屏渲染
  4. 更新階段,通過 key 對比來決定是回收、創建還是復用。

在多維表格看板視圖里面,每個分組都是一個虛擬列表,多個分組(虛擬列表)又組合成一個大的虛擬列表。

多選單元格編輯器也可以基于虛擬列表實現。

虛擬列表 Widget 類適合多維表格這種業務,多個視圖都需要有自己的滾動容器,不同視圖都需要處理節點的回收、復用、新建,通過公用 Widget 可以一步到位去支持,也方便在內部去做更多性能優化。

4.3 臟區渲染

對于 Konva 來說,每次重新渲染都是對整個 Canvas 做 clearRect 清除,然后重新繪制,性能相對比較差。

更好的做法是檢測到當前的改動影響到的范圍,計算出重繪范圍后,只清除重繪區的內容重新進行繪制。

在 Canvas 中可以通過 rect 和 clip 限制繪制區域,從而做到只對部分區域重繪。

以前 ECharts 底層的 ZRender 為例來講解:

  1. 根據圖形前后變化,來計算出重繪區域,比如上圖的區域,在飛書文檔中會將整個移動的路徑當做重繪區域。
  2. 如果有多個重繪區域,那么優先嘗試將相交(包圍盒)的重繪區進行合并,并且優先合并相交面積最大的重繪區。
  3. 如果合并完成后,當前剩余的重繪區數量大于5,則進一步進行合并,直到數量只剩5。
  4. 依次遍歷這些重繪區域,先清除掉原有的內容,再進行繪制。

飛書文檔多維表格沒有做 Canvas 渲染分層,但對各種交互響應速度非常快,也是得益于底層渲染引擎對臟矩形渲染的支持,它的性能也是所有同類產品里面最好的。

除了上述的這些,還有在文檔這邊使用的一些優化手段,比如合并相同屬性的圖形繪制(線、矩形、文本等)、Canvas 分層等等,這些就不多做闡述了。

5. 跨平臺

很多 Canvas 渲染引擎并不滿足于只做 Canvas,一般還會支持一些其他的渲染模式,比如 SVG 渲染、WebGL 渲染、WebGPU 渲染等等。

在 AntV 里面通過引入對應的 package 來實現加載渲染器的,在 ZRender 中則是通過 register 來注冊不同的渲染器。

AntV 中使用 CanvasKit 渲染:

import { Renderer as CanvaskitRenderer } from '@antv/g-canvaskit';
const canvaskitRenderer = new CanvaskitRenderer();

關于跨平臺的架構這里不做講解,主要是抹平不同平臺的差異,這里主要講解一下針對于服務端渲染的不同處理。

主流的服務端渲染方式有兩種,一種是用 node-canvas 來輸出一張圖片,在 echarts 等庫中都有使用,缺陷在于文本排版不夠準確,對于自適應瀏覽器窗口的情況無法處理。因此它不適用于文檔直出的場景。

const { createCanvas, loadImage } = require('canvas')
const canvas = createCanvas(200, 200)
const ctx = canvas.getContext('2d')

// Write "Awesome!"
ctx.font = '30px Impact'
ctx.rotate(0.1)
ctx.fillText('Awesome!', 50, 100)

// Draw line under text
var text = ctx.measureText('Awesome!')
ctx.strokeStyle = 'rgba(0,0,0,0.5)'
ctx.beginPath()
ctx.lineTo(50, 102)
ctx.lineTo(50 + text.width, 102)
ctx.stroke()

// Draw cat with lime helmet
loadImage('examples/images/lime-cat.jpg').then((image) => {
ctx.drawImage(image, 50, 0, 70, 70)

console.log('<img src="' + canvas.toDataURL() + '" />')
})

另一種就是通過 SVG 來模擬 Canvas 的效果,輸出 SVG DOM 字符串。但它的實現會比較麻煩,也無法 100% 還原 Canvas 的效果。

但很多 Canvas 渲染引擎本身也支持 SVG 渲染,即使不支持,也可以通過 canvas2svg 這個庫來進行轉換。

var ctx = new C2S(500,500);

//draw your canvas like you would normally
ctx.fillStyle="red";
ctx.fillRect(100,100,100,100);

//serialize your SVG
var mySerializedSVG = ctx.getSerializedSvg();

//If you really need to you can access the shadow inline SVG created by calling:
var svg = ctx.getSvg();

對于更加通用的場景來說,在瀏覽器端使用 Canvas 渲染,服務端使用 SVG 渲染是更合理的形式。

在新版 ECharts 里面,針對 SVG 服務端渲染的能力,還支持了 Virtual DOM 來代替 JSDOM,最后轉換成 DOM 字符串。

在飛書文檔中使用了一種完全獨立于 node-canvas 和 SVG 的解決方式,非常值得我們借鑒。

由于飛書多維表格底層統一了渲染引擎,所有繪制元素都是 Widget(對齊 Flutter),可以脫水轉換成下面 FVG 格式。

圖片

一般來說,文檔業務首屏加載是下面這么幾步:

獲取首屏數據 -> 資源加載 -> 首屏數據反序列化 -> 初始化 Model 層 -> 計算排版數據 -> Canvas 渲染

在飛書文檔里面直出渲染層 Widget 的數據結構,這個數據結構是最后提供給 Canvas 渲染的數據,也就是已經經過了計算排版數據階段。

當渲染層 JS 資源加載完成后,直接省略反序列化、初始化 Model、計算排版數據等階段,將 FVG 轉換成 Widget 進行 Canvas 渲染,這一步非常接近于 React 的 hydrate,很巧妙。

責任編輯:武曉燕 來源: 前端小館
相關推薦

2022-12-12 09:01:13

2020-10-12 08:56:47

Virtual dom

2012-03-20 11:16:24

MySQLMyISAM

2016-12-08 10:57:08

渲染引擎前端優化

2009-07-16 10:26:49

渲染器接口Swing

2023-02-27 09:10:57

前端組件設計

2013-11-20 13:47:43

瀏覽器渲染引擎

2011-06-03 10:19:53

2009-05-05 10:19:37

存儲引擎InnoDBMyISAM

2022-10-09 14:15:42

短鏈設計

2011-09-01 10:21:52

jQuery Mobi元素

2018-07-11 15:21:25

GPU虛擬化技術

2015-02-28 09:39:24

Windows 10Spartan

2016-11-01 20:26:47

前端模板underscoreWeb

2011-06-28 18:26:59

SEO搜索引擎

2011-05-03 10:09:37

MySQL存儲引擎

2013-11-13 14:00:31

網頁設計設計

2009-07-10 09:31:57

MyEclipse U

2009-08-10 10:19:47

ASP.NET組件設計

2009-09-02 16:23:27

C# Singleto
點贊
收藏

51CTO技術棧公眾號

精品电影在线| 国产午夜精品无码一区二区| 免费成人高清在线视频| 国产精品久久久久久亚洲毛片| 成人有码在线播放| 精品无码av在线| 一区二区三区日本久久久 | 青青草原免费观看| 久久动漫网址| 欧美色视频在线| a级黄色片免费| 蝌蚪视频在线播放| 国产一区二区三区精品视频| 98精品国产自产在线观看| 精品人妻一区二区三区蜜桃视频| 国产亚洲高清在线观看| 欧美午夜精品久久久久久人妖| 一本色道婷婷久久欧美| 色欲av伊人久久大香线蕉影院| 三级影片在线观看欧美日韩一区二区 | 牛牛影视久久网| 欧美日韩成人综合天天影院 | 国产精品一二三区在线观看| 美女日韩一区| 在线看日韩精品电影| 9色porny| 好吊日视频在线观看| 91丨九色porny丨蝌蚪| 国产日韩精品入口| av资源免费观看| 在线成人超碰| 最近日韩中文字幕中文| 国产精品日日摸夜夜爽| 青草综合视频| 色婷婷av一区二区三区大白胸| 九九久久九九久久| 欧洲美女少妇精品| 久久夜色精品一区| 国产精品一区二区av| 国产乱码久久久| 免播放器亚洲一区| 日本高清视频精品| 黄色片免费观看视频| 欧美性色综合| 欧美成人精品xxx| 久久福利免费视频| 日本激情一区| 一区二区三区视频观看| 国产精品jizz| 亚洲制服欧美另类| 日韩大陆毛片av| 少妇激情一区二区三区视频| 国产乱论精品| 亚洲第一视频网| 免费观看一区二区三区| 精品一区二区三区中文字幕在线 | 国产黄色免费大片| 激情欧美一区二区三区在线观看| 国产欧美日韩精品在线观看| 伊人网视频在线| 日本免费新一区视频| 国产精品久久久久免费a∨大胸 | 9191成人精品久久| 九九精品久久久| 2020国产精品小视频| 欧美疯狂做受xxxx富婆| 超碰人人草人人| 国产精品亚洲一区二区在线观看| 欧美一区二区三区视频在线| 性生活在线视频| 亚洲精选av| 亚洲精品白浆高清久久久久久| 四季av综合网站| 免费成人三级| 亚洲欧美色图片| 自拍偷拍你懂的| 午夜影院欧美| 欧美激情一级二级| 日韩乱码在线观看| 久久精品欧洲| 国产免费久久av| 99久久亚洲精品日本无码| 国产99久久久国产精品潘金| 国产亚洲一区二区三区在线播放| 日本福利片高清在线观看| 久久久99久久精品欧美| 在线观看成人av电影| fc2ppv国产精品久久| 亚洲电影在线免费观看| 成人一区二区三| 亚洲影视资源| 亚洲第一偷拍网| 调教驯服丰满美艳麻麻在线视频| 在线精品视频在线观看高清| 欧美亚洲另类激情另类| 亚洲视频在线观看一区二区| 福利电影一区二区三区| 欧美乱偷一区二区三区在线| 黄色成年人视频在线观看| 天天综合色天天综合| 中文av一区二区三区| 一区二区免费| 一本久久综合亚洲鲁鲁| 青青草免费av| 日韩高清不卡一区二区三区| av成人在线电影| 国产小视频在线观看| 亚洲久草在线视频| 无遮挡又爽又刺激的视频| 精品中文视频| 亚洲欧洲第一视频| 玖玖爱免费视频| 免费在线观看一区二区三区| 国产伦精品一区二区三区四区视频| shkd中文字幕久久在线观看| 亚洲二区在线视频| 欧美精品 - 色网| 沈樵精品国产成av片| 久久久久国产精品免费网站| 亚洲午夜无码久久久久| 99免费精品在线| 日日噜噜夜夜狠狠久久丁香五月| 都市激情亚洲一区| 精品福利在线导航| 日韩三级在线观看视频| 日韩激情一区二区| 国产自产精品| 欧美人与牲禽动交com| 欧美日韩国产在线观看| www在线观看免费视频| 亚洲精品九九| 成人av免费电影| 国产美女福利在线| 欧美三级电影在线看| 亚洲一区二区三区蜜桃| 99成人在线| 99中文字幕| 高h视频在线观看| 欧美三级日韩三级国产三级| 中文天堂资源在线| 老司机精品久久| 精品无码久久久久久久动漫| 免费不卡av| 日韩欧美三级在线| 国产67194| 国产在线精品一区二区| 亚洲一区三区电影在线观看| 国产麻豆久久| 亚洲午夜精品久久久久久性色| 国产成人综合欧美精品久久| 99精品国产99久久久久久白柏| 无码人妻少妇伦在线电影| 日韩一级淫片| 久久久久中文字幕2018| 日韩一区免费视频| 欧美日韩国产一区二区三区| 国内精品久久99人妻无码| 99热在线精品观看| 精品在线一区| 一区二区三区电影大全| 亚洲色图35p| 成人免费一级片| 国产精品五月天| 日本黄色的视频| 亚洲国产老妈| 高清不卡一区二区三区| 国产盗摄在线视频网站| 日韩毛片在线看| 色老头在线视频| 国产精品拍天天在线| 九一精品久久久| 国内精品久久久久国产盗摄免费观看完整版| 成人免费观看网站| 久热在线观看视频| 中文亚洲视频在线| 国产精品无码天天爽视频| 一区二区三区久久| 国产制服丝袜在线| 美腿丝袜一区二区三区| 蜜臀在线免费观看| 黄色成人美女网站| 国产国产精品人在线视| 麻豆网站视频在线观看| 亚洲精品在线网站| 天天干天天操天天爱| 国产精品美女久久久久久久| 久久久久久无码精品人妻一区二区| 亚洲性图久久| 日本亚洲自拍| 国产免费av国片精品草莓男男| 国内精品久久久久影院优 | 成年人网站免费视频| 少妇一区二区视频| 亚洲综合在线播放| 国产高清不卡| 两个人的视频www国产精品| 午夜影院免费体验区| 91福利视频在线| 欧美激情图片小说| 久久影院午夜论| 一级黄色高清视频| 久久婷婷一区| 国产专区在线视频| 久久超碰99| av一区二区三区免费| 欧美无毛视频| 久久综合88中文色鬼| 亚洲欧美自偷自拍| 日韩一区二区三区免费看| 免费黄色片视频| 亚洲午夜av在线| 蜜桃av.com| 国产日韩影视精品| 亚洲自拍偷拍精品| 精品在线亚洲视频| 成年人网站大全| 亚洲免费黄色| 97超碰在线视| 久久中文字幕二区| 免费在线观看91| 99久久香蕉| 91免费精品国偷自产在线| 欧美亚洲韩国| 午夜美女久久久久爽久久| av文字幕在线观看| 一本一道久久a久久精品逆3p | 亚洲片在线资源| 好男人www在线视频| 4438x亚洲最大成人网| 天天干天天操天天操| 欧美日韩国产色| 国产又大又黑又粗免费视频| 一区二区三区四区五区视频在线观看 | 日本一区二区三区精品| 亚洲国产精品久久久男人的天堂| 手机av在线看| 国产精品毛片无遮挡高清| 男生草女生视频| 92精品国产成人观看免费| 人妻激情偷乱频一区二区三区 | 91精品中国老女人| 青青青国产精品| 成人疯狂猛交xxx| 日本欧美韩国| 国产精品黄色影片导航在线观看| 欧美xxxxxx| 国产91热爆ts人妖在线| 亚洲精品永久免费视频| 欧美亚洲日本网站| 免费成人在线电影| 91成人精品网站| 精品国产av色一区二区深夜久久| 日韩av网站在线免费观看| 国产精品对白刺激久久久| 亚洲一区二区三区四区电影 | 日本在线xxx| 国产综合网站| 亚洲 欧美 综合 另类 中字| 亚洲激情国产| 美女日批免费视频| 美女精品一区| 激情综合网俺也去| 奇米影视一区二区三区| 日本黄色福利视频| 国产精品亚洲第一区在线暖暖韩国 | 日本不卡一区二区| 亚洲综合日韩欧美| 国产一区二区成人久久免费影院 | 国产精品自拍合集| 黑丝一区二区| 亚洲午夜无码av毛片久久| 老司机免费视频久久| 国产喷水theporn| 国内精品伊人久久久久影院对白| 久草福利在线观看| 99久久久精品| 精品日韩在线视频| 亚洲欧美欧美一区二区三区| 国语对白一区二区| 欧美日韩性视频在线| 国产又粗又猛又爽又| 5858s免费视频成人| 欧美 日韩 国产 成人 在线| 亚洲欧洲高清在线| av免费在线观看网址| 午夜精品久久久久久久99热 | 黄色在线观看av| 国产欧美一区二区精品性色| 亚洲精品卡一卡二| 图片区小说区区亚洲影院| 中文字幕天堂在线| 精品国产自在久精品国产| 你懂的免费在线观看视频网站| 深夜成人在线观看| 成人观看网址| 国产精品自产拍在线观看| 国产精品欧美大片| 日韩精品久久一区| 欧美在线资源| 国产黄色特级片| 国产精品一区久久久久| 99久久人妻无码精品系列| 亚洲免费在线观看| 欧美日韩a v| 精品久久久久久亚洲综合网 | 国产精品网站免费| 美女视频黄 久久| 中文字幕人妻一区二区三区| 国产精品久线在线观看| 日韩av一二三区| 制服丝袜中文字幕一区| 日韩偷拍自拍| 欧美另类高清videos| 欧美xnxx| 久久综合狠狠综合久久综青草| 亚洲一区二区三区无吗| 超碰影院在线观看| 菠萝蜜视频在线观看一区| 羞羞在线观看视频| 在线一区二区三区| 西西人体44www大胆无码| 久久福利网址导航| 国产精品诱惑| 日本免费高清一区| 最新亚洲一区| 特种兵之深入敌后| 亚洲欧洲国产日韩| 国产在线一级片| 亚洲精品综合精品自拍| av老司机在线观看| 69174成人网| 91精品国偷自产在线电影| 性chinese极品按摩| 国产午夜亚洲精品羞羞网站| 日本在线播放视频| 亚洲电影免费观看高清完整版在线 | 亚洲va欧美va人人爽成人影院| 一区在线电影| 捆绑变态av一区二区三区| 亚洲一区视频在线播放| 色哟哟国产精品免费观看| 天堂资源中文在线| 国产91对白在线播放| 日本欧美韩国国产| 337p粉嫩大胆噜噜噜鲁| 99久久er热在这里只有精品15| 久久精品视频国产| 欧美电影免费提供在线观看| 成人video亚洲精品| 成人在线视频福利| 91精品一区二区三区综合在线爱| 在线免费看v片| 亚洲欧美激情一区二区| 99免费在线视频| 久久91精品国产91久久久| 岛国成人av| 黄色影院一级片| 久久九九全国免费| 天天天天天天天干| 精品国产拍在线观看| 国产激情精品一区二区三区| 国产成人三级视频| 国产69精品久久777的优势| 亚洲精品在线观看av| 精品中文字幕久久久久久| 高清电影一区| 在线观看日韩羞羞视频| 国产成人精品三级麻豆| 国产无遮挡又黄又爽在线观看 | 中文字幕一区二区久久人妻网站| 欧美日韩国产中字| 番号集在线观看| 成人写真视频福利网| 国产综合自拍| 久久国产精品影院| 欧美日韩一区二区三区四区| а天堂中文在线官网| 国产v亚洲v天堂无码| 亚洲视频大全| 手机av在线不卡| 91精品国产aⅴ一区二区| xxxx在线视频| 日本在线视频不卡| 国产一区二区三区免费在线观看| 动漫精品一区一码二码三码四码| 亚洲午夜精品久久久久久性色| 国产亚洲字幕| 久久网站免费视频| 国产精品九色蝌蚪自拍| 丁香六月天婷婷| 国产精品久久久久久久久久99| 一区二区三区四区电影| 亚洲黄色在线网站| 小草在线视频免费播放| 91天堂在线视频| 国产欧美激情| 国产稀缺精品盗摄盗拍| 日韩电影中文字幕在线| 亚洲在线资源| 欧美一级片中文字幕|