Zooming and Panning in JS/JQuery like we can do it using SVG

Mathematics

I am creating an org chart with thousands of nodes but created a sample example here,

https://jsfiddle.net/jy6j87g0/2/

As you can see zooming and panning is working but I would like to make it work like one here,

http://live.yworks.com/demobrowser/index.html#Organization-Charts

To be specific

I am trying to make my fiddle,

  • To zoom based on mouse pointer's position not centre of chart
  • Have a limit on minimum size my chart can go so it won't go invisible
  • Have a reset button

I am struggling to find where to start, should I look further into CSS transformation or use d3.js instead and create everything from scratch.

Link to library - https://github.com/dabeng/OrgChart

'use strict';

(function($) {

  $(function() {

    var datascource = {
      'name': 'Lao Lao',
      'title': 'general manager',
      'children': [{
        'name': 'Bo Miao',
        'title': 'department manager'
      }, {
        'name': 'Su Miao',
        'title': 'department manager',
        'children': [{
          'name': 'Tie Hua',
          'title': 'senior engineer'
        }, {
          'name': 'Hei Hei',
          'title': 'senior engineer',
          'children': [{
            'name': 'Pang Pang',
            'title': 'engineer'
          }, {
            'name': 'Xiang Xiang',
            'title': 'UE engineer'
          }]
        }]
      }, {
        'name': 'Yu Jie',
        'title': 'department manager'
      }, {
        'name': 'Yu Li',
        'title': 'department manager'
      }, {
        'name': 'Hong Miao',
        'title': 'department manager'
      }, {
        'name': 'Yu Wei',
        'title': 'department manager'
      }, {
        'name': 'Chun Miao',
        'title': 'department manager'
      }, {
        'name': 'Yu Tie',
        'title': 'department manager'
      }]
    };

    $('#chart-container').orgchart({
        'data': datascource,
        'nodeContent': 'title',
        'pan': true,
        'zoom': true
      })
      .on('touchmove', function(event) {
        event.preventDefault();
      });

  });

})(jQuery);
<link href="https://cdn.rawgit.com/FortAwesome/Font-Awesome/master/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://dabeng.github.io/OrgChart/css/style.css" rel="stylesheet"/>
<link href="https://dabeng.github.io/OrgChart/css/jquery.orgchart.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://dabeng.github.io/OrgChart/js/jquery.orgchart.js"></script>
<div id="chart-container">
</div>

ConnorsFan

The code snippet below fulfills the 3 requirements set out in your question:

  1. The zoom is centered on the mouse position
  2. The scaling cannot go below MIN_ZOOM = 0.25
  3. The Reset button restores the original position and scaling

The transform matrix and the mouse position observed in the mousemove event handler are stored in module variables, and used later in the wheel event handler.

In my tests, the wheel event was always triggered after the scaling had been processed by OrgChart. If this processing order is not the same in your case, or not stable, you can wrap the code of the wheel event handler in a setTimeout(fn, 0) construct to make sure that the scaling has already been performed by OrgChart:

