Html 保持背景图像的响应性以及拖放元素的完整性,并响应wrt背景图像

Html 保持背景图像的响应性以及拖放元素的完整性,并响应wrt背景图像,html,css,angular,Html,Css,Angular,我有一个工作堆栈闪电战: 我已经在包含背景图像的div上编写了角度和拖放元素的自定义拖放事件。我的目标是使div元素具有响应性,使新放置元素的位置与背景图像保持一致,从而也与背景图像成比例地响应 我尝试为父容器和子容器提供定位,因此父div会有响应,但子元素(删除的元素)不会与父背景图像保持完整,也不会保持比例 换句话说,当图像将被缩小或放大时,子元素(删除的元素)的大小也应按比例递增和递减,以保持其位置wrt图像的完整性 这里是拖放工作参考 指令 可降指令 import { Directiv

我有一个工作堆栈闪电战:

我已经在包含背景图像的div上编写了角度和拖放元素的自定义拖放事件。我的目标是使div元素具有响应性,使新放置元素的位置与背景图像保持一致,从而也与背景图像成比例地响应

我尝试为父容器和子容器提供定位,因此父div会有响应,但子元素(删除的元素)不会与父背景图像保持完整,也不会保持比例

换句话说,当图像将被缩小或放大时,子元素(删除的元素)的大小也应按比例递增和递减,以保持其位置wrt图像的完整性

这里是拖放工作参考

指令

可降指令

import { Directive, HostListener } from '@angular/core';
import { DroppableService } from '../_services/droppable.service';

@Directive({
  selector: '[appDroppable]'
})
export class DroppableDirective {

  constructor(private droppableService: DroppableService) { }

  @HostListener('dragStart', ['$event'])
  onDragStart(event: PointerEvent): void {
  this.droppableService.onDragStart(event);
 }

  @HostListener('dragMove', ['$event'])
  onDragMove(event: PointerEvent): void {
  this.droppableService.onDragMove(event);
  }

  @HostListener('dragEnd', ['$event'])
  onDragEnd(event: PointerEvent): void {
    this.droppableService.onDragEnd(event);
 }

}
import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, 
OnInit, Output, SkipSelf } from '@angular/core';
import { DroppableService } from '../_services/droppable.service';

@Directive({
 selector: '[appDropzone]',
 providers: [DroppableService]
})
export class DropzoneDirective implements OnInit {
@HostBinding('class.dropzone-activated') activated = false;
@HostBinding('class.dropzone-entered') entered = false;

@Output() drop = new EventEmitter<PointerEvent>();
@Output() remove = new EventEmitter<PointerEvent>();

private clientRect: ClientRect;

constructor(@SkipSelf() private allDroppableService: DroppableService,
          private innerDroppableService: DroppableService,
          private element: ElementRef) { }

ngOnInit(): void {
this.allDroppableService.dragStart$.subscribe(() => this.onDragStart());
this.allDroppableService.dragEnd$.subscribe(event => 
this.onDragEnd(event));

this.allDroppableService.dragMove$.subscribe(event => {
  if (this.isEventInside(event)) {
    this.onPointerEnter();
  } else {
    this.onPointerLeave();
  }
 });

  this.innerDroppableService.dragStart$.subscribe(() => 
  this.onInnerDragStart());
  this.innerDroppableService.dragEnd$.subscribe(event => 
  this.onInnerDragEnd(event));
  }

 private onPointerEnter(): void {
  if (!this.activated) {
  return;
 }

this.entered = true;
}

private onPointerLeave(): void {
  if (!this.activated) {
    return;
   }

   this.entered = false;
  }

   private onDragStart(): void {
   this.clientRect = this.element.nativeElement.getBoundingClientRect();

   this.activated = true;
  }

   private onDragEnd(event: PointerEvent): void {
   if (!this.activated) {
   return;
  }

    if (this.entered) {
    this.drop.emit(event);
  }

    this.activated = false;
    this.entered = false;
  }

  private onInnerDragStart() {
   this.activated = true;
   this.entered = true;
  }

  private onInnerDragEnd(event: PointerEvent) {
   if (!this.entered) {
   this.remove.emit(event);
 }

    this.activated = false;
    this.entered = false;
  }

