【问题标题】:Draw multiple shapes on a canvas out of arrays of points用点数组在画布上绘制多个形状
【发布时间】:2020-12-31 10:23:10
【问题描述】:

我有一个带有画布的 html 页面,该画布连接到网络服务器并接收一些需要在画布上绘制的点数组。

连接建立后,我得到一个形状作为主模板。这将在整个会话中保持不变。然后我得到了一些其他的形状。

我的问题是我不知道如何只绘制一次主要形状(模板),然后轻松地在画布上绘制和清理其他形状。这是我的申请流程:

1 - 绘制主模板形状 2 - 当一个新形状到达时,在画布上绘制它(不清除已经绘制的模板) 3- 当新形状出现时,绘制旧形状(不是模板)并绘制新形状

我只需要绘制第一个模板一次,因为我得到了大量的点并且我对它们进行了耗时的计算。我不想每次出现新形状时都重绘这个模板...

这是实现这一目标的方法吗?它还应该考虑页面大小调整等...

我已经准备了下面的最小示例来模拟我需要什么......

// Main template shape
let template = [{x: 10, y:10}, {x: 120, y:10}, {x: 110, y:110}, {x: 50, y:175}];

// Some shapes
let shapes = [
    [{x: 20, y:20}, {x: 110, y:20}, {x: 100, y:100}, {x: 60, y:145}],
    [{x: 30, y:30}, {x: 100, y:30}, {x: 90, y:90}, {x: 70, y:135}],
    [{x: 40, y:40}, {x: 90, y:40}, {x: 80, y:70}, {x: 80, y:125}],
];

let shapeIndex = 0; // To cycle through shapes array

let canvas = {}; // Canvas to draw on
let ctx = {}; // Context of the Canvas

// Init elements
$( document ).ready(function() {
    canvas = document.getElementById("myCanvas");
    ctx = canvas.getContext("2d");
});

// Draw the template
function drawTemplate() {
    ctx.save();

    ctx.beginPath();
    ctx.strokeStyle = 'yellow';
    ctx.fillStyle = 'red';
    for(let point of template) {
        ctx.lineTo(point.x, point.y);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    ctx.restore();
}

// Draw one shape from the shapes array
function drawShape() {
    ctx.save();

    ctx.strokeStyle = 'white';
    ctx.fillStyle = 'rgba(127,127,127,0.5)';
    ctx.beginPath();

    let points = getShapeFromArray();
    for(point of points) {
        ctx.lineTo(point.x, point.y);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    ctx.restore();
}

// Draw one shape from the shapes array
function drawShape() {

    // How to clear the perviously drawn shape, if any?

    ctx.save();

    ctx.strokeStyle = 'white';
    ctx.fillStyle = 'rgba(127,127,127,0.5)';
    ctx.beginPath();

    let points = getShapeFromArray();
    for(point of points) {
        ctx.lineTo(point.x, point.y);
    }
    ctx.closePath();
    ctx.fill();
    ctx.stroke();

    ctx.restore();
}

// Helper to get next element in the array
function getShapeFromArray() {
    if(shapeIndex == shapes.length) {
        shapeIndex = 0;
    }
    return shapes[shapeIndex++];

}
<!DOCTYPE html>
<html>

<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <title>Hahaha!</title>
</head>

<body>
<canvas id="myCanvas" width="200" height="200" style="border:1px solid #000000;"></canvas>
<p style="text-align: left">
    <button onclick="drawTemplate()">draw template</button>
    <button onclick="drawShape()">draw shape</button>
</p>
</body>
</html>

【问题讨论】:

  • 不是一个很好的解决方案,但如果它只是一个图形表示,只需在此下方放置一个新的 Canvas,并在下面的画布上绘制模板,以便您可以使用 clearRect 或任何您需要的东西在上面一个
  • 每次有变化时清除画布并只重绘可见的部分
  • @Blindman67 Clearing 将返回白色(默认背景颜色)画布。也许我没明白你的意思?有没有特别的清除功能?
  • 没有特殊的清除功能,从调用ctx.clearRect(0,0,width,height)后清除(透明)的画布重绘所有可见的内容
  • 如果重绘很麻烦,您应该考虑使用库来为您完成:fabricjs.com 听起来不错,这样您就可以显示、隐藏、删除和添加对象和形状更少的头痛......把你的时间集中在你真正想做的事情上

标签: javascript html canvas html5-canvas


【解决方案1】:

嗨@DEKKER,这是可行的解决方案

canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");

// Main template shape
let template = [{x: 10, y:10}, {x: 120, y:10}, {x: 110, y:110}, {x: 50, y:175}];

// Some shapes
let shapes = [];
// put template first into our bucket
shapes.push( template ) ;

// helper fn.
// random int with limits
let RI = function (min, max) {
  if (max == null) { max = min; min = 0; }
  if (min > max) { var tmp = min; min = max; max = tmp; }
  return Math.floor(min + (max - min + 1) * Math.random());
}
// returns array with 3 random points
function genPoint(){
  return [ {x: RI(1, 199), y: RI(1, 199)}, {x: RI(1, 199), y: RI(1, 199)}, {x: RI(1, 199), y: RI(1, 199)}, {x: RI(1, 199), y: RI(1, 199)} ];
}



// Draw the template
function drawPoly( points, isTemplate = false ) {
  ctx.save();
  ctx.beginPath();
  ctx.strokeStyle = (isTemplate) ? 'yellow' : 'white';
  ctx.fillStyle = (isTemplate) ? 'red' : 'rgba(127,127,127,0.5)';
  for( let point of points ) {
    ctx.lineTo(point.x, point.y);
  }
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
  ctx.restore();
}

// main logic
function drawPoint( ajaxData = false ){
  // if bucket contains more additional points (poly's)
  // draw last two poly's
  if( shapes.length > 2 ) {
    ctx.clearRect(0, 0, canvas.width, canvas.height ); // clear canvas
    drawPoly( shapes[ shapes.length -2 ], false ); // draw last two
    drawPoly( shapes[ shapes.length -1 ], false );
  } else {
    drawPoly( shapes[ shapes.length -1 ], ( shapes.length == 1 )  ) ;
  }
  // push random `genPoint` data in `shapes` array or ajaxData or whatsoever
  shapes.push( ( ajaxData ) ? ajaxData : genPoint() ); // add new point on stack
}

// draw and demo working system
setInterval( drawPoint, 2000 );
&lt;canvas id="myCanvas" width="200" height="200" style="border:1px solid #000000;"&gt;&lt;/canvas&gt;

我将评论我添加到您的代码中的内容:

  • 主要逻辑在function drawPoint( ajaxData = false ){..}函数中。它采用新的 poly 作为参数,因此您可以使用 ajax 数据自动将其插入您的程序逻辑中
  • 我已经稍微简化了你的代码,只有一个可以绘制的函数——它是function drawPoly( points, isTemplate = false ){..}
  • 两个简单的辅助函数 RI 作为随机整数和 genPoint 作为点生成器
  • 删除了 jQuery,因为不需要该 dep

【讨论】:

  • 感谢您的努力 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 1970-01-01
  • 2012-02-06
相关资源
最近更新 更多