使用 JavaScript 动画化属性值 |放大 SVG viewBox
Animating attribute values with JavaScript | Zooming in the SVG viewBox
我们有一个用 JavaScript 生成的 SVG 网格。
目标是当用户双击网格上的任何坐标时将 SVG 网格缩放 2 倍,并在先前的缩放状态和当前缩放状态之间进行短暂的动画转换。这实际上在我下面的代码片段中几乎 100% 正常,除了一个问题:
我可以设置缩放级别的动画,但不能很好地设置 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 坐标,而没有动画到它。然后它放大现在居中的坐标。
目标是 x 和 y 随缩放平滑过渡。
我在 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
您的缩放功能似乎不必要地复杂。它具有我不理解的看似任意的方程常数,并且您正在以我看不到目的的方式操纵坐标。
对于下面的版本,我只是将 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 生成的 SVG 网格。
目标是当用户双击网格上的任何坐标时将 SVG 网格缩放 2 倍,并在先前的缩放状态和当前缩放状态之间进行短暂的动画转换。这实际上在我下面的代码片段中几乎 100% 正常,除了一个问题:
我可以设置缩放级别的动画,但不能很好地设置 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 坐标,而没有动画到它。然后它放大现在居中的坐标。
目标是 x 和 y 随缩放平滑过渡。
我在 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
您的缩放功能似乎不必要地复杂。它具有我不理解的看似任意的方程常数,并且您正在以我看不到目的的方式操纵坐标。
对于下面的版本,我只是将 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>