日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【自己给自己题目做】:如何在Canvas上实现魔方效果

發布時間:2023/12/19 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【自己给自己题目做】:如何在Canvas上实现魔方效果 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  最終demo ->?3d魔方?

  體驗方法:

  • 浮動鼠標找到合適的位置,按空格鍵暫停
  • 選擇要翻轉的3*3模塊,找到相鄰兩個正方體,鼠標點擊第一個正方體,并且一直保持鼠標按下的狀態直到移到第二個正方體后放開,比如下圖:

(鼠標點擊1處,然后一直移動到2處松開,中間一行的3*3模塊繞圖示方向發生轉動)

  • 按空格鍵,魔方恢復轉動,繼續尋找下一個要翻動的目標

  示意圖如下(請盡量使用chrome):

  

?

正方體繪制回顧

  Canvas之蛋疼的正方體繪制體驗?說到了如何用canvas在畫布上繪制三維效果的正方體,并且最終給出了一個多正方體的demo ->?多正方體

  具體的過程可以參照前文,這里簡要的再做個概括。

  代碼定義了四個對象,分別是garden(場景)、cube(正方體)、face(面)、ball(點),從屬關系如下:

  而魔方demo中,一個場景有27個正方體,每個正方體有6個面和8個點,每個面有4個點;每幀的渲染中先根據cube的體心排序(前文中說了這不是最佳方案),然后根據排序后的結果繪制每個cube的可見面。歸根結底,每幀的渲染就是對每個正方體8個點的渲染!

  有了這部分經驗,繪制一個無交互的魔方demo就可以手到擒來了 ->?無交互魔方

  無交互魔方demo和前面的多正方體demo最大的區別就是面的顏色,其實很簡單,在初始化的時候可以傳入一個color數組,比如這樣:

// 紅 橙 藍 綠 黃 白 // 0 1 2 3 4 5 window.colors = ['#ff0000', '#ff6600', '#0000ff', '#00ff00', '#ffff00', '#ffffff']; var color = [// 第一排[0, 5, 5, 3, 5, 5],[0, 5, 5, 5, 5, 5],[0, 2, 5, 5, 5, 5],[0, 5, 5, 3, 5, 5],[0, 5, 5, 5, 5, 5],[0, 2, 5, 5, 5, 5],[0, 5, 5, 3, 5, 4],[0, 5, 5, 5, 5, 4],[0, 2, 5, 5, 5, 4],// 第二排[5, 5, 5, 3, 5, 5],[5, 5, 5, 5, 5, 5],[5, 2, 5, 5, 5, 5],[5, 5, 5, 3, 5, 5],[5, 5, 5, 5, 5, 5],[5, 2, 5, 5, 5, 5],[5, 5, 5, 3, 5, 4],[5, 5, 5, 5, 5, 4],[5, 2, 5, 5, 5, 4],// 第三排[5, 5, 1, 3, 5, 5],[5, 5, 1, 5, 5, 5],[5, 2, 1, 5, 5, 5],[5, 5, 1, 3, 5, 5],[5, 5, 1, 5, 5, 5],[5, 2, 1, 5, 5, 5],[5, 5, 1, 3, 5, 4],[5, 5, 1, 5, 5, 4],[5, 2, 1, 5, 5, 4], ];

  初始化每個cube時多傳入一個參數,這樣就能實現你要的顏色了。

?

問題的關鍵

  如何交互,如何實現玩家想要的3*3模塊的旋轉才是問題的關鍵。

  我最終想到的是像demo一樣選擇兩個相鄰的正方體,然后一個監聽mousedown事件,另一個監聽mouseup事件,表面看上去,兩個有順序的正方體似乎能確定了那個想要旋轉的3*3模塊了(其實不然)。而在尋找3*3模塊之前,我們首先要解決的是如何確定這兩個正方體。

?

  • 兩個正方體的確定

  因為我們在畫布上展現出來的圖案其實都是h5的原生api繪上去的,并不像dom一樣能寫個事件監聽。如何得到這兩個正方體,思來想去我覺得唯一方法就是點的判斷。

  遍歷27個正方體在二維空間的6*27個面,判斷鼠標點擊是否在面內。這里可以把場景內的cubes倒排,因為cubes在每幀中都要根據體心重新排序,越后面的越先繪制,而鼠標點擊的cubes按多數情況下應該是離視點近的,所以可以從后到前遍歷,這樣可以加快尋找速度;而遍歷一個正方體6個面時,不可見面也不用判斷。這個問題的最后就是二維系上一個點在一個凸四邊形內的判斷。具體可以參考 ->?判斷一個點是否在給定的凸四邊形內

  我用了博文的第一種方法。

  由于數學能力的欠缺,一開始我把叉積當做點積了,debug了良久才發現。

  鼠標監聽:

