【问题标题】:How to Create a Draggable Line in HTML5 Canvas using ionic?如何使用 ionic 在 HTML5 Canvas 中创建可拖动线?
【发布时间】:2020-08-17 07:04:51
【问题描述】:

如何在画布中拖线?

我可以使用 answer 在画布中添加线条。

在此,answer 线拖动可以工作,但是当我在 ionic 应用程序中使用它时它不起作用。

这是我的代码:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  @ViewChild('canvasDraw', { static: false }) canvas: ElementRef;

  canvasElement: any;
  lines: any[];
  isDown: boolean = false;
  startX: number;
  startY: number;
  nearest: any;
  offsetX: any;
  offsetY: any;

  constructor() {
    setTimeout(() => {
      let ctx = this.canvasElement.getContext('2d');
    }, 1000)
  }

  ngOnInit() {
  }

  ngAfterViewInit(): void {
    this.canvasElement = this.canvas.nativeElement;
    // canvas lets
    let canvas = document.getElementById("canvasDraw");

    // line lets
    // let this.nearest;
    this.lines = [];
    this.lines.push({ x0: 75, y0: 25, x1: 125, y1: 25 });
    this.lines.push({ x0: 75, y0: 100, x1: 125, y1: 100 });
    this.lines.push({ x0: 50, y0: 35, x1: 50, y1: 85 });
    this.lines.push({ x0: 150, y0: 35, x1: 150, y1: 85 });

    this.draw();
  }

  reOffset() {
    let canvas = document.getElementById("canvasDraw");
    let BB = canvas.getBoundingClientRect();
    this.offsetX = BB.left;
    this.offsetY = BB.top;
  }

  ionViewWillLeave() {
  }

  // select the this.nearest line to the mouse
  closestLine(mx, my) {
    let dist = 100000000;
    let index, pt;
    for (let i = 0; i < this.lines.length; i++) {
      //
      let xy = this.closestXY(this.lines[i], mx, my);
      //
      let dx = mx - xy.x;
      let dy = my - xy.y;
      let thisDist = dx * dx + dy * dy;
      if (thisDist < dist) {
        dist = thisDist;
        pt = xy;
        index = i;
      }
    }
    let line = this.lines[index];
    return ({ pt: pt, line: line, originalLine: { x0: line.x0, y0: line.y0, x1: line.x1, y1: line.y1 } });
  }

  // linear interpolation -- needed in setClosestLine()
  lerp(a, b, x) {
    return (a + x * (b - a));
  }

  // find closest XY on line to mouse XY
  closestXY(line, mx, my) {
    let x0 = line.x0;
    let y0 = line.y0;
    let x1 = line.x1;
    let y1 = line.y1;
    let dx = x1 - x0;
    let dy = y1 - y0;
    let t = ((mx - x0) * dx + (my - y0) * dy) / (dx * dx + dy * dy);
    t = Math.max(0, Math.min(1, t));
    let x = this.lerp(x0, x1, t);
    let y = this.lerp(y0, y1, t);
    return ({ x: x, y: y });
  }

  // draw the scene
  draw() {
    let ctx = this.canvasElement.getContext('2d');
    let cw = this.canvasElement.width;
    let ch = this.canvasElement.height;
    ctx.clearRect(0, 0, cw, ch);
    // draw all lines at their current positions
    for (let i = 0; i < this.lines.length; i++) {
      this.drawLine(this.lines[i], 'black');
    }
    // draw markers if a line is being dragged
    if (this.nearest) {
      // point on line this.nearest to mouse
      ctx.beginPath();
      ctx.arc(this.nearest.pt.x, this.nearest.pt.y, 5, 0, Math.PI * 2);
      ctx.strokeStyle = 'red';
      ctx.stroke();
      // marker for original line before dragging
      this.drawLine(this.nearest.originalLine, 'red');
      // hightlight the line as its dragged
      this.drawLine(this.nearest.line, 'red');
    }
  }

  drawLine(line, color) {
    let ctx = this.canvasElement.getContext('2d');
    ctx.beginPath();
    ctx.moveTo(line.x0, line.y0);
    ctx.lineTo(line.x1, line.y1);
    ctx.strokeStyle = color;
    ctx.stroke();
  }

  handleMouseDown(e: { preventDefault: () => void; stopPropagation: () => void; clientX: number; clientY: number; }) {
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    // mouse position
    this.startX = e.clientX - this.offsetX;
    this.startY = e.clientY - this.offsetY;
    // find this.nearest line to mouse
    this.nearest = this.closestLine(this.startX, this.startY);
    this.draw();
    // set dragging flag
    this.isDown = true;
  }

  handleMouseUpOut(e) {
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    // clear dragging flag
    this.isDown = false;
    this.nearest = null;
    this.draw();
  }

  handleMouseMove(e) {
    if (!this.isDown) { return; }
    // tell the browser we're handling this event
    e.preventDefault();
    e.stopPropagation();
    // mouse position
    const mouseX = e.clientX - this.offsetX;
    const mouseY = e.clientY - this.offsetY;
    // calc how far mouse has moved since last mousemove event
    let dx = mouseX - this.startX;
    let dy = mouseY - this.startY;
    this.startX = mouseX;
    this.startY = mouseY;
    // change this.nearest line vertices by distance moved
    let line = this.nearest.line;
    line.x0 += dx;
    line.y0 += dy;
    line.x1 += dx;
    line.y1 += dy;
    // redraw
    this.draw();
  }

}

这是我的 HTML 文件

<canvas #canvasDraw width=300 height=300></canvas>

我试着喜欢这个,但看起来它不起作用

<canvas #canvasDraw width=300 height=300 (mousedown)="handleMouseDown($event)" (mousemove)="handleMouseMove($event)"
    (mouseup)="handleMouseUpOut($event)" (mouseout)="handleMouseUpOut($event)"></canvas>

【问题讨论】:

  • 我可以在 bat #canvasDraw 右侧看到的东西很少被 getDocumentById 检索,而是使用您已经拥有的 viewchild,在添加到 DOM 时也考虑使用 renderer2

标签: angular ionic-framework html5-canvas


【解决方案1】:

您忘记确保使用以下方法设置偏移量:reOffset()。 在您上面共享的代码中,您根本没有使用该方法,代码依赖于正确设置的偏移量。

应该在视图完全初始化后调用此方法,不幸的是,即使 ngAfterViewInit() 也无法帮助捕获实际偏移量,因为 Ionic 的标头不会完全初始化。

所以我使用 requestAnimationFrame 技巧来确保正确设置偏移量(您也可以使用 Promise.resolve 或使用 setTimeout):

ngAfterViewInit() {
    this.canvasElement = this.canvas.nativeElement;
    this.lines = [];
    this.lines.push({ x0: 75, y0: 25, x1: 125, y1: 25 });
    this.lines.push({ x0: 75, y0: 100, x1: 125, y1: 100 });
    this.lines.push({ x0: 50, y0: 35, x1: 50, y1: 85 });
    this.lines.push({ x0: 150, y0: 35, x1: 150, y1: 85 });
    this.draw();
    //this.reOffset();
    requestAnimationFrame(()=>{
      this.reOffset()
    })
  }

这里的工作版本:https://stackblitz.com/edit/ionic-angular-v5-twdvkm?file=src/app/app.component.ts

【讨论】:

  • 感谢您的回答,它可以在网络上运行,但不能在设备上运行。触摸事件怎么办?
  • 这是一个单独的问题 - 你可以创建一个单独的问题吗?
猜你喜欢
  • 2011-07-30
  • 2013-02-07
  • 2011-08-13
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-18
相关资源
最近更新 更多