简体   繁体   English

如何使我的组件的多个实例不共享@ViewChild 地图/区域

[英]How to make multiple instances of my components not share @ViewChild maps/areas

I have a component that has a canvas on it, covered exactly by a mapped image.我有一个组件,上面有一个 canvas,被映射图像完全覆盖。 Based on mouse movement on top of the image, I highlight or unhighlight part of the canvas.基于图像顶部的鼠标移动,我突出显示或取消突出显示 canvas 的一部分。 I had intended to have multiple components like this, each independent from one another, one after another on my page, repeated vertically (so, the "upper" canvas is the one closer to the top of the page, whereas the "lower" canvas is the one after it)我本来打算有多个这样的组件,每个组件彼此独立,在我的页面上一个接一个,垂直重复(因此,“上部” canvas 是靠近页面顶部的一个,而“下部” canvas是它之后的那个)

The problem is that events on the bottom image map only affect the first canvas (ie if I mouseover the correct area on the second canvas, then the first canvas is the one that gets drawn on). The problem is that events on the bottom image map only affect the first canvas (ie if I mouseover the correct area on the second canvas, then the first canvas is the one that gets drawn on). I want it so that each component is independent - if I do something on the second component, it should affect the second canvas and not the first canvas.我希望它使每个组件都是独立的-如果我对第二个组件执行某些操作,它应该会影响第二个 canvas 而不是第一个 canvas。

In other words, Both components are sharing the first component's canvas , instead of each using their own canvases.换句话说,两个组件共享第一个组件的 canvas ,而不是各自使用自己的画布。 This is a problem.这是个问题。

Here's an MCVE:这是一个 MCVE:

dummy-component.html:虚拟组件.html:

<div style="position: relative; display: inline-block; width: 520px; height: 160px; background-color: green">
    <canvas #myCanvas width="520" height="160" 
      style="position: absolute; left: 0; top: 0; background-color: green; user-select: none"></canvas>
    <img #mapOverlay width="520" height="160" 
      style="position: absolute; left: 0; top: 0; background-color: transparent; user-select: none" 
      usemap="#overlay">
  
    <map #overlay id="overlay" name="overlay">
      <area shape="rect" coords="0, 0, 259, 159" 
        (mouseover)="hover()" (mouseout)="unhover()" (click)="drawSmiley()">
    </map>
</div>

dummy-component.ts:虚拟组件.ts:

@Component({
  selector: 'app-dummy',
  templateUrl: './dummy.component.html',
})
export class DummyComponent implements AfterViewInit {
  @ViewChild('myCanvas') private myCanvasElement!: ElementRef<HTMLCanvasElement>
  private myCanvas!: CanvasRenderingContext2D;
  private smileyFace: HTMLImageElement = new Image();

  constructor() { this.smileyFace.src = "assets/smiley.png"; }

  ngAfterViewInit(): void {
    this.myCanvas = <CanvasRenderingContext2D> this.myCanvasElement.nativeElement.getContext('2d');
  }

  hover() {
    this.myCanvas.clearRect(0, 0, 520, 160);
    this.myCanvas.fillStyle = "#ffffff33";  // draw transparent highlight
    this.myCanvas.fillRect(0, 0, 260, 160);
  }
  unhover() { this.myCanvas.clearRect(0, 0, 520, 160); }
  drawSmiley() { this.myCanvas.drawImage(this.smileyFace, 15, 15); }
}

app-component.html:应用组件.html:

<div>
    <app-dummy></app-dummy>
    <app-dummy></app-dummy>
</div>

The first canvas's requisite events also affect the first canvas, fwiw.第一个画布的必要事件也会影响第一个 canvas,fwiw。

I can't find any questions about this, or guides on how to accomplish this - Angular's documentation for @ViewChild says that "The change detector looks for the first element or the directive matching the selector in the view DOM", and that's probably related (I suspect the second element is borrowing the first element's @ViewChild and also using it for itself), but I don't know how to apply that information and make it stick to its own canvas.我找不到有关此的任何问题,或有关如何完成此操作的指南 - Angular 的 @ViewChild 文档说“更改检测器在视图 DOM 中查找与选择器匹配的第一个元素或指令”,这可能是相关的(我怀疑第二个元素是借用第一个元素的@ViewChild 并将其用于自己),但我不知道如何应用该信息并使其坚持自己的 canvas。

The issue appears to be with the maps, which all have the same name.问题似乎出在地图上,它们都具有相同的名称。 When the image binds to a map, it chooses the first one in the entire DOM with the same name, which is happens to be the first component's map - and since functions and whatnot are bound to that first component, it appears as though everything's happening on the first component.当图像绑定到 map 时,它会选择整个 DOM 中同名的第一个,这恰好是第一个组件的 map - 因为函数和诸如此类的东西都绑定到第一个组件,所以看起来好像一切都在发生在第一个组件上。

I feel like this is a bug, and that things like this should be scoped to only inside the component being activated, but it's probably intended behavior based on how the whole template is rendered.我觉得这是一个错误,这样的事情应该仅限于被激活的组件内部,但这可能是基于整个模板的呈现方式的预期行为。

Anyway, Solution : add a public id attribute to the DummyComponent , and inject it into both the name of the map and the useMap attribute of the image:无论如何,解决方案:向DummyComponent添加public id属性,并将其注入 map 的名称和图像的useMap属性中:

HTML: HTML:

<img #mapOverlay width="520" height="160" 
  style="position: absolute; left: 0; top: 0; background-color: transparent; user-select: none" 
  useMap="#{{name}}">

<map #overlay id="overlay" name="{{name}}">
  <area shape="rect" coords="0, 0, 259, 159" 
    (mouseover)="this.hover()" (mouseout)="this.unhover()" (click)="this.drawSmiley()">
</map>

Component:零件:

let idGenerator = 0;

@Component({
  selector: 'app-dummy',
  templateUrl: './dummy.component.html',
})
export class DummyComponent implements AfterViewInit {
  public name: string;

  ...

  constructor() {
    this.smileyFace.src = "assets/smiley.png";
    this.name = "DummyComponent" + ++idGenerator;
  }
  ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM