以前就看到了这个东西,由于太忙了最近才有时间来实现这个;

该文章适合有一定 canvas 基础的人阅读;

首先说说他的原理:

The construction of the Pythagoras tree begins with a square. Upon this square are constructed two squares, each scaled down by a linear factor of ½√2, such that the corners of the squares coincide pairwise. The same procedure is then applied recursively to the two smaller squares, ad infinitum. The illustration below shows the first few iterations in the construction process.

Order 0 Order 1 Order 2 Order 3

以上文字和图像摘自维基百科:https://en.wikipedia.org/wiki/Pythagoras_tree_(fractal)

大意是先有一个正方形,然后在他的上方构建2个正方形,使他们三条边构成一个三角形,再向上蔓延,循环往复,直至无穷;

当然我们是不可能做无穷的,这样很容易崩溃的;

先写上代码,我做了比较详细的注释:

首先制作背景及一些条件的确定:

var Init = function() {var canvas = document.getElementById('canvas');this.ctx = canvas.getContext('2d');this.vw = canvas.width = window.innerWidth;this.vh = canvas.height = window.innerHeight; //定义 canvas大小this.boxSize = 80; //树的大小this.maxLevel = 6; //树的节数this.color1 = { h: 70, s: 75, l: 51 }; //树顶颜色 青绿色this.color2 = { h: 310, s: 98, l: 17 }; //树根颜色 咖啡色// HSL色彩模式:就是色调(Hue)、饱和度(Saturation)、亮度(Lightness)三个颜色通道的改变以及它们相互之间的叠加来获得各种颜色,色调(Hue)色调最大值360,饱和度和亮度有百分比表示0-100%之间。// H:// Hue(色调)。0(或360)表示红色,120表示绿色,240表示蓝色,也可取其他数值来指定颜色。取值为:0 - 360// S:// Saturation(饱和度)。取值为:0.0% - 100.0%// L:// Lightness(亮度)。取值为:0.0% - 100.0%// 当然这里 rgb 肯定也是可以的this.mouse = {x: this.vw / 2,y: this.vh / 2}; //默认鼠标位置 屏幕中心this.lean = 0; //三角形顶点左右偏移程度this.scale = 0; //三角形高度比例this.x = (this.vw - this.boxSize) / 2;//x 时候从浏览器左边开始到树根左边的长度 同样的也是 浏览器右边到树根右边的长度this.y = this.vh;this.getColors(this.color1, this.color2, this.maxLevel + 1);//取色,将颜色从 color1到 color 2 平均分成 maxLevel+1 种,你可以加3,加4 因为 maxLeval 不足的话有一部分颜色计算出来是白色会与白色背景冲突
}

上部有 getColor 函数,现在我们来定义他,他的作用是返回一个用来存储颜色的数组,颜色是根据上述的 color1和 color2 还有this.maxlevel 来确定的;

循环 maxlevel 来分离出这两种颜色之间的不同色差;

Init.prototype.getColors = function(c1, c2, steps) {this.colors = [];var lerp = this.methods.lerp;for (let i = 0; i < steps; i++) {const t = i / (steps - 1);const h = Math.round(lerp(c1.h, c2.h, t));const s = Math.round(lerp(c1.s, c2.s, t));const l = Math.round(lerp(c1.l, c2.l, t));this.colors.push('hsl('+h+','+s+'%,'+l+'%)');}
}

再是绘制出树根的代码,他是根据 size ,scale,lean,level 即,正方形的边长,构成三角形的高的比例,三角形的顶点的左右偏移,还有 level 即上部代码的颜色等级;

他的执行顺序是先绘制左边的正方形,在绘制右边的;

Init.prototype.drawTree = function(size, scale, lean, level) {//最初值80 0.4 0 5//长度,弯曲度,倾斜度,颜色域var ctx = this.ctx;var constitute = this.calcBranches(size, scale, lean);//获取构成三角形的边长和角度
ctx.save(); //因为倾斜的角度是不一样的,所以需要存储ctx.fillRect(0, 0, size, -size); //构成三角形的绘出正方形,初始是树根的位置
ctx.fillStyle = this.colors[level]; //填充树的颜色
ctx.translate(0, -size); //改变canvas的坐标位置 ,移至正方形左上角ctx.rotate(-constitute.leftAngle); //根据一个角度对图像进行旋转,负值代表向左倾斜if (level) {this.drawTree(constitute.leftSize, scale, lean, level - 1);} else {ctx.fillRect(0, 0, constitute.leftSize, -constitute.leftSize);//最后一种颜色,递归结束
    }//根据颜色域,来递归进行渲染三角形 直至颜色用完
ctx.translate(constitute.leftSize, 0); //改变坐标  横坐标移至左边正方形的右下角ctx.rotate(constitute.rightAngle + constitute.leftAngle); //旋转//渲染右边的正方形,rotate 是整个绘图都会旋转,因为刚刚 rotate(-leftAngle),所以加上 leftAngle 是让整个图像变成水平,再根据 rightAngle 进行右旋转if (level) {this.drawTree(constitute.rightSize, scale, lean, level - 1);} else {ctx.fillRect(0, 0,constitute.rightSize, -constitute.rightSize);} //递归绘制右正方形
ctx.restore();
}

上述代码中有一个函数为calcBranches 这是最为关键的代码,他的只要作用是通过计算得出下一个绘制的正方形的边长,角度,顺便将他们存储起来,减缓浏览器的压力;

关于缓存,是通过闭包来实现的:

一个简单的例子就是:

var fun1 = (function (){var arr = [];var fun2 = function(){arr.push( (new Date()).valueOf() )      }return fun2;
})()

这段代码执行的时候 fun1 当即会执行一遍,这样就会出现一个 arr 变量,但是这个变量并不会暴露出来;

但是而每当调用fun1(fun2)时便会对 arr 作出改变,这便是缓存的原理;

calcBranches代码:

Init.prototype.calcBranches = (function() {var cache = {};var memoize = function(width, scale, lean) {//长度,三角形高度比例,倾斜度 初始值 80 0.4 0//lean 范围 -0.5 到0.5 lean接近0.5时 左树枝几乎为0 同理 -.5时 有树枝几乎为0 即构成三角形皆为直角三角形var memoKey = width+'-'+scale+'-'+lean;if (!cache[memoKey]) {//存储这三个值,形成缓存,减少绘制压力var currentH = width * scale; //当前高度即构成三角形对于正方形的高度var result = {leftSize: Math.sqrt(currentH ** 2 + (width * (0.5 - lean)) ** 2),rightSize: Math.sqrt(currentH ** 2 + (width * (0.5 + lean)) ** 2),leftAngle: Math.atan(currentH / ((0.5 - lean) * width)),rightAngle: Math.atan(currentH / ((0.5 + lean) * width))};//**表示几次方 //关键代码,根据当前的长度//初始化时  第一个width = boxSize;//leftSize 构成三角形的左边长//rightSize 右边长//leftAngle 左边的角度的反正切值  -PI/2 到 PI/2 之间的弧度值。 是一个角度//rightAngle 右边的角度的反正切值 同上cache[memoKey] = result;}return cache[memoKey];}memoize.cache = cache;return memoize;
})();//通过闭包实现缓存;

再就是渲染函数,他的主要作用就是绘制初始图形,并且通过计算得出需要绘制的正方形的 scale,lean 这2个关键影响因素;

他们之间的关系你可以自己随便改;

Init.prototype.render = function(){var map = this.methods.map,ctx = this.ctxvar scale = map(this.mouse.y, this.vh, 0, 0, 0.8)//通过 map函数得出高的比例 主要来源鼠标 y 的值var lean = map(this.mouse.x, 0,this.vw, 0.5, -0.5)//通过 map函数得出左右倾斜程度 只要影响:鼠标 x 的值
ctx.clearRect(0,0,this.vw,this.vh)//清空画布
ctx.save(); //因为颜色会渐变,所以需要使用 save 存储为染色前的状态ctx.fillStyle = this.colors[this.maxLevel];//树根颜色ctx.translate(this.x,this.y); //将 canvas 坐标移动到 x,0  x 是树根左边距浏览器左边的长度this.drawTree(this.boxSize, scale, lean,this.maxLevel); //绘制初始树
ctx.restore();requestAnimationFrame(this.render.bind(this));
}

他们之间所用的函数, lerp,map都是纯函数,即当输入的数是不变的情况下,不管输入几次他的输出都是固定的,即没有随机数的影响,比如时间戳等等;

Init.prototype.methods = {//pure functionlerp: function lerp(a, b, t) {return a + (b - a) * t;},map:function map(x, a, b, c, d) {return c + (d - c) * ((x - a) / (b - a)) || 0;}
}

最后一步, new 出对象,进行 render ,再添加监听鼠标移动事件:

var init = new Init();
init.render();
window.addEventListener("mousemove", function(event) {init.mouse.x = event.clientX;init.mouse.y = event.clientY;});

基本过程便是这样;

部分代码我也是从网上获取灵感;

我的 demo 中

这个是最基本的构成;最底下的我取名树根;在上部是树枝;

再是:

该红色的高就是根据鼠标的高度来生成的即代码中的currentH;

  

关于该红色的点的定位主要由鼠标位置和网页大小组成的;代码中的scale决定该点的上下高度(主要受鼠标纵坐标影响),即上面所说的高,而 lean 决定该点的左右坐标(主要受鼠标横坐标影响);

demo地址:https://grewer.github.io/JsDemo/pythagorasTree/pythagorasTree.html

github:https://github.com/Grewer/JsDemo/blob/master/pythagorasTree/pythagorasTree.html

希望大家能给个推荐或 star ,十分感谢!

完;

转载于:https://www.cnblogs.com/Grewer/p/8052805.html

毕达哥拉斯树(pythagorasTree)原理解析及canvas动画实现相关推荐

  1. Merkle Tree(默克尔树)原理解析

    Merkle Tree(默克尔树)原理解析 一.Merkle Tree 1.1 Merkle Tree的特点 二.Hash list 三.Merkle tree VS Hash list 四.Merk ...

  2. 小程序直播-疯狂点赞Canvas动画实现原理解析

    近期,电商直播业务热火朝天,直播间有一个很重要的互动:点赞. 为了烘托直播间的氛围,直播相对于普通视频或者文本内容,点赞通常有两个特殊需求: 点赞动作次数不限制,引导用户疯狂点赞 直播间的所有疯狂点赞 ...

  3. Android动画-Animation原理解析

    Android动画-Animation原理解析 一.概述 在android中动画分为3类,帧动画.补间动画.属性动画 今天要说的就是"补间动画",补间动画的基类是Animation ...

  4. Canvas 动画引擎解析与微信小程序中的应用

    点击观看大咖分享 抗击疫情,腾讯云在行动.在开发微信小程序的过程中,我们经常需要展现一些图形和图表.目前市面上有好几款常用的图形库,在这些图形库的底层都有渲染引擎在支撑. ZRender 是其中一款非 ...

  5. 简单的canvas动画原理

    简单的canvas动画原理一般就是如下步骤: setInterval(    function(){        draw(ctx);        update(canvas.width,canv ...

  6. html动画的 原理,HTML5 Canvas动画效果实现原理

    在线演示 使用html5画布能够帮助我们快速实现简单的动画效果,基本原理如下: 每隔一定时间绘制图形并且清除图形,用来模拟出一个动画过程,可以使用context.clearrect(0, 0, x, ...

  7. 毕达哥拉斯树(Pythagoras-tree)

    分形几何学是一门以不规则几何形态为研究对象的几何学.一个数学意义上分形的生成是基于一个不断迭代的方程式,即一种基于递归的反馈系统.虽然分形是一个数学构造,它们同样可以在自然界中被找到,这使得它们被划入 ...

  8. 5、每周一点canvas动画--速度

    查看全文 http://www.taodudu.cc/news/show-5250190.html 相关文章: 9个非常有趣的HTML5 Canvas动画特效合集 小程序直播-疯狂点赞Canvas动画 ...

  9. 【Unity】 Spine渲染原理解析与源码解读

    Spine渲染原理解析与源码解读 安装环境 从Spine编辑器导出 将资源导入Unity 基础概念 其他相关概念 Spine架构 Spine运行时的各个模块 有状态(Stateful) 和 无状态(S ...

最新文章

  1. 高二下学期计算机考试试题及答案,福建省泉州市第五高级中学校2020-2021学年高二下学期期中考试数学试题含答案.docx...
  2. python三十五:pickle模块
  3. servelet基础
  4. 23-初识正则表达式
  5. python sendto函数返回值_有返回值的函数amp;闭包(python)
  6. 【华为云技术分享】手把手教你如何在ARM上源码编译Redis
  7. 基于事件的异步模式概述
  8. e320/t420/w520等 qm67/hm65/hm67等 6系芯片组更新BIOS以安装三代酷睿cpu提升性能的想法
  9. 操作系统-------------------内存空间的分配方式(连续分配和非连续分配和虚拟存储技术)
  10. 嵌入式车牌识别与称重系统
  11. python 如何实现依据依存关系构造邻接矩阵(有向图)
  12. mac os 直接打开html文件,macos – 在Mac OS X上打开磁盘设备文件以进行写访问
  13. response响应讲解
  14. vue中echarts纵轴添加点击事件
  15. 关于计算机图像基础知识的整理
  16. 学IT,看教程,看视频,你必须知道的学习网站
  17. 分布式系统以及分布式系统架构的优缺点
  18. win11更改系统字体的方法
  19. 格林尼治时间2016-10-15T00:00:00.000+08:00转Date
  20. java怎么输出保留两位小数_剖析Java输出怎么保留两位小数

热门文章

  1. 求图的割点,割边(啊哈算法)
  2. margin属性总结
  3. Pig股票交易数据处理
  4. 数据结构中的status
  5. 用mantis进行缺陷管理
  6. oracle-04045,OracleORA-04045errorsduringrecompilation/revalidation
  7. matlab 绘制椭圆锥波束指向示意图
  8. 嵊泗花鸟岛图片切换【走光效果】
  9. Date日期格式修改
  10. 论文写作笔记3:JAMIA-相关论文