html5 Canvas画图教程(11)—使用lineTo/arc/bezierCurveTo画椭圆形


Posted in HTML / CSS onJanuary 09, 2013

在canvas中可以很方便的用arc方法画出圆形,本来圆形也可以看作是一个宽高相等的椭圆,但canvas中根本没有画椭圆的方法,我们要用其他方法来模拟。
我们首先要明确画一个椭圆需要那些参数,基本的几何知识告诉我们,椭圆需要圆心坐标,宽度,高度——或者还有旋转角度,不过这个可以暂时不要,旋转是比较容易的。
1,使用lineTo画椭圆
你没有看错,lineTo这样一个纯粹用来画直线的方法居然可以用来画椭圆!?但他确实存在,不过写法实在是有些不可思议:

复制代码
代码如下:

function DrawEllipse(Canvas,O,OA,OB){
//画椭圆,例子:var B=new Array(150,150); DrawEllipse(hb,B,50,30);
with (Canvas){
var x=O[0]+OA;
var y=O[1];
moveTo(x,y);
for (var i=0;i<=360;i++){
var ii=i*Math.PI/180;
var x=O[0]+OA*Math.cos(ii);
var y=O[1]-OB*Math.sin(ii);
lineTo(x,y);
}
}
}

这个方法的原理是,一个圆有360度,那么就用lineTo循环360次,画出每一度的线段,最终连成一个椭圆。其中需要用到三角函数正弦余弦进行计算。
注意,这个方法的第2个参数是个数组,即椭圆的圆心坐标.

思路很奇葩,而且画出的椭圆也比较平滑。但不值得大家使用——此方法每画一个椭圆,就要循环360次,只有画的椭圆稍微一多,对浏览器的性能就是个考验。
我们只用了解一下他的思路即可
2,使用arc画圆,然后把他缩放成一个椭圆
这个方法的原文在此,核心函数如下:

复制代码
代码如下:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = 0;
var centerY = 0;
var radius = 50;
// save state
context.save();
// translate context
context.translate(canvas.width / 2, canvas.height / 2);
// scale context horizontally
context.scale(2, 1);
// draw circle which will be stretched into an oval
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
// restore to original state
context.restore()

此方法用了一个我前面还没讲过的canvas函数,即scale,他能实现canvas的缩放。缩放有水平和垂直两个方向,代码中把canvas水平方向放大了,而垂直方向不变,so,原来arc画出的圆形就变成了一个椭圆。
这个方法初看甚妙,代码少,而且原理浅显易懂。但分析一下就能发现他的明显缺点了,就是——不精确!比如我需要宽171高56的椭圆,此时我们如果把arc的半径定为28的话,那么后面就要为171/28/2这个蛋疼的不知所云的数字郁闷了。

不过有个折中的办法是始终把arc的半径设成100,然后,不够就放大,超过了就缩小。但是,还是不精确。
3,使用贝赛尔曲线bezierCurveTo
自从觉得上面的缩放法不精确后,我就很想找到一个精确的画椭圆的方法,最后在stackoverflow上找到了:

复制代码
代码如下:

function drawEllipse(ctx, x, y, w, h) {
var kappa = 0.5522848;
ox = (w / 2) * kappa, // control point offset horizontal
oy = (h / 2) * kappa, // control point offset vertical
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginPath();
ctx.moveTo(x, ym);
ctx.bezierCurveTo(x, ym - oy, xm - ox, y, xm, y);
ctx.bezierCurveTo(xm + ox, y, xe, ym - oy, xe, ym);
ctx.bezierCurveTo(xe, ym + oy, xm + ox, ye, xm, ye);
ctx.bezierCurveTo(xm - ox, ye, x, ym + oy, x, ym);
ctx.closePath();
ctx.stroke();
}

这个方法可以算是比较完美的了。他把一个椭圆分成了4条贝塞尔曲线,用他们连成了一个椭圆。最后宽度高度也比较精确,开销也较少。
但此方法依然有缺点。大家看那个kappa参数,有个很奇特的值,相信很多人在几何专家告诉你为什么他要取这个值之前,都不明白为什么非要取这个值——我到现在还是不知道。并且我有很强烈的想把他改一下看看有什么后果的冲动。

当然我这种类似强迫症患者的冲动并不能说成是此方法的缺点,他真正的缺点是——为什么要用4条贝塞尔曲线?我个人觉得,一个椭圆明显是由两条贝塞尔曲线组成的而不是4条。这个想法最终让我找到了最完美的画椭圆的方法。
 4,使用两条贝赛尔曲线画出椭圆
为了了解上一个方法能否精简,我专门注册了一个stackoverflow的帐号去提问,由于问题里有图片,积分不够不能传,我还迫不得已用勉勉强强的英语水平去回答老外的问题挣积分。但最终好运到了,回答一个问题就解决了我的积分问题。
我提的贝赛尔曲线和椭圆的关系的问题在此.
说实话,下面老外的回答我大部分没看懂,但幸亏他提供了一个代码示例页,让我明白了原理,在此对他表示再次的感谢。而根据他的解答,我找到的画椭圆的方法如下:

复制代码
代码如下:

//椭圆
CanvasRenderingContext2D.prototype.oval = function (x, y, width, height) {
var k = (width/0.75)/2,
w = width/2,
h = height/2;
this.beginPath();
this.moveTo(x, y-h);
this.bezierCurveTo(x+k, y-h, x+k, y+h, x, y+h);
this.bezierCurveTo(x-k, y+h, x-k, y-h, x, y-h);
this.closePath();
return this;
}

此方法既精确,又代码少,而且也没有奇怪的难懂的地方。只需要记住这一点,椭圆的宽度与画出椭圆的贝赛尔曲线的控制点的坐标比例如下:
贝塞尔控制点x=(椭圆宽度/0.75)/2这一点已经在代码中体现了。

大家可自行试验上面的4个方法画出椭圆。
如果你发现了更简单的方法,也请分享出来大家探讨吧。

HTML / CSS 相关文章推荐
CSS3中动画属性transform、transition和animation属性的区别
Sep 25 HTML / CSS
css3 中的新特性加强记忆详解
Apr 16 HTML / CSS
CSS3常用的几种颜色渐变模式总结
Nov 18 HTML / CSS
利用CSS3的3D效果制作正方体
Mar 10 HTML / CSS
使用canvas绘制超炫时钟
Dec 17 HTML / CSS
分享29个基于Bootstrap的HTML5响应式网页设计模板
Nov 19 HTML / CSS
基于HTML5代码实现折叠菜单附源码下载
Nov 27 HTML / CSS
浅谈html5增强的页面元素
Jun 14 HTML / CSS
HTML5之语义标签介绍
Jul 07 HTML / CSS
HTML5去掉输入框type为number时的上下箭头的实现方法
Jan 03 HTML / CSS
CSS使用SVG实现动态分布的圆环发散路径动画
Dec 24 HTML / CSS
html原生table实现合并单元格以及合并表头的示例代码
May 07 HTML / CSS
html5 Canvas画图教程(10)—把面拆成线条模拟出圆角矩形
Jan 09 #HTML / CSS
html5 Canvas画图教程(9)—canvas中画出矩形和圆形
Jan 09 #HTML / CSS
html5 Canvas画图教程(8)—canvas里画曲线之bezierCurveTo方法
Jan 09 #HTML / CSS
html5 Canvas画图教程(7)—canvas里画曲线之quadraticCurveTo方法
Jan 09 #HTML / CSS
html5 Canvas画图教程(6)—canvas里画曲线之arcTo方法
Jan 09 #HTML / CSS
html5 Canvas画图教程(5)—canvas里画曲线之arc方法
Jan 09 #HTML / CSS
html5 Canvas画图教程(4)—未闭合的路径及渐变色的填充方法
Jan 09 #HTML / CSS
You might like
php cli模式学习(PHP命令行模式)
2013/06/03 PHP
php获得客户端浏览器名称及版本的方法(基于ECShop函数)
2015/12/23 PHP
PHP调试及性能分析工具Xdebug详解
2017/02/09 PHP
Laravel 中使用 Vue.js 实现基于 Ajax 的表单提交错误验证操作
2017/06/30 PHP
PHP实现防止表单重复提交功能【基于token验证】
2018/05/24 PHP
Aster vs KG BO3 第三场2.19
2021/03/10 DOTA
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
JS:window.onload的使用介绍
2013/11/13 Javascript
jQuery插件开发的五种形态小结
2015/03/04 Javascript
jquery判断复选框是否被选中的方法
2015/10/16 Javascript
node.js微信公众平台开发教程
2016/03/04 Javascript
阻止表单提交按钮多次提交的完美解决方法
2016/05/16 Javascript
AngularJS实践之使用ng-repeat中$index的注意点
2016/12/22 Javascript
vue实现微信分享朋友圈,发送朋友的示例讲解
2018/02/10 Javascript
jQuery移动端跑马灯抽奖特效升级版(抽奖概率固定)实现方法
2019/01/18 jQuery
Angular Excel 导入与导出的实现代码
2019/04/17 Javascript
vue 设置 input 为不可以编辑的实现方法
2019/09/19 Javascript
vue 获取到数据但却渲染不到页面上的解决方法
2020/11/19 Vue.js
从零学python系列之从文件读取和保存数据
2014/05/23 Python
Python使用Matplotlib实现雨点图动画效果的方法
2017/12/23 Python
python机器学习之随机森林(七)
2018/03/26 Python
python查询mysql,返回json的实例
2018/03/26 Python
python 弹窗提示警告框MessageBox的实例
2019/06/18 Python
Python 中的参数传递、返回值、浅拷贝、深拷贝
2019/06/25 Python
python中的RSA加密与解密实例解析
2019/11/18 Python
对Python中 \r, \n, \r\n的彻底理解
2020/03/06 Python
Python爬虫JSON及JSONPath运行原理详解
2020/06/04 Python
Python控制台实现交互式环境执行
2020/06/09 Python
绝对令人的惊叹的CSS3折叠效果(3D效果)整理
2012/12/30 HTML / CSS
CSS3中的display:grid,网格布局介绍
2019/10/30 HTML / CSS
日本最大的药妆连锁店:Matsukiyo松本清药妆店
2017/11/23 全球购物
高等教育学专业自荐书
2014/06/17 职场文书
简单通用的简历自我评价
2014/09/21 职场文书
家庭贫困证明
2014/09/23 职场文书
学习商务礼仪心得体会
2016/01/22 职场文书
oracle连接ODBC sqlserver数据源的详细步骤
2021/07/25 Oracle