Enhance your SVG path by allowing users to drag points on Quadratic or Cubic Bezier curves

In my project, I am trying to make it so that when a user drags the point, it always stays aligned with the path. The goal is for the command Q, C, or S to adjust the curve based on the position of this draggable point, rather than treating it as a control point. Essentially, I want the point to act as an anchor that pulls the curve along with it.

In the image provided, the (15,5) pointer is shown acting as a control point. However, what I really need is for the draggable circle to follow the path and influence the curve as it moves.

Currently, I am using the x and y coordinates of the draggable pointer in conjunction with the Q command like this:

const d = `M10,0 Q ${draggable.x} ${draggable.y} 400 390`;

https://i.sstatic.net/4opF8.png

The desired outcome is depicted in the image below: the draggable pointer should always adhere to the path and update the Q command (or any Bezier command) based on its x and y values. https://i.sstatic.net/nfvZw.png

Answer №1

Quadratic Béziers Explained

To determine the position of a drag handle, one can use the De Casteljau equation for quadratic curves as shown in the following JavaScript function:

function getPointAtQuadraticT(p0, cp1, p, t = 0.5) {
  let t1 = 1 - t;
  return {
    x: t1 * t1 * p0.x + 2 * t1 * t * cp1.x + t ** 2 * p.x,
    y: t1 * t1 * p0.y + 2 * t1 * t * cp1.y + t ** 2 * p.y
  };
}

The process can be reversed by solving for a modified control point based on the coordinates of the moved drag handle, assuming the drag handle remains at t=0.5:

function quadraticControlPointAtT(p0, ptMid, p, t = 0.5) {
    let t1 = 1 - t;
    let cp1x = (ptMid.x - t1 * t1 * p0.x - t * t * p.x) / (2 * t1 * t);
    let cp1y = (ptMid.y - t1 * t1 * p0.y - t * t * p.y) / (2 * t1 * t);
    return { x: cp1x, y: cp1y };
}

Furthermore, the provided code demonstrates the visualization and manipulation of quadratic Bézier curves using SVG elements and JavaScript functions.

Cubic Béziers Analysis

In contrast to quadratic Béziers, cubic Béziers with two control points present complexities in determining new coordinates for both controls simultaneously. Vector editors often employ various techniques for curve manipulation during dragging operations.

For cubic curves, the calculation of single points along the path involves the use of a helper function like this:

function getPointAtCubicT(p0, cp1, cp2, p, t = 0.5) {
  let t1 = 1 - t;
  let ptMid = {
    x: t1 ** 3 * p0.x + 3 * t1 ** 2 * t * cp1.x + 3 * t1 * t ** 2 * cp2.x + t ** 3 * p.x,
    y: t1 ** 3 * p0.y + 3 * t1 ** 2 * t * cp1.y + 3 * t1 * t ** 2 * cp2.y + t ** 3 * p.y
  };
  return ptMid;
}

Strategies for manipulating cubic Bézier curves are explored, including the challenge of maintaining the drag handle's position at t=0.5.

Adjusting the cubic control points involves peculiar calculations using factors such as 8/3, though the exact rationale behind this constant remains mysterious.

Drawing Conclusions

While the concept of on-path editing may seem intuitive, it comes with limitations when applied to cubic Bézier curves. Directly manipulating control points emerges as a more flexible and standardized approach for curve editing, simplifying application development and enhancing user experience.

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

Utilizing property validation in your codebase

How can I set up my code to display a warning in the console if the value of the Prop is not a string? The player-name prop should always be a string. If this: <greet :player-name="5"></greet> contains a number, I want my code below to generat ...

I'm trying to create a horizontal list using ng-repeat but something isn't quite right. Can anyone help me figure out

Feeling a bit lost after staring at this code for what seems like an eternity. I'm trying to create a horizontal list of 2 image thumbnails within a modal using Angular's ng-repeat. Here's the HTML snippet: <div class="modal-body"> ...

object stored in an array variable

I am looking to find out if there is a way to access a variable or function of an object that has been added to an array. I want to display all the objects in the array on a web page using a function that will return a string containing their content. Thi ...

Does the default input default to text format?