  private isEventInside(event: PointerEvent) {
    return event.clientX >= this.clientRect.left &&
      event.clientX <= this.clientRect.right &&
      event.clientY >= this.clientRect.top &&
      event.clientY <= this.clientRect.bottom;
  }
}
import { AfterContentInit, ContentChildren, Directive, ElementRef, QueryList 
} from '@angular/core';
import { MovableDirective } from './movable.directive';
import { Subscription } from 'rxjs';

interface Boundaries {
 minX: number;
 maxX: number;
 minY: number;
 maxY: number;
}

@Directive({
  selector: '[appMovableArea]'
})
export class MovableAreaDirective implements AfterContentInit {
@ContentChildren(MovableDirective) movables: QueryList<MovableDirective>;

private boundaries: Boundaries;
private subscriptions: Subscription[] = [];

constructor(private element: ElementRef) {}

ngAfterContentInit(): void {
this.movables.changes.subscribe(() => {
  this.subscriptions.forEach(s => s.unsubscribe());

  this.movables.forEach(movable => {
    this.subscriptions.push(movable.dragStart.subscribe(() => 
  this.measureBoundaries(movable)));
    this.subscriptions.push(movable.dragMove.subscribe(() => 
  this.maintainBoundaries(movable)));
  });
  });

  this.movables.notifyOnChanges();
  }

  private measureBoundaries(movable: MovableDirective) {
  const viewRect: ClientRect = 
  this.element.nativeElement.getBoundingClientRect();
  const movableClientRect: ClientRect = 
  movable.element.nativeElement.getBoundingClientRect();

  this.boundaries = {
   minX: viewRect.left - movableClientRect.left + movable.position.x,
   maxX: viewRect.right - movableClientRect.right + movable.position.x,
   minY: viewRect.top - movableClientRect.top + movable.position.y,
   maxY: viewRect.bottom - movableClientRect.bottom + movable.position.y
  };
 }

 private maintainBoundaries(movable: MovableDirective) {
 movable.position.x = Math.max(this.boundaries.minX, movable.position.x);
 movable.position.x = Math.min(this.boundaries.maxX, movable.position.x);
 movable.position.y = Math.max(this.boundaries.minY, movable.position.y);
 movable.position.y = Math.min(this.boundaries.maxY, movable.position.y);
 }
 }
dropzone.directive

import { Directive, HostListener } from '@angular/core';
import { DroppableService } from '../_services/droppable.service';

@Directive({
  selector: '[appDroppable]'
})
export class DroppableDirective {

  constructor(private droppableService: DroppableService) { }

  @HostListener('dragStart', ['$event'])
  onDragStart(event: PointerEvent): void {
  this.droppableService.onDragStart(event);
 }

  @HostListener('dragMove', ['$event'])
  onDragMove(event: PointerEvent): void {
  this.droppableService.onDragMove(event);
  }

  @HostListener('dragEnd', ['$event'])
  onDragEnd(event: PointerEvent): void {
    this.droppableService.onDragEnd(event);
 }

}
import { Directive, ElementRef, EventEmitter, HostBinding, HostListener, 
OnInit, Output, SkipSelf } from '@angular/core';
import { DroppableService } from '../_services/droppable.service';

@Directive({
 selector: '[appDropzone]',
 providers: [DroppableService]
})
export class DropzoneDirective implements OnInit {
@HostBinding('class.dropzone-activated') activated = false;
@HostBinding('class.dropzone-entered') entered = false;

@Output() drop = new EventEmitter<PointerEvent>();
@Output() remove = new EventEmitter<PointerEvent>();

private clientRect: ClientRect;

constructor(@SkipSelf() private allDroppableService: DroppableService,
          private innerDroppableService: DroppableService,
          private element: ElementRef) { }

ngOnInit(): void {
this.allDroppableService.dragStart$.subscribe(() => this.onDragStart());
this.allDroppableService.dragEnd$.subscribe(event => 
this.onDragEnd(event));

this.allDroppableService.dragMove$.subscribe(event => {
  if (this.isEventInside(event)) {
    this.onPointerEnter();
  } else {
    this.onPointerLeave();
  }
 });

  this.innerDroppableService.dragStart$.subscribe(() => 
  this.onInnerDragStart());
  this.innerDroppableService.dragEnd$.subscribe(event => 
  this.onInnerDragEnd(event));
  }

