Выделение и удаление полигонов на canvas

476
21 июля 2021, 18:40

Есть задача позволить пользователю выборочно выделять и удалять полигоны которые он рисует. Проблема 1: как выделить нужный полигон из возможного множества полигонов нарисованных пользователем. Проблема 2: как сделать чтобы когда нужно выделить полигон при клике на полигон не оставалась точка координат и не добавлялись данные в массив с координатами. Проблема 3: при выделении полигона это должно быть видно(изменение strokestyle или fillstyle). Пожалуйста объясните как реализовать задуманное, предложите варианты. Спасибо!

<!DOCTYPE html> 
<html lang="ru"> 
<head> 
 
    <meta charset="UTF-8"> 
    <title>Streaming</title> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> 
    <link rel="stylesheet" type="text/css" href="style.css"> 
 
</head> 
 
<body> 
 
    <p style="color: #dddddd; margin-left:30px;"> 
            X:<span id="offsetX"></span> 
            Y:<span id="offsetY"></span> 
    </p> 
 
<div class="main_cont"> 
 
    <canvas id='canvas' height='500' width='500' style="border:1px solid red;"></canvas> 
    <dialog id='dialog'> 
            <p id='p_dialog'></p> 
            <button id="closeWindow">Закрыть</button> 
    </dialog> 
 
    <div id="button_container"> 
 
        <button id = "clearCanvas">Clear canvas</button> 
        <button id="showCoord">Show coord</button> 
        <button id = "clearSaveCoord">Clear save coord</button> 
        <button id = "repainButton">Load</button> 
        <button id = "saveCoord">Save</button> 
        <button id = "back">Back one point</button> 
        <button id = "readCoord">Read Coord</button> 
 
        <form action="" method="post" target="_blank"> 
        <p> 
            <input type="hidden" id="rect_coord" name="coordinates" required><br> 
        </p> 
        <p> 
            <input class="button2" style='margin-top:20px;' type="submit" name="Save"  title=""> 
        </p> 
 
        </form> 
 
 
    </div> 
 
    <dialog id='dialogRedCoord'> 
        <div id="add" style="display:grid;"> 
            <button id="addInput">&#43;</button> 
        </div> 
        <button id="closeWindowReadCoord">Закрыть</button> 
        <button id="addDataFromInput">Пременить</button> 
    </dialog> 
 
</div> 
 
