简体   繁体   中英

Animating attribute values with JavaScript | Zooming in the SVG viewBox

We have a SVG grid generated with JavaScript.

The goal is to 2X zoom the SVG grid when the user double clicks on any coordinate on the grid and have a short animation transition between the previous zoom state and current zoom state. This actually works almost 100% fine in my snippet below except one problem:

I can animate the level of zoom but cannot smoothly animate the X and Y coordinate transitions well.

View the snippet below ( preferably in full screen ) and double click on the grid a few times.

 '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> 

Notice the smooth zoom animation coupled with the jarring x and y translation? The viewBox just skips to the X and Y coordinate you clicked without animating over to it. Then it zooms in on the now centered coordinates.

The goal is for the x and y to transition smoothly with the zoom.

I've hidden a lot of the code I think is irrelevant in separate files this snippet links to on codepen . If you want to see those without copy and pasting the source code though, here is a list:

MAIN CSS: https://codepen.io/basement/pen/brJLLZ.css

ANIMATION CSS: https://codepen.io/basement/pen/zdXRWo.css

GRID CREATION JS: https://codepen.io/basement/pen/brJLLZ.js

SIDEBAR CODE: https://codepen.io/basement/pen/zdXRWo.js

MAIN JAVASCRIPT: https://codepen.io/basement/pen/yorjXq.js

Your zoom function seems unnecessarily complicated. It has seemingly arbitrary equation constants that I don't understand, and you are manipulating coordinates in a way that I don't see a purpose for.

For the version below, I am just halving the viewBox width and height, then centering that on the coordinates where you click the mouse. Then, for the animation, I just do a linear interpolation from the old viewBox values to the new ones.

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> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM