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

时间:2018-08-07 15:37:05

标签: javascript html5 object canvas

编辑:我将把我所有的代码发布为html和js,请原谅我过多的评论

我正在尝试通过for循环在画布中创建矩形(有输入用户) 我想在另一个函数中访问它们以做一些事情, 主要问题是如何在循环后访问形状的名称,我尝试过这种方法,但是当我在另一个函数中调用它们时,会给我一个提示,

  

未定义的“对象名称”

var canvas = document.querySelector('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;




var c = document.getElementById("myCanvas");

//drawing the base off the towers
var base_twr1 = c.getContext("2d");
base_twr1.beginPath();
base_twr1.moveTo(550, 500);
base_twr1.lineTo(300, 500);
base_twr1.lineWidth = 10;
base_twr1.strokeStyle = '#ff0000';
base_twr1.closePath();
base_twr1.stroke();

var base_twr2 = c.getContext("2d");
base_twr2.beginPath();
base_twr2.moveTo(900, 500);
base_twr2.lineTo(650, 500);
base_twr2.closePath();
base_twr2.stroke();

var base_twr3 = c.getContext("2d");
base_twr3.beginPath();
base_twr3.moveTo(1250, 500);
base_twr3.lineTo(1000, 500);
base_twr3.closePath();
base_twr3.stroke();


//drawing the towers
var twr1 = c.getContext("2d");
twr1.beginPath();
twr1.moveTo(430, 300);
twr1.lineTo(430, 500);
twr1.closePath();
twr1.stroke();

var twr2 = c.getContext("2d");
twr2.beginPath();
twr2.moveTo(780, 300);
twr2.lineTo(780, 500);
twr2.closePath();
twr2.stroke();

var twr3 = c.getContext("2d");
twr3.beginPath();
twr3.moveTo(1130, 300);
twr3.lineTo(1130, 500);
twr3.closePath();
twr3.stroke();

//array to know each tower what contains
//to avoid collisions
var disks_in_twrs = [];
var twr1_holder = [];
var twr2_holder = [];
var twr3_holder = [];

//start function check the user input
//and call another function if everthing
//is fine 
function btn_start() {
    disks_number = document.getElementById("disk_input").value;
    disks_number = parseInt(disks_number);
    if (disks_number > 0) {
        if (disks_number < 8)
            put_disks(disks_number);
    } else
        alert('write number');

}

var width_disks_start = 305;
var height_disks_start = 490;

var disk_width = 220;

function put_disks(disks) {
    for (i = 0; i < disks; i++) {
        //     var r = Math.floor((Math.random() * 256));
        // var g = Math.floor((Math.random() * 256));
        // var b = Math.floor((Math.random() * 256)); 

        str1 = "disk";
        width_disks_start = width_disks_start + 10;
        height_disks_start = height_disks_start - 20;

        disk_width = disk_width - 30;

        // eval("disks_in_twrs.push(str1 + i)" );

        // disks_in_twrs[i]=c.getContext("2d");
        // disks_in_twrs[i].rect((Math.random)*100,(Math.random)*100,150,100);
        // disks_in_twrs[i].stroke();
        //  alert(disks_in_twrs);

        twr1_holder.push(str1 + i);

        // ctx.fillStyle = 'rgb(' + r + ',' + g + ', ' + b + ')';
        // alert(str1 + i);

        //twr1_holder[i] = c.getContext("2d");
        eval("var disk"+i+"= c.getContext('2d');");
        

        // twr1_holder[i].rect(width_disks_start, height_disks_start, disk_width, 20);
        eval("disk"+i+".rect(width_disks_start, height_disks_start, disk_width, 20);");
        // twr1_holder[i].strokeStyle = "black";
        eval("disk"+i+".strokeStyle = 'black';");
        // twr1_holder[i].stroke();
        eval("disk"+i+".stroke();");


        // alert(disk1.toSource());


    }

}
function hide_me(){
    alert("byeeeeeeeeeeeeeeeee");
    twr1.fillRect(430, 500, 250, 250);
    // disk2.rect(515, 51, 6, 20);

    // disk2.strokeStyle = 'red';

}
<!DOCTYPE html>
<html>
    <head>
        <title>tower of Hanoi</title>
        <style type="text/css">
        canvas{
            border : 1px solid black;
        }

        </style>
    </head>
<body>
        <label>how many disk do you want ?</label>
        <input type="text" id="disk_input">
        <button id="start" onclick="btn_start()">start</button>
        <label>note that maximum disk is 8 :P</label>
        <button id="make_hidden" onclick="hide_me()" >make me hide</button>
<canvas id="myCanvas" >
    
</canvas>

<script src="tower.js">

</script>


</body>
</html>

2 个答案:

答案 0 :(得分:1)

这里有很多事情!我建议分别攻击代码中的每个问题并逐步建立理解,因为这是一个需要大量不同组件(DOM操作/事件处理程序,JS画布,对象/数组/循环,设计等)的应用程序。如果您对以上任何一个概念都不满意,请选择一个领域(例如DOM操作)并花时间研究简单易懂的示例,然后将学到的内容应用到主应用程序中。

首先,几乎总是完全避免使用eval。 Mozilla说never to use it!如果您正在使用它,则可能意味着您的设计已经走到了尽头,在我看来,情况就是如此。

对于事件处理程序和文档处理,我建议避免使用onclick。在脚本中添加事件侦听器可以完成这项工作。您可能会在画布上监听点击,以便以后进行交互。

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

最难的部分是设计代码以实现您的目标,对此我尚不完全清楚。您是在制作交互式的Hanoi塔应用程序,还是只是为求解器算法设置动画并且不需要用户输入的应用程序?无论哪种方式,我都选择使用对象构造函数来表示塔和磁盘。使用数组保存这些对象意味着您可以通过塔和磁盘在数组中的位置来标识它们,而不用eval来输入字符串名称。每当您想对塔进行操作(例如绘制塔)时,您要做的就是遍历塔并在每个塔上调用draw。后来,在处理用户输入或编写求解器算法时,应该很容易操纵这些数组以满足您的需求(例如,确定单击了哪个磁盘,在塔之间移动磁盘等)。

请记住,下面的示例只是一个快速入门,它可能不会遵循最佳设计原则或满足您需求的原则。例如,我已经对大多数图形坐标值进行了硬编码,因此它没有响应,因此有许多练习可供读者改进。

const Disk = function(width, color) {
  this.width = width;
  this.color = color;
};

const Tower = function(x, disks) {
  this.x = x;
  this.disks = [];
  this.width = 20;
};
Tower.prototype.draw = function(c, ctx) {
  ctx.lineWidth = this.width;
  ctx.strokeStyle = "#000";
  ctx.beginPath();
  ctx.moveTo(this.x, 0);
  ctx.lineTo(this.x, c.height);
  ctx.stroke();

  this.disks.forEach((e, i) => {
    ctx.fillStyle = e.color;
    ctx.fillRect(
      this.x - e.width / 2,
      c.height - (i + 1) * this.width,
      e.width, this.width
    );
  });
};

const draw = (c, ctx, towers) => {
  ctx.clearRect(0, 0, c.width, c.height);
  towers.forEach(t => t.draw(c, ctx));
};

const initialize = disks => {
  const towers = [
    new Tower(c.width / 5),
    new Tower(c.width / 2),
    new Tower(c.width - c.width / 5)
  ];

  for (let i = disks; i > 0; i--) {
    towers[0].disks.push(
      new Disk(i * 30, `hsl(${Math.random() * 360}, 50%, 50%`)
    );
  }

  return towers;
};

document.getElementById("initialize-form")
  .addEventListener("submit", e => {
    e.preventDefault();
    towers = initialize(parseInt(e.target.elements[0].value), towers);
    draw(c, ctx, towers);
  });

document.getElementById("btn-hide").addEventListener("click",
  e => document.getElementById("menu").style.display = "none"
);

const c = document.getElementById("hanoi");
c.width = 600;
c.height = 200;
const ctx = c.getContext("2d");
let towers;
body {
  margin: 0;
}

#hanoi {
  padding: 0.5em;
}

#initialize-form {
  display: inline-block;
}

#menu {
  padding: 0.5em;
  display: inline-block;
}
<div id="menu">
  <form id="initialize-form">
    <label>Enter disks:</label>
    <input type="number" min="1" max="8" value="6">
    <button type="submit">start</button>
  </form>
  <button id="btn-hide">hide</button>
</div>
<canvas id="hanoi"></canvas>

答案 1 :(得分:0)

对于您尝试做的事情,应该考虑使用画布库,也许是Konva:
https://konvajs.github.io/

这里是一个例子:

<script src="https://cdn.rawgit.com/konvajs/konva/2.1.7/konva.min.js"></script>

<div id="container"></div>

<script>
function KonvaRect(x, y, fill, draggable) {
    return new Konva.Rect({
        x: x, y: y, width: 50, height: 50,
        fill: fill, stroke: 'black',
        strokeWidth: 4, draggable: draggable
    });
}

var boxes = [];
boxes.push(KonvaRect(50, 10, '#00D2FF', true));
boxes.push(KonvaRect(200, 10, '#0000FF', true));
boxes.push(KonvaRect(125, 10, '#FF0000', false));

var layer = new Konva.Layer();
boxes.forEach(function(b) { layer.add(b) });

var stage = new Konva.Stage({
    container: 'container', width: 600, height: 170
});
stage.add(layer);

function moveCenter() {        
    boxes.forEach(function(b) { b.move({ x:0, y: Math.random() * 10 }) });
    layer.batchDraw();
}

boxes[0].on('mouseover', function() {
    moveCenter();
});
</script>

在此示例中,我将3个框放在一个数组中,当我们在浅蓝色框上检测到鼠标时,所有框都随机向下移动,两个框都可以单击并在画布上拖动。

据记录,那里还有许多其他图书馆...