数据结构:
[{
name: '土豆',
num: 200,
}, {
name: '西瓜',
num: 80,
}, {
name: '黄瓜',
num: 85,
}, {
name: '粉丝',
num: 70,
}, {
name: '苹果',
num: 75,
}, {
name: '香蕉',
num: 30,
}, {
name: '樱桃',
num: 5,
}, {
name: '橙子',
num: 100,
}, {
name: '梨',
num: 80,
}, {
name: '菠菜',
num: 85,
}, {
name: '西红柿',
num: 70,
}, {
name: '大白菜',
num: 75,
}, {
name: '西蓝花',
num: 30,
}];复制代码
要求
将n个不同大小(num决定圆大小)的圆在当前画板中呈现,要求每个圆都不会与其他圆重叠;
实现:
网页代码:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<style media="screen">
* {
margin: 0;
padding: 0;
}
#oc {
border: 1px solid #000;
display: block;
}
</style>
</head>
<body>
<canvas id="canvas" width="1280" height="680"></canvas>
</body>
</html>复制代码
1. js获取当前canvas
// 获取canvas
let canvas = document.querySelector('#canvas');
let ctx = canvas.getContext('2d');复制代码
2. 使用canvas API画圆
这里使用 arc(x,y,r,sAngle,eAngle, counterclockwise);
x: 圆心x坐标;
y: 圆心y坐标;
r: 圆的半径;
sAngle: 起始角; 一般设置0就行
eAmhle: 结束角; 整个圆使用 2* Math.PI, 半圆为Math.PI
counterclockwise: 可选,配置逆时针还是顺时针画圆; flase=逆时针
使用arc画圆
ctx.beginPath();
ctx.arc(100,100,50,0,2*Math.PI);
ctx.stroke();复制代码
效果图:
创建多个后 圆会重合并不满足我们的需求如图
随机位置:
有多少条数据就生成多少个圆心坐标代码:
data.forEach(d => {
let ballPrefix = randomPrefix();
ctx.arc(ballPrefix.x, ballPrefix.y, d.num, 0, 2* Math.PI);
ctx.stroke();
})
data.forEach(d => {
let ballPrefix = randomPrefix();
ctx.arc(ballPrefix.x, ballPrefix.y, d.num, 0, 2* Math.PI);
ctx.stroke();
})
function randomPrefix() {
let x = 0;
let y = 0;
x = 80 + Math.random() * (canvas.width - 160);
y = 80 + Math.random() * (canvas.height - 160);
return {
x,
y
}
}复制代码
生成了一个奇怪的图
在data forEach中增加 ctx.beginPath(); 即可
我们看到圆圈是重叠的;
则需要在生产圆心的时候进行判断,该圆是否与其他圆有重叠
查找方法
// 传入坐标和半径 在圆数组中找到与之重叠的那个圆
function findCircle(circle) {
var len = balls.length;
let axiosBall;
for (var i = 0; i < len; i++) {
var x1 = balls[i].x;
var y1 = balls[i].y;
var r1 = balls[i].r;
var x2 = circle.x;
var y2 = circle.y;
var r2 = circle.r;
if ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < (r2 + r1) * (r2 + r1)) {
axiosBall = balls[i];
break;
}
}
return axiosBall;
}复制代码
生成圆心方法
// 为当前的所有数据都随机出来一个圆心位置并且 判断是否有重叠圆
data.forEach(d => {
// 算出当前圆的合适的半径
let r = (canvas.height * (d.num / total))
// 随机出来一个圆心位置
let ball = randomPrefix();
// 循环计数器 防止死循环
let count = 0;
//每个圆只生成200次
while (findCircle({
x: ball.x,
y: ball.y,
r: r
}) && count < 200) {
ball = randomPrefix();
count++;
}
// 记录圆的半径和圆的元数据
ball.r = r;
ball.data = d;
// 将生成的新圆放入数据中 下次检查是否重叠和绘制时使用
balls.push(ball);
})复制代码
以上就生成了所有的圆心位置 (一般都是不重叠的)
画圆方法:
function draw() {
// 遍历圆数组画圆
balls.forEach(ball => {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI);
ctx.stroke();
})
}复制代码
效果:
生成了一个完全不重叠的简单气泡图(效果还差的远,但我会优化的)
给气泡加颜色以及给气泡加文字
随机颜色 以及 气泡上增加文字
function draw() {
// 遍历圆数组画圆
balls.forEach(ball => {
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI);
// 给圆填充颜色
ctx.fillStyle = randomColor();
ctx.fill();
// 将数据中的name显示在圆上
ctx.fillStyle = '#fff';
ctx.fillText(ball.data.name, ball.x - ball.r / 2, ball.y)
ctx.stroke();
})
}
function randomColor() {
return `rgba(${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)},
${Math.floor(Math.random() * 256)})`;
}复制代码
效果图:
最后 添加事件
canvas是不能做交互的;
但是我们可以获取canvas的所有事件以及Event对象;
Event对象可以获取触发事件时候鼠标所在的位置;
鼠标的位置可以用于查找当前位置是否有圆;
代码如下:
canvas.addEventListener('click', function(e) {
let clickBall = findCircle({
x: e.x,
y: e.y,
r: 5
});
console.log(clickBall.data);
})复制代码
点击土豆的圆显示
就是一个简单的canvas 后续还要优化;
有什么有误的地方 敬请指教
代码地址: