代码之家  ›  专栏  ›  技术社区  ›  basement

使用JavaScript设置属性值动画|在SVG视口中缩放

  •  0
  • basement  · 技术社区  · 7 年前

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

    查看下面的代码段(最好是全屏),然后双击网格几次。

    '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坐标,而不会对其设置动画。然后放大现在居中的坐标。

    我已经在这个片段链接到的单独文件中隐藏了很多我认为不相关的代码 codepen . 如果您不想复制和粘贴源代码,请查看以下列表:

    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

    1 回复  |  直到 7 年前
        1
  •  1
  •   Paul LeBeau    7 年前

    对于下面的版本,我只是将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>