前言
最近碰到个用 canvas 绘制椭圆形的问题,研究了一下,发现挺有意思的。
在经过一顿摸索以后,实现的效果,也还是挺不错的,在这个过程中也学到了不少东西吧。
ellipse 方法兼容性问题
之前没绘制过椭圆,上手,当然是想找找 canvas 原生有没有类似的 api 了。
于是就上 mdn 搜索一番,这一找,还真的发现有类似的 api,不得不感概,这就太巧了,这问题不就不算问题了么。
但是一看浏览器兼容性,不禁让人愁绪顿生,这么好的方法,怎么很多浏览器都不支持呢?
image.png对应的代码可以写成下面这样:
const twoPI = Math.PI * 2;
// n 为想要分的份数
Array(...Array(n)).forEach((value, index) => {
let angle = ( twoPI * index) / n;
// center 为中心点坐标位置,r 为半径
let x = center.x + r * Math.cos(angle);
let y = center.y + r * Math.sin(angle);
});
但是如果是椭圆呢?
椭圆也是有计算公式的,如下:
image.png
对应的代码可以写成下面这样:
const twoPI = Math.PI * 2;
// n 为想要分的份数
Array(...Array(n)).forEach((value, index) => {
let angle = ( twoPI * index) / n;
// center 为中心点坐标位置,a 为长轴半径,b 为短轴半径
let x = center.x + a * Math.cos(angle);
let y = center.y + b * Math.sin(angle);
});
用 zrender 开始绘制
这里之所以用 zrender 绘制,纯粹是由于自己从零开始写太过于麻烦了,zrender 本身就是一款不错的 canvas 2d 开源框架,封装了一些功能,能够帮助我们快捷的进行 canvas 绘制。
如果还不了解 zrender 的童鞋,可以去 zrender 官网了解一下:https://ecomfe.github.io/zrender-doc/public/
结合上面的算法,我简单地封装了一个绘制的方法:
/**
* 创建圆形布局的算法
* @paramas {Object} center 中心点坐标
* @paramas {Number} a 长半轴的长度
* @paramas {Number} b 短半轴的长度
* @paramas {Number} n 网元个数
*/
function createEllipse(center, a, b, n) {
const tP = 2 * Math.PI;
Array(...Array(n)).forEach((value, index) => {
const angle = (tP * index) / n;
const x = center.x + a * Math.cos(angle);
const y = center.y + b * Math.sin(angle);
var circle = new zrender.Circle({
shape: {
cx: x,
cy: y,
r: 20
},
style: {
fill: "#0eafd1"
},
z: 1
});
zr.add(circle);
});
}
现在我们调用一下,看一下结果
const a = 300;
const b = 150;
const o = { x: 400, y: 200 };
createEllipse(o, a, b, 10);
结果如下:
image.png
哈哈,效果还是不错的吧。
嗯,不错,此教程到此结束!
......
等等,我们的弧线还没有绘制上来呢!
不好意思!差点忘记了本来的目的,汗颜!
接下来,我们就来加上弧线的效果。
加上曲线连接效果
zrender 里面是由直接提供 ellipse 的图形的,我看打开 zrender 源码看看: image.png可以看出,代码很简洁。
接下来,我们稍微改改我们的代码,在 createEllipse 方法末尾加上绘制椭圆的逻辑:
var ellipse = new zrender.Ellipse({
shape: {
cx: center.x,
cy: center.y,
rx: a,
ry: b,
// r: 20
},
style: {
fill: "#0eafd100",
stroke: "red"
},
});
zr.add(ellipse);
然后效果变成这样了:
image.png
说实话,这种效果并不是我想要的效果,我想要的是一段段可以自由加进去的,那么只有自己动手添加了。
一段段添加
那么该怎么改呢?首先可以分析下,既然我们拓展了浏览器端绘制椭圆的方法,那么直接采用绘制椭圆的方法绘制进去就行了。
但是问题是,我们怎么拿到 canvas 画笔,绘制到 canvas 里面去呢?
1. 创建新的 Ellipse2 对象
我们可以照着 zrender.Ellipse 的代码,创建一个新的图形,就叫 Ellips2 吧:
zrender.Ellipse2 = zrender.Path.extend({
type: "ellipse2",
shape: {
cx: 0,
cy: 0,
rx: 0,
ry: 0,
fAngle: 0,
tAngle: Math.PI*2
},
buildPath: function(ctx, shape) {
var x = shape.cx;
var y = shape.cy;
var a = shape.rx;
var b = shape.ry;
var fAngle = shape.fAngle;
var tAngle = shape.tAngle;
ctx._ctx.ellipse(x, y, a, b, 0, fAngle, tAngle);
}
});
可以看到,我们在 shape对象上新增了 fAngle 和 tAngle 属性,用来控制起始和终止的角度。
2. 调用 zrender.Ellipse2 开始逐段绘制
添加下面的代码,调用 zrender.Ellipse2 方法,绘制我们的弧线
const collections = [];
Array(...Array(n)).forEach((value, index) => {
......
collections.push(angle);
if(index!==0){
var ellipse = new zrender.Ellipse2({
shape: {
cx: center.x,
cy: center.y,
rx: a,
ry: b,
fAngle: collections[index-1],
tAngle: angle
},
style: {
fill: "#0eafd100",
stroke: "red"
}
});
zr.add(ellipse);
}
if(index === n - 1){
var ellipse = new zrender.Ellipse2({
shape: {
cx: center.x,
cy: center.y,
rx: a,
ry: b,
fAngle: angle,
tAngle: collections[0]
},
style: {
fill: "#0eafd100",
stroke: "red"
}
});
zr.add(ellipse);
}
......
});
绘制效果:
image.png
咋一看,好像跟之前的绘制方式相比,没啥优势啊。
但是你稍微控制下绘制参数,就可以改变下效果,比如这样:
image.png
或者这样:
image.png
后记
总而言之,用 canvas 绘制椭圆还是挺有意思的吧,我所言非虚吧。
小伙伴们,赶快学习一波吧。
网友评论