参考链接:https://blog.csdn.net/u010298576/article/details/76609244

HTML网页源码:

 1 <!DOCTYPE html>
2 <html>
3 <head lang="en">
4 <meta charset="UTF-8">
5 <title>生长的树</title>
6 <style>
7 html , body {
8 margin: 0;
9 padding: 0;
10 width: 100%;
11 height: 100%;
12 overflow: hidden;
13 background-color: #fff;
14 }
15 </style>
16 </head>
17 <body>
18 <canvas id="myCanvas">此浏览器不支持canvas</canvas>
19 <script src="tree.js"></script>
20 </body>
21 </html>

js代码:

  1 /**
2 * Created by 004928 on 2017/8/2.
3 */
4 (function (window) {
5
6 var w = window.innerWidth , h = window.innerHeight ; // innerheight 返回窗口的文档显示区的高度。 innerwidth 返回窗口的文档显示区的宽度。
7 var ctx = null ;
8 var treeNum = 5 ;
9 var initRadius = 25 ; // 树干的初始宽度
10 var maxGeneration = 5 ; // 最多分支的次数
11 var branchArray = null ; // 树干的集合
12 var flowers = []; // 花的集合
13
14 window.MyRequestAnimationFrame = window.requestAnimationFrame || //requestAnimationFrame的速度是由浏览器决定的,不同浏览器会自行决定最佳的帧效率。
15 window.mozRequestAnimationFrame || //解决了浏览器不知道javascript动画什么时候开始、不知道最佳循环间隔时间的问题。
16 window.webkitRequestAnimationFrame || //这个API是浏览器提供的js全局方法,针对动画效果。
17 window.msRequestAnimationFrame ;
18
19 window.MyCancelRequestAnimationFrame = window.cancelRequestAnimationFrame || //这是是把动画结束的
20 window.mozCancelRequestAnimationFrame ||
21 window.webkitCancelRequestAnimationFrame ||
22 window.msCancelRequestAnimationFrame ;
23
24 /**
25 * 初始化canvas
26 */
27 function initCanvas () {
28 var canvas = document.getElementById("myCanvas");
29 canvas.setAttribute('width' , w);
30 canvas.setAttribute('height' , h);
31 if(canvas.getContext) { //getContext() 方法返回一个用于在画布上绘图的环境。目前参数唯一的合法值是 "2d",它指定了二维绘图,并且导致这个方法返回一个环境对象,该对象导出一个二维绘图 API。
32 ctx = canvas.getContext('2d');
33 initTree();
34 loop();
35 }
36 }
37
38 /**
39 * 初始化树的数量
40 */
41 function initTree () {
42 branchArray = new BranchArray ();
43 for(var i = 0 ; i < treeNum ; i++) {
44 branchArray.add(new Branch(w / 2 , h));
45 }
46 }
47
48 function BranchArray () { //如果没有手动返回对象,则默认返回this指向的这个对象,也是隐性的
49 this.branchs = []; //具体见https://www.cnblogs.com/echolun/p/10903290.html
50 }
51
52 /**
53 * 树干
54 * @param x
55 * @param y
56 * @constructor
57 */
58 function Branch (x , y) {
59 this.x = x ;
60 this.y = y ;
61 this.radius = initRadius ;
62 this.angle = Math.PI / 2 ; // 树枝的初始角度
63 this.speed = 2.35 ; // 树生长的速度
64 this.generation = 1 ;
65 }
66
67 /**
68 * 生长
69 */
70 Branch.prototype.grow = function () {
71 this.draw();
72 this.update();
73 }
74
75 Branch.prototype.draw = function () {
76 ctx.fillStyle = '#55220F';
77 ctx.beginPath();
78 ctx.arc(this.x , this.y , this.radius , 0 , 2 * Math.PI);
79 ctx.fill();
80 }
81
82 /**
83 * 更改数的高度以及扭曲度
84 */
85 Branch.prototype.update = function () {
86
87 // 计算树干每次的扭曲角度,因为树一般不是笔直生长的,都会有不规则的扭曲
88 this.angle += random( -0.1 * this.generation , 0.1 * this.generation ); //因为树枝半径小的话那么它分支的角度可能会更大
89
90 var vx = this.speed * Math.cos(this.angle); //speed就表示这个树枝的生长长度
91 // 因为初始角度设置为Math.PI , 所以vy要取负数
92 var vy = - this.speed * Math.sin(this.angle);
93
94 if(this.radius < 0.99 || this.generation > maxGeneration) {
95 branchArray.remove(this);
96 }
97
98 this.x += vx ;
99 this.y += vy ;
100
101 this.radius *= 0.99 ;
102
103 if(this.radius >= 0.9) {
104 // 计算当前是第几代分支
105 var g = (maxGeneration - 1) * initRadius / (initRadius - 1) / this.radius + (initRadius - maxGeneration) / (initRadius - 1) ;
106 if( g > this.generation + 1) {
107 this.generation = Math.floor(g) ; //这个应该是把g向下取整
108 // 随机创建分支
109 for(var i = 0 ; i < random(1,3) ; i++) {
110 this.clone(this);
111 }
112 }
113 }
114
115 }
116
117 /**
118 * 创建分支
119 * @param b
120 */
121 Branch.prototype.clone = function (b) {
122 var obj = new Branch(b.x , b.y); //定义一个对象
123 obj.angle = b.angle ;
124 obj.radius = b.radius ;
125 obj.speed = b.speed;
126 obj.generation = b.generation;
127 branchArray.add(obj);
128 // 如果当前分支次数大于3则创建花,这样可以让花在树的顶端显示
129 if( b.generation > 3 ) {
130 flowers.push(new Flower(b.x , b.y));
131 }
132 }
133
134 /**
135 * 添加树干到集合中
136 * @param b
137 */
138 BranchArray.prototype.add = function (b) {
139 this.branchs.push(b);
140 }
141 /**
142 * 从集合中移除树干
143 * @param b
144 */
145 BranchArray.prototype.remove = function (b) {
146 if( this.branchs.length > 0) {
147 var index = this.branchs.findIndex(function (item) {
148 return b === item ;
149 })
150 if(index != -1) {
151 this.branchs.splice(index , 1);
152 }
153 }
154 }
155
156 /**
157 * 花
158 * @param x
159 * @param y
160 * @constructor
161 */
162 function Flower (x , y) {
163 this.x = x ;
164 this.y = y ;
165 this.r = 1 ; // 花瓣的半径
166 this.petals = 5 ; // 花瓣数量
167 this.speed = 1.0235 ;// 花的绽放速度
168 this.maxR = random(3 , 7); // 花的大小
169 }
170
171 /**
172 * 花朵开放(通过改变花的半径实现开放的效果)
173 * @param index
174 */
175 Flower.prototype.update = function (index) {
176 if(this.r == this.maxR) {
177 flowers.splice(index , 1);
178 return ;
179 }
180 this.r *= this.speed ;
181 if(this.r > this.maxR) this.r = this.maxR ;
182 }
183
184 /**
185 * 绘制花朵
186 */
187 Flower.prototype.draw = function () {
188 ctx.fillStyle = "#F3097B" ;
189 for(var i = 1 ; i <= this.petals ; i++) {
190 var x0 = this.x + this.r * Math.cos( Math.PI / 180 * (360 / this.petals) * i) ;
191 var y0 = this.y + this.r * Math.sin( Math.PI / 180 * (360 / this.petals) * i) ;
192 ctx.beginPath();
193 ctx.arc(x0 , y0 , this.r , 0 , 2 * Math.PI) ;
194 ctx.fill();
195 }
196 ctx.fillStyle = "#F56BC1";
197 ctx.beginPath();
198 ctx.arc(this.x , this.y , this.r / 2 , 0 , 2 * Math.PI) ;
199 ctx.fill();
200 }
201
202 function random (min , max) {
203 return Math.random() * (max - min) + min ;
204 }
205
206 /**
207 * 循环遍历所有树干和花,并调用更新和draw方法,实现动画效果
208 */
209 function loop () {
210 for(var i = 0 ; i < branchArray.branchs.length ; i ++) {
211 var b = branchArray.branchs[i];
212 b.grow();
213 }
214 var len = flowers.length ;
215 while (len --) {
216 flowers[len].draw();
217 flowers[len].update();
218 }
219 MyRequestAnimationFrame(loop);
220 }
221
222 window.onload = initCanvas;
223
224 })(window)

