javascript - 当它在 Canvas 中连接时,如何在 4 行之间填充颜色?

标签 javascript angularjs ionic-framework

当它以 ionic 连接时,我试图在线条之间填充颜色。当四线相互接触时,我想在线之间填充颜色。为此,我使用触摸事件创建了一个 Canvas 演示。请帮助解决我的问题。
我们在 Canvas 框中有 4 条线,我们将拖动它们并连接每条线并给出一个形状线框。这意味着我们的线已连接,现在在线之间填充颜色,因此框被颜色填充。

html file:

 <canvas #canvasDraw width="300" height="300" (touchstart)="handleTouchStart($event)"
        (touchmove)="handleTouchmove($event)" 
        (touchend)="handleTouchEnd($event)">
        You need a browser that supports HTML5!
 </canvas>

ts file:

import { Component, ElementRef, ViewChild } 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() {
    }

    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()
        })
    }

    reOffset() {
        let BB = this.canvasElement.getBoundingClientRect();
        this.offsetX = BB.left;
        this.offsetY = BB.top;
    }

    // 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();
    }

    handleTouchStart(e) {
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // mouse position
        this.startX = tch.clientX - this.offsetX;
        this.startY = tch.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;
    }

    handleTouchEnd(e) {
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // clear dragging flag
        this.isDown = false;
        this.nearest = null;
        this.draw();
    }

    handleTouchmove(e) {
        if (!this.isDown) { return; }
        // tell the browser we're handling this event
        let tch = e.touches[0];
        // tch.preventDefault();
        // tch.stopPropagation();
        // mouse position
        const mouseX = tch.clientX - this.offsetX;
        const mouseY = tch.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();

        let ctx = this.canvasElement.getContext('2d');
        ctx.beginPath();
        ctx.rect(line.x0, line.y0, line.x1, line.y1);
        ctx.fillStyle = "red";
        ctx.fill();
    }

}
enter image description here
四线接触或连接时如何填充颜色?

最佳答案

如何获取像素的颜色
基本上,当您进行触摸事件时,像素会改变颜色。您可以找出事件影响了哪些像素。将像素作为输入,您可以找出像素的颜色:https://jsfiddle.net/ourcodeworld/8swevoxo/

var canvas = document.getElementById("canvas");

function getPosition(obj) {
    var curleft = 0, curtop = 0;
    if (obj.offsetParent) {
        do {
            curleft += obj.offsetLeft;
            curtop += obj.offsetTop;
        } while (obj = obj.offsetParent);
        return { x: curleft, y: curtop };
    }
    return undefined;
}

function rgbToHex(r, g, b) {
    if (r > 255 || g > 255 || b > 255)
        throw "Invalid color component";
    return ((r << 16) | (g << 8) | b).toString(16);
}

