Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Canvas系列(20):画布中画满圆

Canvas系列(20):画布中画满圆

作者头像
kai666666
发布于 2025-02-09 13:18:34
发布于 2025-02-09 13:18:34
35101
代码可运行
举报
文章被收录于专栏:橙光笔记橙光笔记
运行总次数:1
代码可运行

今天的内容比较简单,我们学习如何在画布中画满圆。要求圆与圆之间不能相交,最终效果如下。

最终效果
最终效果

HTML结构

首先我们先展示我们基础的HTML结构。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<style type="text/css">
  #canvas{
    background: #eeeeee;
    border: 1px solid #000000;
  }
</style>

<canvas id="canvas" width="600" height="400"></canvas>

这里绘制一个600*400的画布,并设置背景色为灰色,边框为1px的黑色。

绘制一个圆

我们从简单的开始,先绘制一个圆。由于我们这里不需要动画,所以不需要 update 方法,只需要 draw 方法即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const canvas = document.querySelector('canvas')
const context = canvas.getContext('2d')

context.lineWidth = 2

class Circle {
  constructor(options = {}) {
    this.x = options.x || 0
    this.y = options.y || 0
    this.radius = options.radius || minRadius
  }

  draw() {
    context.beginPath()
    context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
    context.stroke()
  }
}

const circle = new Circle({
  x: canvas.width / 2,
  y: canvas.height / 2,
  radius: 100
});

circle.draw()

我们在画布的中心绘制了1个半径为100px的圆。效果如下

画一个圆
画一个圆

绘制500个圆

上面,我们绘制了1个圆,现在绘制500个圆。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const minRadius = 2
const maxRadius = 120
const totalCircles = 500

function createAndDrawCircle() {
  const newCircle = new Circle({
    x: Math.floor(Math.random() * canvas.width),
    y: Math.floor(Math.random() * canvas.height),
    radius: 10
  })
  newCircle.draw()
}

for(let i = 0; i < totalCircles; i++) {
  createAndDrawCircle()
}

上面代码在随机位置生成了500个半径是10px的小圆,效果如下

画500个圆
画500个圆

绘制500个不相交的圆

上面绘制了500个圆,他们可能是相交的,现在我们绘制500个不相交的圆。 我们上面绘制的随机坐标很可能在某个圆内,为了避免这种情况,我们需要先判断圆心坐标是否在某个圆内。如果在某个圆内我们需要舍弃这个坐标,重新生成一个新坐标;如果不在某个圆内,那么我们可以在这里绘制圆。到底绘制多大圆呢?如果绘制的太大了则可能与旁边的圆相交或者超出画布边界,我们这里绘制一个尽可大的圆,尽可能大意味着刚好与其他圆或者边界相切。如果绘制一个这样的大圆呢?这里可以先使用很小的圆来计算,让其半径慢慢增大,当增大到刚好与某个圆或者边界相切时,则绘制它。如果一直不相切,我们最好给一个圆的最大半径,这样可以保证圆不会太大。

算法:

  1. 生成随机圆心坐标;
  2. 判断圆心坐标是否在某个圆内,如果在某个圆内,则舍弃这个坐标,重新生成一个新坐标(最坏情况下,可能一直没有符合要求的新坐标,则应该给限制,如最多尝试生成新坐标的500次);
  3. 如果圆心坐标不在某个圆内,则让圆的半径逐渐增大,从而找到最大刚好跟其它圆或边界相切的最大圆;
  4. 绘制圆;
  5. 重复1~4步,直到所有圆绘制完毕(我们这里限定最多绘制500个圆)。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const circles = []
const createCircleAttempts = 500

