【问题标题】:How to create a low memory consumption snap to grid canvas system HTML + JS?如何创建一个低内存消耗的网格画布系统 HTML + JS?
【发布时间】:2017-12-24 03:30:47
【问题描述】:

我正在尝试做的(我已经能够完成,但性能不佳)是在画布上应用一种网格,以便能够从用户那里获取关于原点的输入点位置。一旦接收到输入,“绘制”坐标将通过键盘提供。

这些天我设法做的是计算画布的宽度和高度,然后将其除以标准 20x20 正方形的面积(以 px 为单位)。通过这种方式,我可以循环结果并创建 n 个正方形,我将在网格元素内的 display flex 中呈现它们。然后这个网格元素被应用在画布上。

一切正常,但是周围有很多 div,如果用户选择将 div 缩小到 10x10,那么,这将对性能产生很大影响......所以我试图找到找到一种更轻松的方法来做到这一点......

我考虑过在两个 div 中使用 HR 元素,这些 div 将应用于画布。一个 div 在列中显示元素,另一个在行中显示元素。这样我应该得到网格,但是快照呢?我怎样才能检测到两个 HR 元素上的交叉点并使用那个确切的点作为位置?

我不能直接在画布上绘制网格的原因是因为它应该尽可能保持“纯”。仅包含用户的最终抽奖。

这是“未优化”的代码:

我使用 Angular 5 作为框架。

<div class="draw-zone" #drawZone>
    <div class="grid" #grid [ngClass]="{'activated': activateDrawZones}">
        <div *ngFor="let block of gridBlocks" class="grid-block" [ngClass]="{'show': showGrid, 'ten-x-ten': blockSize === 10, 'twe-x-twe': blockSize === 20, 'thr-x-thr': blockSize === 30, 'fou-x-fou': blockSize === 40}"
            #gridBlock (click)="draw($event, gridBlock)"></div>
    </div>
    <canvas #canvas [height]="canvasSize.y" [width]="canvasSize.x"></canvas>
</div>

scss:

.draw-zone{
    flex-grow: 2;   
    height: 100%;
    position: relative;

    canvas{
        z-index: 10;
    }

    .grid{
        top: 0;
        left: 0;
        z-index: 11;
        width: 100%;
        display: flex;
        flex-wrap: wrap;
        overflow: hidden;
        position: absolute;
        margin-left: -.1rem;
        border-radius: .5rem;
        align-content: stretch;
        border: 1px solid transparent;

        &.activated{
            border-color: #3f51b5;
        }

        .grid-block{
            opacity: 0;
            border-right: 1px solid #3f51b5;
            border-bottom: 1px solid #3f51b5;

            &.show{
                opacity: .1;
            }

            &:hover{
                opacity: 1;
                border-radius: 50%;
                background-color: #3f51b5;
                transform: scale(1.2);
            }

            &.ten-x-ten{
                width: 10px;
                height: 10px;
            }
            &.twe-x-twe{
                width: 20px;
                height: 20px;
            }
            &.thr-x-thr{
                width: 30px;
                height: 30px;
            }
            &.fou-x-fou{
                width: 40px;
                height: 40px;
            }
        }
    }

以及要cal的组件方法:

private calculateGrid() {
    this.activateDrawZones = false;

    this.canvasSize.x = this._drawZone.nativeElement.clientWidth;
    this.canvasSize.y = this._drawZone.nativeElement.clientHeight;
    const blocksCount = (this.canvasSize.x * this.canvasSize.y) / (this.blockSize * this.blockSize);

    this.gridBlocks = [];
    for (let i = 0; i < blocksCount; i++) {
        this.gridBlocks.push({ size: this.blockSize });
    }

    this.activateDrawZones = true;
}

以及实际绘制的方法:

public draw(e: MouseEvent, block: HTMLDivElement, returnOnFail?: boolean) {
    const x = block.offsetLeft + (this.blockSize / 2);
    const y = block.offsetTop + (this.blockSize / 2);

    if (this.firstClick) {
        this.ctx.beginPath();
        this.ctx.moveTo(x, y);
        this.setCrosshair(x, y);
        this.firstClick = false;
        this.addPathToDrawSequence(x, y);
        return;
    }

    if (this.isNotOnTheSameAxisAsTheLastInsert(x, y)) {
        if (returnOnFail) { return; }

        this.toggleDrawDirection();
        this.draw(e, block, true);
        return;
    }

    this.ctx.lineTo(x, y);

    this.ctx.stroke();
    this.setCrosshair(x, y);
    this.addPathToDrawSequence(x, y);
}

如您所见,我在 canvas 元素上应用了“.grid”元素。网格元素包含以弹性模式显示的所有块。如您所见,网格容器具有 display:flex 和 flex-wrap: wrap 属性。这样,当用户点击一个块时,我可以通过获取其相对于父级的位置来猜测 x、y 坐标。它与画布具有相同的尺寸。一旦我有了 x,y 坐标,我就可以在画布上画画了。

【问题讨论】:

  • 一些代码有助于理解您正在尝试做什么。另外,“在网格元素内显示 flex 渲染”是什么意思?究竟渲染什么?你只是想画网格线吗?你有什么理由不能直接把它画到画布上?
  • 对不起,我试图更好地解释它,也提供了一些代码:) 正如你所说,我正在尝试绘制一个网格,我可以使用它来“捕捉”用户光标点击。然后使用单击的块数据来计算相对于父级的 x,y 坐标。然后在画布上绘制。正如我在编辑中所说,画布应尽可能保持干净。
  • 那么除了检查它们是否被点击之外,你会对 div 做些什么吗?你也可以在画布上添加一个点击监听器,并使用一些基本的数学来计算你需要的位置,而不会增加重排 div 的开销。

