Engaging geometric figures with interactive features

I'm working with some coordinate points, such as [0, 0],[30, 0],[30, 20],[60, 20],[60, 40],[0, 40],[0, 0]

Using these points as input, I want to create shapes with clickable corners. The edges will overlap each other - on the first mouse click, the first segment goes over the second. On the second click, the second segment goes over the first, and on the third click it creates a mitre effect.

[Here are some possible polygon interaction effects][1] [1]: https://i.sstatic.net/Ok0iM.png

Is there a way to place invisible rectangles at the corners for click detection? The challenge is accurately determining where to position the rectangles at each corner.

Additionally, what is the best approach for converting these points into lines or paths? The generated lines should be thick, but without using stroke width settings.

I previously attempted paths with stroke width settings, which caused the line join mitre effect to not work properly. Any suggestions on how to improve this would be greatly appreciated.

Below is a sample code snippet with lines and rectangles placed at corners:

<svg id="SvgjsSvg1001" width="700" height="400" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" viewBox="-100 -20 350 200"><defs id="SvgjsDefs1002"></defs><g id="SvgjsG1008" transform="matrix(2.5,0,0,2.5,0,0)"><line id="SvgjsLine1009" x1="0" y1="0" x2="30" y2="0" stroke-linecap="square" stroke="#ffdc0b" stroke-width="4"></line><line id="SvgjsLine1010" x1="30" y1="0" x2="30" y2="20" stroke-linecap="square" stroke="#002438" stroke-width="4"></line><line id="SvgjsLine1011" x1="30" y1="20" x2="60" y2="20" stroke-linecap="square" stroke="#9b56bb" stroke-width="4"></line><line id="SvgjsLine1012" x1="60" y1="20" x2="60" y2="40" stroke-linecap="square" stroke="#c6c7e2" stroke-width="4"></line><line id="SvgjsLine1013" x1="60" y1="40" x2="0" y2="40" stroke-linecap="square" stroke="#318700" stroke-width="4"></line><line id="SvgjsLine1014" x1="0" y1="40" x2="0" y2="0" stroke-linecap="square" stroke="#fe854f" stroke-width="4"></line>
<rect width="5" height="5" x="30" y="0"></rect>
<rect width="5" height="5" x="30" y="20"></rect>
<rect width="5" height="5" x="60" y="20"></rect>
<rect width="5" height="5" x="60" y="40"></rect>
<rect width="5" height="5" x="0" y="40"></rect>
<rect width="5" height="5" x="0" y="0"></rect></g></svg>

Answer №1

I am providing the solution to draw corner rectangles using bounding boxes of paths. Some may overlap, but this is not an issue for mouse interaction. The missing one can be drawn by utilizing the group's bounding box.

For mouse interaction, I set cursor: pointer; for each corner.

const SVG_NS = "http://www.w3.org/2000/svg";
let pathsRy = Array.from(document.querySelectorAll("path"));
let rectsRy = [];

pathsRy.forEach(p => {
  let pbbox = p.getBBox();
  rectsRy.push(
    drawRect({
      x: pbbox.x,
      y: pbbox.y,
      width: 5,
      height: 5,
      class: "corner"
    },
     svg)
   );
 });

 let gbbox = group.getBBox();
 rectsRy.push(
   drawRect({
     x: gbbox.x + gbbox.width - 5,
     y: gbbox.y + gbbox.height - 5,
     width: 5,
     height: 5,
     class: "corner"
   },
   svg)
 );

 function drawRect(o, parent) {
   var rect = document.createElementNS(SVG_NS, "rect");
   for (var name in o) {
     if (o.hasOwnProperty(name)) {
       rect.setAttributeNS(null, name, o[name]);
     }
   }
   parent.appendChild(rect);
   return rect;
 }
.corner {
  fill: rgba(255, 0, 0, 0.5);
  cursor: pointer;
}

svg {
  overflow: visible;
}

path {
  fill: none;
  stroke: black;
  stroke-linejoin: round;
}
<svg id="svg" viewBox="0 0 70 70" width="200">
<g id="group">  
  <path d="M0,0 L30,0 25,5 5,5z" /> 
           
  <path d="M30,0 L30,20 25,25 25,5z" /> 
  
  <path d="M30,20 L60,20 55,25 25,25" />
  
  <path d="M60,20 L60,40 55,35 55,25" /> 
  
  <path d="M60,40 L0,40 5,35 55,35z" />
           
  <path d="M0,40 L0,0 5,5 5,35z" />
  </g>
</svg>

Please note that this method works well with particular shapes. It may not work effectively for shapes with multiple turning points or different positions.

UPDATE
  1. I simplified your code by consolidating common attributes such as stroke-linecap in the SVG and handling stroke-width within JavaScript.

  2. In the JavaScript section, I created the linesRy array and extracted the values of the x1 and y1 attributes for each line.

  3. Using these attribute values along with stroke-width, I drew pink rectangles at each corner.

The original poster updated the question with additional code snippets. Consequently, I made corresponding adjustments to my code:

const SVG_NS = "http://www.w3.org/2000/svg";

let strokeWidth = 4;

let linesRy = Array.from(document.querySelectorAll("line"));

linesRy.forEach((l)=>{
  l.setAttributeNS(null, "stroke-width", strokeWidth);
  let x = l.getAttribute("x1");
  let y = l.getAttribute("y1");
  
  drawRect({
    x:x-strokeWidth/2,
    y:y-strokeWidth/2,
    width:strokeWidth,
    height:strokeWidth,
    class:"pink"
  }, SvgjsG1008);
})

function drawRect(o, parent) {
  var rect = document.createElementNS(SVG_NS, "rect");
  for (var name in o) {
    if (o.hasOwnProperty(name)) {
      rect.setAttributeNS(null, name, o[name]);
    }
  }
  parent.appendChild(rect);
  return rect;
 }
line{
stroke-linecap:square;
}

.pink{fill:pink}
<svg id="SvgjsSvg1001" width="700" height="400" viewBox="-100 -20 350 200">
<g id="SvgjsG1008" transform="matrix(2.5,0,0,2.5,0,0)"><line id="SvgjsLine1009" x1="0" y1="0" x2="30" y2="0"  stroke="#ffdc0b" ></line>
  
<line id="SvgjsLine1010" x1="30" y1="0" x2="30" y2="20" stroke="#002438"></line>

<line id="SvgjsLine1011" x1="30" y1="20" x2="60" y2="20" stroke="#9b56bb"></line>
  
<line id="SvgjsLine1012" x1="60" y1="20" x2="60" y2="40" stroke="#c6c7e2"></line>
   
<line id="SvgjsLine1013" x1="60" y1="40" x2="0" y2="40"  stroke="#318700" ></line>

<line id="SvgjsLine1014" x1="0" y1="40" x2="0" y2="0" stroke="#fe854f" ></line>
  
  
  
<!--  
<rect width="5" height="5" x="30" y="0"></rect>
<rect width="5" height="5" x="30" y="20"></rect>
<rect width="5" height="5" x="60" y="20"></rect>
<rect width="5" height="5" x="60" y="40"></rect>
<rect width="5" height="5" x="0" y="40"></rect>
<rect width="5" height="5" x="0" y="0"></rect>-->

</g></svg>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it more advantageous to pass a value as a prop to a child component, or should the child component retrieve it from Redux instead?

In my scenario, there are two components called <Parent> and <Child>. The <Parent> component is connected to a Redux state property named prop1 using the mapStateToProps() function. Now, I also need the <Child> component to have acc ...

Retrieved information from Firestore to set as the initial value for my useState hook, but I keep receiving an undefined value

I'm facing an issue where I want to use the fetched data from Firestore as the initial value of my state using useState, but it always returns undefined. This is because when updating a user profile, I need to know which property has been edited or up ...

What would be the most effective approach for creating a reactive setter for an object within an array in Vuex?

I am working with a Vuex object that contains an array of languages consisting of objects with guid, name, and level properties. I am trying to figure out how to write a method that will make it reactive. Currently, I have an input field with a value of " ...

When encountering duplicates within an array, I must ensure that the complete structure of the array is preserved and stored in MongoDB

I have encountered a situation where I am looping through an array and need to save the data in a MongoDB database. However, I need to avoid duplicating the array's indexes. I was thinking of using a filter to achieve this, but I'm concerned abou ...