document.addEventListener('mousedown', function(event){window.rotateArray = [];var obj = canvas.getBoundingClientRect();// 鼠標點擊的地方在canvas上的(x,y)坐標var x = event.clientX - obj.left;var y = event.clientY - obj.top;var v = new Vector2(x, y)var ans = getCubeIndex(v);if(ans)window.rotateArray.push(ans); });

  getCubeIndex函數就是遍歷27個cube和每個cube中6個面的一個兩層循環。

  點在凸四邊形的判斷:

// 判斷點m是否在順時針方向的a,b,c,d四個點組成的凸四邊形內 function isPointIn(a, b, c, d, m) {var f = b.minus(a).dot(m.minus(a));if(f <= 0) return false;var f = c.minus(b).dot(m.minus(b));if(f <= 0) return false;var f = d.minus(c).dot(m.minus(c));if(f <= 0) return false;var f = a.minus(d).dot(m.minus(d));if(f <= 0) return false;return true; }

  至此,2個被點擊的正方體在27個cube中的位置已經找出。

?

  • 3*3模塊的確定

  接著需要尋找由兩個正方體確定的3*3模塊。

  我們知道,玩魔方每次旋轉的肯定是個3*3的模塊,而這樣的模塊在一個魔方中有3*3=9個。而2個相鄰的正方體能不能確定唯一的3*3模塊?答案是不能的,如下圖:

  上圖1和2兩個正方體確定了圖示的兩個3*3模塊。其實如果兩個正方體的位置是在魔方的棱上,那么就能確定兩個。我們暫時不管它,一個也好,兩個也罷,先把它找出來。

  怎么找?最開始我想到的是維護一個三維數組,初始化給每個cube一個index值,值和三維數組值相對應,每次魔方旋轉時同時改變三維數組的值,這樣找到這個3*3的模塊就是遍歷三維數組的三個維度,找到任一維度的3*3=9個正方體中如果有包含點擊得到的兩個正方體,則為一組解。后來被我放棄了,三維數組的維護實在是太麻煩了。

  最后我用深度搜索來解,尋找一條長度為8的閉合回路。已經確定了前兩個值,因為這條閉合回路不會經過魔方最中心的那個正方體,所以每個點的下一個點的取值最多只有4種情況,最大復雜度也就O(4^6),完全在可控范圍之內。而且搜過的點標記掉不用繼續搜索,答案幾乎秒出。

  深度搜索如下:

function dfs(index) {var cubes = garden.cubes;if(index === 8) {var dis = cubes[window.rotateArray[0]].pos3.getDistance(cubes[window.rotateArray[7]].pos3);if(Math.abs(dis - 60) > 10) return;// 判斷8個點在一個平面var cubes = garden.cubes;var a = cubes[window.rotateArray[1]].pos3.minus(cubes[window.rotateArray[0]].pos3);var b = cubes[window.rotateArray[7]].pos3.minus(cubes[window.rotateArray[6]].pos3);// 找一個面的法向量var v = undefined;for(var i = 0; i < 27; i++) {var c = cubes[i].pos3;if(a.isPerpTo(c) && b.isPerpTo(c)) {v = c;break;}if(i === 26 && v === undefined) return;}// 判斷任意相鄰向量是否垂直法向量for(var i = 0; i < 7; i++) {var a = cubes[window.rotateArray[i]].pos3.minus(cubes[window.rotateArray[i + 1]].pos3);if(!a.isPerpTo(v)) return;}// 如果是最前面的面,returnvar zz = 0;for(var i = 0; i < 8; i++) zz += cubes[window.rotateArray[i]].pos3.z;zz /= 8;if(zz < -40) return;// 如果是俄羅斯方塊那種類型var vv = new Vector3();for(var i = 0; i < 8; i+=2) {vv.x += cubes[window.rotateArray[i]].pos3.x;vv.y += cubes[window.rotateArray[i]].pos3.y;vv.z += cubes[window.rotateArray[i]].pos3.z;}vv.x /= 4; vv.y /= 4;vv.z /= 4;var flag = false;for(var i = 0; i < 27; i++) {var vvv = cubes[i].pos3if(vv.getDistance(vvv) > 5) continue;flag = true;break;}if(!flag) return;for(var i = 0; i < 8; i++) {window.isFindRoute = true;window.rotateFinalArray[i] = window.rotateArray[i];}return;}if(window.isFindRoute) return;for(var i = 0; i < 27; i++) {if(window.hash[i]) continue;// 魔方中點不找,待會應該判斷魔方中點,不應該直接賦值if(cubes[i].pos3.isEqual(new Vector3())) continue;var front = window.rotateArray[index - 1];var dis = cubes[front].pos3.getDistance(cubes[i].pos3);if(Math.abs(dis - 60) > 10) continue;window.rotateArray[index] = i;window.hash[i] = true;dfs(index + 1);window.hash[i] = false;} }

  我是先找一條長度為8的閉合回路,找到后再進行判斷:(其實邊找邊判斷效率會更高)

  1、判斷8個點是否在同一個面上。 可以任選兩條不平行的向量做分別垂直于這兩條向量的法向量,如果這8個點成面,則該法向量垂直于平面內兩點組成的任意向量。

  2、如果是最前面的面,則return。 這個判斷有點坑爹,先看下圖:

  如果操作的是1和2兩個正方體,得到兩條回路如圖。我們想要的應該是上面那個3*3模塊的操作,剔除的是前面一塊,這里我根據平均的z值進行判斷,如果z太小(距離視點太近,認為是前面一塊),則剔除。其實這是不準確的,所以demo有時會出錯,而這點也是操作正方體體心無法解決的,如果要解決,程序復雜度可能要上升一個級別,要精確到對面的判斷。所以這里采用了模糊判斷。這也是最前面說的有兩條回路如何選擇的方法。

  3、找到了同一平面的閉合回路,但是不符合要求,如下:

  因為閉合回路所組成的3*3模塊的中心肯定是魔方上某正方體的體心,這里就根據此近似判斷。

  至此,我們得到了需要翻轉的3*3=9個正方體。

?

  • 旋轉軸的確定

  得到了需要翻轉的正方體,最后只需要得到翻轉軸即可。

  我們已經得到繞x軸和y軸旋轉后的坐標變化,那么是否有繞任意軸的坐標變化公式呢?luckily,答案是有的 ->?三維空間里一個點繞矢量旋轉后的新的點的坐標

  

  這樣就好辦了,我們可以獲取需要翻轉面的法向量,然后單位化即可。而這條法向量其實肯定經過27個正方體中某個的體心,遍歷即可。但是一個面的法向量有兩條,還好我們獲取的閉合回路是有方向的,因為翻轉的角度肯定是90度,我們可以知道3*3模塊中某個正方體翻轉90度后的實際位置,其實就是閉合回路往前兩個的正方體的位置;我們獲取的任一法向量,將值代入函數中進行計算,選擇某個正方體,如果該正方體繞該法向量旋轉90度后得到的值就是正確的位置,即這條法向量為正解。(實際上另一條需要旋轉270度)

  于是我們寫成一個rotateP函數:

rotateP: function() {if(this.cube.isRotate) {this.cube.index++;// 一個點達到60改變isRotate值?應該8個點全部達到吧if(this.cube.index === 480) {this.cube.isRotate = false;this.cube.index = 0;}var c = Math.cos(this.cube.garden.angleP);var s = Math.sin(this.cube.garden.angleP);// (x,y,z)為經過原點的單位向量var x = this.cube.rotateVector.x;var y = this.cube.rotateVector.y;var z = this.cube.rotateVector.z;var new_x = (x * x * (1 - c) + c) * this.pos3.x + (x * y * (1 - c) - z * s) * this.pos3.y + (x * z * (1 - c) + y * s) * this.pos3.z;var new_y = (y * x * (1 - c) + z * s) * this.pos3.x + (y * y * (1 - c) + c) * this.pos3.y + (y * z * (1 - c) - x * s) * this.pos3.z;var new_z = (x * z * (1 - c) - y * s) * this.pos3.x + (y * z * (1 - c) + x * s) * this.pos3.y + (z * z * (1 - c) + c) * this.pos3.z;this.pos3.reset(new_x, new_y, new_z);}

  這樣在每幀的渲染中,需要旋轉的cube的點的坐標的位置也會隨著rotateP函數改變,于是出現旋轉效果。

?

總結

  完整代碼:

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title> 3d魔方 </title> 6 <script> 7 window.onload = function() { 8 var canvas = document.getElementById('canvas'); 9 var ctx = canvas.getContext('2d'); 10 var garden = new Garden(canvas); 11 window.garden = garden; 12 13 // 0紅 1橙 3藍 4綠 5黃 6白 // face面繪制順序 前 右 后 左 上 下 14 window.colors = ['#ff0000', '#ff6600', '#0000ff', '#00ff00', '#ffff00', '#ffffff']; 15 16 // 記錄鼠標操作的兩個cube的index值 17 window.rotateArray = []; 18 window.isStill = false; 19 20 // 設置二維視角原點(一般為畫布中心) 21 garden.setBasePoint(500, 250); 22 23 var color = [ 24 // 第一排 25 [2, 5, 5, 5, 5, 5], 26 [0, 5, 5, 5, 0, 5], 27 [2, 0, 5, 5, 4, 5], 28 [0, 5, 5, 4, 5, 5], 29 [4, 5, 5, 5, 5, 5], 30 [5, 3, 5, 5, 5, 5], 31 [3, 5, 5, 5, 5, 0], 32 [0, 5, 5, 5, 5, 3], 33 [1, 4, 5, 5, 5, 2], 34 35 // 第二排 36 [5, 5, 3, 5, 1, 5], 37 [5, 5, 3, 5, 2, 5], 38 [5, 5, 3, 5, 0, 5], 39 [5, 5, 5, 0, 5, 5], 40 [5, 5, 5, 5, 5, 5], 41 [5, 1, 1, 5, 5, 5], 42 [5, 5, 0, 3, 5, 4], 43 [5, 5, 5, 5, 5, 3], 44 [5, 1, 3, 5, 5, 3], 45 46 // 第三排 47 [5, 5, 3, 2, 4, 5], 48 [5, 5, 1, 5, 4, 5], 49 [5, 2, 0, 5, 4, 5], 50 [5, 5, 1, 3, 5, 5], 51 [5, 5, 1, 5, 5, 5], 52 [5, 2, 3, 5, 5, 5], 53 [5, 5, 1, 4, 5, 5], 54 [5, 5, 1, 5, 5, 2], 55 [5, 2, 5, 5, 5, 1], 56 ]; 57 58 var r = 60; 59 var num = 0; 60 var a = [-r, 0, r]; 61 62 // 初始化 63 for(var l = 0; l < 3; l++) // z軸 64 for(var j = 0; j < 3; j++) // y軸 65 for(var i = 0; i < 3; i++) { // x軸 66 var v = new Vector3(a[i], a[j], a[l]); 67 garden.createCube(v, r / 2 - 2, color[num++]); // 初始化cube的index值 68 } 69 70 garden.setListener(); 71 addListener(); 72 73 // 渲染 74 setInterval(function() {garden.render();}, 1000 / 60); 75 }; 76 77 function addListener() { 78 document.addEventListener('mousedown', function(event){ 79 window.rotateArray = []; 80 var obj = canvas.getBoundingClientRect(); 81 // 鼠標點擊的地方在canvas上的(x,y)坐標 82 var x = event.clientX - obj.left; 83 var y = event.clientY - obj.top; 84 var v = new Vector2(x, y) 85 var ans = getCubeIndex(v); 86 if(ans) 87 window.rotateArray.push(ans); 88 }); 89 90 document.addEventListener('mouseup', function(event){ 91 var obj = canvas.getBoundingClientRect(); 92 // 鼠標點擊的地方在canvas上的(x,y)坐標 93 var x = event.clientX - obj.left; 94 var y = event.clientY - obj.top; 95 var v = new Vector2(x, y) 96 var ans = getCubeIndex(v); 97 if(ans) 98 window.rotateArray.push(ans); 99 100 window.isFindRoute = false; 101 window.hash = []; 102 window.hash[window.rotateArray[0]] = window.hash[window.rotateArray[1]] = true; 103 104 // 保存回路答案 105 window.rotateFinalArray = []; 106 dfs(2); 107 108 // 計算中間點在cube數組中的位置 109 var index = getMiddleCube(); 110 rotateFinalArray.push(index); 111 112 // 必定是體心指向某個cube中心的一條向量,返回該cube的index 113 var index2 = getRotateVector(); 114 115 var cubes = garden.cubes; 116 for(var i = 0; i < rotateFinalArray.length; i++) { 117 cubes[rotateFinalArray[i]].isRotate = true; 118 cubes[rotateFinalArray[i]].rotateVector = cubes[index2].pos3.normalize(); 119 } 120 }); 121 122 document.onkeydown = function(e) { 123 if(e.keyCode === 32) { 124 window.isStill = !window.isStill; 125 } 126 } 127 } 128 129 function dfs(index) { 130 var cubes = garden.cubes; 131 if(index === 8) { 132 var dis = cubes[window.rotateArray[0]].pos3.getDistance(cubes[window.rotateArray[7]].pos3); 133 if(Math.abs(dis - 60) > 10) 134 return; 135 136 // 判斷同一平面 137 var cubes = garden.cubes; 138 var a = cubes[window.rotateArray[1]].pos3.minus(cubes[window.rotateArray[0]].pos3); 139 var b = cubes[window.rotateArray[7]].pos3.minus(cubes[window.rotateArray[6]].pos3); 140 141 // 找一個面的法向量,如果8點成面,那么肯定有兩條符合的向量 142 var v = undefined; 143 for(var i = 0; i < 27; i++) { 144 var c = cubes[i].pos3; 145 if(a.isPerpTo(c) && b.isPerpTo(c)) { 146 v = c; 147 break; 148 } 149 if(i === 26 && v === undefined) return; 150 } 151 152 // 判斷任意相鄰向量是否垂直法向量 153 for(var i = 0; i < 7; i++) { 154 var a = cubes[window.rotateArray[i]].pos3.minus(cubes[window.rotateArray[i + 1]].pos3); 155 if(!a.isPerpTo(v)) return; 156 } 157 158 // 如果是最前面的面,return 159 var zz = 0; 160 for(var i = 0; i < 8; i++) 161 zz += cubes[window.rotateArray[i]].pos3.z; 162 zz /= 8; 163 if(zz < -40) return; 164 165 // 如果是俄羅斯方塊那種類型 166 var vv = new Vector3(); 167 for(var i = 0; i < 8; i+=2) { 168 vv.x += cubes[window.rotateArray[i]].pos3.x; 169 vv.y += cubes[window.rotateArray[i]].pos3.y; 170 vv.z += cubes[window.rotateArray[i]].pos3.z; 171 } 172 vv.x /= 4; 173 vv.y /= 4; 174 vv.z /= 4; 175 var flag = false; 176 for(var i = 0; i < 27; i++) { 177 var vvv = cubes[i].pos3 178 if(vv.getDistance(vvv) > 5) continue; 179 flag = true; 180 break; 181 } 182 if(!flag) return; 183 184 for(var i = 0; i < 8; i++) { 185 window.isFindRoute = true; 186 window.rotateFinalArray[i] = window.rotateArray[i]; 187 } 188 return; 189 } 190 191 if(window.isFindRoute) return; 192 193 for(var i = 0; i < 27; i++) { 194 if(window.hash[i]) continue; 195 // 魔方中點不找 196 if(cubes[i].pos3.isEqual(new Vector3())) continue; 197 var front = window.rotateArray[index - 1]; 198 var dis = cubes[front].pos3.getDistance(cubes[i].pos3); 199 if(Math.abs(dis - 60) > 10) continue; 200 window.rotateArray[index] = i; 201 window.hash[i] = true; 202 dfs(index + 1); 203 window.hash[i] = false; 204 } 205 } 206 207 // 不在同一條直線的兩個向量才能確定一個平面 208 function getRotateVector() { 209 // 垂直于rotate面的任意兩條向量 210 var cubes = garden.cubes; 211 var a = cubes[window.rotateFinalArray[1]].pos3.minus(cubes[window.rotateFinalArray[0]].pos3); 212 var b = cubes[window.rotateFinalArray[7]].pos3.minus(cubes[window.rotateFinalArray[6]].pos3); 213 214 // 這里應該有兩個 215 for(var i = 0; i < 27; i++) { 216 var c = cubes[i].pos3; 217 // 因為有兩個向量,所以通過istrue函數判斷是否是答案所要的向量 218 if(a.isPerpTo(c) && b.isPerpTo(c) && isTrue(i)) 219 return i; 220 } 221 } 222 223 // 判斷window.rotateFinalArray里的第0個cube經過90度旋轉是否能到達第2個cube的位置,判斷體心即可 224 function isTrue(index) { 225 var cubes = garden.cubes; 226 // 旋轉向量 227 var v = cubes[index].pos3; 228 // 單位化 229 v = v.normalize(); 230 231 var a = cubes[window.rotateFinalArray[0]]; 232 var c = Math.cos(Math.PI / 2); 233 var s = Math.sin(Math.PI / 2); 234 // (x,y,z)為經過原點的單位向量 235 var x = v.x; 236 var y = v.y; 237 var z = v.z; 238 var new_x = (x * x * (1 - c)+c) * a.pos3.x + (x*y*(1-c)-z*s) * a.pos3.y + (x*z*(1-c)+y*s) * a.pos3.z; 239 var new_y = (y*x*(1-c)+z*s) * a.pos3.x + (y*y*(1-c)+c) * a.pos3.y + (y*z*(1-c)-x*s) * a.pos3.z; 240 var new_z = (x*z*(1-c)-y*s) * a.pos3.x + (y*z*(1-c)+x*s) * a.pos3.y + (z*z*(1-c)+c) * a.pos3.z; 241 var b = new Vector3(new_x, new_y, new_z); 242 243 // 判斷旋轉后所得的b向量是否和rotateArray[2]相同 244 var f = b.isEqual(cubes[window.rotateFinalArray[2]].pos3); 245 return f; 246 } 247 248 function getMiddleCube() { 249 var v = new Vector3(); 250 var cubes = garden.cubes; 251 for(var i = 0; i < 8; i += 2) { 252 v.x += cubes[window.rotateFinalArray[i]].pos3.x; 253 v.y += cubes[window.rotateFinalArray[i]].pos3.y; 254 v.z += cubes[window.rotateFinalArray[i]].pos3.z; 255 } 256 257 v.x /= 4; 258 v.y /= 4; 259 v.z /= 4; 260 for(var i = 0; i < 27; i++) { 261 if(v.isEqual(cubes[i].pos3)) 262 return i; 263 } 264 } 265 266 function getCubeIndex(v) { 267 var length = garden.cubes.length; 268 var cubes = garden.cubes; 269 // 遍歷cube,因為經過排序前面的cube先繪,所以倒著判斷 270 var num = 0; 271 for(var i = length -1 ; i >= 0; i--) { 272 // 遍歷六個面 273 for(var j = 5; j>=0; j--) { 274 num ++; 275 var f = cubes[i].f[j]; 276 if(f.angle < 0) continue; // 夾角大于90不可見 277 // 可見則判斷 278 var isFound = isPointIn(f.a.pos2, f.d.pos2, f.c.pos2, f.b.pos2, v); 279 if(isFound) { // 找到了 280 // 越大越晚繪,所以越前面 281 return i; 282 } 283 } 284 } 285 } 286 287 // 判斷點m是否在順時針方向的a,b,c,d四個點組成的凸四邊形內 288 function isPointIn(a, b, c, d, m) { 289 var f = b.minus(a).dot(m.minus(a)); 290 if(f <= 0) return false; 291 292 var f = c.minus(b).dot(m.minus(b)); 293 if(f <= 0) return false; 294 295 var f = d.minus(c).dot(m.minus(c)); 296 if(f <= 0) return false; 297 298 var f = a.minus(d).dot(m.minus(d)); 299 if(f <= 0) return false; 300 return true; 301 } 302 303 // Garden類 304 function Garden(canvas) { 305 this.canvas = canvas; 306 this.ctx = this.canvas.getContext('2d'); 307 308 // 三維系在二維上的原點 309 this.vpx = undefined; 310 this.vpy = undefined; 311 this.cubes = []; 312 this.angleY = Math.PI / 180 * 0; 313 this.angleX = Math.PI / 180 * 0; 314 this.angleP = Math.PI / 180 * 1.5; 315 } 316 317 Garden.prototype = { 318 setBasePoint: function(x, y) { 319 this.vpx = x; 320 this.vpy = y; 321 }, 322 323 createCube: function(v, r, color, index) { 324 this.cubes.push(new Cube(this, v, r, color)); 325 }, 326 327 render: function() { 328 this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 329 this.cubes.sort(function (a, b) { 330 if(b.pos3.z !== a.pos3.z) 331 return b.pos3.z - a.pos3.z; 332 else if(b.pos3.x !== a.pos3.x) { 333 if(b.pos3.x >= 0 && a.pos3.x >= 0 || b.pos3.x <= 0 && a.pos3.x <= 0) 334 return Math.abs(b.pos3.x) - Math.abs(a.pos3.x); 335 else return b.pos3.x - a.pos3.x; 336 } else { 337 if(b.pos3.y >= 0 && a.pos3.y >= 0 || b.pos3.y <= 0 && a.pos3.y <= 0) 338 return Math.abs(b.pos3.y) - Math.abs(a.pos3.y); 339 else return b.pos3.y - a.pos3.y; 340 } 341 }); 342 343 for(var i = 0; i < this.cubes.length; i++) { 344 this.cubes[i].render(); 345 } 346 }, 347 348 setListener: function() { 349 var that = this; 350 document.addEventListener('mousemove', function(event){ 351 var obj = canvas.getBoundingClientRect(); 352 var x = event.clientX - obj.top - that.vpx; 353 var y = event.clientY - obj.left - that.vpy; 354 that.angleY = -x * 0.0001; 355 that.angleX = y * 0.0001; 356 }); 357 } 358 }; 359 360 // Cube類 361 function Cube(garden, v, r, color) { 362 this.garden = garden; 363 364 // 正方體中心和半徑 365 this.pos3 = v; 366 this.r = r; 367 368 // this.angleX = Math.PI / 180 * 1; 369 // this.angleY = Math.PI / 180 * 1; 370 371 // cube的8個點 372 this.p = []; 373 374 // cube的6個面 375 this.f = []; 376 377 // 6個面的顏色集 378 this.colors = color; // color數組 379 380 // 是否在玩家需要翻轉的3*3矩形中 381 this.isRotate = false; 382 383 // rotateP函數中圍繞的軸的單位向量 384 this.rotateVector = new Vector3(1, 0, 0); 385 386 // 已翻轉的次數,每次翻轉1.5度,需要翻轉60次 387 this.index = 0; 388 389 this.init(); 390 } 391 392 Cube.prototype = { 393 init: function() { 394 // 正方體的每個頂點都是一個ball類實現 395 this.p[0] = new Ball(this, this.pos3.x - this.r, this.pos3.y - this.r, this.pos3.z - this.r); 396 this.p[1] = new Ball(this, this.pos3.x - this.r, this.pos3.y + this.r, this.pos3.z - this.r); 397 this.p[2] = new Ball(this, this.pos3.x + this.r, this.pos3.y + this.r, this.pos3.z - this.r); 398 this.p[3] = new Ball(this, this.pos3.x + this.r, this.pos3.y - this.r, this.pos3.z - this.r); 399 this.p[4] = new Ball(this, this.pos3.x - this.r, this.pos3.y - this.r, this.pos3.z + this.r); 400 this.p[5] = new Ball(this, this.pos3.x - this.r, this.pos3.y + this.r, this.pos3.z + this.r); 401 this.p[6] = new Ball(this, this.pos3.x + this.r, this.pos3.y + this.r, this.pos3.z + this.r); 402 this.p[7] = new Ball(this, this.pos3.x + this.r, this.pos3.y - this.r, this.pos3.z + this.r); 403 404 // 正方體6個面 405 this.f[0] = new Face(this, this.p[0], this.p[1], this.p[2], this.p[3], this.colors[0]); 406 this.f[1] = new Face(this, this.p[3], this.p[2], this.p[6], this.p[7], this.colors[1]); 407 this.f[2] = new Face(this, this.p[6], this.p[5], this.p[4], this.p[7], this.colors[2]); 408 this.f[3] = new Face(this, this.p[4], this.p[5], this.p[1], this.p[0], this.colors[3]); 409 this.f[4] = new Face(this, this.p[0], this.p[3], this.p[7], this.p[4], this.colors[4]); 410 this.f[5] = new Face(this, this.p[5], this.p[6], this.p[2], this.p[1], this.colors[5]); 411 }, 412 413 render: function() { 414 for(var i = 0; i < 8; i++) 415 this.p[i].render(); 416 417 // 八個點的坐標改變完后,改變cube體心坐標,為下一幀cube的排序作準備 418 this.changeCoordinate(); 419 420 for(var i = 0; i < 6; i++) 421 this.f[i].angle = this.f[i].getAngle(); 422 423 // 從小到大排 424 // 不排序會閃 425 this.f.sort(function (a, b) { 426 return a.angle > b.angle; 427 }); 428 429 for(var i = 0; i < 6; i++) { 430 // 夾角 < 90,繪制 431 if(this.f[i].angle > 0) 432 this.f[i].draw(); 433 } 434 }, 435 436 // cube體心坐標改變 437 changeCoordinate: function() { 438 this.pos3.x = this.pos3.y = this.pos3.z = 0; 439 for(var i = 0; i < 8; i++) { 440 this.pos3.x += this.p[i].pos3.x; 441 this.pos3.y += this.p[i].pos3.y; 442 this.pos3.z += this.p[i].pos3.z; 443 } 444 this.pos3.x /= 8; 445 this.pos3.y /= 8; 446 this.pos3.z /= 8; 447 } 448 }; 449 450 // Face類 451 // a, b, c, d為四個ball類 452 // color為數字 453 function Face(cube, a, b, c, d, color) { 454 this.cube = cube; 455 this.a = a; 456 this.b = b; 457 this.c = c; 458 this.d = d; 459 // this.color = '#' + ('00000' + parseInt(Math.random() * 0xffffff).toString(16)).slice(-6); 460 this.color = window.colors[color]; 461 // 面的法向量和面心到視點向量的夾角的cos值 462 this.angle = undefined; 463 } 464 465 Face.prototype = { 466 draw: function() { 467 var ctx = this.cube.garden.ctx; 468 ctx.beginPath(); 469 ctx.fillStyle = this.color; 470 ctx.moveTo(this.a.pos2.x, this.a.pos2.y); 471 ctx.lineTo(this.b.pos2.x, this.b.pos2.y); 472 ctx.lineTo(this.c.pos2.x, this.c.pos2.y); 473 ctx.lineTo(this.d.pos2.x, this.d.pos2.y); 474 ctx.closePath(); 475 ctx.fill(); 476 }, 477 478 // 獲取面的法向量和z軸夾角 479 getAngle: function() { 480 var x = (this.a.pos3.x + this.b.pos3.x + this.c.pos3.x + this.d.pos3.x) / 4 - this.cube.pos3.x; 481 var y = (this.a.pos3.y + this.b.pos3.y + this.c.pos3.y + this.d.pos3.y) / 4 - this.cube.pos3.y; 482 var z = (this.a.pos3.z + this.b.pos3.z + this.c.pos3.z + this.d.pos3.z) / 4 - this.cube.pos3.z; 483 // 面的法向量 484 var v = new Vector3(x, y, z); 485 486 // 視點設為(0,0,-500) 487 var x = 0 - (this.a.pos3.x + this.b.pos3.x + this.c.pos3.x + this.d.pos3.x) / 4; 488 var y = 0 - (this.a.pos3.y + this.b.pos3.y + this.c.pos3.y + this.d.pos3.y) / 4; 489 var z = - 500 - (this.a.pos3.z + this.b.pos3.z + this.c.pos3.z + this.d.pos3.z) / 4; 490 // 面心指向視點的向量 491 var v2 = new Vector3(x, y, z); 492 return v.dot(v2); 493 } 494 }; 495 496 // Ball類 497 function Ball(cube, x, y, z) { 498 this.cube = cube; 499 500 // 三維上坐標 501 this.pos3 = new Vector3(x, y, z) 502 503 // 二維上坐標 504 this.pos2 = new Vector2(); 505 } 506 507 Ball.prototype = { 508 // 繞y軸變化,得出新的x,z坐標 509 rotateY: function() { 510 if(window.isStill) return; 511 var cosy = Math.cos(this.cube.garden.angleY); 512 var siny = Math.sin(this.cube.garden.angleY); 513 var x1 = this.pos3.z * siny + this.pos3.x * cosy; 514 var z1 = this.pos3.z * cosy - this.pos3.x * siny; 515 this.pos3.reset(x1, this.pos3.y, z1); 516 }, 517 518 // 繞x軸變化,得出新的y,z坐標 519 rotateX: function() { 520 if(window.isStill) return; 521 var cosx = Math.cos(this.cube.garden.angleX); 522 var sinx = Math.sin(this.cube.garden.angleX); 523 var y1 = this.pos3.y * cosx - this.pos3.z * sinx; 524 var z1 = this.pos3.y * sinx + this.pos3.z * cosx; 525 this.pos3.reset(this.pos3.x, y1, z1); 526 }, 527 528 // 繞任意穿過原點的軸旋轉 529 rotateP: function() { 530 if(this.cube.isRotate) { 531 this.cube.index++; 532 // 8 * 60 533 if(this.cube.index === 480) { 534 this.cube.isRotate = false; 535 this.cube.index = 0; 536 } 537 538 var c = Math.cos(this.cube.garden.angleP); 539 var s = Math.sin(this.cube.garden.angleP); 540 // (x,y,z)為經過原點的單位向量 541 var x = this.cube.rotateVector.x; 542 var y = this.cube.rotateVector.y; 543 var z = this.cube.rotateVector.z; 544 var new_x = (x * x * (1 - c)+c) * this.pos3.x + (x*y*(1-c)-z*s) * this.pos3.y + (x*z*(1-c)+y*s) * this.pos3.z; 545 var new_y = (y*x*(1-c)+z*s) * this.pos3.x + (y*y*(1-c)+c) * this.pos3.y + (y*z*(1-c)-x*s) * this.pos3.z; 546 var new_z = (x*z*(1-c)-y*s) * this.pos3.x + (y*z*(1-c)+x*s) * this.pos3.y + (z*z*(1-c)+c) * this.pos3.z; 547 this.pos3.reset(new_x, new_y, new_z); 548 } 549 }, 550 551 getPositionInTwoDimensionalSystem: function(a) { 552 // focalLength 表示當前焦距,一般可設為一個常量 553 var focalLength = 300; 554 // 把z方向扁平化 555 var scale = focalLength / (focalLength + this.pos3.z); 556 this.pos2.x = this.cube.garden.vpx + this.pos3.x * scale; 557 this.pos2.y = this.cube.garden.vpy + this.pos3.y * scale; 558 }, 559 560 render: function() { 561 this.rotateX(); 562 this.rotateY(); 563 this.rotateP(); 564 this.getPositionInTwoDimensionalSystem(); 565 } 566 }; 567 568 // 向量 569 function Vector3(x, y, z) { 570 this.x = x || 0; 571 this.y = y || 0; 572 this.z = z || 0; 573 } 574 575 Vector3.prototype.reset = function(x, y, z) { 576 this.x = x; 577 this.y = y; 578 this.z = z; 579 } 580 581 // 向量點積,大于0為0~90度 582 Vector3.prototype.dot = function(v) { 583 return this.x * v.x + this.y * v.y + this.z * v.z; 584 }; 585 586 Vector3.prototype.length = function() { 587 return Math.sqrt(this.sqrLength()); 588 }; 589 590 Vector3.prototype.sqrLength = function() { 591 return this.x * this.x + this.y * this.y + this.z * this.z; 592 }; 593 594 Vector3.prototype.getDistance = function(v) { 595 var dis = (this.x - v.x) * (this.x - v.x) + (this.y - v.y) * (this.y - v.y) + (this.z - v.z) * (this.z - v.z); 596 return Math.sqrt(dis); 597 }; 598 599 // 近似判斷兩個向量是否是同一個 600 // 因為程序中基本上是判斷3*3*3的27個點是否是同一個,不同的點距離實在太遠 601 Vector3.prototype.isEqual = function(v) { 602 if(this.getDistance(v) < 30) return true; 603 else return false; 604 }; 605 606 // 標準化,單位長度為1 607 Vector3.prototype.normalize = function() { 608 var inv = 1 / this.length(); 609 return new Vector3(this.x * inv, this.y * inv, this.z * inv); 610 } 611 612 // 是否垂直,點積為0 613 Vector3.prototype.isPerpTo = function(v) { 614 var ans = this.dot(v); 615 if(Math.abs(ans) < 5) return true; 616 return false; 617 } 618 619 // 向量ab,即為b向量減去a向量返回的新向量 620 Vector3.prototype.minus = function(v) { 621 return new Vector3(this.x - v.x, this.y - v.y, this.z - v.z); 622 } 623 624 //// 625 // 二維向量 626 function Vector2(x, y) { 627 this.x = x || 0; 628 this.y = y || 0; 629 } 630 631 Vector2.prototype.reset = function(x, y) { 632 this.x = x; 633 this.y = y; 634 } 635 636 // 向量叉乘 637 Vector2.prototype.dot = function(v) { 638 return this.x * v.y - v.x * this.y; 639 }; 640 641 Vector2.prototype.minus = function(v) { 642 return new Vector2(this.x - v.x, this.y - v.y); 643 } 644 </script> 645 </head> 646 <body bgcolor='#000'> 647 <canvas id='canvas' width=1000 height=600 style='background-color:#000'> 648 This browser does not support html5. 649 </canvas> 650 </body> 651 </html> View Code

  其實這是蠻坑爹的體驗,h5原生api不適合做這種3d效果。但重要的是思考過程,不是結果。

  這只是一個demo,如果要做一個真正的魔方游戲,還需要以下幾點:

  • 魔方顏色初始化

  現在魔方的顏色我是隨意設置的,如果是個可玩的游戲,先得初始化復原后的魔方顏色,然后在游戲loading過程中隨機打亂。

  • 3*3模塊的精確判斷

  之前我也說了,3*3模塊的判斷是不精確的,更極端的例子見下圖:

?  此時我鼠標操作的是1和2區域,我想旋轉的是黑色箭頭圍成的模塊,但是實際程序中旋轉了黃色箭頭圍成的3*3,這就是因為我的模糊判斷。我無法確定到底是哪一個,因為我一直是根據體心來判斷的,如果要得到正確的結果,就要上升到正方體面的判斷,我不知道代碼量要增加幾倍。(所以demo操作時盡量操作離視點近的面)

  • 游戲結果判斷

  如果在確定3*3步驟使用維護三維數組的方法,這里判斷相對簡單;但是如果不,又得回到面的判斷上,同上,很復雜。

  • 其他

  增加loading、計時等等。

?

  如果有更好的方法或建議歡迎與我交流~

  

轉載于:https://www.cnblogs.com/lessfish/p/4267180.html

總結

以上是生活随笔為你收集整理的【自己给自己题目做】:如何在Canvas上实现魔方效果的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

高清av在线免费观看 | 久草精品免费 | 免费看片网页 | av大片免费看 | 天天操天天射天天 | 91亚洲在线 | 91亚洲视频在线观看 | 香蕉在线观看视频 | 91激情视频在线观看 | 日韩成人精品一区二区 | 欧美日韩二区三区 | 97超在线 | 狠狠色狠狠色合久久伊人 | 99久久日韩精品视频免费在线观看 | 玖玖999| 久久国产精品视频免费看 | 亚洲国产高清在线观看视频 | 国产高清精品在线观看 | 国产精品嫩草55av | 久久草精品 | 成人性生交大片免费看中文网站 | 欧洲av不卡 | 中文字幕五区 | 久艹视频在线观看 | 五月天婷亚洲天综合网精品偷 | 国产日韩中文在线 | 国产精品美女久久久久久免费 | 久久精品一二三区白丝高潮 | 亚洲国产精品视频 | 日本成人中文字幕在线观看 | 婷五月激情 | wwwww.国产| 最新日韩在线观看视频 | 久人人 | 狠狠五月天| 亚洲一级电影 | 五月婷婷丁香六月 | 日韩欧美国产视频 | 美女国产网站 | 日本公妇在线观看高清 | 国产精品手机在线观看 | 国产精品午夜av | 天天射天天舔天天干 | av官网| 色五月色开心色婷婷色丁香 | 婷婷五月色综合 | 欧美aa一级| 久久96国产精品久久99软件 | 国产91免费在线观看 | 国产精品一区二区三区四区在线观看 | www黄色| 99麻豆视频 | 国产又粗又猛又色又黄网站 | 国产91影院 | 91麻豆精品国产自产 | 国产视频一区精品 | 亚洲欧美国产视频 | 一区二区三区日韩在线观看 | 免费日韩一区 | 九九99靖品 | 亚洲免费av观看 | 久久亚洲电影 | 天堂在线一区二区 | 久久婷婷丁香 | 久久综合狠狠综合久久综合88 | 色婷婷啪啪免费在线电影观看 | 天天玩夜夜操 | 色网av| 日韩av网址在线 | 国产精品久久久久影院 | 久久国产精品一国产精品 | 免费观看一区二区 | 日韩成人免费电影 | 日韩99热 | 五月香视频在线观看 | 国产污视频在线观看 | 蜜桃视频在线视频 | 手机成人av| 久久精品婷婷 | 粉嫩一二三区 | 黄色片亚洲 | 久久不射电影网 | 欧美日韩不卡在线观看 | 欧美日韩中文国产一区发布 | 亚洲精品高清一区二区三区四区 | 日韩天天操 | 亚洲国产精品第一区二区 | 成人av中文字幕在线观看 | 精品字幕在线 | 99视频在线免费观看 | 免费看一级一片 | 少妇搡bbb| 97超碰人人澡 | 午夜18视频在线观看 | 中文乱幕日产无线码1区 | 一区二区欧美日韩 | 中文字幕在线一区观看 | www.xxxx变态.com| 在线91网 | 婷婷av色综合 | 欧美另类tv | 亚洲精品一区中文字幕乱码 | 免费激情在线电影 | 热99久久精品 | 久久麻豆视频 | 久久久这里有精品 | 在线成人免费电影 | 国产精品入口a级 | 91人人澡人人爽人人精品 | 婷婷新五月 | 日韩精品一区在线观看 | 免费观看v片在线观看 | 日韩精品网址 | 黄色大片免费网站 | 深爱激情亚洲 | 久久九九国产视频 | 久久综合久久综合久久 | 国产精品av久久久久久无 | 欧美大片aaa | 成人a免费 | 狠狠久久婷婷 | 中文字幕免费不卡视频 | 黄色毛片一级 | 天天要夜夜操 | 久久精品一二三 | 99久久久| 五月天久久狠狠 | 一级黄色片在线免费看 | 日韩网站一区二区 | 国产一区免费 | va视频在线 | 五月天综合激情网 | 一区二区精| 精品一区二三区 | 欧美精品在线免费 | 六月丁香在线观看 | 国产尤物在线 | 最新国产精品拍自在线播放 | 亚洲精品国产综合久久 | 狠狠操天天射 | 91人人澡| 91激情| 亚洲精品mv在线观看 | 欧美三级高清 | 四虎永久免费网站 | 中文字幕在线高清 | 国产精选在线观看 | 国产精品第52页 | 亚洲精品国产精品乱码在线观看 | 在线观看视频三级 | 日韩精品中文字幕一区二区 | 国产一区二区精品久久91 | 日韩亚洲国产中文字幕 | 日本aa在线| 亚洲1级片 | 久久久久久久久久久久电影 | 色偷偷网站视频 | 麻花豆传媒mv在线观看网站 | 亚洲一区天堂 | 成人在线视频在线观看 | 中文字幕日韩精品有码视频 | 网站在线观看你们懂的 | 国产麻豆精品一区 | 在线亚洲欧美视频 | 女人18毛片a级毛片一区二区 | 日韩激情影院 | 国产精品对白一区二区三区 | 亚洲成熟女人毛片在线 | 在线综合色 | 中文字幕色网站 | 日韩在线激情 | 欧洲精品久久久久毛片完整版 | 天天综合网 天天 | 九九热有精品 | 国产精品18p | 国产成人一级 | 午夜123| 亚洲精品影院在线观看 | 久草在线视频首页 | 五月激情在线 | 日日操天天操夜夜操 | 91伊人影院 | 成人福利在线 | 亚洲日韩精品欧美一区二区 | 久久国产视频网站 | 精品国产三级a∨在线欧美 免费一级片在线观看 | 日韩中文幕| 欧美乱大交 | 少妇性xxx | 91丨九色丨国产丨porny精品 | 国产精品久久在线观看 | 亚洲精品乱码久久久久久9色 | 91在线区| 亚洲成av片人久久久 | 亚洲永久精品在线 | 国产小视频免费观看 | 黄色大全在线观看 | 国产精品美女视频网站 | 国产又粗又猛又爽又黄的视频免费 | 成人av免费电影 | 日韩视频区 | 日韩在线视频观看 | 久久久久久久看片 | 国产精品久久影院 | 综合久久精品 | 99资源网| 中文字幕乱在线伦视频中文字幕乱码在线 | 国产成人av电影在线 | 欧美精品一区二区免费 | 波多野结衣在线观看一区二区三区 | 女女av在线 | 中字幕视频在线永久在线观看免费 | 久草综合视频 | 超碰免费97 | 又爽又黄又无遮挡网站动态图 | 最新国产在线视频 | 久久久99精品免费观看app | 九九精品视频在线看 | 中文字幕一二 | 亚洲欧美怡红院 | 黄色一二级片 | 国产精品毛片一区视频播不卡 | 97人人澡人人爽人人模亚洲 | 蜜臀av性久久久久av蜜臀三区 | 爱干视频 | 亚洲国产精品va在线看黑人动漫 | 国产中文字幕视频 | 麻豆免费在线视频 | 99re6热在线精品视频 | 中文字幕你懂的 | 黄a网站| 日韩一区二区三区高清免费看看 | 亚洲精品玖玖玖av在线看 | 中文在线www | 日本mv大片欧洲mv大片 | 久久久国产精品电影 | 九九免费在线观看 | 精品国模一区二区三区 | 天天草天天干天天射 | 伊人久操 | 国产亚洲成av人片在线观看桃 | 九九久久电影 | 日韩美在线观看 | 亚洲精品乱码久久久久久蜜桃欧美 | 久久久久精 | 在线精品视频免费播放 | 精品美女在线视频 | 菠萝菠萝在线精品视频 | 国产精品99久久久久久久久久久久 | 狠狠88综合久久久久综合网 | 久久成年视频 | av怡红院 | 亚洲人成在线电影 | 久久成人18免费网站 | 亚洲不卡123 | 在线观看日韩免费视频 | 亚洲涩综合 | 亚洲伦理精品 | 一区二区视| 香蕉影院在线观看 | 一级片色播影院 | 一本到视频在线观看 | 国产在线免费 | 久久人人爽爽人人爽人人片av | 久久九九久久精品 | 欧美韩国日本在线观看 | 最近中文字幕mv免费高清在线 | 97精品一区| 久久精品99久久久久久 | 国产资源在线观看 | 六月天色婷婷 | 日韩av不卡在线观看 | 高清国产午夜精品久久久久久 | 91在线操| 久久久久久久久久久国产精品 | 999久久久久久久久久久 | 蜜桃麻豆www久久囤产精品 | 手机在线小视频 | 激情欧美一区二区三区免费看 | 成 人 黄 色 片 在线播放 | 日韩精品一区二区三区免费视频观看 | 国产又粗又猛又色 | www.av小说 | 久草观看视频 | 国产免费成人 | 韩国视频一区二区三区 | 热久久在线视频 | 国产成人av综合色 | 国产精品久久久久一区 | 日韩在线播放欧美字幕 | 亚洲视频网站在线观看 | 日韩午夜高清 | 国产精品永久在线 | 中文字幕在线不卡国产视频 | www.色午夜.com | 久久久久久久电影 | 精品国产午夜 | 激情五月婷婷综合 | 精品视频免费在线 | 国产精品久久久久国产a级 激情综合中文娱乐网 | 久久综合射 | 在线精品亚洲一区二区 | 亚洲理论在线观看 | 久久久久久久看片 | 在线观看国产永久免费视频 | 成人动漫视频在线 | 成年人在线免费视频观看 | 亚洲撸撸| 免费黄色av| 五月开心婷婷网 | 国产精品福利小视频 | 国产一级91| 91在线在线观看 | 日韩高清一二区 | 亚洲美女视频在线 | 欧美精品久久久久a | 精品亚洲视频在线观看 | 丁香婷婷久久久综合精品国产 | 97香蕉久久超级碰碰高清版 | 欧美极品久久 | 高清av在线| 国产黄色电影 | 亚洲女在线 | 国产精品综合久久久久 | 黄色小网站在线 | 日韩一二三 | 午夜男人影院 | a成人v在线 | 国产精品精品国产婷婷这里av | 色亚洲激情 | 深夜免费福利 | 国产精品免费小视频 | 欧美人人 | 国产99一区| 色先锋资源网 | 日韩视频一二三区 | 国产一区在线视频播放 | 精品久久网 | 激情一区二区三区欧美 | 久久视频免费在线观看 | 免费看国产黄色 | 久久激情五月婷婷 | 日韩免费电影网站 | 久久精品www人人爽人人 | 精品在线视频观看 | 成人午夜在线电影 | 一区二区三区在线影院 | 综合网伊人 | 欧美福利视频一区 | 国产黄视频在线观看 | 欧美视频日韩 | 国产又粗又长的视频 | 日本乱视频 | www.久久免费 | 美女免费网站 | 免费三级在线 | 偷拍精偷拍精品欧洲亚洲网站 | 欧美黄污视频 | 在线最新av | 久久国产a | 综合色婷婷 | 五月天婷婷综合 | 好看av在线 | 黄色日视频 | 国产96精品 | 国产精品免费观看国产网曝瓜 | 久久国产a | 91黄色视屏 | 久久国产露脸精品国产 | 最近中文字幕完整高清 | 久久精品成人 | 国内精品一区二区 | 日韩精品第一区 | 狠狠干综合网 | 在线黄色观看 | 国产精品视频久久 | av中文在线播放 | 麻豆精品视频在线 | 久久久久国产免费免费 | 色国产精品一区在线观看 | www.亚洲精品 | 日本一区二区三区视频在线播放 | 国产成人高清av | 国产精品久久久久三级 | 国产蜜臀av| 国内精品视频一区二区三区八戒 | 亚洲区视频在线 | 久久视频在线免费观看 | 中文字幕av最新 | 国产女v资源在线观看 | 91人人爱 | 国产尤物在线视频 | 字幕网资源站中文字幕 | 九九免费精品 | 91精品国产欧美一区二区成人 | 一区二区三区韩国免费中文网站 | 日韩欧美精品在线观看 | 久久97精品| 欧美激情综合五月色丁香小说 | 色一级片 | 日韩在线视频网站 | 国产精品18久久久久久vr | 欧美黄色特级片 | 精品一区中文字幕 | 精品久久久99| 天天色天天综合 | 国产精品久久久久一区二区三区 | 久久综合精品国产一区二区三区 | 在线观看黄色大片 | 中文字幕在线国产精品 | 中文在线中文a | 视频91 | 亚洲成a人片在线观看网站口工 | 久久无码精品一区二区三区 | 国产在线视频在线观看 | 国产精品福利在线观看 | 最新av免费在线观看 | 99精品视频免费在线观看 | 久久久久久久久国产 | 国产精品欧美日韩在线观看 | 国产一性一爱一乱一交 | 人人爱在线视频 | 午夜精品999 | 国产丝袜一区二区三区 | 久久成视频 | 色www精品视频在线观看 | 国产亚州av | 国产在线播放一区二区三区 | 免费成人在线电影 | 久久综合激情 | 91精品国产欧美一区二区成人 | 国产99在线免费 | 激情丁香 | 免费观看一级 | 亚洲国产精品500在线观看 | 美女网站在线免费观看 | 国产精品久久久久毛片大屁完整版 | 亚洲成人影音 | 国产女做a爱免费视频 | 成年人在线免费看视频 | 国产精品一区在线观看 | 91在线看视频 | 久久免费国产精品1 | 免费在线观看不卡av | 中文字幕人成乱码在线观看 | 久久丁香 | 国产黄色在线 | 九九九热精品免费视频观看网站 | 欧美色图视频一区 | 丁香六月婷婷开心婷婷网 | 日韩在线国产精品 | 狠狠的干狠狠的操 | 狠狠操在线 | 免费视频成人 | 高潮久久久久久 | 国产99久久99热这里精品5 | av电影中文 | av在线播放快速免费阴 | 久草视频免费在线播放 | 高清av影院 | 国产精品一区二区果冻传媒 | 一区二区三区久久 | 亚洲精品久久激情国产片 | 天天色天天操天天爽 | 中字幕视频在线永久在线观看免费 | 夜夜澡人模人人添人人看 | 亚州精品视频 | 日本在线观看一区二区 | 97视频在线 | 美女视频网站久久 | 精品综合久久久 | 色多视频在线观看 | 蜜桃视频成人在线观看 | 99一区二区三区 | 成人黄色小说网 | 国产精品久久久久久久久久久免费 | 中文一区二区三区在线观看 | 91精品国产一区二区三区 | 欧美日韩在线网站 | 日日天天 | 亚洲91中文字幕无线码三区 | www免费视频com| 人人躁| 精品一区二区三区久久久 | 亚洲 精品在线视频 | 成人精品亚洲 | 麻豆精品国产传媒 | 国产韩国精品一区二区三区 | 成人高清在线观看 | 日韩中文字幕网站 | 国产超碰在线 | 在线观看免费一区 | 99国内精品久久久久久久 | www激情久久 | 亚洲精品国精品久久99热 | 男女啪啪免费网站 | 国产区欧美| 在线观看午夜 | 亚洲无人区小视频 | 最近在线中文字幕 | 婷婷色 亚洲 | a天堂中文在线 | 四川bbb搡bbb爽爽视频 | 五月开心六月伊人色婷婷 | 久久久黄视频 | 天天爽天天碰狠狠添 | 久久精品99国产国产精 | 天天操天天射天天操 | 日本性生活免费看 | 亚洲免费av片 | 3d黄动漫免费看 | 丁香六月婷婷开心婷婷网 | 黄色高清视频在线观看 | 欧美性视频网站 | 亚洲成人免费 | 一区二区三区在线观看免费视频 | 四虎国产永久在线精品 | 字幕网在线观看 | 免费三级黄色 | 精品视频不卡 | 2023av在线 | 亚洲国产成人久久 | 在线国产激情视频 | 久久久精品视频成人 | 夜夜干夜夜 | 日韩在线观看视频网站 | 午夜久久久影院 | 精品国产精品一区二区夜夜嗨 | 免费看成年人 | 五月婷婷六月丁香 | 成年人免费在线播放 | 国产精品久久久久久久久久妇女 | 亚洲va欧美| 91九色pron| 国产一区二区在线播放 | www99久久 | 中文在线中文资源 | 午夜精品一二三区 | 丁香资源影视免费观看 | 国产精品自产拍在线观看网站 | 欧美一区二区三区四区夜夜大片 | 日韩夜夜爽 | 蜜臀av性久久久久蜜臀aⅴ涩爱 | 日韩在线观看免费 | 国产在线精品一区 | 在线播放国产一区二区三区 | 国产精品高潮呻吟久久av无 | 91中文字幕在线视频 | www.黄色| 友田真希x88av| www.人人干| 欧洲激情综合 | 中文十次啦 | 日韩手机在线观看 | 国产精品免费成人 | 免费高清在线观看成人 | 天天做天天爱天天爽综合网 | 久热电影| 精品三级av | 精品在线亚洲视频 | 韩国精品视频在线观看 | 992tv人人网tv亚洲精品 | 日韩网站在线播放 | www.xxxx变态.com | 九九爱免费视频 | 欧美经典久久 | 五月激情天 | 亚洲国内精品在线 | 亚洲 中文字幕av | 国产99区 | 国产精品久久久久久久久久久久冷 | 中文在线免费一区三区 | 2017狠狠干 | 国产免费av一区二区三区 | 99精品视频在线观看播放 | 久久精品伊人 | 欧美少妇影院 | 精品国产伦一区二区三区观看说明 | 一区二区久久久久 | 婷婷丁香激情网 | 亚洲 欧美变态 另类 综合 | 激情欧美xxxx | 成人在线观看影院 | 日韩啪啪小视频 | 最新中文在线视频 | 精品国产伦一区二区三区免费 | 精品国产乱码久久久久久天美 | 91av原创| 亚洲精品资源 | 免费在线观看亚洲视频 | 婷婷av电影 | 久久视频精品在线观看 | 国产在线播放一区二区三区 | 夜夜夜精品 | 97精品国产91久久久久久久 | 97看片| 国产亚洲精品久久久久久 | 500部大龄熟乱视频使用方法 | 精品国产一区二区久久 | 欧美a免费| 日韩黄视频| 国产福利在线不卡 | 免费观看黄色av | 麻豆91精品视频 | 国产精品va最新国产精品视频 | 在线观看视频一区二区 | 久久电影中文字幕视频 | 一区三区在线欧 | 99精品国产一区二区三区麻豆 | 五月天婷亚洲天综合网精品偷 | aaaaaa毛片 | 激情久久久 | 亚洲最大的av网站 | 91在线你懂的 | 亚洲综合在线发布 | 黄色福利网站 | 在线视频欧美日韩 | 狠狠插天天干 | 黄视频色网站 | 久久激情网站 | 日韩免费视频一区二区 | 日韩高清观看 | 97电院网手机版 | 99精品欧美一区二区三区 | 午夜精品一区二区三区四区 | 午夜久久福利视频 | 欧美少妇的秘密 | 精品久久久久久亚洲综合网站 | 久久1区| 日b视频在线观看网址 | 狠狠色丁香婷婷综合 | 免费看av在线| 毛片网在线观看 | 亚洲国产天堂av | 日本中文字幕电影在线免费观看 | 97精品久久 | 久久精品福利 | 亚洲综合视频在线播放 | 成人免费观看网站 | 91完整视频 | 91久久在线观看 | 欧美福利在线播放 | 久久伦理电影网 | 亚洲波多野结衣 | 99精品在线直播 | 国产aa免费视频 | 精品在线观看一区二区三区 | 久草在线中文视频 | 91av片| 亚洲成人中文在线 | 一级成人免费视频 | www.com黄色 | 国产视频1区2区 | 97热在线观看 | 91色吧 | 国产成人av一区二区三区在线观看 | 夜色资源站国产www在线视频 | 久草精品视频在线看网站免费 | 成人av在线一区二区 | 国产手机在线播放 | 骄小bbw搡bbbb揉bbbb | 天天干夜夜夜操天 | 天天草天天爽 | 国产综合婷婷 | 91精品久久香蕉国产线看观看 | 成人av在线一区二区 | 不卡的av在线播放 | 久久婷婷综合激情 | 亚洲精品久久久久久久蜜桃 | 成人在线视频在线观看 | 亚洲精品久久久蜜桃直播 | 亚洲精品在线视频网站 | 亚洲黄色软件 | 久久精品一区二区三区国产主播 | 少妇自拍av | 欧美综合色在线图区 | 国产精品免费高清 | 亚洲91视频| 日本高清xxxx | 久久露脸国产精品 | 久久成人高清 | 叶爱av在线 | 久久精品99国产 | 99热最新精品 | 五月天激情在线 | 成人a视频片观看免费 | 天天操欧美 | 国内亚洲精品 | 色九九影院 | 国产视频欧美视频 | 国产一区二区三区四区在线 | 久久久久综合 | 在线国产视频 | 日日夜夜骑 | 亚洲高清视频在线观看免费 | 欧美中文字幕第一页 | 亚洲日本欧美在线 | 男女啪啪网站 | 欧美日韩国产精品一区二区 | www蜜桃视频| 亚洲精品乱码久久久久久蜜桃动漫 | 色婷婷狠狠干 | 成人污视频在线观看 | 五月婷婷丁香六月 | 在线影院 国内精品 | 色婷婷精品 | 久久婷婷一区 | 国产精品 美女 | 五月婷香蕉久色在线看 | 日本中文字幕一二区观 | 国内外成人在线 | 日韩国产在线观看 | 五月婷婷在线视频观看 | 外国av网 | 草久久久久久 | 在线看污网站 | 久久一区二区三区四区 | 免费看三级网站 | 国产免费午夜 | 免费久草视频 | 蜜臀av夜夜澡人人爽人人桃色 | 91一区二区在线 | 久草在线久草在线2 | 国产视频999 | 午夜精品一区二区三区在线视频 | av成人在线电影 | 久久免费视频网站 | 国产乱码精品一区二区蜜臀 | 国产老太婆免费交性大片 | 国产亚洲资源 | 成人性生交视频 | www国产亚洲 | 欧美精品一二三 | 在线免费观看视频 | 国产小视频在线观看 | 91免费版在线 | 91丨九色丨丝袜 | 国产成人黄色在线 | 在线播放亚洲激情 | 天天玩天天干 | 久久成人毛片 | 久久国产亚洲视频 | 亚洲精品视频在线播放 | 国产成人免费网站 | 日韩av电影国产 | 中文在线资源 | 日韩欧美精品一区 | 免费成人av网站 | 中文字幕有码在线观看 | 91在线区 | av女优中文字幕在线观看 | 午夜精品久久久久久久久久 | 中文字幕黄色 | 亚洲国产片 | 国产精品久久久久久久久软件 | 97av色 | 在线观看免费观看在线91 | 深爱激情久久 | 色香蕉视频 | 日韩高清一区 | 国产九九精品 | 天天插天天射 | 奇米影视777四色米奇影院 | 91麻豆精品国产自产在线 | 久草在线免费播放 | 日韩在线观看网站 | 正在播放久久 | 午夜视频播放 | 免费在线观看日韩欧美 | 日韩欧美一区二区三区视频 | 最近更新中文字幕 | 99自拍视频在线观看 | 涩五月婷婷 | 国产高清视频在线 | 在线免费av网站 | 国产精品美女久久久久久久 | 欧美激情视频一二区 | 6080yy精品一区二区三区 | 成人亚洲欧美 | a一片一级 | 国产传媒中文字幕 | 99免费| 69精品人人人人 | 免费看的黄色的网站 | 中文av日韩 | 国产精品 视频 | 久草在线高清视频 | 亚洲激情在线视频 | 碰超在线 | 亚洲最新毛片 | 成年人视频免费在线播放 | 97超碰资源网 | 国产人成一区二区三区影院 | 人人爽人人舔 | 国产精品手机播放 | 免费污片 | 久久99精品国产麻豆婷婷 | 精品在线观看国产 | 国产精品九九久久久久久久 | 亚洲综合网站在线观看 | 中文字幕在线网址 | 丁香六月网 | 午夜av一区二区三区 | 首页国产精品 | 96av视频 | 成年人电影免费在线观看 | 国产精品九九九九九九 | 深爱开心激情 | 在线观影网站 | 91视视频在线直接观看在线看网页在线看 | 国产欧美精品xxxx另类 | 欧美日韩免费视频 | 久久成人免费电影 | 探花视频免费在线观看 | av免费观看高清 | 色综合网在线 | a黄色一级 | 九月婷婷色 | 国产字幕在线看 | 色综合久久久久久久 | 国产成人久久av免费高清密臂 | 97免费在线观看 | 日本乱视频 | 日韩在线中文字幕 | 久久国产精品99久久人人澡 | 超碰在线1 | 99精品久久久久久久 | 日韩在线免费视频 | 丁香六月婷婷开心 | 久久99精品热在线观看 | 亚洲欧洲久久久 | 久久综合九色综合久久久精品综合 | 丝袜美腿亚洲 | 日韩中文在线字幕 | 三级动态视频在线观看 | 天天综合日 | 亚洲午夜精品在线观看 | 国产成人在线一区 | 欧美一级在线 | 国产精品一区二区美女视频免费看 | www.色午夜 | 一区 二区电影免费在线观看 | 国产一区成人 | 欧美激情综合五月色丁香 | 黄色亚洲在线 | 免费日韩视 | 激情欧美一区二区三区免费看 | 国产这里只有精品 | 免费看久久 | 在线 影视 一区 | 国产91九色蝌蚪 | 欧美日韩中文视频 | 综合久久久久 | 波多野结衣一区三区 | 国产精品1000 | 免费看亚洲毛片 | 园产精品久久久久久久7电影 | 天天精品视频 | 亚洲作爱视频 | 一区二区三区中文字幕在线 | 四虎影视成人精品 | 色偷偷人人澡久久超碰69 | 中文字幕在线观看免费观看 | 911国产| 超碰在线日韩 | 免费看的视频 | 国产精品久久久久久久久免费看 | 成人性生交视频 | 久草网免费 | 久久在草 | 在线中文字幕电影 | 亚州精品天堂中文字幕 | 中文国产字幕在线观看 | 日韩三级成人 | 日韩午夜视频在线观看 | 亚洲一级片在线看 | 性色av香蕉一区二区 | 天天艹天天爽 | 久久激情综合网 | 在线免费观看视频一区二区三区 | 九九热免费在线观看 | 精品视频网站 | 午夜精品福利一区二区 | 欧美日韩中文在线 | 精品一区精品二区高清 | 99视频精品全国免费 | 久久91网 | 亚洲精品66 | 91视频在线免费 | 国产在线久久久 | 欧美成年人在线视频 | 欧美精品一二三 | 欧美人体xx | 国产一区二区在线观看免费 | 午夜国产一区二区 | 欧美性成人 | 狠狠色丁香久久婷婷综合_中 | 久久精品一区二区三区四区 | 亚洲国产资源 | 人人澡视频 | 精品久久久久国产 | 国产又粗又硬又长又爽的视频 | 欧美久久久久久久久久久久 | 丁香导航 | 亚洲精品在线一区二区三区 | 91桃色在线免费观看 | 五月婷婷伊人网 | 黄色视屏免费在线观看 | 免费网站v| 国产淫片 | 成人av在线直播 | 久久99在线观看 | 欧美日韩另类视频 | 狠狠狠色丁香婷婷综合久久五月 | 亚洲综合在线播放 | 欧美日韩国产二区 | 婷婷色伊人 | 国产精品免费在线视频 | av观看久久久 | 91黄色影视 | 伊人婷婷 | 一区二区精品在线 | 97碰在线 | 99tvdz@gmail.com| 成人免费观看网址 | 91av视频在线播放 | 日韩精品久久久久久久电影竹菊 | 伊人资源站 | 五月开心激情网 | 国产精品久久久久久电影 | 美女在线观看av | 天天操天天干天天摸 | 国产精品黑丝在线观看 | 97色婷婷成人综合在线观看 | 国产高清久久 | 九九热视频在线 | 91av视频| 久久久精品亚洲 | 日韩欧美一区二区在线播放 | 精品久久久久久久久亚洲 | 黄色软件大全网站 | 91麻豆高清视频 | 狠狠色伊人亚洲综合网站色 | 免费一区在线 | 久久国产热 | 91大片网站 | 亚洲精品黄网站 | 日日天天狠狠 | 天天操婷婷 | 日韩精品极品视频 | 五月天婷婷在线播放 | 香蕉视频在线网站 | 国产成人av电影在线 | 日韩伦理片hd | 国产精品视频99 | 久久久久在线 | 久久婷婷精品 | 麻豆视频免费在线播放 | 天天草天天干天天射 | av视屏在线 | 亚洲三级国产 | 手机av在线网站 | 三级动态视频在线观看 | www日日夜夜 | 婷婷福利影院 | 日韩欧美区 | 91在线看网站| av网站地址 | 日韩精品中字 | 四虎国产精品成人免费影视 | 亚洲日韩欧美一区二区在线 | 免费日韩一区二区三区 | 中文字幕视频免费观看 | 在线 欧美 日韩 | 激情影院在线观看 | wwwwww色| 日本公妇色中文字幕 | 在线看片视频 | 色av网站 | 青草视频在线免费 | www亚洲视频| 欧美午夜精品久久久久久浪潮 | 日韩欧美国产精品 | 国产视频在线免费 | 日韩极品在线 | 久久久久久久国产精品视频 | 亚洲精品色 | 久久69精品久久久久久久电影好 | 欧美激情精品一区 | 国产又粗又猛又黄又爽视频 | 丁香激情综合 | 亚洲国产精品va在线看 | 久久久久久久久久久久电影 | 高潮毛片无遮挡高清免费 | 亚洲综合情 | 成人h动漫在线看 | 国产一区二区在线精品 | 成人久久影院 |