JavaScript 中检测线段与圆的相交

javascript 中检测线段与圆的相交

本文详细介绍了如何使用 JavaScript 检测线段与圆是否相交,并提供两种优化后的方法。第一种方法通过计算线段到圆心的距离来判断是否相交,避免了昂贵的平方根计算。第二种方法则返回线段与圆的交点距离,方便进一步处理碰撞事件。同时,提供完整的示例代码,帮助开发者理解和应用这些技术。

在 HTML5 Canvas 游戏中,碰撞检测是一个至关重要的环节。本文将深入探讨如何使用 JavaScript 检测线段与圆是否相交,并提供两种高效且易于理解的实现方法。

方法一:避免平方根的相交检测

该方法的核心思想是计算线段到圆心的最短距离,并将其与圆的半径进行比较。如果该距离小于半径,则线段与圆相交。这种方法避免了平方根的计算,提高了性能。

function rayInterceptsCircle(ray, circle) {        const dx = ray.p2.x - ray.p1.x;    const dy = ray.p2.y - ray.p1.y;    const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx)));    const nx = ray.p1.x + dx * u - circle.x;    const ny = ray.p1.y + dy * u - circle.y;        return nx * nx + ny * ny < circle.radius * circle.radius;}

代码解释:

立即学习“Java免费学习笔记(深入)”;

ray: 包含线段起点 p1 和终点 p2 的对象。circle: 包含圆心坐标 x, y 和半径 radius 的对象。dx 和 dy:线段的 x 和 y 方向上的差值。u: 线段上距离起点 p1 最近的点与圆心连线,该点在线段上的比例,范围是 [0, 1]。Math.min(1, Math.max(0, …)) 确保 u 的值始终在线段范围内。nx 和 ny: 线段上距离圆心最近的点与圆心的 x 和 y 轴距离。nx * nx + ny * ny

使用示例:

const Point = (x, y) => ({x, y}); const Ray = (p1, p2) => ({p1, p2}); const Circle = (p, radius) => ({x: p.x, y: p.y, radius});const c1 = Circle(Point(150, 120), 60);const r1 = Ray(Point(0, 50), Point(300, 50));if (rayInterceptsCircle(r1, c1)) {    console.log("线段与圆相交!");} else {    console.log("线段与圆不相交!");}

方法二:计算交点距离

该方法返回线段与圆的交点距离,如果线段与圆不相交,则返回 Infinity。该方法需要计算平方根,但可以提供更多信息,例如交点的位置。

function rayDist2Circle(ray, circle) {    const dx = ray.p2.x - ray.p1.x;    const dy = ray.p2.y - ray.p1.y;    const vcx = ray.p1.x - circle.x;     const vcy = ray.p1.y - circle.y;    var v =  (vcx * dx +  vcy * dy) * (-2 / Math.hypot(dx, dy));    const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius);    if (dd <= 0) { return Infinity; }    return  (v - Math.sqrt(dd)) / 2;}

代码解释:

立即学习“Java免费学习笔记(深入)”;

ray: 包含线段起点 p1 和终点 p2 的对象。circle: 包含圆心坐标 x, y 和半径 radius 的对象。dx 和 dy:线段的 x 和 y 方向上的差值。vcx 和 vcy: 线段起点 p1 到圆心的 x 和 y 轴距离。v: 一个中间变量,用于简化后续计算。dd: 判别式,如果小于等于 0,则表示线段与圆不相交。(v – Math.sqrt(dd)) / 2: 计算线段与圆的交点距离。

使用示例:

const Point = (x, y) => ({x, y}); const Ray = (p1, p2) => ({p1, p2}); const Circle = (p, radius) => ({x: p.x, y: p.y, radius});const c1 = Circle(Point(150, 120), 60);const r1 = Ray(Point(0, 50), Point(300, 50));const distance = rayDist2Circle(r1, c1);if (distance === Infinity) {    console.log("线段与圆不相交!");} else {    console.log("线段与圆相交,交点距离为:", distance);}

完整示例代码

以下是一个完整的示例,演示了如何在 HTML5 Canvas 中使用这两种方法检测线段与圆的相交,并根据结果绘制不同的颜色。

