小练手:用HTML5 Canvas绘制谢尔宾斯基三角形
文章首发于我的知乎专栏,原地址:https://zhuanlan.zhihu.com/p/26606208
以前看到过一个问题:谢尔宾斯基三角形能用编程写出来么?该怎么写? - 知乎,在回答里,各方大神用各种语言各种方法实现了一遍,非常精彩。我当时也回答了这个问题,是用H5的Canvas实现的。这在前端技术上没什么难度,主要是算法比较有可玩性,所以当时就手痒了。
谢尔宾斯基三角形是分形图形的一种,大概很多人第一次见到它都是在中学教科书上。它长这样:

我用了两种方法构造它:直接绘制三角形和间接用折线逼近。
1.直接绘制三角形。具体方法就是先画一个大三角形,再递归绘制一堆倒三角形。这种方法最为直观也最好想。放码过来:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sierpinski Triangle</title>
</head>
<body>
<canvas id="canvas"
width="600" height="600"
style="display:block;margin:50px auto">
你的浏览器不支持canvas
</canvas>
</body>
<script type="text/javascript">
var context =
document.getElementById("canvas")
.getContext("2d"); //根据三顶点坐标绘制一个三角形
function triangle(p1,p2,p3){
context.moveTo(p1.x,p1.y);
context.lineTo(p2.x,p2.y);
context.lineTo(p3.x,p3.y);
context.lineTo(p1.x,p1.y);
} /*绘制谢尔宾斯基三角形的方法
p:正三角形中心点坐标,len:三角形边长*/
function SierpinskiTriangle(p,len){
var r=len/Math.sqrt(3);
//计算顶点坐标
var p1={x:p.x, y:p.y-r};
var p2={x:p.x-len/2, y:p.y+r/2};
var p3={x:p2.x+len, y:p2.y};
triangle(p1,p2,p3); //绘制正三角形外框
//递归绘制所有的倒三角形
middleTriangle(p1,p2,p3); function middleTriangle(p1,p2,p3){
var tp1={x:(p2.x+p3.x)/2, y:(p2.y+p3.y)/2};
var tp2={x:(p1.x+p3.x)/2, y:(p1.y+p3.y)/2};
var tp3={x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2};
triangle(tp1,tp2,tp3);
//递归前判断最短线条长度是否短于临界值
if((tp1.x-tp2.x)*(tp1.x-tp2.x)+
(tp1.y-tp2.y)*(tp1.y-tp2.y)>20){
middleTriangle(p1,tp2,tp3);
middleTriangle(p2,tp1,tp3);
middleTriangle(p3,tp1,tp2);
}
}
} //绘制
SierpinskiTriangle({x:300,y:360},560);
context.lineWidth = 0.5;
context.strokeStyle = "#F5270B";
context.stroke();
</script>
</html>
保存成html文件用浏览器打开,效果如下:

2.折线逼近法:

这个方法比较神奇,简单来说,是从一条水平线开始,每次递归都把所有线段替换成有规律的三段折线。无限递归下去,整段折线会越来越逼近谢尔宾斯基三角形。
用这个思路实现的SierpinskiTriangle函数如下,多了一个设置递归深度的depth参数:
/*绘制谢尔宾斯基三角形的方法
p:正三角形中心点坐标,len:三角形边长,depth:递归深度*/
function SierpinskiTriangle(p,len,depth){
var r=len/Math.sqrt(3);
//记录当前端点,默认为左下角顶点坐标
var currentPoint={x:p.x-len/2, y:p.y+r/2};
//记录当前方向角
var currentAngle=0; //旋转方法,将下次画线的方向逆时针旋转
function turn(angle){
currentAngle+=angle;
}
//画线方法,根据当前端点和画线方向绘制
function draw_line(length){
var angle=currentAngle/180*Math.PI;
currentPoint.x+=length*Math.cos(angle);
currentPoint.y-=length*Math.sin(angle);
context.lineTo(currentPoint.x,currentPoint.y);
} //开始画折线,如果深度是偶数便可直接绘制折线,否则需要以斜60度为初始方向
context.moveTo(currentPoint.x,currentPoint.y);
if (depth%2==0){
curve(depth,len,-60);
}else{
turn(60);
curve(depth,len,-60);
} function curve(order,length,angle)
{
if (order==0){
draw_line(length);
}else{
//递归画三段折线
curve(order-1,length/2,-angle);
turn(angle);
curve(order-1,length/2,angle);
turn(angle);
curve(order-1,length/2,-angle);
}
}
}
用这个函数替换上一段HTML中的SierpinskiTriangle函数就行了,调用时需要给depth参数赋值,推荐为9。我将depth从0到9十种情况的曲线放在同一个canvas里输出了,可供直观理解:

