canvas里面如何基于随机点绘制一个多边形的方法


Posted in HTML / CSS onJune 13, 2018

起因

今天在学习《HTML5+Javascript动画基础》这本书的时候,在第八章的第三节讲到如何用三个弹簧连接三个点来做拉伸运动。

在做完例子之后,就想到如果是四个点,五个点,怎么样。

就改写了一下代码,把点的数目变量化。最终的效果是能实现各个点最终的拉伸运动到平衡,可是点之间的连线不是很好看,有些是交叉的。

canvas里面如何基于随机点绘制一个多边形的方法

于是就想着能不能优化这一块。

旋转连线

前面例子里面的点,都是随机位置,所以连线不可控。所以想先从这块着手。

先以某一个点为参照点,获得其他点相对于这个点的角度。

然后按照角度从小到大的去连接这些点,这样就能画出一个正常的多边形了。

大致实现代码如下:

let balls = [];
let ballNum = 6;
let firstBall = null;
while(ballNum--) {
  let ball = new Ball(20, parseColor(Math.random() * 0xffffff))
  ball.x = Math.random() * width;
  ball.y = Math.random() * height;
  balls.push(ball)

  if (!firstBall) {
    firstBall = ball
    ball.angle = 0
  } else {
    const dx = ball.x - firstBall.x,
          dy = ball.y - firstBall.y;

    ball.angle = Math.atan2(dy, dx);
  }
}

// 尝试让球连线是一个正多边形
balls = balls.sort((ballA, ballB) => {
  return ballA.angle - ballB.angle
})

这样在最后绘制连线的时候,遍历数组就能按照角度从小到大来绘制了。

效果如下:

canvas里面如何基于随机点绘制一个多边形的方法

这样是能极大的减少交叉线的情况,可还是无法完全避免。

接下来,想尝试优化这个方案,比如angle用Math.abs来取正,或者每一个点都找夹角最小的点来连线。可是结果都不行,无法避免交叉线。

基于中心点旋转

后面又想到一个思路,如果能确定多边形的中心点,那么分别计算所有点相对于中心点的夹角,就能以顺时针或者逆时针来连接这些点。

可是在网上找了半天,所有点算法里面,都是要求有一系列按某个时针顺序排列的点。

可是如果我有这些点,就已经能绘制多边形了。只好放弃

X轴两极点分割

无奈之下只好找Google,然后就发现了知乎上的一个答案挺好的: 如何将平面上无序的一组点连成一个简单多边形?

具体算法描述,大家看那个答案就好,我就不赘述了。

不过在连接上链和下链的时候,其实只要保证上链是X轴降序连接,下链是X轴升序连接即可(以逆时针方向绘制)。至于X轴相同的点,不管是优先Y轴大的还是小的都可以。

实现的时候,是严格按照答案里面的算法实现的。

在判断一个点是属于上链还是下链的时候,一开始想的是基于两点确定直线的函数方程,再引入点的坐标来计算。不过后面想到,所有的点都以最左边的极点来计算斜角,然后根据角度大小来划分,视觉上更好理解。

大致代码如下:

let balls = [];
let tempBalls = [];
let ballNum = 6;
let isDragingBall = false;

while(ballNum--) {
  let ball = new Ball(10, parseColor(Math.random() * 0xffffff))
  ball.x = Math.random() * width;
  ball.y = Math.random() * height;
  tempBalls.push(ball)
}

// 让点按X轴升序排序
tempBalls = tempBalls.sort((ballA, ballB) => {
  return ballA.x - ballB.x
})

// 找X轴左右极点
let firstBall = tempBalls[0],
    lastBall = tempBalls[tempBalls.length -1];
let smallXBalls = tempBalls.filter(ball => ball.x === firstBall.x),
    bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)

// 处理左右极点有多个的情况
if (smallXBalls.length > 1) {
  smallXBalls.sort((ballA, ballB) => {
    return ballB.y - ballA.y
  })
}
if (bigXBalls.length > 1) {
  bigXBalls.sort((ballA, ballB) => {
    return ballB.y - ballA.y
  })
}

firstBall = smallXBalls[0]
lastBall = bigXBalls[0]

// 获得极点连线的角度
let splitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);
let upperBalls = [],
    lowerBalls = [];

// 所有其他点跟firstBall计算角度
// 大于splitLineAngle的都是下链
// 其他是上链
tempBalls.forEach(ball => {
  if (ball === firstBall || ball === lastBall) {
    return false
  }
  let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x);
  if (angle > splitLineAngle) {
    lowerBalls.push(ball)
  } else {
    upperBalls.push(ball)
  }
})

// 处理X轴相同情况的排序
lowerBalls = lowerBalls.sort((ballA, ballB) => {
  if (ballA.x !== ballB.x) {
    return ballA.x - ballB.x
  }
  return ballB.y - ballA.y
})

upperBalls = upperBalls.sort((ballA, ballB) => {
  if (ballA.x !== ballB.x) {
    return ballB.x - ballA.x
  }
  return ballB.y - ballB.x
})