.on("wheel", function (event) {
    setTimeout(function () {
        // Process the event after OrgChart
        (put the code here)
    }, 0);
}

'use strict';

(function ($) {

    $(function () {

        var datascource = {
            'name': 'Lao Lao',
            'title': 'general manager',
            'children': [{
                'name': 'Bo Miao',
                'title': 'department manager'
            }, {
                'name': 'Su Miao',
                'title': 'department manager',
                'children': [{
                    'name': 'Tie Hua',
                    'title': 'senior engineer'
                }, {
                    'name': 'Hei Hei',
                    'title': 'senior engineer',
                    'children': [{
                        'name': 'Pang Pang',
                        'title': 'engineer'
                    }, {
                        'name': 'Xiang Xiang',
                        'title': 'UE engineer'
                    }]
                }]
            }, {
                'name': 'Yu Jie',
                'title': 'department manager'
            }, {
                'name': 'Yu Li',
                'title': 'department manager'
            }, {
                'name': 'Hong Miao',
                'title': 'department manager'
            }, {
                'name': 'Yu Wei',
                'title': 'department manager'
            }, {
                'name': 'Chun Miao',
                'title': 'department manager'
            }, {
                'name': 'Yu Tie',
                'title': 'department manager'
            }]
        };

        var MIN_ZOOM = 0.25; // Mimimum value for scaling
        var defaultMatrix = [1, 0, 0, 1, 0, 0]; // Chart at normal scaling and position
        var prevMatrix = defaultMatrix;
        var prevPosition = { x: 0, y: 0 };

        var parseNumber = function (str) {
            return parseFloat(str.replace(/[^\d\.\-]/g, ""));
        }

        var getTransformMatrix = function () {
            var transform = $(".orgchart").css("transform");
            if (transform !== 'none') {
                var tokens = transform.split(",");
                var matrix = [];
                for (var i = 0; i < tokens.length; i++) {
                    matrix.push(parseNumber(tokens[i]));
                }
                return matrix;
            } else {
                return null;
            }
        };

        var setTransformMatrix = function (matrix) {
            $(".orgchart").css("transform", "matrix(" + matrix.join(",") + ")");
            prevMatrix = matrix;
        };

        var getMousePosition = function (event) {
            var rect = $(".orgchart")[0].getBoundingClientRect();
            return {
                x: event.clientX - rect.left - rect.width / 2,
                y: event.clientY - rect.top - rect.height / 2
            }
        };

        $("#btnReset").click(function () {
            setTransformMatrix(defaultMatrix);
        });

        $("#chart-container").orgchart({
            'data': datascource,
            'nodeContent': 'title',
            'pan': true,
            'zoom': true
        }).on("touchmove", function (event) {
            event.preventDefault();
        }).on("mousemove", function (event) {
            // Remember transform matrix and mouse position
            prevMatrix = getTransformMatrix() || prevMatrix;
            prevPosition = getMousePosition(event);
        }).on("wheel", function (event) {
            // In my tests, this event is triggered after processing has been done by OrgChart
            // If not the case, the following code can be wrapped in setTimeout(fn, 0) call
            var $this = $(this);
            var matrix = getTransformMatrix();
            if (matrix) {
                var $orgchart = $(".orgchart");

                // Prevent scaling below minimum zoom
                matrix[0] = Math.max(matrix[0], MIN_ZOOM);
                matrix[3] = Math.max(matrix[3], MIN_ZOOM);

                var position = getMousePosition(event);

                // Calculate expected mouse position with new scaling
                // corresponding to previous mouse position with old scaling
                var expectedPosition = {
                    x: prevPosition.x * matrix[0] / prevMatrix[0],
                    y: prevPosition.y * matrix[3] / prevMatrix[3]
                };

                // Translate chart position to match the expected position
                matrix[4] += position.x - expectedPosition.x;
                matrix[5] += position.y - expectedPosition.y;

                setTransformMatrix(matrix);
                prevPosition = expectedPosition;
            }
        });
    });

})(jQuery);
#btnReset
{
    position: absolute;
    left: 16px;
    top: 16px;
    z-index: 1000;
}
<link href="https://cdn.rawgit.com/FortAwesome/Font-Awesome/master/css/font-awesome.min.css" rel="stylesheet" />
<link href="https://dabeng.github.io/OrgChart/css/style.css" rel="stylesheet" />
<link href="https://dabeng.github.io/OrgChart/css/jquery.orgchart.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script src="https://dabeng.github.io/OrgChart/js/jquery.orgchart.js"></script>
<button id="btnReset">Reset</button>
<div id="chart-container">
</div>

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Apply panning and zooming on inline SVG

Background image not zooming and panning with SVG

D3: Zooming/Panning Line Graph in SVG is not working in Canvas

How do I intercept mapview panning and zooming (Swift 4)

How to do panning/zooming operation in directx/c++

Zooming and panning an image in a QScrollArea

Zooming and panning TextureView

Is Pinch Zoom(zooming like we do on image) available in Firefox Developer Edition for macOS? If yes, how to enable it?

D3: keeping position of SVG's relative while panning and zooming

Flot zooming and panning with logarithmic axis

CALayer with NSScrollView, zooming panning and clicking

zooming and panning with buttons in android emulator

How can I detect panning and zooming by the user with d3 zoomBehavior?

Can't we do something like if (y = Foo()) > 2: in python?

How can i implement concurrency in C like we do in go?

Can we do arithmetic using Django Subqueries?

Laravel LiveWire 2 : Do we have to make a new class or we can do like traditional Controller

Can we implement Rust like Traits using Kotlin Interfaces

How we can write delimiter like sep=, using CsvHelper library?

Can we structure the Javascript object like this using reduce or something else?

How can we get a layout like this in tkinter using grid?

Is there a Google Map event listener for panning or zooming map?

Saving edited image after zooming, rotating and panning

SciChart - zooming and panning from code in MVVM

Why do we still need parser like BeautifulSoup if we can use Selenium?

How can we save all files in (VSCode) like we do in Visual Studio

how can we write an unchecked event for checkbox in jquery?. like we do for checked event?

Can we estimate Big Omega just like we do with Big O?

How can we refresh our Windows with python just like we do at the desktop (right click and click to refresh)