【问题标题】:Animating attribute values with JavaScript | Zooming in the SVG viewBox使用 JavaScript 动画属性值 |放大 SVG 视图框
【发布时间】:2017-09-05 17:36:59
【问题描述】:

我们有一个用 JavaScript 生成的 SVG 网格。

目标是当用户双击网格上的任何坐标时将 SVG 网格放大 2 倍,并在先前的缩放状态和当前缩放状态之间进行短暂的动画转换。这实际上在我下面的 sn-p 中几乎 100% 正常工作,除了一个问题:

我可以为缩放级别设置动画,但不能很好地平滑 X 和 Y 坐标转换。

查看下面的 sn-p(最好全屏)并双击网格几次。

'use strict'
function zoom( evt ){
  var loc = getCoords( evt ),
      newX = loc.x / 0.8 - 12.5,
      newY = loc.y / 0.8 - 12.5,
      grid = document.getElementById( 'grid' ),
      viewBoxAttr = grid.getAttribute( 'viewBox' ),
      viewBoxAry = viewBoxAttr.split( ' ' ),
      curX = viewBoxAry[ 0 ], curY = viewBoxAry[ 1 ],
      curZm = viewBoxAry[ 2 ], dblZm = curZm / 2,
      tweenZm = curZm, diffX = 0,
      interval = setInterval( 
        function(){
          if( tweenZm >= dblZm ){
            tweenZm = tweenZm / 1.015625;
            diffX = newX - curX;
          }
          else {
            clearInterval( interval );
          }
          zmOnPt( newX, newY, tweenZm );
        },
        10
      ),
      ary = [];
  
  ary.push( curZm );
  ary.push( dblZm );
}

var grid = document.getElementById( 'grid' );
grid.addEventListener( 'dblclick', zoom );

createLines( '.h-lns' ); createLines( '.v-lns' );
createLabels( '.h-num' ); createLabels( '.v-num' );
recalibrate();
<head>
  <link id="main" rel="stylesheet" 
         href="https://codepen.io/basement/pen/brJLLZ.css"
  >
  <link id="animations" rel="stylesheet"
        href="https://codepen.io/basement/pen/zdXRWo.css"
  >
</head>
<body id="body">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="cntr" id="grid">
    <script id="injectGrid" xlink:href="https://codepen.io/basement/pen/brJLLZ.js">
    </script> 
    <g id="drawing">  
      <circle cx="60" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
      <circle cx="70" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
      <path
        fill="none" opacity="0.5" stroke="#0dd" stroke-width="0.5"
        d="
          M60, 40
          A10, 10
           0,
           0, 1
           70, 50
          C70, 55
           65, 60
           60, 60
          Q50, 60
           50, 50
          T55, 35
          T70, 40
        "
      />      
    </g>
  </svg>
  <script id="sidebar" src="https://codepen.io/basement/pen/zdXRWo.js"></script>
  <script id="main" src="https://codepen.io/basement/pen/yorjXq.js"></script>
</body>

注意到平滑的缩放动画加上不和谐的 x 和 y 平移了吗? viewBox 只是跳到您单击的 X 和 Y 坐标,而没有动画到它。然后它会放大现在居中的坐标。

目标是让 x 和 y 随缩放平滑过渡。

我已经隐藏了很多我认为在单独文件中无关的代码,这个 sn-p 链接到 codepen。如果你想看那些不复制和粘贴源代码的,这里有一个列表:

主要 CSS: https://codepen.io/basement/pen/brJLLZ.css

动画 CSS: https://codepen.io/basement/pen/zdXRWo.css

网格创建 JS: https://codepen.io/basement/pen/brJLLZ.js

边栏代码: https://codepen.io/basement/pen/zdXRWo.js

主要的 JAVASCRIPT: https://codepen.io/basement/pen/yorjXq.js

【问题讨论】:

  • 您的代码对我不起作用。我收到错误“未捕获的 ReferenceError:未定义 getCoords”。
  • @PaulLeBeau 感谢您指出这一点,我不小心保存了我的一些 codepen 链接,并且我已经更新了它们。当你现在尝试它时,它有效吗?

标签: javascript html svg coordinates zooming


【解决方案1】:

您的缩放功能似乎过于复杂。它具有我不理解的看似任意的方程常数,并且您正在以我看不到目的的方式操纵坐标。

对于下面的版本,我只是将 viewBox 的宽度和高度减半,然后以单击鼠标的坐标为中心。然后,对于动画,我只是从旧的 viewBox 值到新的值进行线性插值。