function drawImageFromWebUrl(sourceurl){
      var img = new Image();

      img.addEventListener("load", function () {
          // The image can be drawn from any source
          canvas.getContext("2d").drawImage(img, 0, 0, img.width,    img.height, 0, 0, canvas.width, canvas.height);
          
      });

      img.setAttribute("src", sourceurl);
}
// Draw a base64 image because this is a fiddle, and if we try with an image from URL we'll get tainted canvas error
// Read more about here : http://ourcodeworld.com/articles/read/182/the-canvas-has-been-tainted-by-cross-origin-data-and-tainted-canvases-may-not-be-exported
drawImageFromWebUrl("data:image/gif;base64,R0lGODlhPQBEAPeoAJosM//AwO/AwHVYZ/z595kzAP/s7P+goOXMv8+fhw/v739/f+8PD98fH/8mJl+fn/9ZWb8/PzWlwv///6wWGbImAPgTEMImIN9gUFCEm/gDALULDN8PAD6atYdCTX9gUNKlj8wZAKUsAOzZz+UMAOsJAP/Z2ccMDA8PD/95eX5NWvsJCOVNQPtfX/8zM8+QePLl38MGBr8JCP+zs9myn/8GBqwpAP/GxgwJCPny78lzYLgjAJ8vAP9fX/+MjMUcAN8zM/9wcM8ZGcATEL+QePdZWf/29uc/P9cmJu9MTDImIN+/r7+/vz8/P8VNQGNugV8AAF9fX8swMNgTAFlDOICAgPNSUnNWSMQ5MBAQEJE3QPIGAM9AQMqGcG9vb6MhJsEdGM8vLx8fH98AANIWAMuQeL8fABkTEPPQ0OM5OSYdGFl5jo+Pj/+pqcsTE78wMFNGQLYmID4dGPvd3UBAQJmTkP+8vH9QUK+vr8ZWSHpzcJMmILdwcLOGcHRQUHxwcK9PT9DQ0O/v70w5MLypoG8wKOuwsP/g4P/Q0IcwKEswKMl8aJ9fX2xjdOtGRs/Pz+Dg4GImIP8gIH0sKEAwKKmTiKZ8aB/f39Wsl+LFt8dgUE9PT5x5aHBwcP+AgP+WltdgYMyZfyywz78AAAAAAAD///8AAP9mZv///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAKgALAAAAAA9AEQAAAj/AFEJHEiwoMGDCBMqXMiwocAbBww4nEhxoYkUpzJGrMixogkfGUNqlNixJEIDB0SqHGmyJSojM1bKZOmyop0gM3Oe2liTISKMOoPy7GnwY9CjIYcSRYm0aVKSLmE6nfq05QycVLPuhDrxBlCtYJUqNAq2bNWEBj6ZXRuyxZyDRtqwnXvkhACDV+euTeJm1Ki7A73qNWtFiF+/gA95Gly2CJLDhwEHMOUAAuOpLYDEgBxZ4GRTlC1fDnpkM+fOqD6DDj1aZpITp0dtGCDhr+fVuCu3zlg49ijaokTZTo27uG7Gjn2P+hI8+PDPERoUB318bWbfAJ5sUNFcuGRTYUqV/3ogfXp1rWlMc6awJjiAAd2fm4ogXjz56aypOoIde4OE5u/F9x199dlXnnGiHZWEYbGpsAEA3QXYnHwEFliKAgswgJ8LPeiUXGwedCAKABACCN+EA1pYIIYaFlcDhytd51sGAJbo3onOpajiihlO92KHGaUXGwWjUBChjSPiWJuOO/LYIm4v1tXfE6J4gCSJEZ7YgRYUNrkji9P55sF/ogxw5ZkSqIDaZBV6aSGYq/lGZplndkckZ98xoICbTcIJGQAZcNmdmUc210hs35nCyJ58fgmIKX5RQGOZowxaZwYA+JaoKQwswGijBV4C6SiTUmpphMspJx9unX4KaimjDv9aaXOEBteBqmuuxgEHoLX6Kqx+yXqqBANsgCtit4FWQAEkrNbpq7HSOmtwag5w57GrmlJBASEU18ADjUYb3ADTinIttsgSB1oJFfA63bduimuqKB1keqwUhoCSK374wbujvOSu4QG6UvxBRydcpKsav++Ca6G8A6Pr1x2kVMyHwsVxUALDq/krnrhPSOzXG1lUTIoffqGR7Goi2MAxbv6O2kEG56I7CSlRsEFKFVyovDJoIRTg7sugNRDGqCJzJgcKE0ywc0ELm6KBCCJo8DIPFeCWNGcyqNFE06ToAfV0HBRgxsvLThHn1oddQMrXj5DyAQgjEHSAJMWZwS3HPxT/QMbabI/iBCliMLEJKX2EEkomBAUCxRi42VDADxyTYDVogV+wSChqmKxEKCDAYFDFj4OmwbY7bDGdBhtrnTQYOigeChUmc1K3QTnAUfEgGFgAWt88hKA6aCRIXhxnQ1yg3BCayK44EWdkUQcBByEQChFXfCB776aQsG0BIlQgQgE8qO26X1h8cEUep8ngRBnOy74E9QgRgEAC8SvOfQkh7FDBDmS43PmGoIiKUUEGkMEC/PJHgxw0xH74yx/3XnaYRJgMB8obxQW6kL9QYEJ0FIFgByfIL7/IQAlvQwEpnAC7DtLNJCKUoO/w45c44GwCXiAFB/OXAATQryUxdN4LfFiwgjCNYg+kYMIEFkCKDs6PKAIJouyGWMS1FSKJOMRB/BoIxYJIUXFUxNwoIkEKPAgCBZSQHQ1A2EWDfDEUVLyADj5AChSIQW6gu10bE/JG2VnCZGfo4R4d0sdQoBAHhPjhIB94v/wRoRKQWGRHgrhGSQJxCS+0pCZbEhAAOw==");

