Adjusting SVG size based on the position of the cursor for transforming origin

My goal is to scale an SVG circle using the trackpad (by moving two fingers up and down), with the origin of the transform being the cursor position. Initially, the scaling works as intended, but on subsequent attempts, the circle changes position, which should not happen. This issue becomes apparent when the cursor is inside the circle or near its perimeter. Here is the code snippet:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" content="width=device-width">

    <style>
        .container
        {
            position: fixed;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
        }

    </style>
</head>
<body>


<div class="container">
    <svg id="svg" height="600" width="600">
        <circle cx="300" cy="300" r="300" stroke="black" stroke-width="3" fill="white"/>
    </svg>
</div>

<script>
    let scale = 1;
    const e = document.getElementById("svg");

    function wheelZoom(event)
    {
        event.preventDefault();

        scale += event.deltaY * -0.01;
        scale = Math.min(Math.max(.5, scale), 2);

        x = 100*(event.clientX-e.getBoundingClientRect().x)/e.getBoundingClientRect().width;
        y = 100*(event.clientY-e.getBoundingClientRect().y)/e.getBoundingClientRect().height;

        e.style.transformOrigin = `${x}% ${y}%`;
        e.style.transform = `scale(${scale})`;
    }

    e.addEventListener("wheel", wheelZoom);
</script>


</body>
</html>

Answer №1

I have a few uncertainties:

  • What do you mean when you say you don't want the circle to change position?
  • Do you want the entire SVG to scale or just the circle inside the SVG?

In the demo below, I decided to keep the SVG unchanged but scale the circle based on the position inside the SVG when you scroll the mouse wheel.

Hopefully this is what you were looking for.

//    let scale = 1;
const svg = document.getElementById("svg");
const circle = document.querySelector("svg circle");

// Circle transform. Inits to 1:1 scale (called an "identity transform").
var   circleTransform = svg.createSVGMatrix();  // start

svg.addEventListener("wheel", wheelZoom);


function wheelZoom(event)
{
   event.preventDefault();

   // Get the mouse position as SVG coordinates
   var coords = convertScreenCoordsToSvgCoords(event.clientX, event.clientY);

   // Calculate an appropriate scale adjustment
   var scale = 1.0 + (event.deltaY * 0.001);

   // To scale around the mouse coords, first we transform the coordinate
   // system so that the origin is at the mouse coords.
   circleTransform = circleTransform.translate(coords.x, coords.y);
   // Then we apply the scale
   circleTransform = circleTransform.scale(scale, scale);
   // Finally we move the coordinate system back to where it was
   circleTransform = circleTransform.translate(-coords.x, -coords.y);

   // Now we need to update the circle's transform
   var transform = svg.createSVGTransform();        // An SVGTransform DOM object...
   transform.setMatrix(circleTransform);            // set to the new circleTransform...
   circle.transform.baseVal.initialize(transform);  // and used to update the circle transform property
}


function convertScreenCoordsToSvgCoords(x, y) {
   var pt = svg.createSVGPoint();  // An SVGPoint SVG DOM object
   pt.x = x;
   pt.y = y;
   // getScreenCTM tells us the combined transform that determines where 
   // the circle is rendered. Including any viewBox.
   // We use the inverse of that to convert the mouse X and Y to their
   // equivalent values inside the SVG.
   pt = pt.matrixTransform(circle.getScreenCTM().inverse());
   return {'x': pt.x, 'y': pt.y};
}
svg {
  background-color: linen;
}
<div class="container">
   <svg id="svg" height="600" width="600">
      <circle cx="300" cy="300" r="300" stroke="black" stroke-width="3" fill="white"/>
   </svg>
</div>

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

Struggles with closing dropdown menus