<script type="text/javascript"> 
 
    //====================================================================================================== 
    let can = document.getElementById('canvas');// холст 
    can.width          = 500;                   // ширина холста 
    can.height         = 500;                   // высота холста 
    let ctx            = can.getContext('2d');  // выбрать пространство 
    let pts            = [[]];                  // массив с точками координат 
    let data           = null;                  // массив координат из localStorage 
    let clearCoordinat;                         // сохраненые данные в localStorage 
    let x, y; 
    //====================================================================================================== 
    // выводим отображение координат(offset) на холсте при ведении мыши 
    document.getElementById('canvas').onmousemove = function(event){ 
        event = event || window.event; 
        document.getElementById('offsetX').innerHTML = event.offsetX; 
        document.getElementById('offsetY').innerHTML = event.offsetY; 
    }; 
 
    //====================================================================================================== 
    // убирает выделение холста синим цветом при двойном нажатии мыши 
    document.getElementById('canvas'); 
    canvas.onselectstart = function () { return false; }; 
 
    //====================================================================================================== 
    // отрисовывает сохраненные координаты фигур 
    document.getElementById('repainButton').addEventListener('click', function(event) { 
        data = JSON.parse(window.localStorage.getItem('storeObj')); 
        // извлекаем данные из localstorege 
        if (data === null){ 
            return alert('Нет сохраненых объектов'); 
        } else{ 
        console.info('ЭТО data', data) 
        pts = data       // новая коллекция точек /// я изменил одну эту строчку 
        repaintButton(); // вызываев функцию для отрисовки с кнопки 
              } 
 
        document.getElementById('clearCanvas').addEventListener('click', function() { 
            ctx.clearRect(0, 0, can.width, can.height); 
        }); 
    },false); 
 
    //======================================================================================================= 
    // шаг назад по нажатию кнопки при раставлении точек 
    document.getElementById('back').addEventListener('click', function(event) { 
        pts[pts.length-1].pop() 
        repaint(); 
    },false); 
 
    //====================================================================================================== 
    // замыкаение и отрисовка фигуры по двойному клику вызовом func(repaint) (JQUERY) 
    $( "#canvas" ).dblclick(function() { 
        pts[pts.length-1].pop(); // удалим повторную точку 
        pts.push([])             // новая коллекция точек 
        repaint();               // вызываем функцию отрисовки 
 
    //------------------------------------------------------------------------------------------------------- 
        // сохранение фигур в localStorage 
        clearCoordinat = pts; 
        document.getElementById('saveCoord').onclick = function(event) { 
            if (clearCoordinat === null){ 
                alert('Нет данных для сохранения') 
            } else { 
                window.localStorage.setItem('storeObj', JSON.stringify(clearCoordinat)); 
                alert('Сохранено') 
                   }; 
        }; 
    }); 
 
    //======================================================================================================== 
    // обработка массива с сохранеными точками в localStorage для отображения координат 
    document.getElementById('showCoord').onclick = function(event) { 
        let showCoord = JSON.parse(window.localStorage.getItem('storeObj')) 
 
        if(showCoord !== null){ 
        let prop = Object.values(showCoord) 
 
        for (let i in prop){ 
            for (let j in prop[i]){ 
                for(let v in prop[i][j]){ 
                    if(v%2==0){ 
                        prop[i][j][v] = ' x: ' + prop[i][j][v] 
                    } else { 
                        prop[i][j][v] = ' y: ' + prop[i][j][v] 
                           }; 
                    }; 
                }; 
            }; 
 
    //-------------------------------------------------------------------------------------------------------- 
        // делаем из масива словарь в формате json и придаем красивый вид 
        showCoord = JSON.stringify(Object.assign({}, showCoord), null, 4) 
        console.info(showCoord) 
 
    //-------------------------------------------------------------------------------------------------------- 
        // вывод обработаных данных в сплывающее окно 
        document.getElementById('p_dialog').innerText = 'Координаты: ' + showCoord // заполнение координатами 
 
    //------------------------------------------------------------------------------------------------------- 
        // всплывающее окно с сохраненными координатами 
        let dialog = document.querySelector('dialog'); 
        dialog.show(); 
        document.querySelector('#closeWindow').onclick = function() { 
        dialog.close(); 
        }; 
        } else{ 
            alert('Нет сохраненых координат для отображения') 
              }; 
    }; 
 
    //========================================================================================================= 
        // отчистка поля input с кнопки  и удаление сохраненых координат из localStorage 
        document.getElementById('clearSaveCoord').onclick = function(event) { 
            if (clearCoordinat !== null){ 
                let delCoord = confirm("Вы уверены что хотите отчистить все? Это также приведет к удалению сохраненых координат."); 
                    if (delCoord){ 
                    dialog.close(); 
                    document.getElementById("rect_coord").value = null; 
                    window.localStorage.removeItem('storeObj'); 
                    return clearCoordinat = null; 
          }; 
        }  else { 
            alert('Нет сохраненных данных для удаления') 
               }; 
    }; 
    //======================================================================================================= 
    // события нажатия мыши и добавления координат в массив 
    can.addEventListener('mousedown', function(e){ 
        pts[pts.length-1].push([ 
            e.pageX - e.target.offsetLeft, 
            e.pageY - e.target.offsetTop 
        ]); 
        repaint(); 
        [e.pageX - e.target.offsetLeft, e.pageY - e.target.offsetTop] 
 
    }); 
 
    //======================================================================================================= 
    // функция рисования и обновления точек на холсте по точкам из массива pts 
    function repaint(){ 
 
        ctx.clearRect(0,0,can.width,can.height) 
 
        ctx.strokeStyle = '#00f'; 
        ctx.fillStyle = 'rgba(255, 255, 253, 0.5)' // Цвет 
        ctx.lineWidth = 3;                         // Толщина линий 
        ctx.lineJoin = "miter";                    // Закругленность соединений линий 
 
        // ***рисуем фигуры*** 
        // shape = фигура 
        // j     = номер фигуры 
        // p     = точка. выражена как значение двух координат x, y в массиве по 2 элемента 
        // i     = index массива 
        pts.forEach((shape, j)=> { 
            ctx.beginPath(); 
            shape.forEach((p, i) => { 
                ctx[i ? 'lineTo' : 'moveTo'](...p); 
            }) 
 
            if(j !== pts.length-1){ 
                ctx.fill(); 
                ctx.closePath(); 
            } 
 
        ctx.stroke(); 
        }) 
 
        // рисуем синие точки только для последней фигуры 
        pts[pts.length-1].forEach((p, i) => { 
            ctx.strokeStyle = '#000'; 
            ctx.fillStyle = 'rgba(255, 255, 253, 0.5)' 
            ctx.beginPath(); 
            ctx.arc(...p, 3.6 / 1.4 ,0, Math.PI * 2); //Круг 
            // ctx.rect(...p, 6, 6) 
            ctx.stroke(); 
 
        }) 
 
    //----------------------------------------------------------------------------------------------------- 
        // кнопка очистки canvas 
        document.getElementById('clearCanvas').addEventListener('click', function() { 
            ctx.clearRect(0, 0, can.width, can.height); 
 
            return pts = [[]];// отчищаю массив с точками для отрисовки фигуры 
        }, false) 
    }; 
 
    //====================================================================================================== 
    // функция асинхронный запрос к локальному файлу на сервере coord.json 
    function readTextFile(path_to_file, callback) { 
        let rawFile = new XMLHttpRequest(); 
        rawFile.overrideMimeType("application/json"); 
        rawFile.open("GET", path_to_file, true); 
        rawFile.onreadystatechange = function() { 
            if (rawFile.readyState === 4 && rawFile.status == "200") { 
                callback(rawFile.responseText); 
            } 
        } 
        rawFile.send(null); 
    }; 
 
    //======================================================================================================= 
    // получаем фаил 
    readTextFile("http://localhost:5000/static/json/coord.json", function(text){ 
        let file_data = JSON.parse(text); 
    }); 
 
    //====================================================================================================== 
    // функция для отрисовки из сохраненых координат в localStorage 
    function repaintButton(){ 
        ctx.clearRect(0,0,can.width,can.height); 
 
        ctx.strokeStyle = '#00f'; 
        ctx.fillStyle = 'rgba(255, 255, 253, 0.5)' // Цвет 
        ctx.lineWidth = 3;                         // Толщина линий 
        ctx.lineJoin = "miter";                    // Закругленность соединений линий 
 
        // рисуем фигуры 
        data.forEach((shape, j) => { 
            ctx.beginPath(); 
            shape.forEach((p, i) => { 
                ctx[i ? 'lineTo' : 'moveTo'](...p); 
 
            }) 
            if(j !== data.length-1){ 
                ctx.fill(); 
                ctx.closePath(); 
            } 
            ctx.stroke(); 
        }) 
 
        // рисуем синие точки только для последней фигуры 
        data[data.length-1].forEach((p, i) => { 
            ctx.strokeStyle = 'blue'; 
            ctx.beginPath(); 
            ctx.arc(...p, 10 / 20 ,0, Math.PI * 2); //Круг 
            ctx.stroke(); 
 
        }) 
    }; 
 
    //====================================================================================================== 
    // создаю всплывающее окно с динамическим добавлением элементов input для ввода координат 
    document.getElementById('readCoord').addEventListener('click', function(event) { 
        let dialogReadCoord = document.getElementById('dialogRedCoord'); 
        dialogReadCoord.show(); 
        addElementInput 
        document.querySelector('#closeWindowReadCoord').onclick = function() { 
        dialogReadCoord.close(); 
        }; 
    }); 
 
    //===================================================================================================== 
    // функция динамического создания, добавления в DOM и удаления элементов input (JQUERY) 
        let addElementInput = $(document).ready(function () { 
        let max_fields   = 1000; 
        let wrapper      = $("#add"); 
        let add_button   = $("#addInput"); 
 
        let x = 1; 
        $(add_button).click(function(e){ 
            e.preventDefault(); 
            if(x < max_fields){ 
                x++; 
                $(wrapper).append('<div><span>X:</span><input class="input_coord" type="" name=""><span>Y:</span><input class="input_coord" type="" name=""><button class="delete">Delete</button></div>'); //add input box 
            } else{ 
                  alert('You Reached the limits') 
                  } 
        }); 
 
        $(wrapper).on("click",".delete", function(e){ 
            e.preventDefault(); $(this).parent('div').remove(); x--; 
        }) 
    }); 
 
    //===================================================================================================== 
    document.getElementById('addDataFromInput').addEventListener('click', function(event) { 
    let coordFromInput = [[]]; 
    let text = document.getElementsByClassName("input_coord"); 
 
    coordFromInput = coordFromInput.push(text); 
    console.log(typeof coordFromInput); 
}); 
 
    //====================================================================================================== 
    // pts = this.pts; 
    // функция отслеживания состояния canvas 
 
 
 