I have a somewhat peculiar question. I'm currently experimenting with Javascript for a plugin and came across an interesting scenario. Let's say I have an input element that is structured like this: <input type='cc' id='cc&apo ...

Error in Typescript for the prop types of a stateless React component

When reviewing my project, I came across the following lines of code that are causing a Typescript error: export const MaskedField = asField(({ fieldState, fieldApi, ...props }) => { const {value} = fieldState; const {setValue, set ...

Guidelines for iterating through a nested JSON array and extracting a search query in Angular

I'm currently working with a complex nested JSON Array and I need to filter it (based on the name property) according to what the user enters in an input tag, displaying the results as an autocomplete. I've started developing a basic version of t ...

Exploring JSON Object with ng-disabled in AngularJS

One unique feature of my web application is the ability to add new users (username + password) through a form. To prevent duplicate usernames, I have a JSON object called l_usernames defined in a controller named UsersController. This object contains all u ...

"Enable real-time editing with inline save/submit feature in

I'm struggling to figure out how to extract the modified content from a CKEditor instance and send it to a URL. I've been referencing something like this: but I can't seem to determine how to save the changes. Is there a way to post the up ...

In Firefox, long strings are automatically truncated, while in Google Chrome they display perfectly without any truncation

Here is a block of code where I am using a web service to retrieve a JSON string. It is encapsulated in an XML tag, which I then read and parse with jQuery's parser jQuery.parseJSON(xml.getElementsByTagName("string")[0].firstChild.nodeValue); $.ajax ...

Steps for connecting data to a react table

This is my first time working with React and I want to display the following data (id, name, status, and quantity): interface Goods { id: number; name: string; status: string; quantity?: number; } export const getGoods = (): Promise<Goods[]> ...

What could be the reason for scope.$watch failing to function properly?

My AngularJS directive has a template with two tags - an input and an empty list. I am trying to watch the input value of the first input tag. However, the $watch() method only gets called once during initialization of the directive and any subsequent chan ...

Eliminate redundant entries from an Ionic3 array

Looking for a solution to eliminate duplicate values in the storage array within Ionic3 this.storage.get('thestations').then((val) => { for(let i =0;i<val.length;i++){ if(this.newarray.indexOf(this.newarray) == -1) ...

Learn the process of retrieving JSON objects through AJAX using jQuery

When making a jQuery call to an API website, I receive the results in JSON format: { "results":[ { "user":{ "gender":"female", "name":{ "title":"mrs", "first":"linda", "last":"diaz" }, ...

In an AngularJS application, when using fullPage.js on the homepage, it is recommended to ensure that it is

Within my angularJs application, I have implemented fullPage.js on the homepage exclusively to ensure no interference with other pages or routes. To make this possible, I am currently triggering a destroy action during the locationChangeSuccess event. I a ...

Bootstrap 4 Card Body Spinner Overlay with Flex Alignment

Seeking to center a spinner both vertically and horizontally within a bootstrap 4 card body. Despite trying my-auto, justify-content-center & align-items-center, it seems like I'm missing something. I've double-checked the display types and ...

Integrating image transitions into JavaScript

Could you provide guidance on how to create buttons from the three images within the "fadein" div in order to navigate directly to a specific jpeg? Currently, the three jpgs are displayed in a continuous loop. Thank you. ...

Despite passing JSLint, an unexpected end of input still occurred

It seems a bit absurd; despite my efforts, I'm unable to locate the syntax error. The Chrome debugger keeps indicating an "unexpected end of input" in line two. Any suggestions? $("head meta").each(function () { var content = JSON.parse(this.cont ...

Avoid the ability for individuals to interact with the icon embedded within a button

I'm currently working on a website where users need to interact with a button to delete an "Infraction." The button has a basic bootstrap style and includes an icon for the delete action. <button referencedInfraction="<%= i.infractionID %>" ...

Pass the array data stored in React state over to Node/Express

I have been exploring ways to transfer an array from my react front end to my node/express back end. Initially, I attempted the following method. In React: saveUpdates = (clickEvent) => { var list = []; var length = this.props.title.length; ...

The function auth.createUserWithEmailAndPassword is not recognized in the context of React

base.jsx: import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; const firebaseConfig = { config }; export const app = initializeApp(firebaseConfig); export const auth = getAuth(); Register.jsx ...