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

发布于 2024-12-23 14:20:17 字数 1722 浏览 2 评论 0 原文

这看起来应该很简单,但由于某种原因我无法完全理解它。我在“视口”div 中有一个图像,其溢出属性设置为隐藏。

我已经使用 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/

This seems like it should be quite simple, but for some reason I can't quite wrap my brain around it. I have an image inside a "viewport" div, of which the overflow property is set to hidden.

I've implemented a simple zooming and panning with jQuery UI, however I am having trouble getting the zoom to appear to originate from the center of the viewport. I did a little screencast from Photoshop the effect I'm trying to reproduce: http://dl.dropbox.com/u/107346/share/reference-point-zoom.mov

In PS you can adjust the scaling reference point an the object will scale from that point. Obviously this is not possible with HTML/CSS/JS, so I'm trying to find the appropriate left and top CSS values to mimic the effect.

Here is the code in question, with a few unnecessary bits removed:

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)
        });
    }
});

Here is the project on JSFiddle: http://jsfiddle.net/christiannaths/W4seR/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

回忆追雨的时光 2024-12-30 14:20:17

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

函数逻辑:

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

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

      //#map上边框与#viewport上边框的绝对差
      var DifferenceY = viewport.offset().top - map.offset().top;
      // 我们想要获取中心位置,所以添加它。
      var centerPosition = DifferenceY + viewport.height() * 0.5;
      // 不要忘记边框(每个 CSS 3px)
      中心位置 += 3;
      //计算#map的相对中心位置
      varrelativeCenterY = centerPosition / map.height();
      // 结果:相对偏移量。初始化时,#map的中心位于
      // #viewport 的中心,所以 50% (= 0.5)
      //与relativeCenterX相同的方法
      
    2. 计算新的顶部和左侧偏移量:

      //计算缩放的效果(例:zoom 1->2 = 2)
      varrelativeChange = new_width / old_width;
      // 计算新的高度
      var new_height =relativeChange * old_height;
      // 计算 `top` 和 `left` CSS 属性。
      // 如果左上角在视口之外,则这些值必须为负数
      // 添加 #viewport 高度的 50% 以正确定位 #map
      //(否则中心将位于左上角)
      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));
    }
});

Here's the working solution. I will explain the logic at the next edit.

Function Logic:

  • Summary: Remember the center position of the image, relatively.
    The calculations for width and height are similar, I will only explain the height calculation
    The detailled explanation is just an example of function logic. The real code, with different variable names can be found at the bottom of the answer.

    1. Calculate the center (x,y) of the #map, relative to #viewport. This can be done by using the offset(), height() and width() methods.

      // 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 new top and left offsets:

      // 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. Change the CSS property

      map.css("top", newTopCss);
      

Demo: 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));
    }
});
停顿的约定 2024-12-30 14:20:17

我一直依赖陌生人的善意。相关更改:

// 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 替换为视口实例化时设置的变量。

I have always relied on the kindness of strangers. Pertinent changes:

// 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"
};

Replace the hardcoded 150 with a variable set on viewport instantiation if necessary.

吝吻 2024-12-30 14:20:17

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

可能不是最简洁的解决方案,但似乎效果很好,希望有帮助。

更新:抱歉,只有当地图移动时缩放为 0 时,这才有效。

Here is a quick working version:
http://jsfiddle.net/flabbyrabbit/chLkZ/

Probably not the neatest solution but seems to work nicely, hope it helps.

Update: sorry this only works if zoom is 0 when the map is moved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文