function createAndDrawCircle() {
  let newCircle
  let circleSafeToDraw = false
  // 最多尝试500次寻找圆外坐标
  for(let tries = 0; tries < createCircleAttempts; tries++) {
    // 步骤1,生成随机圆心坐标
    newCircle = new Circle({
      x: Math.floor(Math.random() * canvas.width),
      y: Math.floor(Math.random() * canvas.height),
      radius: 10
    })

    // 步骤2,判断圆心坐标是否在某个圆内 如果与其他圆或者边界相交则舍弃当前坐标,重新生成一个新坐标,不相交则标记可以绘制
    if(doesCircleHaveACollision(newCircle)) {
      continue;
    } else {
      circleSafeToDraw = true
      break;
    }
  }

  // 如果循环500次都没有找到合适的新坐标则不绘制了
  if(!circleSafeToDraw) return

  // 保存需要绘制的圆
  circles.push(newCircle)
  // 步骤4,绘制圆
  newCircle.draw()
}

首先我们定义 circles 数组用来存放生成好的圆,createCircleAttempts 表示最多尝试 500 次寻找其他圆外新坐标。上面代码结合注释还是比较好理解的,我们还没有实现 doesCircleHaveACollision 方法,下面我们来实现它。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function doesCircleHaveACollision(circle) {
  for(let i = 0; i < circles.length; i++) {
    const otherCircle = circles[i]
    const a = circle.radius + otherCircle.radius
    const x = circle.x - otherCircle.x
    const y = circle.y - otherCircle.y

    if (a >= Math.hypot(x, y)) {
      return true;
    }
  }

  if(circle.x + circle.radius >= canvas.width ||
    circle.x - circle.radius <= 0) {
    return true;
  }

  if(circle.y + circle.radius >= canvas.height ||
      circle.y - circle.radius <= 0) {
    return true;
  }

  return false
}

当调用 doesCircleHaveACollision(circle) 方法的时候,新圆并没有加入到 circles 数组中,所以我们只需要判断新圆是否与数组中的圆相交并判断是否与边界相交就可以了。圆与圆的相交可以通过圆心间的距离跟半径之和做比较来判断,圆与边界可以通过圆的坐标和半径跟上下左右边界的距离做比较来判断。对碰撞检测感兴趣的同学可以翻看之前的文章

此时效果如下

绘制500个不相交的圆
绘制500个不相交的圆

绘制500个不相交且大小不等的圆

上面我们少了第3步,只绘制了大小都是10px的圆,现在我们处理一下第3步,实际上也不复杂。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 最小半径
const minRadius = 2
// 最大半径
const maxRadius = 120

function createAndDrawCircle() {
  let newCircle
  let circleSafeToDraw = false
  // 最多尝试500次寻找圆外坐标
  for(let tries = 0; tries < createCircleAttempts; tries++) {
    // 步骤1,生成随机圆心坐标
    newCircle = new Circle({
      x: Math.floor(Math.random() * canvas.width),
      y: Math.floor(Math.random() * canvas.height),
      radius: minRadius, // 这里初始化使用最小半径
    })

    // 步骤2,判断圆心坐标是否在某个圆内 如果与其他圆或者边界相交则舍弃当前坐标,重新生成一个新坐标,不相交则标记可以绘制
    if(doesCircleHaveACollision(newCircle)) {
      continue;
    } else {
      circleSafeToDraw = true
      break;
    }
  }
  
  // 如果循环500次都没有找到合适的新坐标则不绘制了
  if(!circleSafeToDraw) return

  // 步骤3,让圆的半径逐渐增大,从而找到最大刚好跟其它圆或边界相切的圆
  for(let radiusSize = minRadius; radiusSize < maxRadius; radiusSize++) {
    newCircle.radius = radiusSize
    // 相切则跳出循环 由于描边宽度是2 所以需要减去1像素保证不相交
    if(doesCircleHaveACollision(newCircle)){
      newCircle.radius--
      break
    }
  }

  // 保存需要绘制的圆
  circles.push(newCircle)
  // 步骤4,绘制圆
  newCircle.draw()
}

此时的效果跟刚开始的效果一下了,点击这里查看

小优化

