Как оптимизировать данный фрагмент кода? HTML, JS

    let can = document.getElementById("can");
    let ctx = can.getContext("2d");
    ctx.fillStyle="fff";

    function zad1(){
        let x = document.getElementById("xpos").value;
        let y = document.getElementById("ypos").value;
        let red = Math.random() * 256;
        let green = Math.random() * 256;
        let blue = Math.random() * 256;
        console.log(red)
        if(document.getElementById("fig").value == "square"){
            ctx.beginPath();
            ctx.rect(x, y, 50, 50)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")"
            ctx.fill();
        }
        else if(document.getElementById("fig").value == "round"){
            ctx.beginPath();
            ctx.arc(x, y, 50, 0, Math.PI*2, false)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")"
            ctx.fill();
        }
        else if(document.getElementById("fig").value == "triangle"){
            ctx.beginPath();
            ctx.moveTo(x, y)
            ctx.lineTo(x-50, y)
            ctx.lineTo(x-50, y-30)
            ctx.lineTo(x, y)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")"
            ctx.fill();
        }
        else if(document.getElementById("fig").value == "car"){
            ctx.beginPath();
            ctx.rect(x-100, y-30, 120, 20)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")";
            ctx.fill();
            ctx.closePath();
            ctx.beginPath();
            ctx.rect(x-70, y-50, 60, 20)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")";
            ctx.fill();
            ctx.closePath();
            ctx.beginPath();
            ctx.arc(x,y, 15, 0, Math.PI*2, false)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")"
            ctx.fill();
            ctx.closePath()
            ctx.beginPath();
            ctx.arc(x-80,y, 15, 0, Math.PI*2, false)
            ctx.stroke();
            ctx.fillStyle="rgb("+red+"," + green + "," + blue + ")"
            ctx.fill();
            ctx.closePath();

        }

    }

Ответы (2 шт):

Автор решения: Aziz Umarov

Можно по двигаться в таком направлении.

let can = document.getElementById("can");
let ctx = can.getContext("2d");

function beforeDraw(ctx){
  ctx.beginPath();
}

function afterDraw(ctx){
  ctx.stroke();
  ctx.fill();
  ctx.closePath();
}

const figureDrawer = {
   "square": function (ctx, x, y, x1 = 50,  y1 = 50) {
      beforeDraw(ctx);
      ctx.rect(x, y, x1, y1);
      afterDraw(ctx);
    },
   "round": function (ctx, x, y, radius = 50) {
      beforeDraw(ctx);
      ctx.arc(x, y, radius, 0, Math.PI*2, false)
      afterDraw(ctx);
    }, 
   "triangle": function (ctx, x, y) {
      beforeDraw(ctx);
      ctx.moveTo(x, y)
      ctx.lineTo(x-50, y)
      ctx.lineTo(x-50, y-30)
      ctx.lineTo(x, y)
      afterDraw(ctx);
    },
   "car": function (ctx, x, y){
      this["square"](ctx, x-100, y-30, 120, 20);
      this["square"](ctx, x-70, y-50, 60, 20);
      this["round"](ctx, x, y, 15);
      this["round"](ctx, x-80, y, 15);
    }
}

function zad1(){
  let x = document.getElementById("xpos").value;
  let y = document.getElementById("ypos").value;
  let figure = document.getElementById("fig").value;
  let red = Math.random() * 256;
  let green = Math.random() * 256;
  let blue = Math.random() * 256;
  let color = "rgb("+red+"," + green + "," + blue + ")";
  ctx.fillStyle=color;
  
  figureDrawer[figure](ctx, x, y);
}

zad1();
<canvas id="can" width="400" height="400"></canvas>
<input id="fig" value="car">
<input id="xpos" value="100">
<input id="ypos" value="100">

→ Ссылка
Автор решения: OPTIMUS PRIME

Во-первых, если предстоит рисовать много сложных фигур, важно сразу подготовить набор функций для рисования простых элементов, которые в первую очередь будет удобно вызывать. Например, избавившись от необходимости бесконечно повторять beginPath, closePath, или одинаковые параметры функций (углы 0, 2 * Math.PI для окружностей, эллипсов и пр.).

Если всё же рисуете составную фигуру одним большим блоком кода, хорошо бы разделять пробельными строками каждый beginPath, чтобы легче было в него вникать. + Оставлять комментарии, что конкретно рисует данная часть кода.


Ответы на вопросы про оптимизацию кода, которые на самом деле не про оптимизацию, а про реорганизацию кода, сильно зависят от вкусовщины и настроения. Ну получилось так:

const cnv = {
  ctx: document.getElementById('can').getContext('2d'),
  
  clear: function() {
    let ctx = this.ctx;    
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    
    return this;
  },

  rect: function (x, y, wid, hei) {
    if (hei === undefined) hei = wid;
    // передана только одна сторона? Рисуем квадрат.

    this.ctx.beginPath();
    this.ctx.rect(x, y, wid, hei);

    return this;
  },

  arc: function (cx, cy, r, start = 0, stop = 2 * Math.PI) {
    this.ctx.beginPath();
    this.ctx.arc(cx, cy, r, start, stop);

    return this;
  },

  triangle: function (x1, y1, x2, y2, x3, y3) {
    let ctx = this.ctx;

    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.lineTo(x3, y3);
    ctx.lineTo(x1, y1);

    return this;
  },

  fill: function (color) {
    if (color) this.ctx.fillStyle = color;
    this.ctx.fill();

    return this;
  },

  stroke: function (color) {
    if (color) this.ctx.strokeStyle = color;
    this.ctx.stroke();

    return this;
  },

  /***/
  randColor: function () {
    let r = Math.random() * 256 | 0;
    let g = Math.random() * 256 | 0;
    let b = Math.random() * 256 | 0;

    return `rgb(${r},${g},${b})`;
  },
};

/***/
let elem = {
  xpos: document.getElementById('xpos'),
  ypos: document.getElementById('ypos'),
  shape: document.getElementById('shape'),
};

draw();
elem.shape.addEventListener('change', draw);

/***/
function draw() {
  cnv.clear();
  
  // Из value всегда прилетает строка.
  // Если ожидается работа с числами - явно превращайте в число.  
  let x = Number(elem.xpos.value);
  let y = Number(elem.ypos.value);
  let shape = elem.shape.value;

  let color = cnv.randColor();

  switch (shape) {
    case 'square':
      cnv.rect(x, y, 50).stroke().fill(color);
      break;

    case 'circle':
      cnv.arc(x, y, 50).stroke().fill(color);
      break;

    case 'triangle':
      cnv.triangle(x, y, x - 50, y, x - 50, y - 30).stroke().fill(color);
      break;

    case 'car':
      cnv.rect(x - 100, y - 30, 120, 20).stroke().fill(color); // тело
      cnv.rect(x - 70, y - 50, 60, 20).stroke().fill(color); // крыша

      cnv.arc(x, y, 15).stroke().fill(color); // правое колесо
      cnv.arc(x - 80, y, 15).stroke().fill(color); // левое колесо
      break;
  }
}
<input id="xpos" value="120">
<input id="ypos" value="60">
<select id="shape">
  <option>car</option>
  <option>circle</option>
  <option>square</option>
  <option>triangle</option>
</select>
<button onclick="draw()">New</button>

<canvas id="can"></canvas>

Рисовашки фигур однострочные, и их мало. Для разнообразия оставил в switch. Но тоже хотелось вынести в отдельный объект, примерно как у @AzizUmarov.

→ Ссылка