使用CSS3变换缩放放大点 [英] Zooming on a point with CSS3 transform scale

查看:160
本文介绍了使用CSS3变换缩放放大点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

即使以下代码段似乎短暂,我也在几天内挣扎(羞愧我!)找到一种方法来缩放点击的点仅使用CSS3 transform 。现在可以使用:



  var current = {x:0,y:0,zoom:1},c = document.getElementById '); 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)'; };  

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

 < div id =container> ; div id =item>< img src =http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png>< / img>< / div>< ; / div>  



过渡是奇怪的,就好像它先翻译然后放大;它产生奇怪的锯齿效果。如何在这种情况下实现平滑的CSS3转换?



查看动画GIF =http://gget.it/zf3fmwum/weirdtransition.gif =nofollow> http://gget.it/zf3fmwum/weirdtransition.gif



注意:点击的点是缩放变换的固定点(例如:点击眼睛,图像缩放,光标仍在

解决方案

使用转换时需要注意的一点是您应用它们的顺序。如果你切换 scale translate ,你会发现你的示例工作有所不同。



这是一篇有趣的文章:



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



我无法修复您的版本,主要是因为当您切换转换的顺序时它意外地发生错误。基本上看来,你正在陷入奇怪的行为,因为规模本身导致自动翻译的位置,然后你也翻译...似乎这些不同的翻译是以一个略有不同的速度发生。



然而我没有重新实现一个工作的版本,并允许您在缩放之前翻译。保持这种顺序的转换似乎避免了这个问题。



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,///容器的偏移量ox = 20,oy = 20,///鼠标线mx = e.clientX- ox,my = e.clientY -  oy,///计算当前缩放点击ix =(mx  -  current.x)/ oz,iy =(my  -  current.y)/ oz,///计算点击新缩放nx = ix * nz,ny = iy * nz,///移动到差分///确保我们把鼠标指针偏移到帐户! cx = mx-nx,cy = my-ny; // update current current.zoom = nz; current.x = cx; current.y = cy; ///确保我们在规模之前翻译! con.style.transform ='translate3D('+ cx +'px,'+ cy +'px,0)'+'scale('+ nz +')';};  

  #container {position:absolute; left:20px; top:20px; width:100%;高度:100%; transform-origin:0 0 0; transition:transform 0.3s;过渡定时功能:易于进入; transform:translate3D(0,0,0)scale(1);}#item {position:absolute;}  

 < div id =container> < div id =item> < img src =http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png/> < / div>< / div>  



h2>

我更新了我的答案(和上面的代码片段)以考虑到您的额外要求,您只需要修改计算,以包括鼠标指针偏移的差异。 / p>

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



现在每次点击计算出的非标定位置与 e.clientX,e.clientY 。这给你需要保持放大的翻译发生在鼠标指针周围的偏移量。关键变化如下:

  cx =(ix +(e.clientX  -  ix) -  nx),
cy =(iy +(e.clientY - iy)-ny)




注意:由于您依靠 e.clientX e.clientY 如果您将 #container 从当前的 0,0 坐标移开,就会发生恼人的偏移。这可以做,但你必须修改你的计算以将坐标本地化为#container's 位置。




更新2



好的调用@Basj,我不知道转换发生的顺序相反,请在此处添加您的评论中的链接:



CSS3转换顺序问题:最右边的操作第一



正如你所说,你需要在翻译处理术语,而是在实际转换值之前的缩放之前写入的翻译。如果这是有意义的:)仍然不是确切确定为什么在另一个之前做一个奇怪的插值。



注意到有一个相当明显的优化—我相信,当你实现这一点,你会发现—没有点添加的东西只是减去它以后。我想我那一天会有太多的节日欢呼!

  cx = e.clientX  -  nx,
cy = e.clientY - ny



更新3



@jdavies没有问题,只是转换您的鼠标坐标,因此它们相对于容器的左上角。如何计算这个偏移将完全取决于你的项目(使用 jQuery.offset )。然而,我已经更新了这个答案中的代码,以考虑到硬编码/固定偏移量从0,0使用位置绝对—只是为了说明。这是一个更新的小提琴:



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



由于我们使用 clientX clientY 的鼠标坐标总是从浏览器窗口的左上角计算,使它们全局到页面(忽略滚动)。为了将它们本地化到容器,你只需要减去容器x和y的位置。

 容器在0,0集装箱在80,100 

+ ------ + -------屏幕x 0 + --------------- 0
| | |
| | | + ------ +
| x | < - 鼠标点击| | x | < - 鼠标点击
+ ------ + at 100,120 | | | at 100,120
| | | |但相对
| | + ------ + 20,20
| |所以我们20,20

0屏幕y 0

c $ c> #container 也可以包含在其他元素中,您只需要考虑这些元素给 #container 。在下面的小提琴中有一个#page-setting 元素,它抵消了所有带边距的元素,只要 ox,oy

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


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



Even if the following code snippet seems short, I struggled during days (shame on me!) to find a way to zoom on the point that is clicked using only CSS3 transform. It works now:

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

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

  <div id="container"><div id="item"><img src="http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png"></img></div></div>

The only problem is that the transition is weird, like if it first translates and then zooms ; it produces a weird zigzag effet. How to have a smooth CSS3 transition in this case?

See animated GIF here of the weird transition effect: http://gget.it/zf3fmwum/weirdtransition.gif

Note: the point which is clicked on is a fixed point of the scaling transform (example: click on the eye, the image is zoomed, and the cursor is still on the eye), like in GoogleMaps-doubleclick-zooming.

解决方案

One thing to watch out for when using transforms is the order that you apply them. You'll find your example works rather differently if you switch the scale and the translate around.

Here is an interesting article on the matter:

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

I wasn't able to repair your version, mainly because it misbehaves unexpectedly when you switch the order of the transforms. Basically it seems you are running into odd behaviour because the scale itself causes an automatic translation in position, and then you also translate... and it seems these different translations are occurring at a slightly different pace.

I did however re-implement a version that works, and allows you to translate before scaling. Keeping the transforms in this order seems to avoid the issue.

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

I've modified the version below to use translate3D just because it performs better for many systems.

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

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

<div id="container">
    <div id="item">
        <img src="http://fadili.users.greyc.fr/demos/WaveRestore/EMInpaint/parrot_original.png" />
    </div>
</div>

update

I've updated my answer (and the snippet above) to take into account your additional requirement, you just need to modify the calculation to include the difference in mouse pointer offset.

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

Now with every click the difference between the calculated unscaled position and e.clientX, e.clientY is added. This gives you the offset you need to keep the zoomed translation occurring around the mouse pointer. The key change is here:

cx = (ix + (e.clientX - ix) - nx),
cy = (iy + (e.clientY - iy) - ny)

NOTE: Because you are relying on e.clientX and e.clientY you will find annoying offseting will occur if you move #container away from its current 0,0 coordinate. This can be done, but you will have to modify your calculations to localise the coordinates to whatever #container's location ends up being.

update 2

Good call @Basj, I wasn't aware that the transformations occurred in reverse order, I'll add the link in from your comment here:

CSS3 transform order matters: rightmost operation first

So as you say, you require the scale to occur before the translate in processing terms, but the translate to be written before the scale in the actual transform value — if that makes sense :) Still not exactly sure why doing one before the other results in the odd interpolation however.

Also, I've noticed there is a rather obvious optimisation — which I'm sure, as you are implementing this, you will have spotted — no point adding something only to subtract it later. I guess I'd just had too much festive cheer that day!

cx = e.clientX - nx,
cy = e.clientY - ny

update 3

No problem @jdavies, it is just a matter of converting your mouse coordinates so they are relative to the container's top left. How you calculate this offset will depend entirely on your project (it is much easier to get a layer's offset — cross browser — using something like jQuery.offset). However I've updated the code in this answer to take into account a hard-coded/fixed offset away from 0,0 using position absolute — just to illustrate. Here is an updated fiddle too:

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

As we are using clientX and clientY the mouse coordinates will always be calculated from the top left of the browser window, making them global to the page (disregarding scrolling). In order to localise them to the container, you just need to subtract the containers x and y position.

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

The #container can also be contained within other elements, you just again have to take into account any positional offset these elements give to the #container. In the following fiddle there is a #page-setting element that is offsetting everything with margin, as long as the ox, oy variables are updated with the margin values everything should behave.

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

NOTE: If you place this system inside a scrollable page you will also need to add the viewport's scroll offsets to the mouse coordinates, I give an example here, but this is most likely not a full cross browser solution. You are better off looking at an established library like jQuery to calculate coordinates and offsets for you.

这篇关于使用CSS3变换缩放放大点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