HTML 5 Canvas 参考手册:https://www.w3school.com.cn/tags/html_ref_canvas.asp

1、树是由一个一个实心圆来构成的

test网页HTML源码:

 1 <!DOCTYPE html>
2 <html>
3 <head lang="en">
4 <meta charset="UTF-8">
5 <title>生长的树</title>
6 <style>
7 html , body {
8 margin: 0;
9 padding: 0;
10 width: 100%;
11 height: 100%;
12 overflow: hidden;
13 background-color: #fff;
14 }
15 </style>
16 </head>
17 <body>
18 <canvas id="myCanvas">此浏览器不支持canvas</canvas>
19 <script>
20 (function (window) {
21 var ctx=null;
22 var w = window.innerWidth , h = window.innerHeight ;
23 var canvas = document.getElementById("myCanvas");
24 canvas.setAttribute('width' , w);
25 canvas.setAttribute('height' , h);
26 if(canvas.getContext) { //getContext() 方法返回一个用于在画布上绘图的环境。目前参数唯一的合法值是 "2d",它指定了二维绘图,并且导致这个方法返回一个环境对象,该对象导出一个二维绘图 API。
27 ctx = canvas.getContext('2d');
28 ctx.fillStyle = '#55220F';
29 ctx.beginPath();
30 ctx.arc(100 ,90 , 25 , 0 , 2 * Math.PI);
31 ctx.fill(); //用所选颜色将圆染色
32
33 ctx.fillStyle = '#55220F';
34 ctx.beginPath();
35 ctx.arc(100 ,80 , 25 , 0 , 2 * Math.PI);
36 ctx.fill();
37
38 ctx.fillStyle = '#55220F';
39 ctx.beginPath();
40 ctx.arc(100 ,70 , 25 , 0 , 2 * Math.PI);
41 ctx.fill();
42 }
43
44 })(window)
45 </script>
46 </body>
47 </html>