 private onPointerEnter(): void {
  if (!this.activated) {
  return;
 }

this.entered = true;
}

private onPointerLeave(): void {
  if (!this.activated) {
    return;
   }

   this.entered = false;
  }

   private onDragStart(): void {
   this.clientRect = this.element.nativeElement.getBoundingClientRect();

   this.activated = true;
  }

   private onDragEnd(event: PointerEvent): void {
   if (!this.activated) {
   return;
  }

    if (this.entered) {
    this.drop.emit(event);
  }

    this.activated = false;
    this.entered = false;
  }

  private onInnerDragStart() {
   this.activated = true;
   this.entered = true;
  }

  private onInnerDragEnd(event: PointerEvent) {
   if (!this.entered) {
   this.remove.emit(event);
 }

    this.activated = false;
    this.entered = false;
  }

  private isEventInside(event: PointerEvent) {
    return event.clientX >= this.clientRect.left &&
      event.clientX <= this.clientRect.right &&
      event.clientY >= this.clientRect.top &&
      event.clientY <= this.clientRect.bottom;
  }
}
import { AfterContentInit, ContentChildren, Directive, ElementRef, QueryList 
} from '@angular/core';
import { MovableDirective } from './movable.directive';
import { Subscription } from 'rxjs';

interface Boundaries {
 minX: number;
 maxX: number;
 minY: number;
 maxY: number;
}

@Directive({
  selector: '[appMovableArea]'
})
export class MovableAreaDirective implements AfterContentInit {
@ContentChildren(MovableDirective) movables: QueryList<MovableDirective>;

private boundaries: Boundaries;
private subscriptions: Subscription[] = [];

constructor(private element: ElementRef) {}

ngAfterContentInit(): void {
this.movables.changes.subscribe(() => {
  this.subscriptions.forEach(s => s.unsubscribe());

  this.movables.forEach(movable => {
    this.subscriptions.push(movable.dragStart.subscribe(() => 
  this.measureBoundaries(movable)));
    this.subscriptions.push(movable.dragMove.subscribe(() => 
  this.maintainBoundaries(movable)));
  });
  });

  this.movables.notifyOnChanges();
  }

  private measureBoundaries(movable: MovableDirective) {
  const viewRect: ClientRect = 
  this.element.nativeElement.getBoundingClientRect();
  const movableClientRect: ClientRect = 
  movable.element.nativeElement.getBoundingClientRect();

  this.boundaries = {
   minX: viewRect.left - movableClientRect.left + movable.position.x,
   maxX: viewRect.right - movableClientRect.right + movable.position.x,
   minY: viewRect.top - movableClientRect.top + movable.position.y,
   maxY: viewRect.bottom - movableClientRect.bottom + movable.position.y
  };
 }

 private maintainBoundaries(movable: MovableDirective) {
 movable.position.x = Math.max(this.boundaries.minX, movable.position.x);
 movable.position.x = Math.min(this.boundaries.maxX, movable.position.x);
 movable.position.y = Math.max(this.boundaries.minY, movable.position.y);
 movable.position.y = Math.min(this.boundaries.maxY, movable.position.y);
 }
 }
如果这种方法是错误的,那么请建议正确的方法使其工作

