HTML 5 Canvas 遞歸畫(huà)樹(shù)
上圖就是用html5隨機(jī)生成的大樹(shù) : ) 但是你應(yīng)該沒(méi)想到40+行代碼就可以搞定了吧~接下來(lái)就跟大家說(shuō)說(shuō)這棵大樹(shù)是如何實(shí)現(xiàn)的。
同樣必須要有html容器。新建Index.html,代碼如下:
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <title>canvas tree</title>
- </head>
- <body>
- <script type="text/javascript" src="tree.js"></script>
- </body>
- </html>
接下來(lái)咱們開(kāi)始tree.js:
- var canvas = document.createElement("canvas");
- var ctx = canvas.getContext("2d");
- canvas.width = 640;
- canvas.height = 480;
- document.body.appendChild(canvas);
代碼很好理解,創(chuàng)建一個(gè)canvas畫(huà)布,然后選擇為2d畫(huà)布,設(shè)置長(zhǎng)寬,***將這個(gè)畫(huà)布添加到body標(biāo)簽下。
這個(gè)腳本最重要的函數(shù)在下面,大樹(shù)就是遞歸調(diào)用這個(gè)函數(shù)實(shí)現(xiàn)的,調(diào)用一次畫(huà)一條線段:
- var drawTree = function (ctx, startX, startY, length, angle, depth, branchWidth){
- var rand = Math.random,
- newLength, newAngle, newDepth, maxBranch = 3,
- endX, endY, maxAngle = 2 * Math.PI / 4,
- subBraches;
- ctx.beginPath();
- ctx.moveTo(startX, startY);
- endX = startX + length * Math.cos(angle);
- endY = startY + length * Math.sin(angle);
- ctx.lineCap = 'round';
- ctx.lineWidth = branchWidth;
- ctx.lineTo(endX, endY);
- if (depth <= 2){
- ctx.strokeStyle = 'rgb(0,' + (((rand() * 64) + 128) >> 0) + ',0)';
- } else {
- ctx.strokeStyle = 'rgb(' + (((rand() * 64) + 64) >> 0) + ',50,25)';
- }
- ctx.stroke();
- newDepth = depth - 1;
- if (!newDepth)
- return;
- subBranches = (rand() * (maxBranch - 1)) + 1;
- branchWidth *= 0.7;
- for (var i = 0; i < subBranches; i++){
- newAngle = angle + rand() * maxAngle - maxAngle * 0.5;
- newLength = length * (0.7 + rand() * 0.3);
- drawTree(ctx, endX, endY, newLength, newAngle, newDepth, branchWidth);
- }
- }
接下來(lái)一點(diǎn)點(diǎn)解釋:
首先,解釋下各個(gè)變量的含義。ctx就是前面我們的2d畫(huà)布;startX是線段開(kāi)始的橫坐標(biāo),同理startY是縱坐標(biāo);length是線段長(zhǎng)度;angle是角度;depth是深度,葉子深度為1,樹(shù)干為12(可自己設(shè)定);branchWidth就線段的粗細(xì)。有了這些信息,其實(shí)就描述了一個(gè)線段,通過(guò)這些信息我們才能畫(huà)一個(gè)線段。
接下來(lái)又很可恥地一大段定義:
- var rand = Math.random,
- newLength, newAngle, newDepth, maxBranch = 3,
- endX, endY, maxAngle = 2 * Math.PI / 4,
- subBraches;
rand其實(shí)就是隨機(jī)一個(gè)0~1之間的實(shí)數(shù),顧名思義,接下來(lái)這些new的就是下一節(jié)線段的各種參數(shù)。maxBranch就是最多有3個(gè)分叉,***的角度 PI/2 即為,下一級(jí)調(diào)整角度在90%范圍內(nèi)。subBranches就是分叉的個(gè)數(shù)。
好了,重要可以畫(huà)了:
- ctx.beginPath();
- ctx.moveTo(startX, startY);
- endX = startX + length * Math.cos(angle);
- endY = startY + length * Math.sin(angle);
- ctx.lineCap = 'round';
- ctx.lineWidth = branchWidth;
- ctx.lineTo(endX, endY);
beginPath()表示告訴瀏覽器“我要開(kāi)始畫(huà)了!”,把之前的記錄放棄了,這點(diǎn)有點(diǎn)像ps。moveTo()把光標(biāo)移動(dòng)到(startX, startY),再計(jì)算終點(diǎn)坐標(biāo),endX,endY,有點(diǎn)像高中學(xué)的參數(shù)方程。然后告訴瀏覽器,lineCap要round,線段的兩頭要是圓形的。有多粗呢?等于branchWidth。線段一直畫(huà)到(endX, endY)。
- if (depth <= 2){
- ctx.strokeStyle = 'rgb(0,' + (((rand() * 64) + 128) >> 0) + ',0)';
- } else {
- ctx.strokeStyle = 'rgb(' + (((rand() * 64) + 64) >> 0) + ',50,25)';
- }
如果是已經(jīng)畫(huà)到了***兩級(jí),即為葉子,那么就rgb就為(0, 128~192, 0)(rgb代表顏色,分別為紅綠藍(lán),red green blue)。還沒(méi)的話,就在(64~128, 50 ,25)中取。大家可能發(fā)現(xiàn)了,rgb必須為整數(shù),但是rand()只能rand實(shí)數(shù)。大家其實(shí)也注意到了有個(gè)” >> 0″,js當(dāng)中表示位運(yùn)算,整體向右移動(dòng)n位,0就是移動(dòng)0位。其實(shí)它的作用和Math.floor()一樣,但是速度更快。
動(dòng)手畫(huà)!
- ctx.stroke();
這個(gè)線段就畫(huà)好了,是時(shí)候準(zhǔn)備下它的分叉的時(shí)候了。
- newDepth = depth - 1;
- if (!newDepth)
- return;
如果這個(gè)線段是***一級(jí),就沒(méi)有分叉了,也是一個(gè)遞歸的終止條件。
- subBranches = (rand() * (maxBranch - 1)) + 1;
- branchWidth *= 0.7;
- for (var i = 0; i < subBranches; i++){
- newAngle = angle + rand() * maxAngle - maxAngle * 0.5;
- newLength = length * (0.7 + rand() * 0.3);
- drawTree(ctx, endX, endY, newLength, newAngle, newDepth, branchWidth);
- }
分叉數(shù)是1~3中的一個(gè)數(shù)。然后有多少個(gè)分叉,就畫(huà)幾條線段,newAngle為原角度調(diào)整90度之內(nèi),新長(zhǎng)度為原長(zhǎng)度的0.7~1.0之間。
***畫(huà)出主干,這棵樹(shù)就可以開(kāi)始畫(huà)了。
- drawTree(ctx, 320, 470, 60, -Math.PI / 2, 12, 12);
大家可能注意到角度為負(fù),不符合傳統(tǒng)觀念。但你要知道,畫(huà)布的縱坐標(biāo)和傳統(tǒng)的坐標(biāo)軸正好是相反的。
剩下可以發(fā)揮的東西還很多,比如大家可以調(diào)整各種參數(shù),使樹(shù)的顏色、大小變化,或者用這種方法去做些其他的事~
打完收工~附上文件:tree.zip