How can I modify my drop down menu so that the first drop box closes when a new link is clicked? Additionally, how do I ensure that the menu closes when clicking on a child item without refreshing the page (since the content is dynamically pulled from a ...

"Node.js is giving an error stating that the object does not have a

I am attempting to save the user details from the registration form into a JSON file for authentication purposes. However, I am having trouble appending the data in the correct format. Here is the code snippet that I have tried: var filename = "./user_log ...

Ensuring text is perfectly centered within a label and creating a label that is entirely clickable

Struggling to style a file input button and encountering some issues with labels. Here is the button in question. Firstly, how can I center the text in the label? Secondly, how can I make the entire button clickable instead of just the upper half? Also, ...

AngularJS JSON data computation

As I delve into learning angularjs (not 2), one of the challenges I face is calculating the total amount for a customer order. The data I'm working with is structured as follows: var clients = [ { id: 1, jo ...

Tips for switching back and forth between two classes using jQuery?

I'm having some trouble with the toggleClass function. It doesn't seem to be working correctly for me. The image should toggle between displaying and hiding, but it only changes to hide, not back again. You can view my example here, along with t ...

Assign a variable to the result of ajax that remains unchanged until it is specifically invoked

I am currently working on the final part of my radio script, specifically focusing on the "last song" section. My goal is to retrieve a URL from an external PHP script, play the song, and once the song ends, I want to set a global variable equal to the cur ...

Executing JavaScript in HttpClient or HtmlUnitHow to trigger javascript in HttpClient or HtmlUnit

Currently, I am using the HttpClient POST method to perform a specific action on a website. This involves using Javascript for an ajax connection which generates a unique requestID in the form of var reqID = Math.floor(Math.random()*1000001);. I need to ac ...

Checking for an empty value with javascript: A step-by-step guide

Below is an HTML code snippet for checking for empty or null values in a text field: function myFormValidation() { alert("Hello"); var name = document.getElementById("name").value; alert(name); if (name == null || name == "") { document.ge ...

Guide on how to streamline JSON output from aggregation result

I have written a NodeJs api using mongo db aggregation to obtain some output. However, the result I received is not what I expected. Can anyone help me figure out how to get the desired output? app.get('/polute', function (req, res) { Light. ...

Issue encountered with Fabric js: Unable to apply pattern fill to a group of rectangles

Greetings, I am in need of some assistance with a coding issue. I have a for loop that generates and adds multiple rectangles to a fabric js canvas. To set a texture for each rectangle, I am using the following code snippet. var rect = new fabric.Rect( ...

The AngularJS ng-if directive is failing to function properly, despite the logged boolean accurately reflecting the

I created a custom directive that handles the visibility of text elements on a webpage. While this logic works correctly half of the time, it fails the other half of the time. Here is the code snippet from my directive: newco.directive 'heroHeadline& ...

Using `useState` within a `while` loop can result in

I'm working on creating a Blackjack game using React. In the game, a bot starts with 2 cards. When the user stands and the bot's card value is less than 17, it should draw an additional card. However, this leads to an infinite loop in my code: ...

Personalized Design Incorporating Sections and Sidebars

I need the aside to be positioned on the right side and the section on the left side, both centered in the space. Feel free to check out this link <!DOCTYPE html> <html> <head> <style> #main { width: 800px; margin: 0 auto; } ...

Original code

I have a database containing HTML code for a table. I would like to display this code in its original form within a textarea so that I can edit it. However, when I try to echo the code, it appears in compiled form as a table rather than the original HTML ...

Props in Vue components are exclusively accessible within the $vnode

Exploring the realm of Vue.js, I am tasked with constructing a recursive Component renderer that transforms JSON into rendered Vue components. The recursive rendering is functioning smoothly; however, the props passed to the createElement function (code b ...

Enhancing Animation Speed with jQuery

I've provided the code snippet at this link: http://jsfiddle.net/LnWRL/4/: Here is the HTML: <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script> <div id="wrap_demo"> <div id="demo"></di ...

HTML5 Mouse Canvas

Here's a simple example of what's happening: function handleClick(event) { ... } canvas.addEventListener("click", handleClick, false); function drawRectangle(x, y) { context.fillRect(x, y, 16, 16); }; ...

Tips for designing websites using HTML and CSS

current result: https://i.sstatic.net/eXLPv.png desired output: https://i.sstatic.net/vgl6z.png I am aiming to center the text. add image description here add image description here ...

Is it possible to incorporate Vue and Vuetify into an existing project that requires IE compatibility?

Currently in the process of enhancing a legacy project with new functionality. The front end is currently relying solely on jQuery for all the webpages. I have been tasked with adding another webpage and would like to incorporate Vuetify + Vue due to the i ...

Frontend update: Changing the display format for dates

Received from the backend is an array of objects (leavedays)- var leavedays = [{"_id":"62d544ae9f22d","season":2022,"name":"LEAVEDAY1","dateFrom":"2022- 07-26T00:00:00.000Z","date ...