// 逆时针连接所有的点
balls = [firstBall].concat(lowerBalls, [lastBall], upperBalls)

balls = balls.map((ball, i) => {
  ball.text = i + 1;
  return ball
})

最终返回的balls,就是按逆时针排序的多边形的点了。

效果如下:

canvas里面如何基于随机点绘制一个多边形的方法

各个球的内部状态如下:

canvas里面如何基于随机点绘制一个多边形的方法 

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

HTML / CSS 相关文章推荐
纯css3无js实现的Android Logo(有简单动画)
Jan 21 HTML / CSS
使用Filters滤镜弥补CSS3的跨浏览器问题以及兼容低版本IE
Jan 23 HTML / CSS
使用CSS3制作饼状旋转载入效果的实例
Jun 23 HTML / CSS
Html5页面在微信端的分享的实现方法
Aug 30 HTML / CSS
html5 canvas-2.用canvas制作一个猜字母的小游戏
Jan 07 HTML / CSS
HTML5 window/iframe跨域传递消息 API介绍
Aug 26 HTML / CSS
浅谈HTML5 & CSS3的新交互特性
Jul 19 HTML / CSS
html5使用canvas实现弹幕功能示例
Sep 11 HTML / CSS
详解快速开发基于 HTML5 网络拓扑图应用
Jan 08 HTML / CSS
详解html2canvas截图不能截取圆角图片的解决方案
Jan 30 HTML / CSS
基于MUI框架使用HTML5实现的二维码扫描功能
Mar 01 HTML / CSS
CSS link与@import的区别和用法解析
May 07 HTML / CSS
html5 canvas简单封装一个echarts实现不了的饼图
Jun 12 #HTML / CSS
基于canvas的骨骼动画的示例代码
Jun 12 #HTML / CSS
详解android与HTML混合开发总结
Jun 06 #HTML / CSS
Html5应用程序缓存(Cache manifest)
Jun 04 #HTML / CSS
Html5之title吸顶功能
Jun 04 #HTML / CSS
浅谈Html5移动端ios/Android兼容性总结
Jun 01 #HTML / CSS
HTML5中的websocket实现直播功能
May 21 #HTML / CSS
You might like
Yii获取当前url和域名的方法
2015/06/08 PHP
php文档工具PHP Documentor安装与使用方法
2016/01/25 PHP
PHP精确计算功能示例
2016/11/29 PHP
用javascript获取地址栏参数
2006/12/22 Javascript
用一段js程序来实现动画功能
2007/03/06 Javascript
csdn 批量接受好友邀请
2009/02/19 Javascript
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
简略的前端架构心得&&基于editor为例子的编码小技巧
2010/11/25 Javascript
Extjs中的GridPanel隐藏列会显示在menuDisabled中解决方法
2013/01/27 Javascript
js数组操作常用方法
2014/05/08 Javascript
浅谈AngularJS中ng-class的使用方法
2016/11/11 Javascript
JavaScript 计算笛卡尔积实例详解
2016/12/02 Javascript
微信小程序实现滴滴导航tab切换效果
2018/07/24 Javascript
Vue中的作用域CSS和CSS模块的区别
2018/10/09 Javascript
vue 实现 rem 布局或vw 布局的方法
2019/11/13 Javascript
用js限制网页只在微信浏览器中打开(或者只能手机端访问)
2020/12/24 Javascript
WebPack工具运行原理及入门教程
2020/12/02 Javascript
el-table表头根据内容自适应完美解决表头错位和固定列错位
2021/01/07 Javascript
[38:41]2014 DOTA2国际邀请赛中国区预选赛 LGD VS CNB
2014/05/22 DOTA
Pyhthon中使用compileall模块编译源文件为pyc文件
2015/04/28 Python
Django中URL视图函数的一些高级概念介绍
2015/07/20 Python
python爬虫框架talonspider简单介绍
2017/06/09 Python
Python基础类继承重写实现原理解析
2020/04/03 Python
python tkinter的消息框模块(messagebox,simpledialog)
2020/11/07 Python
20佳惊艳的HTML5应用程序示例分享
2011/05/03 HTML / CSS
Champs Sports加拿大:北美最大的以商场为基础的专业运动鞋和服装零售商之一
2018/05/01 全球购物
请问如下代码执行后a和b的值分别是什么
2016/05/05 面试题
舞蹈教育学专业推荐信
2013/11/27 职场文书
关于母亲节的感言
2014/02/04 职场文书
幼儿园家长评语大全
2014/04/16 职场文书
节约每一滴水演讲稿
2014/09/09 职场文书
2014单位领导班子四风对照检查材料思想汇报
2014/09/25 职场文书
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
Redis遍历所有key的两个命令(KEYS 和 SCAN)
2021/04/12 Redis
使用Springboot实现健身房管理系统
2021/07/01 Java/Android
详解MySQL中timestamp和datetime时区问题导致做DTS遇到的坑
2021/12/06 MySQL