你会发现三个圆可以构成一个树的树干(前提是圆与圆之间的距离不能太大。很好理解,距离太大他们重合部分就会小,就看起来不像树干了)

2、树的扭曲

this.angle += random( -0.1 * this.generation  , 0.1 * this.generation  );  //因为树枝半径小的话那么它分支的角度可能会更大

树的生长方向是通过角度angle来控制的,在区间[-0.1,0.1]中随机产生角度,然后通过三角函数计算x,y轴偏移量,之后就在坐标(x,y)位置画圆

因为this.generation 的大小是和圆的半径相关(关系在下面),那么半径小的时候this.generation就会大,那么也就是

树枝半径小的话那么它分支的角度可能会更大

3、树的分支

对于在什么时候应该让树去产生分支,我之前的想法是规定一段树干的长度,然后每次计算当前位置
距离上一个分支点的距离是否大于我规定的长度,然后产生分支,但是这样就会看到每节分支之间的长度是一样的
看起来不美观,比较死板,最终使用双曲线方程 y=-1/x 去控制因为双曲线的走势是先快后慢的,
而树的生长也是越往后分支越多,可能你会奇怪曲线是先快后慢,树分叉是先慢后快的,不符合逻辑啊
别着急,下看下图:

从图中可以看见,X轴表示树干的粗细,Y轴表示分支的次数,当树干越来越细的时候,X轴变小
是不是Y轴就越来越大,且是先慢后快,这样就符合我们的需求了。

又因为我们不能让它无条件产生分支,所以我们需要加一个条件限制,因为如果无条件分支,那么这棵树的分支集合里面的分支对象会越来越多,永远也执行不完,就会死循环。

我们控制分支最大数量为maxGeneration

var g = (maxGeneration - 1) * initRadius / (initRadius - 1) / this.radius + (initRadius - maxGeneration) / (initRadius - 1) ;
            

4、花的开放

它是通过判断分支个数来决定什么时间开花,比如分支大于3就开花。花的属性具体看代码

5、程序整体运行

function loop () {
for(var i = 0 ; i < branchArray.branchs.length ; i ++) {
var b = branchArray.branchs[i];
b.grow();
}
var len = flowers.length ;
while (len --) {
flowers[len].draw();
flowers[len].update();
}
MyRequestAnimationFrame(loop);
}

由程序我们知道,每当一个branch执行grow函数的时候,它执行过后,里面的属性可能会发生改变,然后它有被放入branchArray里面以待下次执行