线段与圆相交检测canvas {  position: absolute;  top: 0px;  left: 0px;}const ctx = canvas.getContext("2d");const TAU = Math.PI * 2;requestAnimationFrame(renderLoop);var W = canvas.width, H = canvas.height;const Point = (x, y) => ({x, y}); const Ray = (p1, p2) => ({p1, p2}); const Circle = (p, radius) => ({x: p.x, y: p.y, radius});function drawRayLeng(ray, len) {  ctx.beginPath();  ctx.lineTo(ray.p1.x, ray.p1.y);  if (len < Infinity) {    const dx = ray.p2.x - ray.p1.x;    const dy = ray.p2.y - ray.p1.y;    const scale = len / Math.hypot(dx, dy);    ctx.lineTo(ray.p1.x + dx * scale , ray.p1.y + dy  * scale);  } else {    ctx.lineTo(ray.p2.x, ray.p2.y);  }  ctx.stroke();}function drawRay(ray) {  ctx.beginPath();  ctx.lineTo(ray.p1.x, ray.p1.y);  ctx.lineTo(ray.p2.x, ray.p2.y);  ctx.stroke();}function drawCircle(circle) {  ctx.beginPath();  ctx.arc(circle.x, circle.y, circle.radius, 0, TAU);  ctx.stroke();}function rayInterceptsCircle(ray, circle) {        const dx = ray.p2.x - ray.p1.x;    const dy = ray.p2.y - ray.p1.y;    const u = Math.min(1, Math.max(0, ((circle.x - ray.p1.x) * dx + (circle.y - ray.p1.y) * dy) / (dy * dy + dx * dx)));    const nx = ray.p1.x + dx * u - circle.x;    const ny = ray.p1.y + dy * u - circle.y;        return nx * nx + ny * ny < circle.radius * circle.radius;}function rayDist2Circle(ray, circle) {    const dx = ray.p2.x - ray.p1.x;    const dy = ray.p2.y - ray.p1.y;    const vcx = ray.p1.x - circle.x;     const vcy = ray.p1.y - circle.y;    var v =  (vcx * dx +  vcy * dy) * (-2 / Math.hypot(dx, dy));    const dd = v * v - 4 * (vcx * vcx + vcy * vcy - circle.radius * circle.radius);    if (dd <= 0) { return Infinity; }    return  (v - Math.sqrt(dd)) / 2;}const mouse  = {x : 0, y : 0}function mouseEvents(e){    mouse.x = e.pageX;    mouse.y = e.pageY;}document.addEventListener("mousemove", mouseEvents);const c1 = Circle(Point(150, 120), 60);const r1 = Ray(Point(0, 50), Point(300, 50));function renderLoop(time) {   ctx.clearRect(0, 0, W, H);   r1.p1.x = c1.x + Math.cos(time / 5000) * 100;   r1.p1.y = c1.y + Math.sin(time / 5000) * 100;   r1.p2.x = mouse.x;   r1.p2.y = mouse.y;   ctx.lineWidth = 0.5;   drawCircle(c1);   drawRay(r1);   ctx.lineWidth = 5;   if (rayInterceptsCircle(r1, c1)) {     ctx.strokeStyle = "red";     drawRayLeng(r1, rayDist2Circle(r1, c1));   } else {     drawRay(r1);   }   ctx.strokeStyle = "black";   requestAnimationFrame(renderLoop);}

总结:

本文提供了两种在 JavaScript 中检测线段与圆相交的方法。第一种方法避免了平方根的计算,适用于对性能要求较高的场景。第二种方法可以提供更多信息,例如交点的位置,适用于需要进一步处理碰撞事件的场景。开发者可以根据实际需求选择合适的方法。

以上就是JavaScript 中检测线段与圆的相交的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1510215.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 05:50:07
下一篇 2025年12月20日 05:50:19