标签: javascript html css canvas


【解决方案1】:

是的,创建多个 DOM 元素并尝试使用 javascript 动态定位和调整它们的大小不会特别高效。我认为hr 元素不会为您解决这个问题。

首先,您是否考虑过将网格直接绘制到画布上?

另一种选择是在画布后面放置一个带有网格的背景图像。这将自动调整大小,就像您网页的任何其他方面一样。


现在是“捕捉”部分。一旦获得所需的网格信息,您似乎已经知道如何在画布上绘制所需的内容。您需要的是一种方法来获取用户单击了哪个网格。我猜这就是你覆盖所有这些 div 的原因......

相反,画布本机跟踪鼠标点击。使用here 中列出的一些技术应该能够为您提供您正在寻找的网格交互信息。


编辑:一种生成和查找网格的方法:

var height = 100;
var width = 200;
var horizontal_grids = 8;
var vertical_grids = 4;

function bounding_grid_1d(length, grids, x) {
  var divisions = [];
  var grid_width = length / grids;
  
  for ( i = 0; i <= grids; i++ ) {
    if (x || x == 0) {
      if (i*grid_width > x) {
        divisions.push((i-1)*grid_width);
        divisions.push(i*grid_width);
        break;
      }
      else if (i*grid_width == x) {
        divisions.push(i*grid_width);
        break;
      }
    }
    else {
      divisions.push(i*grid_width);
    }
  }
  
  return divisions;
}

console.log("Get all the x and y grid line locations");
console.log(bounding_grid_1d(width, horizontal_grids));
console.log(bounding_grid_1d(height, vertical_grids));


console.log("Get the x and y grid line locations that surround the coordinates (60,30)");
console.log(bounding_grid_1d(width, horizontal_grids, 60));
console.log(bounding_grid_1d(height, vertical_grids, 30));

【讨论】:

  • 感谢您的建议,我已经在阅读 :) 我有一个问题,让我们假设在画布上绘制这个网格,并将绘制坐标保持在一个单独的对象中。然后我可以使用该网格来检测每个单独的“网格方块”上的点击?
  • 我倾向于动态生成我的坐标。然后我可以动态确定我想要多少个网格。以这样的方式编写函数应该很容易,它既可以返回坐标序列,也可以返回给定坐标的最接近的网格坐标。让我在我的回答中举个例子......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 1970-01-01
  • 2021-11-13
  • 2019-11-10
  • 2018-12-16
相关资源
最近更新 更多