Javascript 通过在画布中循环创建形状

Javascript 通过在画布中循环创建形状,javascript,html,object,canvas,Javascript,Html,Object,Canvas,编辑:我会把我所有的代码发布到html和js上,请原谅我的评论太多了 我正在尝试创建画布中的矩形,因为循环中有输入用户 我想在另一个函数中访问它们来做一些事情, 主要的问题是如何在循环后访问形状的名称我已经尝试过了,但是当我在另一个函数中调用它们时,它给了我 未定义的对象名 var canvas=document.querySelector'canvas'; canvas.width=window.innerWidth; canvas.height=window.innerHeight; va

编辑:我会把我所有的代码发布到html和js上,请原谅我的评论太多了

我正在尝试创建画布中的矩形,因为循环中有输入用户 我想在另一个函数中访问它们来做一些事情, 主要的问题是如何在循环后访问形状的名称我已经尝试过了,但是当我在另一个函数中调用它们时,它给了我

未定义的对象名

var canvas=document.querySelector'canvas'; canvas.width=window.innerWidth; canvas.height=window.innerHeight; var c=document.getElementByIdmyCanvas; //从塔楼上拔下基座 var base_twr1=c.getContext2d; base_twr1.beginPath; 基地1。移动到550500; 基准线至300500; 基线宽度=10; base_twr1.strokeStyle='ff0000'; base_twr1.closePath; 基本行程; var base_twr2=c.getContext2d; base_twr2.beginPath; 基地2.移动至900500; 基准线为650500; base_twr2.closePath; 基础冲程; var base_twr3=c.getContext2d; base_twr3.beginPath; 基数为1250500; 基线为1000500; base_twr3.closePath; 基础行程3.行程; //画塔 var twr1=c.getContext2d; twr1.beginPath; twr1.移动至430300; twr1.lineto430500; twr1.closePath; twr1.1中风; var twr2=c.getContext2d; twr2.beginPath; twr2.移动到780300; twr2.lineto780500; twr2.closePath; twr2.1中风; var twr3=c.getContext2d; twr3.beginPath; twr3.移动到1130300; twr3.5至1130500; twr3.closePath; twr3.中风; //阵列以了解每个塔包含的内容 //避免碰撞 var disks_in_twrs=[]; var twr1_持有人=[]; var twr2_持有人=[]; var twr3_持有人=[]; //启动功能检查用户输入 //并调用另一个函数,如果 //很好 功能btn\U启动{ disks\u number=document.getElementByIddisk\u input.value; 磁盘号=parseIntdisks\u号; 如果磁盘数量>0{ 如果磁盘数量<8 将磁盘放入磁盘编号; }否则 警报“写入编号”; } var宽度\u磁盘\u开始=305; var height_disks_start=490; var盘_宽度=220; 功能放盘盘盘{ 对于i=0;i这里发生了很多事情!我建议您分别解决代码中的每个问题,并逐步建立理解,因为这是一个需要大量不同组件的应用程序—DOM操纵/事件处理程序、JS画布、对象/数组/循环、设计等。如果您对其中任何一个概念感到不适,选择一个领域,比如DOM操作,花时间编写简单易懂的示例,然后将所学知识应用到主应用程序中

首先,几乎总是完全避免评估。Mozilla说!如果你正在使用它,这可能意味着你的设计在某个方面已经失控了,我认为这里就是这样

至于事件处理程序和文档操作,我建议避免onclick。在脚本中添加事件侦听器可以完成任务;您可能会监听画布上的单击,以便稍后启用交互

下一步:使用画布。通常,每个应用程序只需检索一次上下文,而不是在每个图形之前。除此之外,您的绘图代码看起来还不错,只是不太好,这通常是重新设计的信号

最困难的部分是设计代码以满足您的目标,这一点我并不完全清楚。你是在制作一个互动的河内塔应用程序,还是一个简单的动画解算器算法和要求 没有用户输入?无论哪种方式,我都选择使用对象构造函数来表示塔和磁盘。使用数组保存这些对象意味着您可以通过塔和磁盘在数组中的位置来识别它们,而不是使用字符串名称。每当你想在你的塔上执行一个动作,比如画它们,你所需要做的就是在塔上循环并调用每个塔上的draw。稍后,当涉及到处理用户输入或编写解算器算法时,操作这些阵列以满足您的需要应该相当容易,例如,确定单击了哪个磁盘,在塔之间移动磁盘,等等

请记住,下面的示例只是一个简单的示意图,可能无法遵循最佳设计原则或满足您需求的原则。例如,我已经硬编码了大多数图形坐标值,因此它没有响应性,所以有很多练习留给读者改进

