首页 > 解决方案 > 一起使用 jQuery Pan 和 Zoomooz 来平移和缩放 DOM 元素

问题描述

在这里看到了一些类似的问题,但大多数似乎是指缩放和平移图像,我找不到任何可以回答我的问题的东西。

我正在寻找创建类似https://timmywil.com/panzoom/demo/的东西,但我希望在单击时缩放 DOM 元素,然后在鼠标移动时平移。

我已经能够想出非常接近那里的东西,例如这里。https://jsfiddle.net/kevngibsn/5okxr8n3/29/

为此,我使用 Zoomooz 来处理缩放,并使用 jQuery Pan 来处理平移。这个例子的问题是 jQuery Pan 在页面加载时计算出 DOM 元素的大小,并且没有考虑缩放后增加的大小,因此鼠标移动不会平移到边缘。

这是来自 jQuery Pan 的代码:

(function( $ ){

var getSize = function($element) {
    return {
        'width': $element.width(), 
        'height': $element.height()
    };
};

var toCoords = function(x, y) {
    return {'x': x, 'y': y};
};

var vectorsEqual = function(v1, v2) {
    return v1.x == v2.x && v1.y == v2.y;
}

$.fn.pan = function(options) {

    //Container is element this plugin is applied to;
    //we're pan it's child element, content
    var container = this;
    var content = this.children(':first');

    //Precalculate the limits of panning - offset stores
    //the current amount of pan throughout
    var offset = toCoords(
        Number(content.css('left').replace('px', '')) | 0,
        Number(content.css('top').replace('px', ''))  | 0
    );

    var containerSize = getSize(container);
    var contentSize = getSize(content);

    var minOffset = toCoords(
        -contentSize.width + containerSize.width,
        -contentSize.height + containerSize.height
    );

    var maxOffset = toCoords(0, 0);

    //By default, assume mouse sensitivity border
    //is 25% of the smallest dimension
    var defaultMouseEdge = 0.25 * Math.min(
        containerSize.width,
        containerSize.height
    );

    var settings = $.extend( {
        'autoSpeedX'            : 0,
        'autoSpeedY'            : 0,
        'mouseControl'          : 'kinetic',
        'kineticDamping'        : 0.8,
        'mouseEdgeSpeed'        : 5,
        'mouseEdgeWidth'        : defaultMouseEdge,
        'proportionalSmoothing' : 0.5,
        'updateInterval'        : 50,
        'mousePan'              : null
    }, options);

    //Mouse state variables, set by bound mouse events below
    var mouseOver = false;
    var mousePanningDirection = toCoords(0, 0);
    var mousePosition = toCoords(0, 0);

    var dragging = false;
    var lastMousePosition = null;
    var kineticVelocity = toCoords(0, 0);

    //Delay in ms between updating position of content
    var updateInterval = settings.updateInterval;

    var onInterval = function() {

        if (container.hasClass('pan-off')) return false; //Temporarily disabling pan add/remove class pan-off

        var mouseControlHandlers = {
            'edge'          : updateEdge,
            'proportional'  : updateProportional,
            'kinetic'       : updateKinetic
        };

        var currentHandler = settings.mouseControl;

        if(!mouseControlHandlers[currentHandler]()) {
            //The handler isn't active - just pan normally
            offset.x += settings.autoSpeedX;
            offset.y += settings.autoSpeedY;
        }

        //If the previous updates have take the content
        //outside the allowed min/max, bring it back in
        constrainToBounds();

        //If we're panning automatically, make sure we're
        //panning in the right direction if the content has
        //moved as far as it can go
        if(offset.x == minOffset.x) settings.autoSpeedX = Math.abs(settings.autoSpeedX);
        if(offset.x == maxOffset.x) settings.autoSpeedX = -Math.abs(settings.autoSpeedX);
        if(offset.y == minOffset.y) settings.autoSpeedY = Math.abs(settings.autoSpeedY);
        if(offset.y == maxOffset.y) settings.autoSpeedY = -Math.abs(settings.autoSpeedY);

        //Finally, update the position of the content
        //with our carefully calculated value
        content.css('left', offset.x + "px");
        content.css('top', offset.y + "px");
    }

    var updateEdge = function() {

        if(!mouseOver) return false;

        //The user's possibly maybe mouse-navigating,
        //so we'll find out what direction in case we need
        //to handle any callbacks
        var newDirection = toCoords(0, 0);

        //If we're in the interaction zones to either
        //end of the element, pan in response to the
        //mouse position.
        if(mousePosition.x < settings.mouseEdgeWidth) {
            offset.x += settings.mouseEdgeSpeed;
            newDirection.x = -1;
        }
        if (mousePosition.x > containerSize.width - settings.mouseEdgeWidth) {
            offset.x -= settings.mouseEdgeSpeed;
            newDirection.x = 1;
        }
        if(mousePosition.y < settings.mouseEdgeWidth) {
            offset.y += settings.mouseEdgeSpeed;
            newDirection.y = -1;
        }
        if (mousePosition.y > containerSize.height - settings.mouseEdgeWidth) {
            offset.y -= settings.mouseEdgeSpeed;
            newDirection.y = 1;
        }

        updateMouseDirection(newDirection);

        return true;
    }

    var updateProportional = function() {

        if(!mouseOver) return false;

        var rx = mousePosition.x / containerSize.width;
        var ry = mousePosition.y / containerSize.height;
        targetOffset = toCoords(
            (minOffset.x - maxOffset.x) * rx + maxOffset.x,
            (minOffset.y - maxOffset.y) * ry + maxOffset.y
        );

        var damping = 1 - settings.proportionalSmoothing;
        offset = toCoords(
            (targetOffset.x - offset.x) * damping + offset.x,
            (targetOffset.y - offset.y) * damping + offset.y
        )

        return true;
    }

    var updateKinetic = function() {
        if(dragging) {

            if(lastMousePosition == null) {
                lastMousePosition = toCoords(mousePosition.x, mousePosition.y);    
            }

            kineticVelocity = toCoords(
                mousePosition.x - lastMousePosition.x,
                mousePosition.y - lastMousePosition.y
            );

            lastMousePosition = toCoords(mousePosition.x, mousePosition.y);
        }

        offset.x += kineticVelocity.x;
        offset.y += kineticVelocity.y;

        kineticVelocity = toCoords(
            kineticVelocity.x * settings.kineticDamping,
            kineticVelocity.y * settings.kineticDamping
        );

        //If the kinetic velocity is still greater than a small threshold, this
        //function is still controlling movement so we return true so autopanning
        //doesn't interfere.
        var speedSquared = Math.pow(kineticVelocity.x, 2) + Math.pow(kineticVelocity.y, 2);
        return speedSquared > 0.01
    }

    var constrainToBounds = function() {
        if(offset.x < minOffset.x) offset.x = minOffset.x;
        if(offset.x > maxOffset.x) offset.x = maxOffset.x;
        if(offset.y < minOffset.y) offset.y = minOffset.y;
        if(offset.y > maxOffset.y) offset.y = maxOffset.y;
    }

    var updateMouseDirection = function(newDirection) {
        if(!vectorsEqual(newDirection, mousePanningDirection)) {
            mousePanningDirection = newDirection;
            if(settings.mousePan) {
               settings.mousePan(mousePanningDirection);
            }
        }   
    }

    this.bind('mousemove', function(evt) {
        mousePosition.x = evt.pageX - container.offset().left;
        mousePosition.y = evt.pageY - container.offset().top;
        mouseOver = true;
    });

    this.bind('mouseleave', function(evt) {
        mouseOver = false;
        dragging = false;
        lastMousePosition = null;
        updateMouseDirection(toCoords(0, 0));
    });

    this.bind('mousedown', function(evt) {
        dragging = true;
        return false; //Prevents FF from thumbnailing & dragging
    });

    this.bind('mouseup', function(evt) {
        dragging = false;
        lastMousePosition = null;
    });

    //Kick off the main panning loop and return
    //this to maintain jquery chainability
    setInterval(onInterval, updateInterval);
    return this;
};

})( jQuery );

我对此的限制差不多,关于如何获得整个元素的平移有什么建议吗?

标签: javascriptjqueryzoomingpan

解决方案


推荐阅读