【Canvas与数学】动态生成圆形中运动的包络线

news2025/1/16 16:18:08

【题设】

F为圆内任一一点,A为圆周上一点,AF连线的中垂线交圆周与CD两点,求CD围成的图形形状。

【关键点】

使用解析几何方法求出FA中垂线与圆的交点,应用中学数学知识就可做到。

注意中间变量及时求出来比较好,在代码和书写中都省事方便。

【图示】

【代码】

<!DOCTYPE html>
<html lang="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<head>
     <title>使用HTML5/Canvas绘制圆形中运动的包络线,明显包络线是椭圆。</title>
     <style type="text/css">
     .centerlize{
        margin:0 auto;
        width:1200px;
     }
     </style>
	 </head>

     <body onload="init();">
        <div class="centerlize">
            <canvas id="myCanvas" width="12px" height="12px" style="border:1px dotted black;">
                如果看到这段文字说您的浏览器尚不支持HTML5 Canvas,请更换浏览器再试.
            </canvas>
        </div>
     </body>
</html>
<script type="text/javascript">
<!--
/*****************************************************************
* 将全体代码(从<!DOCTYPE到script>)拷贝下来,粘贴到文本编辑器中,
* 另存为.html文件,再用chrome浏览器打开,就能看到实现效果。
******************************************************************/

// canvas的绘图环境
var ctx;

// 高宽
const WIDTH=512;
const HEIGHT=512;

const RADIUS=240;

// 舞台对象
var stage;

//-------------------------------
// 初始化
//-------------------------------
function init(){
	// 获得canvas对象
	var canvas=document.getElementById('myCanvas');  
	canvas.width=WIDTH;
	canvas.height=HEIGHT;

	// 初始化canvas的绘图环境
	ctx=canvas.getContext('2d');  
	ctx.translate(WIDTH/2,HEIGHT/2);// 原点平移到画布中央

	// 准备
	stage=new Stage();	
	stage.init();

	// 开幕
    animate();
}

// 播放动画
function animate(){    
    stage.update();	
	stage.paintBg(ctx);
	stage.paintFg(ctx);	 

	// 循环
    if(true){
		sleep(100);
		window.requestAnimationFrame(animate);   
    }
}