用于将blob转换为图像并将其作为blob上载的代码 我得到的数据的引用

 "<div xmlns="http://www.w3.org/1999/xhtml" _ngcontent-c18="" appdropzone=""
 appmovablearea="" class="dropzone image-area"
 id="toget" ng-reflect-ng-style="[object Object]" style="width: 100%; 
 background-image: 
 url(&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALoAA 
 AC6CAMAAAAu0KfDAAAAwFBMVEX//XkFi/hkn/9ySZhLa0CgAAAAABJRU5ErkJggg==&quot;); 
 background-repeat: no-repeat; background-position: center center; back
 ground-size: 100% 100%;"><!--bindings={ 
 "ng-reflect-ng-for-of": ""
 }--><div _ngcontent-c18="" appdroppable="" appmovable=""
 class="box draggable movable ng-star-inserted" touch-action="none" 
  style="transform: translateX(136.8px) translateY(112.8px);"> vav18 </div> 
 </div>"

   downloadImageFromBlob('abc', (err, data) => {
   if (data) {
    this.url = data.split('url(&quot;')[1].split('&quot;); background- 
    repeat: no-repeat;')[0];
    data = data.replace(/&quot;/g,'')
    this.myHtmlString = data;
    }
    }

  <div id="existing" *ngIf="!abc" class="image-area" 
  [innerHTML]="myHtmlString | safeHtml"></div>


  uploadImage(): void {
  var svgString = new 
  XMLSerializer().serializeToString(document.getElementById('toget'));

  var svgBlob = new Blob([svgString], { type: "image/svg+xml;charset=utf-8" 
 });

  let file = this.fileUploadService.convertToFile(svgBlob, "floorPlan.svg");
  }
“vav18
"
下载ImageFromBlob('abc',(错误,数据)=>{
如果(数据){
this.url=data.split('url('')[1]。split('');背景-
重复:不重复;')[0];
数据=数据。替换(/“/g”“)
this.myHtmlString=数据;
}
}
uploadImage():void{
var svgString=new
XMLSerializer().serializeToString(document.getElementById('toget');
var svgBlob=newblob([svgString],{type:“image/svg+xml;charset=utf-8”
});
让file=this.fileUploadService.convertToFile(svgBlob,“floorPlan.svg”);
}

我推荐一种稍微不同的解决方案:您可以将对象添加到SVG中,而不是将其作为SVG上的精灵进行管理

粗略的程序大纲:

  • 确保您正在使用SVG基础文件的副本,因为您将要修改它。我认为您已经在这样做了
  • 使用NG服务添加组(
    g
    element)。确保它位于现有SVG元素之上(在之后绘制)。这是您希望在指令中保留句柄的内容
  • 拖放时,将SVG
    rect
    元素添加到新组中。您已经获得了定位逻辑,但需要对其进行转换,以了解SVG的缩放和viewporting
  • 使用SVG的事件将事件冒泡到NG中。这应该能够很容易地包装到NG指令中,因为SVG实际上只是存在于文档中的HTML
  • 此时,您应该能够将移动和缩放事件重定向到SVG缩放和viewporting中。也就是说,当您重新缩放时,您只需重新缩放SVG。您放置的框将与图形的其余部分一起重新缩放
  • 您可能可以做一些事情来简化这一过程:

    • 尝试一个支持NG的SVG操作库。我还没有检查,但肯定有一个。这可以节省您编写指令包装器的时间
    • 研究一个映射解决方案。也许可以尝试使用来自的NG包装器提示。除了实际绘制地图之外,管理带有其他精灵的基本矢量图像正是映射解决方案所做的。无需重新发明轮子。只需重定向从某个世界地图提供商读取的地图即可获取SVG
    • 我做了一个评论来尝试,很多人都用它来注释地图。从他们的图库中已经完成了你的部分工作。我无法想象D3没有一个可用的NG包装器
    希望这里面有什么东西能帮助你

    更新(回答评论中的一些问题)

    在D3或其他库上:添加另一个库会影响性能,特别是会降低性能。当添加更多运行的代码时,情况总是如此

    我认为更好的问题是,“如果我添加了另一个库,我的性能是否会降低到可接受的水平以下?”这取决于库以及有关它的一些细节(例如从何处加载以及如何使用它)以及您可接受的性能水平。我不相信从一开始就编写最终性能的代码:编写可维护的代码并优化特定的内容以满足特定的需求。也就是说,链接到一个库中肯定会很明显地利用任何合理的标准

    如果你的项目已经加载了D3,特别是如果你(个人)如果您对它不太熟悉,请注意,编写代码可能需要一个非常好的库,并使其完成比必须完成的更多的工作。这会降低您的性能。这种情况也很可能是库的首次使用者。如果您的项目中有人了解D3,我建议在lea寻求他们的帮助完成后,在代码复查中使用st

    在纯HTML/CSS/JS中执行此操作:当然这是可能的。您已经开始实现其中一个

    有一种解决方案需要更少的JS,包括CSS剪辑和转换。它们的行为与SVG视口和转换非常相似,但它们附带了更密集的代码列表。也就是说,您必须在一行CSS中完成可以分布在许多SVG元素和属性上的内容。这将使调试和转换更加复杂当然。从你原来的帖子来看,你最大的问题似乎是调试。考虑到你问题的答案的数量和质量(截至本次编辑),似乎没有人真的想帮助你调试。如果我处在你的位置,我会专注于可维护性和调试能力,以便完成你的项目

    话虽如此,总体思路是:

    • 把一切都放在一个H中