参考来源:
Sierpinski triangle
Sierpiński arrowhead curve
小练手:用HTML5 Canvas绘制谢尔宾斯基三角形的更多相关文章
- Python使用递归绘制谢尔宾斯基三角形
谢尔宾斯基三角形使用了三路递归算法,从一个大三角形开始,通过连接每一个边的中点,将大三角型分为四个三角形,然后忽略中间的三角形,依次对其余三个三角形执行上述操作. 运行效果: 源代码: 1 impor ...
- python 使用turtule绘制递归图形(螺旋、二叉树、谢尔宾斯基三角形)
插图工具使用Python内置的turtle模块,为什么叫这个turtle乌龟这个名字呢,可以这样理解,创建一个乌龟,乌龟能前进.后退.左转.右转,乌龟的尾巴朝下,它移动时就会画一条线.并且为了增加乌龟 ...
- python---使用递归实现谢尔宾斯基三角形及汉诺塔
渐入佳境. # coding: utf-8 import turtle ''' # =================turtle练手== def draw_spiral(my_turtle, lin ...
- 分形之谢尔宾斯基(Sierpinski)三角形
谢尔宾斯基三角形(英语:Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出,它是一种典型的自相似集.也有的资料将其称之为谢尔宾斯基坟垛. 其生成过程为: 取一个 ...
- 分形之谢尔宾斯基(Sierpinski)地毯
前面讲了谢尔宾斯基三角形,和这一节的将把三角形变为正方形,即谢尔宾斯基地毯,它是由瓦茨瓦夫·谢尔宾斯基于1916年提出的一种分形,是自相似集的一种. 谢尔宾斯基地毯的构造与谢尔宾斯基三角形相似,区别仅 ...
- 分形之谢尔宾斯基(Sierpinski)四面体
前面讲了谢尔宾斯基三角形,这一节的将对二维三角形扩展到三维,变成四面体.即将一个正四面体不停地拆分,每个正四面体可以拆分成四个小号的正四面体.由二维转变到三维实现起来麻烦了许多.三维的谢尔宾斯基四面体 ...
- 混沌分形之谢尔宾斯基(Sierpinski)
本文以使用混沌方法生成若干种谢尔宾斯基相关的分形图形. (1)谢尔宾斯基三角形 给三角形的3个顶点,和一个当前点,然后以以下的方式进行迭代处理: a.随机选择三角形的某一个顶点,计算出它与当前点的中点 ...
- 【数据结构与算法Python版学习笔记】递归(Recursion)——定义及应用:分形树、谢尔宾斯基三角、汉诺塔、迷宫
定义 递归是一种解决问题的方法,它把一个问题分解为越来越小的子问题,直到问题的规模小到可以被很简单直接解决. 通常为了达到分解问题的效果,递归过程中要引入一个调用自身的函数. 举例 数列求和 def ...
- html5 Canvas绘制图形入门详解
html5,这个应该就不需要多作介绍了,只要是开发人员应该都不会陌生.html5是「新兴」的网页技术标准,目前,除IE8及其以下版本的IE浏览器之外,几乎所有主流浏览器(FireFox.Chrome. ...
随机推荐
- iOS开发之 -- 帧动画的使用
在项目的开发过程中,我们经常会遇到使用动画的时候,比如我们在请求接口直接开始一个动画,请求结束后结束动画,下面我就给大家展示一个很方便的帧动画的用法: 代码如下: .h #import <Fou ...
- Linux产生随机数的几种常见方法
方法1: [root@localhost ~]# echo $RANDOM 方法二 [root@localhost ~]# openssl rand -base64 7X6HMer5hhY= 方法三 ...
- JSP内置对象——session
sessionsession表示客户端与服务器的一次会话Web中的session指的是用户在浏览某个网站时,从进入网站到浏览器关闭所进过的这段时间,也就是用户浏览这个网站所花费的时间从上述定义中可以看 ...
- [Android Tips] 17. 查看 APK 签名信息
从 APK 文件中获取签名信息 方法一 $ keytool -list -printcert -jarfile <path of APK> 方法二 解压 APK 文件,释放出 META-I ...
- Jsp 公用标签库
<%@ page language="java" pageEncoding="UTF-8"%> <%@ taglib prefix=" ...
- Struts2的表单标签还可以为集合中的对象赋值
•Struts 还允许填充 Collection 里的对象, 这常见于需要快速录入批量数据的场合 代码如下 : TestCollectionAction.java package com.atgu ...
- 浏览器加载不上css,样式走丢
来自:http://www.cnblogs.com/crizygo/p/5466444.html 问题描述:使用eclipse修改样式文件,浏览器的页面一时显示一时不显示,最后直接没有加载最新的css ...
- API网关+Kubernetes集群的架构替代了传统的Nginx(Ecs)+Tomcat(Ecs)
API网关为K8s容器应用集群提供强大的接入能力_最佳实践_API 网关-阿里云 https://help.aliyun.com/document_detail/71623.html 在最后一节,我们 ...
- echarts容器动态设置高度
测试提了bug,柱状图数据多的情况下,都叠到了一起,效果如下图. 要解决这个bug,首先想到的是让柱状图的容器自适应高度.于是,把原本div上写固定的高度去掉. <div id="my ...
- 最长回文---hdu3068 (回文串 manacher 算法模板)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题意很清楚:就是求一个串s的子串中最长回文串的长度:这类题用到了manacher算法 #incl ...