// 舞台类
function Stage(){
	this.f={"x":-125,"y":0};
	this.arr=new Array();
	this.theta=0;
	this.pa={};

	// 初始化
	this.init=function(){
		
	}

	// 更新
	this.update=function(){
		this.theta+=Math.PI/180;
		this.pa.x=RADIUS*Math.cos(this.theta);
		this.pa.y=RADIUS*Math.sin(this.theta);

		// 计算FA中垂线与圆的交点
		if(this.pa.y==this.f.y){
			// 中垂线平行Y轴时 
			var x=(this.pa.x+this.f.x)/2;
			var y=Math.sqrt(RADIUS*RADIUS-x*x);

			var p1=createPt(x,y);
			var p2=createPt(x,-y);

			// 放入数组
			this.arr.push({"p1":p1,"p2":p2});
		}else{
			// 中垂线不平行Y轴时 
			var k1=(this.pa.y-this.f.y)/(this.pa.x-this.f.x);
			var k=-1/k1;

			// 算中点,注意单字母远比字母数字组合要优越
			var m=(this.pa.x+this.f.x)/2;
			var n=(this.pa.y+this.f.y)/2;

			// 算直线方程的b值
			var j=n-k*m;

			// 算一元二次方程的abc三值
			var a=k*k+1;
			var b=2*k*j;
			var c=j*j-RADIUS*RADIUS;

			// 算一元二次方程的delta
			var delta=Math.sqrt(b*b-4*a*c);

			// 第一根
			var x1=(-b+delta)/a/2;
			var y1=k*(x1-m)+n;
			var p1=createPt(x1,y1);

			// 第二根
			var x2=(-b-delta)/a/2;
			var y2=k*(x2-m)+n;
			var p2=createPt(x2,y2);

			// 放入数组
			this.arr.push({"p1":p1,"p2":p2});
		}

		// 保持数组容量,多了从头里删掉
		if(this.arr.length>360){
			this.arr.splice(0,1);
		}
	}

	// 画背景
	this.paintBg=function(ctx){
		ctx.clearRect(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT);// 清屏	
	
		// 背景色
		ctx.fillStyle="white";
		ctx.fillRect(-WIDTH/2,-HEIGHT/2,WIDTH,HEIGHT);

		// 背景圆
		ctx.beginPath();
		ctx.arc(0,0,RADIUS,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.2;
		ctx.strokeStyle="black";
		ctx.stroke();

		// F		
		ctx.beginPath();
		ctx.arc(this.f.x,this.f.y,2,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.5;
		ctx.strokeStyle="black";
		ctx.stroke();
		writeText(ctx,this.f.x,this.f.y,"F","16px consolas","black");

		// O		
		ctx.beginPath();
		ctx.arc(0,0,2,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.5;
		ctx.strokeStyle="black";
		ctx.stroke();
		writeText(ctx,0,0,"O","16px consolas","black");

		// A
		ctx.beginPath();
		ctx.arc(this.pa.x,this.pa.y,2,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.5;
		ctx.strokeStyle="black";
		ctx.stroke();
		writeText(ctx,this.pa.x,this.pa.y,"A","16px consolas","black");

		// AO连线
		ctx.beginPath();
		ctx.moveTo(0,0);
		ctx.lineTo(this.pa.x,this.pa.y);
		ctx.lineWidth=0.2;
		ctx.strokeStyle="green";
		ctx.stroke();

		// AF连线
		ctx.beginPath();
		ctx.moveTo(this.f.x,this.f.y);
		ctx.lineTo(this.pa.x,this.pa.y);
		ctx.lineWidth=0.5;
		ctx.strokeStyle="black";
		ctx.stroke();
		
		writeText(ctx,WIDTH/2-30,HEIGHT/2-10,"逆火原创","8px consolas","black");// 版权
	}

	// 画前景
	this.paintFg=function(ctx){
		for(var i=0;i<this.arr.length;i++){
			var c=this.arr[i].p1;
			var d=this.arr[i].p2;

			// CD连线
			ctx.beginPath();
			ctx.moveTo(c.x,c.y);
			ctx.lineTo(d.x,d.y);
			ctx.lineWidth=0.2;
			ctx.strokeStyle="black";
			ctx.stroke();
		}

		var c=this.arr[this.arr.length-1].p1;
		var d=this.arr[this.arr.length-1].p2;

		// c
		ctx.beginPath();
		ctx.arc(c.x,c.y,2,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.5;
		ctx.strokeStyle="blue";
		ctx.stroke();
		writeText(ctx,c.x,c.y,"C","16px consolas","blue");

		// d
		ctx.beginPath();
		ctx.arc(d.x,d.y,2,0,Math.PI*2,false);
		ctx.closePath();
		ctx.lineWidth=0.5;
		ctx.strokeStyle="blue";
		ctx.stroke();
		writeText(ctx,d.x,d.y,"D","16px consolas","blue");

		// CD连线
		ctx.beginPath();
		ctx.moveTo(c.x,c.y);
		ctx.lineTo(d.x,d.y);
		ctx.lineWidth=0.5;
		ctx.strokeStyle="blue";
		ctx.stroke();
	}
}

/*----------------------------------------------------------
函数:书写文字
ctx:绘图上下文
x:横坐标
y:纵坐标
text:文字
font:字体
color:颜色
----------------------------------------------------------*/
function writeText(ctx,x,y,text,font,color){
	ctx.save();
	ctx.textBaseline="bottom";
	ctx.textAlign="center";
	ctx.font = font;
	ctx.fillStyle=color;
	ctx.fillText(text,x,y);
	ctx.restore();
}

/*----------------------------------------------------------
函数:创建一个二维坐标点
x:横坐标
y:纵坐标
Pt即Point
----------------------------------------------------------*/
function createPt(x,y){
	var retval={};
	retval.x=x;
	retval.y=y;
	return retval;
}

/*----------------------------------------------------------
函数:延时若干毫秒
milliseconds:毫秒数
----------------------------------------------------------*/
function sleep(milliseconds) {
	const date = Date.now();
	let currentDate = null;
	do {
		currentDate = Date.now();
	} while (currentDate - date < milliseconds);
}

/*-------------------------------------------------------------
我希望我老了,去徒步去露营,然后,一睡不醒,死在旅途上,无需
落叶归根,无所谓曝尸荒野,是尘归尘土归土,不让它们赚半分黑钱。
--------------------------------------------------------------*/
//-->
</script>

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1608658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++程序员的自我修炼】友元

心之所向 素履以往 目录 友元函数 cout 如何输出自定义类型 cin 如何输入自定义类型 总结 友元类 日期类Date 如何访问 时间类Time 内部类 概念&#xff1a; 总结&#xff1a; 契子✨ 我们之前已经把类与对象的基础知识已经学完了&#xff0c;这些是只针对一个类的操作 那么…

endnote21从安装到使用!文献引用!Mac版

视频学习和资源获取 新建库 选择上方导航栏处的File下的New 软件 软件界面可以分成四个部分 2是个人图书馆 3是对某一分类中文献的展示 最右侧是对具体一篇文献的摘要、编辑以及PDF 有回形针标志意味着这篇有全文&#xff0c;也就是有pdf 如果没有回形针代表它只有引文信…

【电力工程】电力大数据和云架构智能AI服务平台研发建设项目可行性研究报告范例

1、项目概况 本项目拟进行基于电力大数据和云架构的智能 AI 服务平台的研究,具体包括电力多元大数据中心、技术中台、数据中台和智能 AI 中台,基于电力大数据云平台基础构建 BI 可视化开发平台和智能 AI 服务平台。 该项目的实施旨在引领公司在大数据领域发展的新趋势,从功…

【第三版 系统集成项目管理工程师】 十五至尊图

持续更新。。。。。。。。。。。。。。。 【第三版】十五至尊图 十五至尊图【必会】1.整合&#xff08;7&#xff09;2.范围 &#xff08;6&#xff09;3.进度 &#xff08;6&#xff09;4.成本 &#xff08;4&#xff09;5.质量&#xff08;3&#xff09;6.资源&#xff08;6&…

el-table使用show-summary合计,但只需要合并某一列

el-table使用show-summary合计&#xff0c;但只需要合并某一列 这里有两种方法&#xff0c;一种是网上的&#xff0c;我会引用他的链接给你们看。 一种是我自己看源码发现的 方法一 这个就是方法一的链接 点击我跳转方法一 方法二 不需要计算的列 去掉prop 然后用插槽显示即可…

利用动态规划在有向图上实现高效语音识别算法

在现代语音识别系统中&#xff0c;动态规划是一种非常关键的技术。它能够帮助我们将复杂的语音信号转换为可理解的文字信息。在本文中&#xff0c;我们将探讨如何使用动态规划方法在有向图上实现语音识别。我们将首先介绍问题的背景和基本概念&#xff0c;然后提供一个高效的算…

【路径规划】局部路径规划算法——DWA算法(动态窗口法)|(含python实现 | c++实现)

文章目录 参考资料1. DWA算法原理1.1 简介1.2 算法原理1. 速度采样2. 轨迹预测&#xff08;轨迹推算&#xff09;3. 轨迹评价 2. Python实现2.1 参数配置2.2 机器人运动学模型2.3 DWA算法类实现2.4 画图2.5 主函数 3. c实现4. 总结 参考资料 The Dynamic Window Approach to C…

如何在Windows 10中启用和使用上帝模式,这里有详细步骤

序言 上帝模式&#xff08;God Mode&#xff09;是一个特殊的文件夹&#xff0c;只在一个窗口中显示所有可用的操作设置。它可以节省搜索命令的时间&#xff0c;而无需知道通过“开始”菜单或“控制面板”查找命令的步骤。上帝模式默认情况下是隐藏的&#xff0c;所以我们需要…

ROS 2边学边练(29)-- 使用替换机制

前言 启动文件用于启动节点、服务和执行流程。这组操作可能有影响其行为的参数。替换机制可以在参数中使用&#xff0c;以便在描述可重复使用的启动文件时提供更大的灵活性。替换是仅在执行启动描述期间评估的变量&#xff0c;可用于获取特定信息&#xff0c;如启动配置、环境变…

链表带环问题——leetcode环形链表1 2

证明链表带环 链表的带环问题指的是本该指向NULL的最后一个节点指向了之前的节点&#xff0c;导致链表成环&#xff0c;找不到尾结点的情况&#xff0c;那么我们该如何证明链表带环呢&#xff1f; 我们可以类比物理中的追及问题&#xff0c;让快慢指针同时走&#xff0c;两者相…

在wsl下安装QT

文章目录 一、前言二、安装QT1、安装依赖 2、安装qt1、先下载到window中&#xff0c;复制到wsl上2、执行命令 三、命令行打开QT1、打开~/.bashrc,在里面添加命令2、测试 四、mysql驱动 一、前言 本方案可以在wsl下正常安装QT&#xff0c;但是QT菜单栏的字体大小调整不了&#…

【开源】使用Python+Flask+Mysql快速开发一个用户增删改查系统

项目演示 项目本身很简单&#xff0c;增删改查是几乎所有系统的骨架。正所谓万丈高楼平地起&#xff0c;学会了增删改查&#xff0c;航母就指日可待了&#xff1a;&#xff09;&#xff0c;光速入门&#xff0c;直接看演示图&#xff1a; 项目地址 https://github.com/mudf…

[C++][算法基础]欧拉函数(常规求质数)

给定 n 个正整数 &#xff0c;请你求出每个数的欧拉函数。 欧拉函数的定义 1∼N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为 ϕ(N)。 若在算数基本定理中&#xff0c;N…&#xff0c;则&#xff1a; ϕ(N) N… 输入格式 第一行包含整数 n。 接下来 n 行&#xf…

雨云:让你的服务器体验不再“阴霾”

引言 在当今数字化的时代&#xff0c;服务器已经成为了我们生活中不可或缺的一部分。无论是个人网站、企业应用还是游戏服务器&#xff0c;都需要一个稳定可靠的平台来运行。然而&#xff0c;在选择服务器提供商时&#xff0c;很多人常常陷入选择困难&#xff0c;不知道哪家更适…

IO进程(线程Thread)

线程Thread 1.什么是线程 1.1 概念 线程是一个轻量级的进程&#xff0c;为了提高系统的性能引入线程。 线程和进程都参与统一的调度。 在同一个进程中可以创建的多个线程, 共享进程资源。 &#xff08;Linux里同样用task_struct来描述一个线程&#xff09; 1.2 进程和线程的区别…

精益思维驱动人工智能革新:理论到实践的跃迁之旅

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已成为引领未来的关键力量。在这个变革的时代&#xff0c;如何将精益思维与人工智能相结合&#xff0c;推动AI从理论走向实践&#xff0c;成为行业内外关注的焦点。本文&#xff0c;天行健精益生产顾问将分享…

拷贝构造函数与运算符重载

目录 一、拷贝构造函数 1.概念 2.特性 二、运算符重载 1.运算符重载 2.运算符重载实现的形式 3.赋值运算符重载 一、拷贝构造函数 1.概念 拷贝构造函数是一种特殊的构造函数&#xff0c;它在创建对象时&#xff0c;使用同一类中之前创建的对象来初始化新创建的对象…

Vitis HLS 学习笔记--scal 函数-探究

目录 1. Vitis HLS重器-Vitis_Libraries 2. 初识scal() 3. 函数具体实现 3.1 变量命名规则 3.2 t_ParEntries解释 3.3 流类型详解 3.4 双重循环 4. 总结 1. Vitis HLS重器-Vitis_Libraries 在深入探索Vitis HLS&#xff08;High-Level Synthesis&#xff09;的旅程中&…

【单调栈】力扣85.最大矩形

好久没更新了 ~ 我又回来啦&#xff01; 两个好消息&#xff1a; 我考上研了&#xff0c;收到拟录取通知啦&#xff01;开放 留言功能 了&#xff0c;小伙伴对于内容有什么疑问可以在文章底部评论&#xff0c;看到之后会及时回复大家的&#xff01; 前面更新过的算法&#x…

kafka的概念以及Zookeeper集群 + Kafka集群 +elfk集群

目录 zookeeper同步过程 分布式通知和协调 zookeeper同步过程 分布式通知和协调 准备 3 台服务器做 Zookeeper 集群 192.168.68.5 192.168.68.6 192.168.68.7 安装前准备 //关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 node1服务器&a…