【发布时间】: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