let mouse = { 
        x: 0, 
        y: 0, 
    }; 
 
let selected = false; 
 
 
 
 
 
let Poligon = function(...points) { 
  this.points = pts 
} 
 
Poligon.prototype = { 
  draw() { 
    this.points.forEach((shape, j)=> { 
            ctx.beginPath(); 
            shape.forEach((p, i) => { 
                ctx[i ? 'lineTo' : 'moveTo'](...p); 
            }) 
 
            if(j !== this.points.length-1){ 
                ctx.fill(); 
                ctx.closePath(); 
            } 
 
        ctx.stroke(); 
        }) 
  }, 
  mouseIn() { 
    return PointInPoligon(mouse, ...this.points) 
  } 
}; 
 
 
 
let shapes = [ 
  new Poligon( 
    pts.forEach((shape, j)=> { 
            ctx.beginPath(); 
            shape.forEach((p, i) => { 
                ctx[i ? 'lineTo' : 'moveTo'](...p); 
            }) 
 
            if(j !== pts.length-1){ 
                ctx.fill(); 
                ctx.closePath(); 
            } 
 
        ctx.stroke(); 
        }) 
  ), 
 
] 
 
shapes.forEach(n => n.draw()) 
 
window.onmousemove = function(e) { 
  mouse.x = e.pageX - can.offsetLeft; 
  mouse.y = e.pageY - can.offsetTop; 
 
  if (selected) { 
    selected.points.forEach(n => { 
      n.x += e.movementX 
      n.y += e.movementY 
    }) 
 
    ctx.clearRect(0, 0, can.width, can.height) 
    shapes.forEach(n => n.draw()) 
  } 
}; 
 
window.onmousedown = function() { 
  if (!selected) { 
    shapes.forEach(n => { 
      if (n.mouseIn(mouse)) { 
        selected = n 
      } 
    }) 
  } 
}; 
 
window.onmouseup = function() { 
  selected = null; 
}; 
 
 
 
 
 
function PointInPoligon(points) { 
    return this.points 
} 
</script> 
 
</body> 
</html>

READ ALSO
Проблема с логированием (log4js) в TypeScript

Проблема с логированием (log4js) в TypeScript

Решил изучать TypeScript; дошел до логовИмея опыт разработки в Java, выбор пал на библиотеку log4js

248
Yandex map CustomControl

Yandex map CustomControl

Подскажите, как к этому примеру прикрутить подписку на событие, например клик по кастомному контролу? Можно конечно в HTML написать, но это...

173
React Css Modules Каскад

React Css Modules Каскад

Всем приветПытаюсь понять как работаю css модули для вложеных компонентов

112
Заполнение одномерных массивов

Заполнение одномерных массивов

Нужно при помощи подпрограмм сформировать и вывести на экран массивы A[15] и B[14]И потом, при помощи функций найти их минумумы и максимумы

195