相关推荐

  • React应用登录后重定向失败的常见原因与解决方案

    本文旨在探讨React应用中用户登录后无法正确重定向至主页的常见问题。核心原因在于状态管理与组件生命周期中的时序问题,即loggedIn状态未在导航前及时更新。通过在成功登录后立即更新loggedIn状态,并结合useEffect的正确使用,可以有效解决此问题,确保用户体验的流畅性。 在构建现代We…

    2025年12月20日
    000
  • React 应用中登录后重定向失败的解决方案

    本文旨在解决 React 应用中用户登录成功后无法正确重定向至主页的问题。核心原因在于状态管理与导航时序不匹配:在导航到受保护页面之前,表示用户登录状态的 loggedIn 变量未能及时更新。通过在登录成功后、执行页面跳转前立即更新 loggedIn 状态,可以确保目标页面正确识别用户登录状态,从而…

    2025年12月20日
    000
  • React应用登录重定向:状态管理与路由导航的同步

    本教程旨在解决React应用中用户登录成功后无法正确重定向至首页的问题。核心在于理解React状态更新的异步性与路由导航的时序关系。通过在导航前同步更新用户登录状态,确保目标页面在渲染时能基于最新的认证状态进行逻辑判断,从而避免不必要的重定向循环,实现流畅的用户体验。 在构建基于mern(mongo…

    2025年12月20日
    000
  • javascript闭包怎样实现桥接模式

    使用闭包模拟桥接模式的方法是:1. 定义实现部分(如绘图api),通过闭包封装具体行为;2. 定义抽象部分(如形状类),接收实现对象并利用闭包持久化对该对象的引用;3. 抽象部分通过闭包访问实现方法,实现解耦。闭包的优势在于封装实现细节、降低耦合、保护私有变量。可能的问题包括增加内存消耗、影响垃圾回…

    2025年12月20日 好文分享
    000
  • javascript怎么实现惰性数组

    惰性数组的核心是延迟计算,即只在需要时才计算元素值,它通过生成器函数或自定义迭代器实现,解决了大数据集或无限序列处理中的内存和性能问题。1. 惰性数组并非真实数组,而是一种基于迭代协议的惰性求值模式,利用生成器函数(function*)和yield实现按需计算;2. 常见实现方式包括使用生成器函数构…

    2025年12月20日 好文分享
    000
  • JavaScript 深度嵌套数组中获取指定子元素的实用指南

    第一段引用上面的摘要: 本文旨在提供一种高效且易于理解的方法,用于在 JavaScript 中处理深度嵌套的数组结构,并根据指定的 ID 列表提取目标元素的子元素。通过迭代而非递归的方式,避免了潜在的栈溢出风险,并提供了清晰的代码示例和详细的步骤说明。无论您是处理复杂的数据结构还是构建动态的用户界面…

    2025年12月20日
    000
  • 使用 Angular 14 实现 Stripe 个性化支付隧道

    本文介绍了如何在 Angular 14 项目中集成 Stripe 个性化支付隧道,避免使用 stripe-ngx 库带来的弹出窗口设计限制。文章重点讲解了如何捕获支付成功状态,防止页面重定向,并解决在使用 JavaScript Checkout 时可能遇到的 clientSecret 缺失问题。通过…

    2025年12月20日
    000
  • js中如何克隆一个对象

    深拷贝需根据对象复杂度选择方法:1. 浅拷贝用object.assign或展开运算符,仅复制顶层属性,嵌套对象仍共享引用;2. json.parse(json.stringify(obj))可深拷贝但丢失函数、undefined等且不支持循环引用;3. 递归实现可处理复杂类型包括函数,但需手动解决循…

    2025年12月20日 好文分享
    000
  • JS如何实现柯里化?柯里化的应用

    柯里化是将一个接收多个参数的函数转化为一系列只接收一个参数的函数,其核心优势在于提升函数的复用性与组合性。通过逐步传入参数并返回新的函数,柯里化支持参数复用、延迟执行和函数工厂模式,例如可从通用的 fetchdata(baseurl, endpoint, params) 派生出固定 baseurl …

    2025年12月20日
    000
  • JS如何实现撤销重做

    js实现撤销重做核心是通过命令模式维护操作历史栈;1. 使用数组存储状态历史,每次操作后推入新状态;2. 将操作封装为包含execute和undo方法的命令对象;3. 撤销时弹出当前命令并执行undo,重做时从重做栈弹出并执行execute;4. 处理复杂对象状态需使用深拷贝(如json.parse…

    2025年12月20日
    000
  • js 怎样用pipe按顺序执行多个函数

    最直接的方式是使用管道(pipe)函数实现函数的顺序执行与数据传递,1. pipe函数通过reduce方法将多个函数从左到右依次执行,前一个函数的输出作为下一个函数的输入;2. compose函数则从右到左执行,符合数学上的函数复合概念;3. 实际应用中pipe更符合数据流动的直觉,适用于数据转换、…

    2025年12月20日
    000
  • 解决React中多个按钮打开同一组件的问题

    第一段引用上面的摘要: 本文旨在解决React应用中,多个按钮点击后总是打开同一个组件的问题。通过分析问题代码,我们将探讨如何使用状态管理来控制不同组件的显示与隐藏,并提供相应的代码示例,帮助开发者避免类似错误,构建更健壮的React应用。 在React开发中,经常会遇到需要通过多个按钮来控制不同组…

    2025年12月20日
    000
  • 解决Web媒体文件在线播放问题的调试指南:以缓存与路径问题为例

    本文旨在深入探讨Web应用中媒体文件(如MP3、MP4)在本地正常运行但部署到线上后失效的常见问题。我们将分析潜在的技术原因,特别是浏览器缓存、文件路径和服务器配置,并提供一套系统的调试方法,强调利用浏览器开发者工具进行故障排查,以帮助开发者高效解决此类部署难题,确保媒体内容在生产环境下的稳定播放。…

    2025年12月20日
    000
  • js 怎么实现数组扁平化

    数组扁平化是将多层嵌套数组转化为一层数组,常用方法包括ES2019的flat()、递归、reduce结合递归及迭代法;flat()性能好且简洁,适合现代环境,递归灵活但可能栈溢出,迭代法可避免栈溢出,适用于深度嵌套场景。 JavaScript数组扁平化,简单来说,就是把一个多层嵌套的数组(也就是二维…

    2025年12月20日
    000
  • JS如何实现反应式编程?响应式原理

    JS实现反应式编程的核心是数据变化自动触发视图更新,依赖可观察对象、观察者、订阅、操作符和Proxy等技术,通过数据绑定与依赖追踪实现高效更新,适用于用户界面更新、异步处理等场景。 JS实现反应式编程,核心在于数据变化能够自动触发相应的视图更新或其他操作。这得益于对数据变化的监听和高效的更新机制。 …

    2025年12月20日
    000
  • javascript闭包怎么实现多步表单流程

    闭包可用于在javascript中实现多步表单的状态管理,通过创建私有变量如currentstepindex和formdata来持久化表单状态;2. 使用工厂函数createmultistepform返回包含nextstep、prevstep、getformdata等方法的对象,这些方法共享并操作闭…

    2025年12月20日 好文分享
    000
  • js 怎么用without创建排除某些值的新数组

    javascript中创建排除某些值的新数组应使用filter方法而非寻找without函数;2. 可自定义without函数利用filter和includes实现灵活排除;3. reduce方法也可用于排除,但代码较filter复杂;4. 对象数组可通过属性值使用filter进行排除;5. 当排除…

    2025年12月20日
    000
  • js如何复制对象的原型

    在javascript中,“复制对象的原型”实际上是指创建一个新对象并将其原型链指向目标原型,而非真正复制一份独立的副本;2. 最推荐的方式是使用object.create(),它能直接创建新对象并将传入的对象作为其原型,实现继承;3. 原型的设计本意是共享和动态继承,若真正复制原型会破坏其可维护性…

    2025年12月20日 好文分享
    000
  • JS如何实现Monad?函数式编程中的Monad

    在javascript中实现monad的核心是构建具有of和flatmap方法的对象,用于封装值并管理计算流;常见monad包括处理异步的promise、避免空值错误的maybe、处理失败结果的either,其实用价值在于提升代码的可组合性、可读性和健壮性,但面临概念抽象、缺乏类型系统支持、语法冗长…

    2025年12月20日
    000
  • js怎么判断对象是否没有原型

    判断一个javascript对象是否没有原型的最直接方法是使用object.getprototypeof()检查其原型是否为null。1. 使用object.getprototypeof(obj) === null可准确判断对象是否无原型,该方法返回对象的[[prototype]],若为null则表…

    2025年12月20日 好文分享
    000

发表回复

登录后才能评论
关注微信