因为我们不停的往branchArray里面放树枝(即,branch),那么又因为只有分支个数大于某个值才会移除这个树枝。所以它是先把所有树枝长出来之后才开花

所以loop这个函数只执行了一次

js实现一棵树的生长的更多相关文章

  1. js画一棵树

    用纯js画一棵树.思路: 1.一棵树的图片,作为页面背景: 2.通过html5中的canvas画布进行遮罩: 3.定时每隔10ms,从下往上清除1px的遮罩: <!DOCTYPE html> ...

  2. LeetCode——Same Tree(判断两棵树是否相同)

    问题: Given two binary trees, write a function to check if they are equal or not. Two binary trees are ...

  3. 小希的迷宫(MST单棵树判断法则)

    小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  4. 【LeetCode】Symmetric Tree 推断一棵树是否是镜像的

    题目:Symmetric Tree <span style="font-size:18px;"><span style="font-size:18px; ...

  5. A Simple Problem with Integers(100棵树状数组)

    A Simple Problem with Integers Time Limit: 5000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K ...

  6. 2017年陕西省网络空间安全技术大赛——种棵树吧——Writeup

    2017年陕西省网络空间安全技术大赛——种棵树吧——Writeup 下载下来的zip解压得到两个jpg图片,在Kali中使用binwalk查看文件类型如下图: 有两个发现: 1111.jpg 隐藏了一 ...

  7. bzoj 2816: [ZJOI2012]网络 (LCT 建多棵树)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2816 题面: http://www.lydsy.com/JudgeOnline/upload ...

  8. 如何打印一棵树(Java)

    1.有一棵多叉树,将它打印出来. import java.util.LinkedList; /** * 需求:按层打印一棵树 * 说明:树是保存在一个链表中 * created by wangjunf ...

  9. [剑指Offer]判断一棵树为平衡二叉树(递归)

    题目链接 https://www.nowcoder.com/practice/8b3b95850edb4115918ecebdf1b4d222?tpId=0&tqId=0&rp=2&a ...

随机推荐

  1. 解决Establishing SSL connection without server‘s identity verification is not recommended.

    每次从数据库中进行查询或者其他操作控制台都会出现以下警告,虽说不是error,但是很显眼.. WARN: Establishing SSL connection without server's id ...

  2. Azure 存储简介

    Azure Storage Account(存储账户)包含所有Azure Storage的数据对象,包括Blob.Data Lake Gen2,File.Queue.Disk和Table等服务,该St ...

  3. ctfhub技能树—信息泄露—PHPINFO

    打开靶机 查看页面,是PHP info界面 只有这一个页面,查找一下有没有flag 拿到flag 浅谈ctf中phpinfo需要关注的点(转自先知社区) 1 https://xz.aliyun.com ...

  4. MyBatis初级实战之四:druid多数据源

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  5. LocalDateTime去掉T

    最近在使用阿里巴巴的fastjson反序列化对象的时候,对象里面时间格式属性总是会多了一个T  2021-1-09T18:29:09.097 这个T是啥国际标准,但是我们的前端又不需要这个T,所以就要 ...

  6. Android事件分发机制五:面试官你坐啊

    前言 很高兴遇见你~ 事件分发系列文章已经到最后一篇了,先来回顾一下前面四篇,也当个目录: Android事件分发机制一:事件是如何到达activity的? : 从window机制出发分析了事件分发的 ...

  7. CODING 再携手腾讯云 Serverless,让开发者跑步上云

    近年来,腾讯云持续在云原生领域打磨和完善产品矩阵,致力于为开发者上云提供更好的产品和服务.继前段时间 CODING CI 助力腾讯云 Serverless 全新应用控制台.持续保障 Serverles ...

  8. 攻击JWT的一些方法

    JWT安全隐患之绕过访问控制 https://mp.weixin.qq.com/s/xe8vOVhaysmgvxl-A3nkBA 记录一次JWT的越权渗透测试 https://mp.weixin.qq ...

  9. spark join 广告用户特征 与广告特征的 join 拿到训练集

    spark join 广告特征做广播

  10. Hyper-v安装centos后的网络配置

    修改配置文件 进入目录:cd /etc/sysconfig/network-scripts/ 修改ifcfg-eth0文件(不通机器文件名可能不同,可以通过 ip addr 命令查看网卡名) HWAD ...