const Disk=函数宽度,颜色{ 这个。宽度=宽度; 这个颜色=颜色; }; 常数塔=函数x,磁盘{ 这个.x=x; this.disks=[]; 这个宽度=20; }; Tower.prototype.draw=函数C,ctx{ ctx.lineWidth=此.width; ctx.strokeStyle=000; ctx.beginPath; ctx.moveTothis.x,0; ctx.lineTothis.x,c.高度; ctx.stroke; this.disks.forEache,i=>{ ctx.fillStyle=e.color; ctx.fillRect 这个.x-e.width/2, c、 高度-i+1*此宽度, e、 宽度,这个 ; }; }; 施工图=c、ctx、塔楼=>{ ctx.clearRect0,0,c.宽度,c.高度; towers.forEacht=>t.drawc,ctx; }; 常量初始化=磁盘=>{ 恒塔=[ 新塔楼,宽/5, 新塔楼宽/2, 新塔楼c.width-c.width/5 ]; 对于let i=磁盘;i>0;i-{ 塔[0].disks.push 新磁盘*30,`hsl${Math.random*360},50%,50%` ; } 返回塔; }; document.getElementById初始化-form .addEventListenersubmit,e=>{ e、 防止违约; towers=InitializeParsente.target.elements[0]。值,towers; drawc、ctx、塔楼; }; document.getElementByIdbtn-hide.addEventListenerclick, e=>document.getElementByIdmenu.style.display=none ; const c=document.getElementByIdhanoi; c、 宽度=600; c、 高度=200; const ctx=c.getContext2d; 让塔楼; 身体{ 保证金:0; } 河内{ 填充:0.5em; } 初始化表单{ 显示:内联块; } 菜单{ 填充:0.5em; 显示:内联块; } 输入磁盘: 开始 隐藏
对于你想要做的事情,你应该考虑使用画布库,也许Konva:

以下是一个例子:

函数KonvaRectx,y,fill,draggable{ 返回新的Konva.Rect{ x:x,y:y,宽度:50,高度:50, 填充:填充,笔划:“黑色”, 冲程宽度:4,可拖动:可拖动 }; } 变量框=[]; box.pushKonvaRect50,10,'00D2FF',正确; box.pushKonvaRect200,10,'0000FF',正确; box.pushKonvaRect125,10,'FF0000',假; var层=新Konva.层; box.forEachfunctionb{layer.addb}; var阶段=新Konva.阶段{ 容器:“容器”,宽:600,高:170 }; stage.addlayer; 函数移动中心{ boxes.forEachfunctionb{b.move{x:0,y:Math.random*10}; 图层绘制; } 框[0]。在“鼠标悬停”上,函数{ 移动中心; };
twr1_holder是字符串数组吗?在循环之后,你在哪里尝试访问矩形?你能出示密码吗?@mattl。我已经尝试过用这种方式访问,通过保存在数组中,但没有结果请发布所有代码。twr1_holder似乎是一个可以保存数据的数组,但是为什么在这里使用string和eval?您不需要每次通过循环都获得一个新的画布上下文。应该只有一个图形上下文。如果要保存矩形,则需要在数组中保存对象。您应该将矩形的高度、宽度、x和y存储在object@mo.khashab您不必命名它们来循环它们。如果将对象推入一个数组,然后使用for循环或forEach在该数组上迭代,则可以单独访问每个对象。请完全忘记eval。很好的实现!但是我认为以后最大的障碍将是处理用户输入/拖放,我强烈建议使用canvas库,请参见另一个答案上的示例。很好的建议和示例,库肯定可以提供帮助。我不确定这部作品是不是为了互动,也不清楚它的目标是什么,可能只是一部动画?是的,不太清楚它的目标是什么,闻起来很像家庭作业troubles@ggorlen非常感谢您的努力,但是现在如果我想在另一个函数中处理磁盘,让我们假设我想让它移动,我怎么称呼这个对象,我不知道我该怎么做,非常感谢你的努力,很好的实现,我会认真考虑你的笔记,如果你能给我举个例子,我会更加感激,是的,这是分配。我并不是有意接受答案的
有很多,我只是想了解发生了什么。@mo.khasab是的,发生了很多事情。这一步由您决定如何做到最好,但如果由我决定,我会开始向类原型中添加函数。例如,您可能需要一个方法,该方法弹出一个塔式堆栈的顶部并将其附加到另一个塔式堆栈上。您甚至可能需要一个具有此方法并封装所有塔的Hanoi类。我在想一个像河内这样的头球。移动DiskSourceTower,destinationTower。希望能为您指出正确的方向,首先在这里学习基本知识,然后应该清楚如何移动磁盘。感谢您的努力,但不允许使用库,我正在尝试在for循环中创建对象时命名对象,以便在其他地方使用它们,我创建了一个单独的ctx并尝试移动它,但没有结果,它创建了一个新的ctx,而没有重新设置原始ctx