function zoom( evt ) {
  var loc = getCoords( evt ),
      grid = document.getElementById( 'grid' ),
      viewBoxAttr = grid.getAttribute( 'viewBox' ),
      viewBoxAry = viewBoxAttr.split(' ');
  var oldX = parseFloat(viewBoxAry[0]);
  var oldY = parseFloat(viewBoxAry[1]);
  var oldWidth = parseFloat(viewBoxAry[2]);
  var oldHeight = parseFloat(viewBoxAry[3]);
  var newWidth = oldWidth / 2;  // Halving the view width => zoom X2
  var newHeight = oldHeight / 2;
  var newX = loc.x - newWidth / 2;
  var newY = loc.y - newHeight / 2;
  var animProgress = 0;  // Goes from 0 to 1
  var animStep = 0.02;   // Change in animProgress per interval function invocation.

  var interval = setInterval( function() {
    animProgress += animStep;
    if (animProgress > 1)
      animProgress = 1;
    // Calculate a new viewBox corresponding to our animation progress
    var nextViewBox = [
      oldX + animProgress * (newX - oldX),
      oldY + animProgress * (newY - oldY),
      oldWidth + animProgress * (newWidth - oldWidth),
      oldHeight + animProgress * (newHeight - oldHeight)
    ];
    grid.setAttribute("viewBox", nextViewBox.join(' '));
    if (animProgress >= 1)
      clearInterval( interval );
  }, 10);
}

'use strict'
function zoom( evt ) {
  var loc = getCoords( evt ),
      grid = document.getElementById( 'grid' ),
      viewBoxAttr = grid.getAttribute( 'viewBox' ),
      viewBoxAry = viewBoxAttr.split(' ');
  var oldX = parseFloat(viewBoxAry[0]);
  var oldY = parseFloat(viewBoxAry[1]);
  var oldWidth = parseFloat(viewBoxAry[2]);
  var oldHeight = parseFloat(viewBoxAry[3]);
  var newWidth = oldWidth / 2;
  var newHeight = oldHeight / 2;
  var newX = loc.x - newWidth / 2;
  var newY = loc.y - newHeight / 2;
  var animProgress = 0;  // Goes from 0 to 1
  var animStep = 0.02;   // Change in animProgress per interval function invocation.
  
  var interval = setInterval( function() {
    animProgress += animStep;
    if (animProgress > 1)
      animProgress = 1;
    // Calculate a new viewBox corresponding to our animation progress
    var nextViewBox = [
      oldX + animProgress * (newX - oldX),
      oldY + animProgress * (newY - oldY),
      oldWidth + animProgress * (newWidth - oldWidth),
      oldHeight + animProgress * (newHeight - oldHeight)
    ];
    grid.setAttribute("viewBox", nextViewBox.join(' '));
    if (animProgress >= 1)
      clearInterval( interval );
  }, 10);
}

var grid = document.getElementById( 'grid' );
grid.addEventListener( 'dblclick', zoom );

createLines( '.h-lns' ); createLines( '.v-lns' );
createLabels( '.h-num' ); createLabels( '.v-num' );
recalibrate();
<head>
  <link id="main" rel="stylesheet" 
         href="https://codepen.io/basement/pen/brJLLZ.css"
  >
  <link id="animations" rel="stylesheet"
        href="https://codepen.io/basement/pen/zdXRWo.css"
  >
</head>
<body id="body">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" class="cntr" id="grid">
    <script id="injectGrid" xlink:href="https://codepen.io/basement/pen/brJLLZ.js">
    </script> 
    <g id="drawing">  
      <circle cx="60" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
      <circle cx="70" cy="40" r="0.5" fill="#0dd" opacity="0.9" />
      <path
        fill="none" opacity="0.5" stroke="#0dd" stroke-width="0.5"
        d="
          M60, 40
          A10, 10
           0,
           0, 1
           70, 50
          C70, 55
           65, 60
           60, 60
          Q50, 60
           50, 50
          T55, 35
          T70, 40
        "
      />      
    </g>
  </svg>
  <script id="sidebar" src="https://codepen.io/basement/pen/zdXRWo.js"></script>
  <script id="main" src="https://codepen.io/basement/pen/yorjXq.js"></script>
</body>

【讨论】:

  • 这太神奇了。谢谢我还在学习 JavaScript 和我喜欢这个答案的简单性的东西。
猜你喜欢
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
  • 2015-11-10
  • 2019-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
相关资源
最近更新 更多