Multiple jQuery load() requests are being triggered by calling it from within the ajaxComplete() callback

I've been stuck on this issue for quite some time now. To troubleshoot, I suggest using Firebug to inspect the AJAX requests. It seems that they are multiplying with each click of the next and previous buttons, causing the page to load slowly: You ca ...

Activate text-entry fields after a button has been pressed on polymer 1.0

I am currently developing a project focused on creating a list of doctors using the Polymer 1.0 framework. Within the doctor-list, I have integrated a Vaadin grid called doctors-grid.html to display data sourced from a JSON file. Upon double-clicking on a ...

When the nav-element is clicked, it will load the new content/file.php into the div with the id of "include."

Seeking the most efficient method to load content from a php file into a designated div on my webpage, prioritizing speed, minimal performance requirements, and ease of implementation. Unsure whether it's preferable to utilize php or javascript for t ...

jQuery not triggering when selecting items from dropdown list

I used to have a dropdownlist in my HTML with the following code: <select id="customDropDown" name="mydropdown"> <option value="CourseIDFilter"id ="1" class ="choosefilter" >Course ID </option> <option value="CourseNameFilter" i ...

Implementing a for loop within a scrolling function

How do I multiply functions inside the scroll loop? This is what I currently have: $(window).scroll(function() { b1Center = $("#block-1").offset().top - ($(window).height() - divHeight) / 2; b1Bottom = $("#block-1").offset().top - $(window).height(); b1T ...

Is it possible to reverse the use of JQuery's .each() function without any additional plugins or modifications?

Similar Question: Reversing JQuery .each() Is there a better approach to using .each() in reverse? Currently, I am implementing it like this: var temp = []; $("#nav a").each(function() { temp.push($(this)); }); temp.reverse(); for(var i = 0; i ...

Can we add to the input field that is currently in focus?

Recently, I've been working on a bookmarklet project. My goal is to append an element to the currently focused input field. For instance, if a user clicks on a textarea and then activates my bookmarklet, I want to insert the text "Hello" into that sp ...

The UseEffect hook continues to run even if the dependency (router.query) remains the same

useEffect(() => { console.log('applying filter'); const updatedFilters = { status: { values: { label: router.query.status, value: router.query.status }, }, // Add additional filter properties here... }; ...

Registration of Laravel Vue.js components

I am currently working on a Vue.js application in conjunction with Laravel. To get started, I registered Vue.js like this: import Vue from 'vue'; import VueRouter from 'vue-router'; Vue.use(VueRouter); import App from './compone ...

Keep things in line with async functions in Node JS

Hello, I am just starting out with NodeJs and I have a question. I am trying to push elements into an array called files based on the order of the urls provided, but it seems like I'm getting a random order instead. Below is the code I've been wo ...

Develop a custom input field feature that utilizes both JavaScript and CSS

I really appreciate the feature that allows me to resize the textarea by simply clicking and dragging on the slanted lines in the lower right hand corner. However, I am looking for a way to apply CSS styles to text which is not possible with a textarea. ...

Incorporate the module into both the parent and child class

In my coding project, I have a situation where both a parent class and a child class are importing the same lodash library. This raises the question: will the final bundled JavaScript file contain duplicate lodash code? //Parent Class import Component fro ...

"The file upload function is populating the req.body object, but not the req.file

I successfully implemented a file upload API using multer and express, which functions well when accessed through POSTMAN. However, I encountered an issue when trying to utilize the same API with another file upload API: The code I used can be found below ...

Updating information on a website

My goal is to allow users to provide input text that will dynamically replace existing text on the webpage. For example, if there is a name displayed in an HTML element, I have created a form where the user can type their own name and submit it. Once sub ...

Setting up the Materialize autocomplete feature by fetching data from an API using AJAX and jQuery

Welcome everyone, I have integrated materialize.css into my PHP application I am trying to initialize the auto-complete data using an API call. The .autocomplete () function initializes the data using a JSON array called " data ":, which I ret ...

After refreshing the page, the local storage does not appear

I've been struggling to get it working for hours with no luck. After submitting the form, I create local storage values for name, surname, and email so that they can be automatically filled in the form next time without requiring the user to retype th ...