Javascript(jQuery)将图像缩放到其容器的中心点

时间:2011-12-30 10:12:18

标签: javascript jquery math scaling offset

这看起来应该很简单,但出于某种原因,我无法将自己的大脑包裹起来。我在"视口内有一个图像" div,其overflow属性设置为隐藏。

我已经使用jQuery UI实现了简单的缩放和平移,但是我无法让缩放看起来来自视口的中心。我在Photoshop上做了一个小小的截屏视频我试图重现的效果:http://dl.dropbox.com/u/107346/share/reference-point-zoom.mov

在PS中,您可以调整缩放参考点,对象将从该点开始缩放。显然这对HTML / CSS / JS来说是不可能的,所以我试图找到合适的左边和顶部CSS值来​​模仿效果。

以下是相关代码,删除了一些不必要的内容:

HTML

<div id="viewport">
    <img id="map" src="http://dl.dropbox.com/u/107346/share/fake-map.png" alt="" />
</div>

<div id="zoom-control"></div>

的javascript

$('#zoom-control').slider({
    min: 300,
    max: 1020,
    value: 300,
    step: 24,
    slide: function(event, ui) {
        var old_width = $('#map').width();
        var new_width = ui.value;
        var width_change = new_width - old_width;
        $('#map').css({
            width: new_width,

            // this is where I'm stuck...
            // dividing by 2 makes the map zoom
            // from the center, but if I've panned
            // the map to a different location I'd
            // like that reference point to change.
            // So instead of zooming relative to
            // the map image center point, it would
            // appear to zoom relative to the center
            // of the viewport. 
            left: "-=" + (width_change / 2),
            top: "-=" + (width_change / 2)
        });
    }
});

这是关于JSFiddle的项目:http://jsfiddle.net/christiannaths/W4seR/

3 个答案:

答案 0 :(得分:5)

这是工作解决方案。我将在下一次编辑中解释逻辑。

功能逻辑:

  • 摘要:请记住图像的中心位置,相对 宽度和高度的计算类似,我只解释height计算。详细解释只是函数逻辑的一个例子。可以在答案的底部找到具有不同变量名称的真实代码。

    1. 计算#map相对于#viewport的中心(x,y)。这可以通过使用offset()height()width()方法来完成。

      // Absolute difference between the top border of #map and #viewport
      var differenceY = viewport.offset().top - map.offset().top;
      // We want to get the center position, so add it.
      var centerPosition = differenceY + viewport.height() * 0.5;
      // Don't forget about the border (3px per CSS)
      centerPosition += 3;
      // Calculate the relative center position of #map
      var relativeCenterY = centerPosition / map.height();
      // RESULT: A relative offset. When initialized, the center of #map is at
      //  the center of #viewport, so 50% (= 0.5)
      // Same method for relativeCenterX
      
    2. 计算新的顶部和左侧偏移量:

      // Calculate the effect of zooming (example: zoom 1->2 = 2)
      var relativeChange = new_width / old_width;
      // Calculate the new height
      var new_height = relativeChange * old_height;
      // Calculate the `top` and `left` CSS properties.
      // These must be negative if the upperleft corner is outside he viewport
      // Add 50% of the #viewport's height to correctly position #map
      //   (otherwise, the center will be at the upperleft corner)
      var newTopCss = -relativeCenterY * new_height + 0.5 * viewport.height();
      
    3. 更改CSS属性

      map.css("top", newTopCss);
      

演示:http://jsfiddle.net/W4seR/12/

var map = $('#map');
var viewport = $('#viewport');
// Cache the size of the viewport (300x300)
var viewport_size = {
    x: viewport.width(),
    y: viewport.height()
};

map.draggable();

$('#zoom-control').slider({
    min: 300,
    max: 1020,
    value: 300,
    step: 24,
    create: function() {
        map.css({
            'width': 300,
            'left': 0,
            'top': 0
        });
    },
    slide: function(event, ui) {
        var old_width = map.width();
        var old_height = map.height();
        var viewport_offset = viewport.offset();
        var offset = map.offset();
        offset = {
            top: viewport_offset.top - offset.top + .5*viewport_size.y +3,
            left: viewport_offset.left - offset.left + .5*viewport_size.x +3
        };
        // Relative offsets, relative to the center!
        offset.top = offset.top / old_height;
        offset.left = offset.left / old_width;

        var new_width = ui.value;
        var relative = new_width / old_width;
        var new_height = relative * old_height;

        offset = {
            top: -offset.top * new_height + .5*viewport_size.y,
            left: -offset.left * new_width + .5*viewport_size.x
        };

        var css_properties = {
            width: new_width,
            left: offset.left,
            top: offset.top
        };

        map.css(css_properties);

        trace((map.position().left));
    }
});

答案 1 :(得分:1)

我一直依赖the kindness of strangers。相关变化:

// Calculate the offset as a percentage, accounting for the height of the window
var x_offset = ((map.position().left-150))/(old_width/2);
var y_offset = ((map.position().top-150))/(old_width/2);

var css_properties = {
    width: new_width,
    // Set the offset based on the existing percentage rather than 1/2
    // then readjust for the height of the window
    left: (new_width * x_offset /2 ) + 150 + "px", 
    top: (new_width * y_offset /2 ) + 150 + "px"
};

如果需要,用视口实例化设置的变量替换硬编码的150

答案 2 :(得分:0)

这是一个快速工作版本: http://jsfiddle.net/flabbyrabbit/chLkZ/

可能不是最好的解决方案,但似乎工作得很好,希望它有所帮助。

更新:对不起,这只适用于移动地图时缩放为0的情况。