canvas.addEventListener("mousemove",function(e){
      var pos = getPosition(this);
    var x = e.pageX - pos.x;
    var y = e.pageY - pos.y;
    var coord = "x=" + x + ", y=" + y;
    var c = this.getContext('2d');
    var p = c.getImageData(x, y, 1, 1).data; 
        
    // If transparency on the image
    if((p[0] == 0) && (p[1] == 0) && (p[2] == 0) && (p[3] == 0)){
                coord += " (Transparent color detected, cannot be converted to HEX)";
    }
    
    var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6);
    
    document.getElementById("status").innerHTML = coord;
    document.getElementById("color").style.backgroundColor = hex;
},false);
<canvas id="canvas" width="150" height="150"></canvas>
<div id="status"></div><br>
<div id="color" style="width:30px;height:30px;"></div>
<p>
Move the mouse over the BUS !
</p>

这段代码不是我写的。
理解问题
既然我们知道如何逐个像素地获取颜色,我们需要将我们的问题转换为等价的东西,但它更容易计算。我将问题定义如下:

Traverse pixels starting from a given point in a continuous manner in order to find out whether a cycle can be formed, which contains pixels of a certain (default) color inside the boundary, which should change their color to some fill color.


如何解决
  • 从给定点开始像素遍历循环,并始终在每一步中将坐标值更改为与当前点相邻的点或循环中较早访问的某个点
  • 始终存储当前访问点的坐标,因此如果您要访问同一点,则第二次忽略它
  • 使用一个数据结构,像一个堆栈来存储所有要访问但尚未访问的邻居,将您访问的每个点的所有邻居都放入堆栈(除非该点已被访问)
  • 如果您从一个尚未访问过的点返回到起点,则记录这是一个循环
  • 始终跟踪边界点
  • 当您发现它是一个循环并且您知道它的位置时,遍历该区域中的每个点,如果它具有默认颜色,请检查是否有一条连续线可以通过仅访问邻居来“离开”循环默认颜色,与您检查它是否是一个循环非常相似。如果不是,则将像素(及其所有相似颜色的邻居)绘制为您需要的颜色
  • 关于javascript - 当它在 Canvas 中连接时,如何在 4 行之间填充颜色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64554645/

    相关文章:

    sqlite - Ionic PouchDb Sqlite 插件问题

    javascript - Chrome 扩展 - 用于在任何页面上运行 js 的简单内容脚本

    javascript - ng-selected 不能与 ng-options 或 ng-repeat 一起使用

    angularjs - 仅当在angularjs中选择了另一个时如何显示表单字段

    javascript - AngularJS 在选择时显示图像

    html - 如何制作完整的滚动文本 - ios

    ios - cordova.file.dataDirectory 未定义

    javascript - 为什么添加的电子邮件地址不会显示在消息框中?

    javascript - 有谁知道如何复制这种滚动行为?

    javascript - AJAX回调后如何选择点击的链接类或ID