上面我们绘制圆的时候,由于第一个绘制的圆只受边界相交的限制,假设第一个圆的坐标在靠近中心的位置,就有很大概率绘制一个最大的圆,所以当你多次刷新网页的时候就会发现,通常有一个很大的圆,这样不是那么美观。我们现在让绘制的最大半径修改成 圆最大半径最小半径 + 1 之间的一个随机值,这样可以避免每次都有一个极大圆存在。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createAndDrawCircle() {
  // ... 其他代码相同

  // 新的最大值为圆最小值和最大值之间的随机值
  const max = Math.floor(Math.random() * (maxRadius - minRadius - 1) + minRadius + 1)
  for(let radiusSize = minRadius; radiusSize < max; radiusSize++) {
    // ... 其他代码相同
  }

  // ... 其他代码相同
}

此时效果如下

最终效果
最终效果
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
❤️创意网页:萌翻少女心的发光果冻泡泡 - 使用Canvas绘制可爱动态泡泡效果
在这篇技术博客中,我们将学习如何使用HTML5 Canvas和JavaScript创建一个令人陶醉的发光果冻泡泡动画效果。我们将绘制一系列可爱的、多彩的果冻泡泡,并使它们在画布上随机运动,形成一个令人心动的动态效果。本项目将让你的少女心萌翻!
命运之光
2024/03/20
2940
❤️创意网页:萌翻少女心的发光果冻泡泡 - 使用Canvas绘制可爱动态泡泡效果
❤️创意网页:萌翻少女心的果冻泡泡 - 创造生动有趣的视觉效果
大家好!欢迎来到本篇技术博客。今天我们将一起学习如何使用HTML5 Canvas和JavaScript创建一个可爱又有趣的果冻泡泡效果。我们将绘制一组彩色泡泡,并通过动画让它们像果冻一样晃动,给人一种充满活力的感觉。让我们开始吧!
命运之光
2024/03/20
3290
❤️创意网页:萌翻少女心的果冻泡泡 - 创造生动有趣的视觉效果
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
在节日或特殊场合,绚丽的烟花总能带来无尽的欢乐和惊喜。今天,我们将通过 HTML5 Canvas 实现一个绚丽的烟花效果,让你的网页也能绽放出美丽的烟花。
码事漫谈
2025/01/27
1.5K1
绚丽烟花:HTML5 Canvas 烟花效果实现(文末附完整代码)
Canvas系列(18):实战-烟花效果
今天我们来学习 Canvas 的一个经典案例 —— 烟花效果,具体效果可以看下图。本章的内容会涉及之前的加速度和速度相关的知识,如果对这部分不太了解的建议先看其他章节。
kai666666
2024/11/26
2900
Canvas系列(18):实战-烟花效果
Python+opencv 机器视觉 - 基于霍夫圈变换算法检测图像中的圆形实例演示
这个是设定半径范围 50-70 后的效果,因为原图稍微大一点,半径也大了一些。
小蓝枣
2022/01/11
1.8K0
Python+opencv 机器视觉 - 基于霍夫圈变换算法检测图像中的圆形实例演示
Canvas系列(14):实战-小球碰撞
两小球碰撞是Canvas非常经典的案例,他是一个很简单的需求,但做起来却非常复杂。
kai666666
2020/10/19
1.9K0
Canvas系列(14):实战-小球碰撞
Canvas系列(13):实战--星空连线图
Canvas能做好多东西,本章就来一节实战性的东西吧。好多人来到这个博客的时候总是觉得后面这种点和线的背景很有意思,这种效果是怎么实现的呢,当然是使用Canvas了,我们这章就来实现一个简易版的这种星空连线图。
kai666666
2020/10/17
1.3K0
使用canvas实现一个圆球的触壁反弹
HTML <canvas id="canvas" width="500" height="500" style="border: 1px solid #FF0000;"></canvas> JS 1.获取上下文 let canvas = document.getElementById('canvas'); let ctx = canvas.getContext('2d'); 2.实现一个球类 1 class circle { 2 constructor() { 3
李文杨
2018/03/14
8690
快过年了,用JS让你的网页放烟花吧
https://www.bilibili.com/video/bv1fr4y1T7TB
海拥
2021/08/23
2.3K0
快过年了,用JS让你的网页放烟花吧
Canvas <粒子文字效果>
若你不在画布上做任何操作,那么当你输出 data 时为。所以加上 data[i] !== 0 (rgb(0, 0, 0) 为黑色,只要你绘制的东西不是这个色值,肯定会出现不等于0的像素模块,因此就能确定出你作画的位置坐标)
网罗开发
2021/04/07
1.3K0
Canvas <粒子文字效果>
OpenCV 圆检测
OpenCV 的 HoughCircles() 函数可以用来在一张单通道图像里检测圆形物体。下面是各参数的介绍:
用户6021899
2019/09/08
2.4K0
基于Python利用OpenCV实现Hough变换的形状检测
在我们开始对图像应用霍夫变换之前,我们需要了解霍夫空间是什么,我们将通过一个例子来进行了解。
小白学视觉
2022/02/14
2.9K0
基于Python利用OpenCV实现Hough变换的形状检测
邀你看一场浪漫的烟火 -- canvas放烟花
漫天的烟火,在这璀璨的星空中闪耀,成就了这片星空的绚丽,更散发出了自己无限的光芒,今天就使用canvas来做一个烟花效果吧!✨
小丞同学
2021/08/16
2.7K2
【被玩坏的博客园】之canvas装饰博客园侧边栏
最近抽空学了学canvas,然后用canvas做了个小球运动的demo,大致的效果如下: 虽然网上已经有很多这样的demo,但是还是想根据自己的思路来写一个,下面先跟大家讲解一下源代码,先看html代
用户1749219
2018/05/16
1.6K0
OpenCV 图像分析之 —— 霍夫变换(Hough Transform)
Hough(霍夫)变换是一种用于检测线、圆或者图像中其他简单形状的方法。最初Hough变换是一种线变换,这是一种相对较快的检测二值图像中直线的方法,可以进一步推广到除简单线之外的情况。
为为为什么
2022/08/09
6.2K0
OpenCV 图像分析之 —— 霍夫变换(Hough Transform)
创建canvas设置canvas尺寸绘制图形Canvas库
Canvas是常见的前端技术,但是由于API众多,使用复杂,且对程序员的数学功底、空间想象能力乃至审美都有一定要求,所以真正擅长canvas的前端并不多,但并不代表大家就学不好canvas。我在此将常用的canvas使用场景罗列出来希望能帮助到大家。
MudOnTire
2020/05/12
4.7K0
创建canvas设置canvas尺寸绘制图形Canvas库
canvas高效绘制10万图形,你必须知道的高效绘制技巧
最近的一个客户项目中,简化的需求是绘制按照行列绘制很多个圆圈。需求看起来不难,上手就可以做,写两个for循环。
用户3158888
2019/05/29
1K0
Canvas基础
HTML5中引入<canvas>标签,用于图形的绘制,<canvas>为图形的绘制提供了画布,是图形容器,具体的图形绘制由JavaScript来完成。
WindRunnerMax
2020/08/27
1.3K0
使用canvas绘制圆弧动画
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/j_bleach/article/details/86365888
j_bleach
2019/07/02
1.6K0
使用canvas绘制圆弧动画
手把手教你用Javascript制作随机星星效果图
在浏览一些图片网站的时候,经常会看到很多的漂亮的星空图,比如,下面的图片。其实这种星星图片的效果,也可以通过html+css样式和js的方式来实现。今天教大家如何实现星星图的效果。
前端皮皮
2020/12/22
1.2K0
手把手教你用Javascript制作随机星星效果图
推荐阅读
相关推荐
❤️创意网页:萌翻少女心的发光果冻泡泡 - 使用Canvas绘制可爱动态泡泡效果
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验