现在,当鼠标点击画布时,我想检测哪一侧被点击.类似于这个提示:每条边由 4 个 (x,y,z) 角定义使用我认为常规的透视投影"将每个角绘制到画布上这是我的代码 - 很酷:使用鼠标旋转//edit: 请参阅下面我的回答中的代码 解决方案 完美!我想我需要问这个问题,这样我才能回答自己:-)该解决方案基于我发现的一些惊人的片段,用于检测多边形中点的存在.这是 2d 函数,但是嘿......我的画布也是 - 愚蠢的我.这里是完整的跨浏览器:<头><meta http-equiv="content-type" content="text/html; charset=UTF-8"/><title>קובייה</title><link rel="快捷方式图标" href="Rubiks.png"/><script>var isIE = false</script><!--[如果 IE]><script language="javascript" type="text/javascript" src="../js_canvas/excanvas_r69.js"></script><script>isIE = true</script><![endif]--><风格>身体 {填充:2px;字体系列:arial;}帆布 {边框:1px纯黑色;背景:白色;光标:默认;}.移动 {光标:移动;}div,td,输入{字体大小:15px;}.header {字体大小:22px;字体粗细:粗体;底边距:10px;}.subHeader {字体大小:16px;}.tblHeader {背景:#003f00;白颜色;字体粗细:粗体;字体大小:24px;边框:0px;}.信息{背景:浅黄色;边框:1px纯黑色;字体大小:15px;宽度:350px;}.opac {/*不透明度:0.85;过滤器:阿尔法(不透明度= 85);-ms-filter: "alpha(opacity=85)";-khtml-不透明度:.85;-moz-不透明度:0.85;*/}.btn {颜色:黑色;显示:内联块;宽度:100px;边框:2px 开始#ddd;文字装饰:无;填充:2px;背景:#ddd;文字对齐:居中;字体系列:arial;字体大小:12px;字体粗细:粗体;}.btn_hover {颜色:蓝色;}.btn_down {边框:2px 插入 #ddd;}</风格><script type="text/javascript">//Eric Pascarello 共享的立方体代码无边长 = 50;无功宽度= 600;变量高度 = 450;var center = new Point(width/2, height/2)var 透视 = sideLength * 16;var xzRotation = -Math.PI/2;var yzRotation = 0;var xyRotation = 0;var isColored = true;var 立方体、calcCube、lgthvar 画布,ctx,动画var mouse = new Point(0,0);var absMouse = new Point(0,0)var clickedMouse = new Point(0,0)var posCanvasvar clickRGB = [248, 128, 23]//橙色 = #F88017var clickRGB = [255,192,203]//粉红色var arrPolygons = []var arrSortedIndex/** 立方体的东西 **/函数旋转(bForce){如果 (!bForce && (this.last_xyRotation == xyRotation &&this.last_xzRotation == xzRotation &&this.last_yzRotation == yzRotation||!dragCube) ) {返回}var drawStyle = getRadioValue("drawStyle")//color, bw, or trans如果(drawStyle==trans"){$("chkWire").disabled = true$("chkWire").checked = true} 别的 {$("chkWire").disabled = false}//将立方体旋转为 calcCube.还设置颜色.for (var i=0; i calcCube.sides[j].avgZ){var temp = calcCube.sides[i].avgZcalcCube.sides[i].avgZ = calcCube.sides[j].avgZcalcCube.sides[j].avgZ = 温度var temp = arrSortedIndex[i]arrSortedIndex[i] = arrSortedIndex[j]arrSortedIndex[j] = 温度}}}//绘制所有边ctx.clearRect(0,0, width, height);for (var i=0; i=0; i--) {if (isPointInPoly(calcCube.sides[arrSortedIndex[i]].polygon, inside)) {dragCube.side = arrSortedIndex[i]休息;}}/*ctx.beginPath()ctx.arc(inside.x, inside.y, 1, 0, Math.PI*2, false);ctx.stroke();*/}函数 mouseUp(e) {如果(dragCube && !dragCube.moved){var index = dragCube.side如果(索引> = 0){cube.sides[index].clickState = !cube.sides[index].clickState旋转(真)}}dragCube = nullremoveClass ($("cv"), "move")}/** 纽扣 **/函数 addClass(objElement, strClass) {如果(!objElement)返回;如果(objElement.className){removeClass(objElement, strClass);objElement.className += ' '+strClass;} 别的 {objElement.className = strClass;}}函数 removeClass(objElement, strClass) {如果(!objElement)返回;如果(objElement.className){var arrList = objElement.className.split(' ');var strClassUpper = strClass.toUpperCase();for (var i = 0; i < arrList.length; i++) {如果 (arrList[i].toUpperCase() == strClassUpper) {arrList.splice(i, 1);一世 - ;}}objElement.className = arrList.join(' ');}}/** 杂项和实用程序 **/函数 $(id) {返回 document.getElementById(id);}函数 findPos(obj) {//http://www.quirksmode.org/js/findpos.htmlvar curleft = curtop = 0;if (obj && obj.offsetParent) {做 {curleft += obj.offsetLeft;curtop += obj.offsetTop;} while (obj = obj.offsetParent);}返回新点(curleft,curtop);}函数 dec2hex(d, 填充) {var hex = Number(d).toString(16);填充 = 填充 ||2while (hex.length < padding) {十六进制 = "0" + 十六进制;}返回十六进制;}函数trimVal(val,min,max){返回 Math.max( Math.min(val, max), min)}函数 getRadioValue(名称){for (i=0;i<body onload="init()"onmousemove="mouseMove(事件)"onmouseup="mouseUp(事件)"><div dir=rtl class="header">קובייה</div><div dir=rtl class="subHeader">גיררו את הקוביה עם העכבר על מנת לסובב אותה</div</div<div dir=rtl class="subHeader">ליחצו על פאה על מנת לסמן אותה.</div><BR><!-- 主画布--><中心><div id="wrapper" dir="ltr"><canvas onmousedown="mouseDown(event)" id="cv" width="100" height="100"></canvas></中心><!-- 控制面板--><div dir="rtl" class="opac" style="position:absolute; background:lightyellow; border:1px纯黑; right:10px; top:100px; font-size:12px; padding:4px; width:120px"><div 目录=rtl><表格名称=frm><input name="drawStyle" type="radio" value="color" onclick="rotate(true)" onchange="rotate(true)" 选中>צבעוני<br><input name="drawStyle" type="radio" value="bw" onclick="rotate(true)" onchange="rotate(true)">שחור לבן<br><input name="drawStyle" type="radio" value="trans" onclick="rotate(true)" onchange="rotate(true)">שקוף<br><input id="chkWire" type="checkbox" onclick="rotate(true)" onchange="rotate(true)">מסגרת<BR></表单><div id="调试"></div>哦...我不介意文档类型,但感谢您的评论.I am writing a small 3d "engine" in canvas.. not really an engine, but more of an application based on code from Eric Pascarello's demo (hit number "7" a few times to see it rotating)it's all going very nice, I even added sorting by average z-value for each plane ("side") - so that I can use a body made of several cubes so that close to camera shapes are drawn last.now, I want to detect which side was clicked, when a mouse clickes the canvas.something like thishints:each side is defined by 4 (x,y,z) cornerseach corner is drawn to canvas using what I believe is regular "perspective projection"here's my code - it's cool: use the mouse to rotate//edit: see the code in my answer below 解决方案 perfect! guess I needed to ask this question so I could answer myself :-)the solution is based on some amazing snippet i found for detecting presence of point in polygon. this is 2d function, but hey... so is my canvas - silly me.here it is, complete and cross-browser:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>קובייה</title> <link rel="shortcut icon" href="Rubiks.png" /> <script>var isIE = false</script> <!--[if IE]> <script language="javascript" type="text/javascript" src="../js_canvas/excanvas_r69.js"></script> <script>isIE = true</script> <![endif]--> <style> body { padding: 2px; font-family: arial; } canvas { border:1px solid black; background:white; cursor:default; } .move { cursor:move; } div, td, input { font-size:15px; } .header { font-size:22px; font-weight:bold; margin-bottom:10px; } .subHeader { font-size:16px; } .tblHeader { background:#003f00; color:white; font-weight:bold; font-size:24px; border:0px; } .info { background:lightyellow; border:1px solid black; font-size:15px; width:350px; } .opac { /* opacity: .85; filter: alpha(opacity=85); -ms-filter: "alpha(opacity=85)"; -khtml-opacity: .85; -moz-opacity: .85; */ } .btn { color: black; display: inline-block; width:100px; border: 2px outset #ddd; text-decoration:none; padding:2px; background: #ddd; text-align:center; font-family: arial; font-size:12px; font-weight:bold; } .btn_hover { color: blue; } .btn_down { border: 2px inset #ddd; } </style> </head><script type="text/javascript">// cube code shared by Eric Pascarellovar sideLength = 50;var width = 600;var height = 450;var center = new Point(width/2, height/2)var perspective = sideLength * 16;var xzRotation = -Math.PI/2;var yzRotation = 0;var xyRotation = 0;var isColored = true;var cube, calcCube, lgthvar canvas, ctx, animationvar mouse = new Point(0,0);var absMouse = new Point(0,0)var clickedMouse = new Point(0,0)var posCanvasvar clickRGB = [248, 128, 23] // orange = #F88017var clickRGB = [255,192,203] // pinkvar arrPolygons = []var arrSortedIndex/** cube stuff **/function rotate(bForce) { if (!bForce && (this.last_xyRotation == xyRotation && this.last_xzRotation == xzRotation && this.last_yzRotation == yzRotation || !dragCube) ) { return } var drawStyle = getRadioValue("drawStyle") // color, bw, or trans if (drawStyle=="trans") { $("chkWire").disabled = true $("chkWire").checked = true } else { $("chkWire").disabled = false } // rotate cube into calcCube. also set colors. for (var i=0; i<lgth; i++) { var side = cube.sides[i]; var calcSide = calcCube.sides[i]; var avgZ = 0 var side_polygon = [] for (var j=0 ; j<4; j++){ var corner = side.corners[j]; var calc1 = calc(corner.x, corner.y, xyRotation, 1); var calc2 = calc(calc1.p1, corner.z, xzRotation, 1); var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1); var x = (calc2.p1 * perspective) / (perspective - calc3.p2) + center.x; var y = (calc3.p1 * perspective) / (perspective - calc3.p2) + center.y; calcSide.corners[j].x = x calcSide.corners[j].y = y side_polygon.push (new Point(x, y)) avgZ += calc3.p2 } calcSide.avgZ = avgZ // /4 calcSide.polygon = side_polygon var light = side.light; var calc1 = calc(light.x, light.y, xyRotation, 1); var calc2 = calc(calc1.p1, light.z, xzRotation, 1); var calc3 = calc(calc1.p2, calc2.p2, yzRotation, -1); calcSide.light = calc3.p2; // decide color var brightness = Math.floor(calcSide.light); brightness = trimVal(brightness, 0, 255) var colorRGB = [] var colorCodeRGB = (side.clickState) ? clickRGB : (drawStyle=="color" ? side.color : "b,b,b").split(",") for (var c=0; c<3; c++) { colorRGB[c] = (colorCodeRGB[c]=="b") ? brightness : colorCodeRGB[c] } fillRGBA = "rgba(" + colorRGB + ",255)"; calcSide.fillRGBA = fillRGBA } // sort sides by avgZ !! arrSortedIndex = [] for (var i=0; i<lgth; i++) { arrSortedIndex[i] = i } for (var i=0; i<lgth-1; i++) { for (var j=i+1; j<lgth; j++) { if (calcCube.sides[i].avgZ > calcCube.sides[j].avgZ) { var temp = calcCube.sides[i].avgZ calcCube.sides[i].avgZ = calcCube.sides[j].avgZ calcCube.sides[j].avgZ = temp var temp = arrSortedIndex[i] arrSortedIndex[i] = arrSortedIndex[j] arrSortedIndex[j] = temp } } } // draw all sides ctx.clearRect (0,0, width, height); for (var i=0; i<lgth; i++) { var calcSide = calcCube.sides[arrSortedIndex[i]]; ctx.fillStyle = calcSide.fillRGBA var corners = calcSide.corners; ctx.beginPath(); ctx.moveTo (corners[0].x, corners[0].y); ctx.lineTo (corners[1].x, corners[1].y); ctx.lineTo (corners[2].x, corners[2].y); ctx.lineTo (corners[3].x, corners[3].y); ctx.lineTo (corners[0].x, corners[0].y); if (drawStyle!="trans") { ctx.fill(); } if ($("chkWire").checked) { ctx.stroke(); } } this.last_xyRotation = xyRotation this.last_xzRotation = xzRotation this.last_yzRotation = yzRotation}function calc(p1,p2,ang,pn){ var cosAng = Math.cos(ang); var sinAng = Math.sin(ang); var r1 = cosAng * p1 - pn * sinAng * p2; var r2 = cosAng * p2 + pn * sinAng * p1; return { "p1": r1,"p2":r2};}function getCube(sideLength) { var ret = { sides : [ { //FRONT corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength}, {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ], light : {x: 0, y: 0, z: 255 }, color : "0,b,0" }, { //BACK corners : [ {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength}, {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ], light : {x: 0, y: 0, z: -255 }, color : "0,0,b" }, { //RIGHT corners : [ {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength} ], light : {x: 255, y: 0, z: 0 }, color : "b,0,0" }, { //LEFT corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength}, {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength}, {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength} ], light : {x: -255, y: 0, z: 0}, color : "0,b,b" }, { //top corners : [ {x:-1*sideLength, y:-1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y:-1*sideLength, z:-1*sideLength}, {x:-1*sideLength, y:-1*sideLength, z:-1*sideLength} ], light : {x: 0, y:-255 , z: 0}, color : "b,b,0" }, { //bottom corners : [ {x:-1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z: 1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-1*sideLength}, {x:-1*sideLength, y: 1*sideLength, z:-1*sideLength} ], light : {x: 0, y: 255, z: 0}, color : "b,0,b" }, // anoter cube behind and above - my addition { //FRONT corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength}, {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ], light : {x: 0, y: 0, z: 255 }, color : "0,b,0" }, { //BACK corners : [ {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength}, {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength}, {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ], light : {x: 0, y: 0, z: -255 }, color : "0,0,b" }, { //RIGHT corners : [ {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength}, {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength} ], light : {x: 255, y: 0, z: 0 }, color : "b,0,0" }, { //LEFT corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength}, {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength}, {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength} ], light : {x: -255, y: 0, z: 0}, color : "0,b,b" }, { //top corners : [ {x:-1*sideLength, y: 1*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 1*sideLength, z:-3*sideLength}, {x:-1*sideLength, y: 1*sideLength, z:-3*sideLength} ], light : {x: 0, y:-255 , z: 0}, color : "b,b,0" }, { //bottom corners : [ {x:-1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 3*sideLength, z: -1*sideLength}, {x: 1*sideLength, y: 3*sideLength, z:-3*sideLength}, {x:-1*sideLength, y: 3*sideLength, z:-3*sideLength} ], light : {x: 0, y: 255, z: 0}, color : "b,0,b" } ] } lgth = ret.sides.length; calcCube = {sides:[]} for (var i=0; i<lgth; i++) { calcCube.sides[i] = {corners : [{},{},{},{}], light:0, avgZ:0} ret.sides[0].clickState=false } return ret}/** 2d stuff **/function Point(x,y) { this.x = x this.y = y}function Polygon() { var ret = [] for (var i=0; i<arguments.length; i++) { ret.push (arguments[i]) } return ret}function isPointInPoly(poly, pt) { // from http://snippets.dzone.com/posts/show/5295 - wow! for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) ((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y)) && (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x) && (c = !c); return c;}/** event handlers **/var dragCube = nullfunction mouseMove(e) { var posx = 0; var posy = 0; e = e || window.event; if (isIE&&false) { posx = e.offsetX posy = e.offsetY } else { if (e.pageX || e.pageY) { posx = e.pageX; posy = e.pageY; } else if (e.clientX || e.clientY) { posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } absMouse = (new Point(posx, posy)) } //$("debug").innerHTML = myStringify(absMouse) if (dragCube) { var diff = new Point (posx - dragCube.anchor.x, posy - dragCube.anchor.y) if (!dragCube.moved) { dragCube.moved = true addClass ($("cv"), "move") } dragCube.anchor = new Point (posx, posy) xzRotation -= diff.x/100 yzRotation += diff.y/100 //$("debug").innerHTML = myStringify(diff) }}function mouseDown(e) { e = e || window.event; dragCube = { anchor : new Point( absMouse.x, absMouse.y), moved : false, side: -1 } var inside = new Point (absMouse.x - posCanvas.x, absMouse.y - posCanvas.y ) //for (var i=0; i<lgth; i++) { for (var i=lgth-1; i>=0; i--) { if (isPointInPoly(calcCube.sides[arrSortedIndex[i]].polygon, inside)) { dragCube.side = arrSortedIndex[i] break; } } /* ctx.beginPath() ctx.arc(inside.x, inside.y, 1, 0, Math.PI*2, false); ctx.stroke(); */}function mouseUp(e) { if (dragCube && !dragCube.moved) { var index = dragCube.side if (index>=0) { cube.sides[index].clickState = !cube.sides[index].clickState rotate(true) } } dragCube = null removeClass ($("cv"), "move")}/** buttons **/function addClass(objElement, strClass) { if (!objElement) return; if (objElement.className) { removeClass(objElement, strClass); objElement.className += ' '+strClass; } else { objElement.className = strClass; }}function removeClass(objElement, strClass) { if (!objElement) return; if (objElement.className) { var arrList = objElement.className.split(' '); var strClassUpper = strClass.toUpperCase(); for (var i = 0; i < arrList.length; i++) { if (arrList[i].toUpperCase() == strClassUpper) { arrList.splice(i, 1); i--; } } objElement.className = arrList.join(' '); }}/** misc and util **/function $(id) { return document.getElementById(id);}function findPos(obj) { //http://www.quirksmode.org/js/findpos.html var curleft = curtop = 0; if (obj && obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); } return new Point(curleft,curtop);}function dec2hex(d, padding) { var hex = Number(d).toString(16); padding = padding || 2 while (hex.length < padding) { hex = "0" + hex; } return hex;}function trimVal(val, min, max) { return Math.max( Math.min(val, max), min)}function getRadioValue (name) { for (i=0;i<document.forms["frm"][name].length;i++) { if (document.forms["frm"][name][i].checked) { return document.forms["frm"][name][i].value; } } return null}/** init **/function init() { canvas = $("cv"); canvas.style.width = width; canvas.style.height = height; canvas.setAttribute("width", width) canvas.setAttribute("height", height) ctx = canvas.getContext('2d'); posCanvas = findPos(canvas) cube = getCube(sideLength) rotate(true) animation = window.setInterval("rotate()", 50);}</script><body onload="init()" onmousemove="mouseMove(event)" onmouseup="mouseUp(event)"> <div dir=rtl class="header">קובייה</div> <div dir=rtl class="subHeader">גיררו את הקוביה עם העכבר על מנת לסובב אותה.</div> <div dir=rtl class="subHeader">ליחצו על פאה על מנת לסמן אותה.</div> <BR> <!-- main canvas --> <center> <div id="wrapper" dir="ltr"> <canvas onmousedown="mouseDown(event)" id="cv" width="100" height="100"></canvas> </div> </center> <!-- control panel --> <div dir="rtl" class="opac" style="position:absolute; background:lightyellow; border:1px solid black; right:10px; top:100px; font-size:12px; padding:4px; width:120px"> <div dir=rtl> <form name=frm> <input name="drawStyle" type="radio" value="color" onclick="rotate(true)" onchange="rotate(true)" checked>צבעוני<br> <input name="drawStyle" type="radio" value="bw" onclick="rotate(true)" onchange="rotate(true)">שחור לבן<br> <input name="drawStyle" type="radio" value="trans" onclick="rotate(true)" onchange="rotate(true)">שקוף<br> <input id="chkWire" type="checkbox" onclick="rotate(true)" onchange="rotate(true)">מסגרת<BR> </form> </div> </div> <div id="debug"></div></body></html>oh... and I don't mind much for the doctype, but thanks for commenting. 这篇关于如何检测 3d 立方体的哪一侧被点击?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-23 16:51