demo:http://cnwander.com/demo/billiards/
Original address: http://cnwander.com/blog/?p=11
Put the code First:
Run Code Box
<! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 transitional//en" "Http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd "> <ptml xmlns=" http://www.w3.org/1999/xhtml "> <pead> <meta http-equiv=" Content-type "content=" text/html; Charset=utf-8 "/> <title> Billiards by cnwander</title> <style type=" Text/css "> * {margin:0; padding:0} bod y {background:black; Text-align:center font-size:12px} h1 {font-size:12px; color:gray; font-weight:normal; line-height:200%} h1. sub {vertical-align:super; color:red; font-size:9px;. info {position:absolute; right:0; color: Gray} #table {position:relative; width:800px; margin:20px auto 10px; height:544px; Background:url (/web/uploadpic/ 2010-6/2010625184041599.jpg) No-repeat}. Ball {position:absolute; width:30px; height:30px} #dotWrap {position: absolute; Z-index:2; left:32px; top:32px; width:736px; height:480px}. Guide {z-index:3; Background-image:url (/web/uploadpic/2010-6/2010625184041297.png); _background: None _filter:proGid:DXImageTransform.Microsoft.AlphaImageLoader (enabled= "true", sizingmethod= "scale", src=http://www.webjx.com/ javascript/"/web/uploadpic/2010-6/2010625184041297.png"); Background-repeat:no-repeat}. Target {left:500px; top:250px; Background-image:url (/web/uploadpic/2010-6/ 2010625184041791.png); _background:none; _filter:progid:dximagetransform.microsoft.alphaimageloader (enabled= "true", sizingmethod= "scale", src=http:// www.webjx.com/javascript/"/web/uploadpic/2010-6/2010625184041791.png"); Background-repeat:no-repeat}. Cue {background-image:url (/web/uploadpic/2010-6/2010625184042812.png); _background: None _filter:progid:dximagetransform.microsoft.alphaimageloader (enabled= "true", sizingmethod= "scale", src=http:// www.webjx.com/javascript/"/web/uploadpic/2010-6/2010625184042812.png"); Background-repeat:no-repeat}. Bot {position:relative; width:800px margin:10px auto 10px; height:70px}. Ctrl {position: absolute; top:0; right:0; width:200px; height:80px; Background:url (/web/uploadpic/2010-6/2010625184042667.png) No-repeat} #force {position:absolute; left:0; top:18px; width:75px; height:20px; background: URL (/web/uploadpic/2010-6/2010625184042960.png) no-repeat} #shootPos {position:absolute; top:0; right:14px; width : 52px; HEIGHT:52PX} #dot {position:absolute; top:22px; left:22px; width:8px; height:8px; Background:url (/web/UploadPic/) 2010-6/2010625184042110.png) No-repeat; FONT-SIZE:1PX} #scoreBoard {position:absolute; z-index:3; top:230px; left:346px; font-size:50px; color:white; filter: Alpha (opacity=0); -moz-opacity:0; opacity:0} #tips {padding:15px 0 0 20px; text-align:left; color:red font-size:12px} </style> <script type= "text /javascript ">//Common function $ (str) {return document.getElementById (str);} function $tag (str,target) {target = t Arget document; return Target.getelementsbytagname (str); } function addEventHandler (OBJ,ETYPE,FUC) {if (Obj.addeventlistener) {Obj.addeventlistener (etype,fuc,false);} else if (obj.attachevent) {obj.attachevent ("on" + ETYPE,FUC); }else{obj["on" + eType] = fuc;} function removeEventHandler (OBJ,ETYPE,FUC) {if (Obj.removeeventlistener) {Obj.removeeventlistener (EType,fuc,false) ; }else if (obj.attachevent) {obj.detachevent ("on" + Etype,fuc);} function Randownum (start,end) {return Math.floor (Math.random () * (End-start)) + start;} Array.prototype.remove=function (DX) {if (isNaN (dx) dx>this.length) {return false;} for (Var i=0,n=0;i< this.length;i++) {if (This[i]!=this[dx]) {this[n++]=this[i]} this.length-=1}//const var totalr = 15,//radius of the sphere (including shadow) R = 12,//ball true radius POKER = =, W = 736,//Case Wide H = 480,//Case High THICKNESS = 32,//edge thickness RATE = 100,//Refresh frequency F = 0.01,//friction LOSS = 0. 2,//collision speed Loss TIPS = ["Tip1: Reference ball, target ball, target bag, 3.1 line, this is the most basic goal method", "TIP2: The lower right corner of the blue bar to represent the intensity of the ball, small strength more easily control the position of the female sphere", "TIP3: Lower right corner white ball on the Blue Point control point, high pole, low pole , stoppering are controlled by it, master and rookie difference often in this "," TIP4: Table ball, in fact, playing is not the target ball, is the female ball "]; var table,//Case Cueball,//female ball Guideball,//reference ball Dotwrap,//reference line speed = 0, rollUp = 0, timer, Rollright, BA LLS = [], movingballs = [], pokes = [[0,0],[W/2,-5],[w,0],[0,h],[w/2,h+5],[w,h]], hasshot = false; shots = 0; Combo Number window.onload = function () {inittable (); Initshootpos (); Showtips (); Startgame ();} function Startgame () {Initbal L (); addEventHandler (table, "MouseMove", Dragcueball); addEventHandler (table, "MouseUp", Setcueball); function inittable () {table = $ ("table"); var dotwrapdiv = document.createelement ("div"), Guideballdiv = Document.creat Eelement ("div"); Dotwrapdiv.id = "Dotwrap"; Guideballdiv.classname = "Guide Ball"; SetStyle (Guideballdiv, "display", "none"); Dotwrap = Table.appendchild (Dotwrapdiv); Guideball = Table.appendchild (Guideballdiv); function Initball () {//Add parent Ball Cueball = new Ball ("Cue", 170,H/2); Balls.push (cueball);//Add Target ball for (var i = 0; i < 5; i+ +) {for (var j = 0; J <= i; j +) {var ball = new Ball ("target", 520 + i*2*r, H/2-r*i + j*2*r); Balls.push (ball);}} function Initshootpos () {var wrap = $ ("Shootpos"), Handler = $ ("dot"), ARROWR = n addEventHandler (wrap, "MouseDown", s ELECTDOT); FunctIon Selectdot (e) {e = e event; var pos = Getelempos (wrap), x = e.clientx-pos[0]-handler.offsetwidth/2, y = E.clienty -pos[1]-HANDLER.OFFSETHEIGHT/2; if (MATH.SQRT (x-22) * (x-22) + (y-22) * (y-22)) > Arrowr) {var angle = math.atan2 (x-22,y-22); x = Arrowr*math.sin (angle) + 22; y = Arrowr*math.cos (angle) + 22; } setpos (Handler,x,y); } function Getelempos (target,reference) {reference = reference document; var left = 0,top = 0; return GetPos (target); function GetPos (target) {if (target!= reference) {left = Target.offsetleft; top + = Target.offsettop; return GetPos (Targ Et.parentnode); else {return [left,top];}} }//Ball class function Ball (type,x,y) {var div = document.createelement ("div"); div.classname = type + "Ball"; this.el em = Table.appendchild (div); This.type = type; this.x = x; Position this.y = y; This.angle = 0; Angle THIS.V = 0; Speed (not including direction) Setballpos (this.elem,x,y); return this; function Setcueball () {removeEventHandler (table, "MouseMove", Dragcueball); removeeVenthandler (table, "MouseUp", Setcueball); Startshot (); function Startshot () {Show (Cueball.elem); addEventHandler (table, "MouseMove", ShowGuide); addEventHandler (table, " MouseDown ", Updateforce); addEventHandler (table, "MouseUp", Shotcueball); function Dragcueball (e) {var Tox,toy e = e event; ToX = e.clientx-table.offsetleft-thickness, ToY = e.clienty-t able.offsettop-thickness; ToX = ToX >= R? Tox:r; ToX = ToX <= 170? tox:170; ToY = ToY >= R? Toy:r; ToY = ToY <= h-r? Toy:h-R; Setballpos (Cueball,tox,toy); function Shotcueball () {removeEventHandler (table, "MouseMove", ShowGuide); removeEventHandler (table, "MouseDown", Updateforce); removeEventHandler (table, "MouseUp", Shotcueball); Window.clearinterval (Forcetimer); Speed = $ ("Force"). offsetwidth * 0.15; var Dotdisx = $ ("dot"). OffsetLeft-22, Dotdisy = $ ("dot"). OffsetTop-22, Dotdis = math.sqrt (Dotdisx*dotdisx + dotdisy*dotdi SY), Dotangle = Math.atan2 (Dotdisx,dotdisy); Rollright = Math.Round (Dotdis*math.sin (Dotangle))/5; RollUp =-math.round (Dotdis*math.cos (Dotangle))/5; var formpos = Getballpos (Cueball.elem), ToPos = Getballpos (guideball), angle = Math.atan2 (Topos[0]-formpos[0],topos[1]- FORMPOS[1]); Hide (Dotwrap); Hide (Guideball); CUEBALL.V = speed; Cueball.angle = angle; Movingballs.push (Cueball); Timer = Window.setinterval (roll,1000/rate); function ShowGuide (e) {var Fromx,fromy,tox,toy e = e event; ToX = e.clientx-table.offsetleft-thickness, ToY = e.c lienty-table.offsettop-thickness; Setballpos (Guideball,tox,toy); Show (Dotwrap); Show (Guideball); DrawLine (); Reference line function DrawLine () {var dotnum =, pos = Getballpos (Cueball.elem); dotwrap.innerhtml = ""; FromX = Pos[0]; FromY = pos[1]; var partx = (TOX-FROMX)/dotnum, party = (toy-fromy)/dotnum; for (var i = 1; i < Dotnum. i++) {var x = FromX + Partx * I, y = fromY + party * i; Drawdot (Dotwrap, x, y);} function Roll () {if (movingballs.length <= 0) {if (!hasshot) shots = 0; else shots + +;//cumulative Combo HaSshot = false; SetStyle ($ ("Force"), "width", 80+ "px"); SetPos ($ ("dot"), 22,22); Window.clearinterval (timer); if (Shots > 1) showscore (shots); Show combo number startshot (); for (var i = 0; i < movingballs.length i++) {var ball = movingballs[i], sin = Math.sin (ball.angle), cos = Math.Cos (b All.angle); BALL.V = F; Remove a stationary ball if (math.round (ball.v) = = 0) {ball.v = 0; Movingballs.remove (i); continue;} var vx = ball.v * sin, vy = ball.v * COS; Ball.x + VX; Ball.y + = VY; Into the bag if (Ispocket (ball.x,ball.y)) {hide (Ball.elem); if (Ball.type = = "Cue") {if (!hasshot) shots = 0; hasshot = false; wind Ow.settimeout (function () {ball.v = 0; Setballpos (ball,170,250);},500); }else {//remove bag ball Hasshot = true; ball.v = 0 for (var k = 0, l =0 K < balls.length; k++) {if (balls[k)!= ball) {Balls [l++] = balls[k]; } balls.length-= 1; } return; }//Edge collision if (Ball.x < R ball.x > W-r) {ball.angle *=-1; ball.angle%= math.pi; ball.v = ball.v * (1-loss); VX = Ball.v*math.sin (Ball.angle); VY = Ball.v*maTh.cos (Ball.angle); if (Ball.x < r) ball.x = R; if (ball.x > W-r) ball.x = W-r; The female ball stoppering if (Ball.type = = "Cue") {if (Ball.angle > 0) vy-= Rollright; else vy = rollright; vx = rollUp; rollUp *= 0.2; Rollright *= 0.2; BALL.V = math.sqrt (vx*vx + vy*vy); Ball.angle = Math.atan2 (Vx,vy); } if (Ball.y < R ball.y > h-r) {ball.angle = ball.angle > 0? Math.pi-ball.angle:-Math.pi-ball.angle; Ball.angle%= Math.PI; BALL.V = ball.v * (1-loss); VX = Ball.v*math.sin (Ball.angle); VY = Ball.v*math.cos (Ball.angle); if (Ball.y < r) Ball.y = R; if (Ball.y > H-r) ball.y = H-r; Female ball stoppering if (Ball.type = = "Cue") {if (Math.Abs (Ball.angle) < MATH.PI/2) VX + = Rollright; vx-= Rollright; VY + = Rollu P RollUp *= 0.2; Rollright *= 0.2; BALL.V = math.sqrt (vx*vx + vy*vy); Ball.angle = Math.atan2 (Vx,vy); }///Ball collision for (var j = 0; J < Balls.length; J + +) {var obj = balls[j]; if (obj = = ball) continue; var disx = Obj.x-bal l.x, Disy = obj.y-ball.y, Gap = 2 * R; if (dIsX <= Gap && Disy <= gap) {var dis = math.sqrt (Math.pow (disx,2) +math.pow (disy,2)); if (dis <= gap) {//AS The fruit is still, then added to the array movingballs if (Math.Round (obj.v) = = 0) movingballs.push (obj); Rotate the coordinates to the X axis for collision calculation/calculation angle and positive cosine-exact value//var c = (obj.x*ball.y-obj.y*ball.x)/(2*R),//d = math.sqrt (ball.x*ball.x + ball.y *BALL.Y),//angle = Math.asin (ball.y/d)-Math.asin (C/D)-ball.angle% (MATH.PI/2),//angle = Math.asin (Oy/(2 * R)),// Reduction of two spheres tangent state-approximate value ball.x-= (Gap-dis) *sin; BALL.Y-= (Gap-dis) *cos; DISX = obj.x-ball.x; Disy = OBJ.Y-BALL.Y; Calculation angle and positive cosine var angle = math.atan2 (Disy, disx), Hitsin = Math.sin (angle), Hitcos = Math.Cos (angle), OBJVX = OBJ.V * Math. Sin (obj.angle), Objvy = obj.v * Math.Cos (Obj.angle); Trace (ANGLE*180/MATH.PI); Rotation coordinates var x1 = 0, y1 = 0, x2 = disx * hitcos + disy * hitsin, y2 = disy * HITCOS-DISX * hitsin, VX1 = VX * Hitcos + VY * Hitsin, vy1 = vy * HITCOS-VX * hitsin, vx2 = objvx * hitcos + objvy * hitsin, vy2 = Objvy * HITCOS-OBJVX * * Hitsin; Velocity and position after collision var plusvx = vx1-vx2; VX1 = VX2; VX2 = PLUSVX + vx1; Female ball stoppering if (Ball.type = = "Cue") {vx1 = rollUp; rollUp *= 0.2;} x1 + = VX1; x2 + = vx2; Rotate the position back var x1final = x1 * hitcos-y1 * hitsin, y1final = y1 * hitcos + x1 * hitsin, x2final = x2 * Hitcos-y2 * Hits In, y2final = y2 * hitcos + x2 * hitsin; obj.x = ball.x + x2final; OBJ.Y = Ball.y + y2final; ball.x = ball.x + x1final; BALL.Y = Ball.y + y1final; Rotate the speed back to VX = VX1 * Hitcos-vy1 * hitsin; VY = vy1 * hitcos + vx1 * hitsin; OBJVX = vx2 * Hitcos-vy2 * hitsin; Objvy = vy2 * hitcos + vx2 * hitsin; Final velocity ball.v = math.sqrt (VX*VX + vy*vy) * (1-0); OBJ.V = math.sqrt (OBJVX*OBJVX + objvy*objvy) * (1-0); Calculation angle Ball.angle = MATH.ATAN2 (VX, VY); Obj.angle = Math.atan2 (OBJVX, Objvy); Break }} setballpos (BALL,BALL.X,BALL.Y); } function Ispocket (x,y) {if (Y < POKER) return check (0,2); else if (Y > H-poker) return check (3,5); False function Check (m,n) {for (var i=m; i<=n; i++) {if (x>= Pokes[i][0]-POKER && x <= pokes[i][0] + POKER) {var dis = math.sqrt (Math.pow (x-pokes[i][0],2) + Math . POW (y-pokes[i][1],2)); if (dis <= POKER) return true; else return false; }}} function Getballpos (obj) {var pos = []; Pos.push (obj.offsetleft-thickness + totalr); Pos.push (obj.offsettop-t Hickness + totalr); return POS; function SetPos (obj,x,y) {obj.style.left = x + "px"; obj.style.top = y + "px";} function Setballpos (ball,x,y) {if (Bal L.constructor = = Ball) {ball.x = x; ball.y = y; Ball = Ball.elem;} setpos (ball,x + thickness-totalr,y + thickness-to TALR); function Drawdot (wrap,x,y) {var elem = document.createelement ("div"); SetStyle (elem,{position: "Absolute", Width: "1px ", Height:" 1px ", FontSize:" 1px ", Background:" White "}); SetPos (Elem,x,y); Wrap.appendchild (Elem); function Updateforce () {var obj = $ ("Force"), Len = n, up = true; Forcetimer = Window.setinterval (update,10); function Update () {if (UP) SetStyle (obj, "width", len+++ "px"); else SetStyle (obj, "width", len--+ "px"); if (len > 136) up = false; if (len <= 0) up = true; } function SetStyle () {if (arguments.length = = 2 && typeof arguments[1] = = "Object") {for (var key in arguments [1]) {Arguments[0].style[key] = Arguments[1][key];} else if (Arguments.length > 2) {arguments[0].style[arguments[1]] = arguments[2];} function Hide (obj) {setStyle (obj, "display", "none");} function Show (obj) {setStyle (obj, ' Display ', ' block ');}//Output information fun ction Trace (sth,who) {who = who $ ("Tips"), if (document.all) Who.innertext = sth; else who.textcontent = STH; function Showscore (n) {var wrap = $ ("scoreboard"), Trace (n+ "Connecting rod", wrap); fadeIn (wrap);} function FadeIn (obj) {var FromY = 230, Posstep = [8,14,19,23,26,28,29,29,30,30,30], opastep = [0,0.05,0.1,0.15,0.2,0.25,0.3,0.4,0.5,0.6,0.8], FromOpa = 0, t = 0, step = posstep.length, Intimer = Window.setinterval (showin,20), Outtimer; function Showin () {setopacity (obj,opastep[t]); obj.style.top = fRomY + posstep[t] + "px"; t++; if (t>=step) {window.clearinterval (intimer); Outtimer = Window.setinterval (fadeout,50);}} function fadeout () {t--; SetOpacity (Obj,opastep[t]); obj.style.top = FromY + posstep[t] + "px"; if (t <= 0) {window.cl Earinterval (Outtimer); Hide (obj); }} function SetOpacity (obj,n) {obj.style.cssText = Filter:alpha (opacity= "+ n*100 +");-moz-opacity: "+ n +"; opacity: " + N; function Showtips () {var i = 0; tip (); Window.setinterval (tip,3000); function tip () {Trace (tips[i++)); if (I >= TIPS . length) i = 0; } </script> </pead> <body> <div class= "Info" > Discussion: <a href= "http://bbs.blueidea.com/ Thread-2951566-1-1.html ">Blueidea</a> <a href=" http://cnwander.com/blog/?p=11 ">wander" s Space</a></div> <p> Chinese people 烎 up! <span class= "Sub" >60 Anniversary </span></p> <div id= "table" > <div id= "scoreboard" ></div> </div> <div class= "bot" > <div id= "Tips" ></div> <div class = "Ctrl" > <div id= "Force" ></div> <div id= "Shootpos" > <div id= "dot" ></div > </div> </div> </div> </body> </ptml>
[Ctrl + A ALL SELECT hint: You can modify some of the code, and then run]
Although the shameless title table ball, in fact, and the real table ball still far away, there are too many need to improve the place.
Specific issues to be resolved:
- Because the browser can not refresh the frequency is too high, may be detected when two ball distance, the two balls have overlapped most, or even completely crossed.
Completely crossed the situation first not to consider, the overlapping part if restores to the precise tangent state, the movement is very slow, therefore I only uses the computation quantity less approximate value, the concrete question mainly manifests in the Kick-off, the multiple ball collision is somewhat strange.
(If there is a good way to optimize the calculation, you can take out to share with wander, that wander really grateful)
- Ball itself rolling and desktop friction problem. For example, the frictional force in the sliding state of the ball is greater than the frictional force in the forward rolling, and is less than the frictional force of the shrinking rod. This problem is good to solve, but at the beginning did not take into account, and then did not add, the specific embodiment of two balls after the collision, the ball will be completely static, this is not true.
- Energy loss problem, whether with the edge collision or ball and ball collision, are directly minus a fixed value, this is certainly a big problem.
Others will certainly have a lot of problems, worry about the holiday to play with the heart of oil, not mind to continue, simply coherent, rush to some haste, the problem back to slowly solve it, first sent up, to this piece of interested students together to explore.
University mathematics is basically cut, high school physics math and forget little, really do things just find themselves this piece is too weak, hope in this aspect of the students with rich experience is generous to enlighten.