使用CSS3变换比例缩放点

Bas*_*asj 13 css transition transform css3 css-transitions

即使以下代码片段看起来很短,我也会在几天内挣扎(对我感到羞耻!)找到一种方法来缩放仅使用CSS3 点击的点transform.它现在有效:

    var current = {x: 0, y: 0, zoom: 1}, c = document.getElementById('container');
    window.onclick = function(e) {
      wx = current.x + e.clientX / current.zoom;
      wy = current.y + e.clientY / current.zoom;
      var coef = e.ctrlKey ? 0.5 : 2;
      current.zoom *= coef;    
      current.x = wx - e.clientX / current.zoom; 
      current.y = wy - e.clientY / current.zoom; 
      c.style.transform = 'scale(' + current.zoom +') translate(' + (-current.x) + 'px,' + (-current.y) + 'px)';
    };
Run Code Online (Sandbox Code Playgroud)
    html, body { margin: 0; padding: 0; overflow: hidden; min-height: 100%; }
    #container { position: absolute; transform-origin: 0 0; transition-duration: 3s;}
    #item { position: absolute; left:0px; top:0px; }
Run Code Online (Sandbox Code Playgroud)
  <div id="container"><div id="item"><img src="http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png"></img></div></div>
Run Code Online (Sandbox Code Playgroud)

唯一的问题是转换是奇怪的,就像它首先翻译然后缩放一样; 它会产生一种奇怪的锯齿形状.在这种情况下如何顺利过渡CSS3?

在这里看到奇怪的过渡效果的动画GIF:http://gget.it/zf3fmwum/weirdtransition.gif

注意:点击的是缩放变换的固定点(例如:单击眼睛,图像被缩放,光标仍在眼睛上),就像在GoogleMaps-doubleclick-zoom中一样.

Peb*_*bbl 19

使用变换时要注意的一件事是您应用它们的顺序.如果你切换它scale,你会发现你的例子的工作方式有所不同translate.

这是一篇关于此事的有趣文章:

https://staff.washington.edu/fmf/2011/07/15/css3-transform-attribute-order/

我无法修复你的版本,主要是因为当你切换变换的顺序时它意外地行为异常.基本上你似乎遇到了奇怪的行为,因为音阶本身导致位置自动翻译,然后你也翻译......似乎这些不同的翻译以稍微不同的速度发生.

然而,我确实重新实现了一个有效的版本,并允许您在缩放之前进行翻译.按顺序保持转换似乎可以避免这个问题.

http://jsfiddle.net/fxpc5rao/32/

我已经修改了下面的版本,translate3D因为它对许多系统来说效果更好.

var current = {x: 0, y: 0, zoom: 1},
    con = document.getElementById('container');
    
window.onclick = function(e) {
    var coef = e.shiftKey || e.ctrlKey ? 0.5 : 2,
        oz = current.zoom,
        nz = current.zoom * coef,
        /// offset of container
        ox = 20,
        oy = 20,
        /// mouse cords
        mx = e.clientX - ox,
        my = e.clientY - oy,
        /// calculate click at current zoom
        ix = (mx - current.x) / oz,
        iy = (my - current.y) / oz,
        /// calculate click at new zoom
        nx = ix * nz,
        ny = iy * nz,
        /// move to the difference
        /// make sure we take mouse pointer offset into account!
        cx = mx - nx,
        cy = my - ny
    ;
    // update current
    current.zoom = nz;
    current.x = cx;
    current.y = cy;
    /// make sure we translate before scale!
    con.style.transform
        = 'translate3D('+cx+'px, '+cy+'px,0) '
        + 'scale('+nz+')'
    ;
};
Run Code Online (Sandbox Code Playgroud)
#container {
    position: absolute;
    left: 20px;
    top: 20px;
    width: 100%;
    height: 100%;
    transform-origin: 0 0 0;
    transition: transform 0.3s;
    transition-timing-function: ease-in-out;
    transform: translate3D(0,0,0) scale(1);
}

#item {
    position: absolute;
}
Run Code Online (Sandbox Code Playgroud)
<div id="container">
    <div id="item">
        <img src="http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png" />
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

更新

我已经更新了我的答案(以及上面的代码段)以考虑您的额外要求,您只需要修改计算以包括鼠标指针偏移的差异.

http://jsfiddle.net/fxpc5rao/33/

现在每次点击计算的未缩放位置之间的差异都会e.clientX, e.clientY被添加.这为您提供了在鼠标指针周围保持缩放平移所需的偏移量.关键变化在于:

cx = (ix + (e.clientX - ix) - nx),
cy = (iy + (e.clientY - iy) - ny)
Run Code Online (Sandbox Code Playgroud)

注意:因为你依赖e.clientX,e.clientY你会发现如果你#container离开它的当前0,0坐标会发生令人讨厌的异常.这可以完成,但您必须修改计算以将坐标本地化到最终的任何#container's位置.

更新2

好的电话@Basj,我不知道转换是以相反的顺序发生的,我会在你的评论中添加链接:

CSS3转换顺序很重要:最先操作

所以就像你说的,你需要的规模加工方面的翻译之前发生,但转化为可在实际转换值规模之前写的-如果是有道理的:)还没确切地知道为什么做一个其它结果前然而,在奇数插值中.

此外,我注意到有一个相当明显的优化 - 我敢肯定,正如你实现这一点,你会发现 - 没有必要添加一些东西只是为了以后减去它.我想那天我的节日欢呼太多了!

cx = e.clientX - nx,
cy = e.clientY - ny
Run Code Online (Sandbox Code Playgroud)

更新3

没问题@jdavies,这只是转换鼠标坐标的问题,因此它们相对于容器的左上角.如何计算此偏移量将完全取决于您的项目(使用jQuery.offset之类的东西,更容易获得图层的偏移量 - 跨浏览器).但是我已经更新了这个答案中的代码,考虑到使用绝对位置从0,0开始的硬编码/固定偏移 - 只是为了说明.这里还有一个更新的小提琴:

http://jsfiddle.net/fxpc5rao/5/

正如我们正在使用的那样clientX,clientY鼠标坐标将始终从浏览器窗口的左上角开始计算,使其成为页面的全局(忽略滚动).为了将它们本地化到容器中,您只需要减去容器的x和y位置.

Container at 0,0                Container at 80,100

+------+------- screen x 0      +--------------- 0
|      |                        |      
|      |                        |  +------+
|   x  | <-- mouse click        |  |x     | <-- mouse click
+------+     at 100,120         |  |      |     at 100,120
|                               |  |      |     but relative
|                               |  +------+     20,20
|                               |               so we us 20,20

0 screen y                      0
Run Code Online (Sandbox Code Playgroud)

#container还可以包含在其他元素中,你只是不得不再次考虑任何位置偏移这些元素给的#container.在下面的小提琴中,有一个#page-setting元素可以用保证金抵消所有内容,只要ox, oy变量使用保证金值更新一切应该表现.

http://jsfiddle.net/fxpc5rao/34/

注意:如果您将此系统放在可滚动页面中,您还需要将视口的滚动偏移添加到鼠标坐标,我在这里给出一个示例,但这很可能不是一个完整的跨浏览器解决方案.你最好看一下像jQuery这样的已建立的库来为你计算坐标和偏移量.