기본 SVG 애니메이션

중급 SVG 애니메이션의 전제 조건인 이 항목에서는 기본 SVG 애니메이션을 설명합니다. HTML 및 JavaScript에 대한 기본 지식이 있다고 가정합니다. 이 항목에서 제공하는 자료를 완전히 이해하려면 약 1시간 정도 걸립니다.

참고  이 항목에 포함된 예제를 보려면 SVG 요소를 지원하는 브라우저(예: Windows Internet Explorer 9 이상)를 사용해야 합니다.

Windows Internet Explorer에서 지원되지는 않지만(아래에서 설명), SVG의 선언적 애니메이션 구조(SMIL)를 사용할 때 기본 애니메이션은 간단합니다. 예를 들어 다음 코드는 사각형을 5초 간격으로 90도씩 회전시킵니다.

예제 1 - 기본 선언적(SMIL) 애니메이션

라이브 링크: 예제 1(이 SMIL 예제는 Internet Explorer에서 작동하지 않습니다.)


<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg width="800px" height="800px" viewBox="0 0 800 800"
     version="1.1" xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"> <!-- Note that this is required in order to use xlink in the <use> element. -->

  <!-- THIS EXAMPLE NOT SUPPORTED IN INTERNET EXPLORER -->
  
  <title>Simplest SVG Animation</title>
  <desc>SVG declarative animation is used to rotate a square.</desc>

  <!-- Create a Cartesian coordinate system (with the y-axis flipped) for the animated square. 
       That is, place the origin at the center of the 800 x 800 SVG viewport. -->
  <g transform="translate(400, 400)"> 
  
    <!-- A 200 x 200 square with the upper left-hand corner at (-100, -100). This places the center 
         of the square at the origin (0, 0): -->  
    <rect x="-100" y="-100" width="200" height="200" rx="5" ry="5" 
          style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;">
      <animateTransform 
        attributeType="xml"
	    attributeName="transform" type="rotate"
	    from="0" to="90"
	    begin="0" dur="5s" 
	    fill="freeze"
	  />
    </rect>
    
    <line x1="-400" y1="0" x2="400" y2="0" style="stroke: black;" /> <!-- Represents the x-axis. -->
    
    <line x1="0" y1="-400" x2="0" y2="400" style="stroke: black;" /> <!-- Represents the y-axis (although up is negative and down is positive). -->  
        
  </g>
</svg>

이전 예제와 모든 다음 예제에서는 주석이 잘 처리되어 있습니다. 그러나 몇 가지 유용한 내용을 살펴 보겠습니다.

  • 애니메이션할 개체(animateTransform)의 하위인 rect 요소는 모든 힘든 일을 수행하며 비교적 별도의 설명이 필요 없습니다.

  • 사각형은 중심점이 (400, 400)에서 뷰포트의 원점과 만나도록 배치되므로 사각형은 중심점을 중심으로 90도 회전합니다. 예를 들어 다음과 같이 사각형이 x="0" 및 y="0"으로 정의된 경우:

    
    <rect x="0" y="0" width="200" height="200" rx="5" ry="5" style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;">
    
    

    사각형의 왼쪽 위 모서리(중심과 반대)가 원점을 중심으로 90도 회전합니다. 한 번 시도해 보세요.

선언적 애니메이션은 간단하지만 제한적일 수도 있습니다.. SVG Essentials의 저자인 David Eisenberg는 다음과 같이 말합니다. "스크립팅을 사용하여 애니메이션을 만들려는 경우 스크립팅의 모든 상호 작용 기능을 사용할 수 있습니다. 마우스 위치 또는 여러 변수가 관련된 복잡한 조건에 의존하는 애니메이션을 만들 수 있습니다."

즉, 스크립트 기반 애니메이션을 사용하면 복잡한 애니메이션뿐만 아니라 단순한 애니메이션도 만들 수 있습니다. 이런 이유와 CSS 애니메이션 등의 다른 이유로 Internet Explorer에서는 선언적 애니메이션을 지원하지 않습니다. 분명히 스크립트 기반 애니메이션과 관련된 작업이 더 많기도 하지만 이 스크립팅 방법을 완전히 익히면 선언적 애니메이션 방법만으로는 구현할 수 없는 애니메이션을 구현할 수 있습니다. 다음 예제는 예제 1의 JavaScript 버전(HTML5에서)으로서, 이러한 방법 중 일부를 보여 줍니다.

예제 2 - 기본 JavaScript 애니메이션

라이브 링크: 예제 2


<!DOCTYPE html>
<html>

<head>  
  <title>JavaScript SVG Animation</title>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge"/> <!-- Remove this line in production. -->
</head>

<body>
  <svg width="800px" height="800px" viewBox="0 0 800 800">
    <g transform="translate(400, 400)"> <!-- Create a Cartesian coordinate system (with the y-axis flipped) for the animated square. That is, place the origin at the center of the 800 x 800 SVG viewport: -->
  
      <!-- A 200 x 200 square with the upper left-hand corner at (-100, -100). This places the center of the square 
      at the origin (0, 0): -->  
      <rect id="mySquare" x="-100" y="-100" width="200" height="200" rx="5" ry="5" 
            style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;" />
            
      <!-- Represents the x-axis: -->
      <line x1="-400" y1="0" x2="400" y2="0" style="stroke: black;" /> 
    
      <!-- Represents the y-axis (although up is negative and down is positive): -->  
      <line x1="0" y1="-400" x2="0" y2="400" style="stroke: black;" /> 
                
    </g>
  </svg>
  <script>
    "use strict";

    /* CONSTANTS */
    var initialTheta = 0; // The initial rotation angle, in degrees.
    var thetaDelta = 0.3; // The amount to rotate the square about every 16.7 milliseconds, in degrees.
    var angularLimit = 90; // The maximum number of degrees to rotate the square.
    var theSquare = document.getElementById("mySquare");

    theSquare.currentTheta = initialTheta; // The initial rotation angle to use when the animation starts, stored in a custom property.

    var requestAnimationFrameID = requestAnimationFrame(doAnim); // Start the loop.
    function doAnim() {
      if (theSquare.currentTheta > angularLimit) {
        cancelAnimationFrame(requestAnimationFrameID); // The square has rotated enough, instruct the browser to stop calling the doAnim() function.
        return; // No point in continuing, bail now.
      }

      theSquare.setAttribute("transform", "rotate(" + theSquare.currentTheta + ")"); // Rotate the square by a small amount.
      theSquare.currentTheta += thetaDelta;  // Increase the angle that the square will be rotated to, by a small amount.
      requestAnimationFrameID = requestAnimationFrame(doAnim); // Call the doAnim() function about 60 times per second (60 FPS), or about once every 16.7 milliseconds until cancelAnimationFrame() is called.
    }
  </script>
</body>
</html>

중요  <meta http-equiv-"X-UA-Compatible" content="IE9" /> 또는 <meta http-equiv-"X-UA-Compatible" content="Edge" /><head> 블록 내에 포함하는 것이 아니라 X-UA-Compatible HTTP 헤더를 IE=Edge와 함께 보내도록 웹 개발 서버를 구성하면 인트라넷에서 개발 중인 경우 최신 표준 모드에서 실행 중인지 확인할 수 있습니다.

전통적인 "만화" 애니메이션의 기본을 이해하고 있다면 스크립트 기반 애니메이션은 실제로 비교적 단순합니다. 알고 있겠지만 애니메이션은 단지 점차적으로 바뀌고 빨리 연달아 표시되는 일련의 정지 이미지에 불과합니다.

애니메이션 정지 이미지

이러한 6개의 이미지가 충분히 빠르게 연속으로 표시되면 튀어 오르는 공을 보게 됩니다.

튀어 오르는 공 애니메이션 GIF 파일

여기서 튀어 오르는 공 애니메이션은 각 이미지를 100밀리초 동안 표시한 다음 다음 이미지를 표시하는 방식으로 6개의 이미지를 반복적으로 표시하여 생성한 것입니다. 같은 개념이 스크립트 기반 애니메이션에서 사용됩니다. 예제 2의 코드에서는 몇 밀리초마다 이미지를 점차적으로 변경하는 함수를 호출합니다. 특히, requestAnimationFrame을 사용하여 doAnim 함수를 16.7밀리초(약 60FPS) 간격으로 호출하도록 브라우저에 지정합니다. doAnim 함수는 호출될 때마다 사각형을 조금씩 회전합니다. doAnim이 몇 밀리초마다 계속 호출되므로 사각형이 매끄럽게 회전하는 것처럼 보입니다. 사각형이 angularLimit도(이 예제에서는 90°) 회전한 후 cancelAnimationFrame을 호출하여 브라우저에 doAnim 호출을 중지하도록 지정하면 애니메이션이 멈춥니다(자세한 내용은 코드 설명 참조).

더 복잡한 예제로 진행하기 전에 두 가지 JavaScript 코딩 스타일을 살펴 보는 것이 중요합니다.

  • DOM L2 스크립팅
  • SVG DOM 스크립팅

DOM L2 스크립팅은 "전통적인" 스크립팅이며, 다음과 같이 다양한 항목을 설정하기 위해 "값 문자열"을 만들어 구체화됩니다.


theSquare.setAttribute("transform", "rotate(" + theSquare.currentTheta + ")");

SVG DOM 스크립팅은 "값 문자열"이 없고 일반적으로 요소 값을 숫자로 설정하는 방식을 보여줍니다.


mySquare.transform.baseVal.getItem(0).setRotate(mySquare.currentTheta, 0, 0);

두 코드 줄 모두 정확히 같은 작업을 수행합니다. 두 코드 줄의 사소한 차이점은 setRotate 메서드에는 개체가 회전할 때 기준으로 삼는 중심점(이 경우에는 중심점(0, 0))을 의미하는 두 개의 값이 필요하다는 점뿐입니다. SVG DOM 스크립팅 스타일의 장점은 “값 문자열”을 빌드할 필요가 없다는 점이고 이 스크립팅은 DOM L2 스크립팅보다 성능을 향상시킬 수 있습니다.

다음 예제는 예제 2의 SVG DOM 스크립팅 버전입니다.

예제 3 - SVG DOM 스크립팅

라이브 링크: 예제 3


<!DOCTYPE html>
<html>

<head>  
  <title>JavaScript SVG Animation</title>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> <!-- Remove this line in production. -->
</head>

<body>
  <svg id="svgElement" width="800px" height="800px" viewBox="0 0 800 800"> <!-- Give the svg element a name so that we can easily access it via JavaScript. -->

    <g transform="translate(400, 400)"> <!-- Create a Cartesian coordinate system (with the y-axis flipped) for the animated square. That is, place the origin at the center of the 800 x 800 SVG viewport: -->
  
      <!-- A 200 x 200 square with the upper left-hand corner at (-100, -100). This places the center of the square 
      at the origin (0, 0). Give the square a name so we can easily access it via JavaScript: -->  
      <rect id="mySquare" x="-100" y="-100" width="200" height="200" rx="5" ry="5"
            style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;" />
            
      <!-- Represents the x-axis: -->
      <line x1="-400" y1="0" x2="400" y2="0" style="stroke: black;" /> 
    
      <!-- Represents the y-axis (although up is negative and down is positive): -->  
      <line x1="0" y1="-400" x2="0" y2="400" style="stroke: black;" /> 
                
    </g>
  </svg>
  <script>
    "use strict";

    /* CONSTANTS */
    var initialTheta = 0; // The initial rotation angle, in degrees.
    var thetaDelta = 0.3; // The amount to rotate the square about every 16.7 milliseconds, in degrees.
    var angularLimit = 90; // The maximum number of degrees to rotate the square.

    /* GLOBALS */
    var requestAnimationFrameID;
    var mySquare = document.getElementById("mySquare");
    var transformObject;

    mySquare.currentTheta = initialTheta; // The initial rotation angle to use when the animation starts, stored in a custom property.
    transformObject = svgElement.createSVGTransform(); // Create a generic SVG transform object so as to gain access to its methods and properties, such as setRotate().
    mySquare.transform.baseVal.appendItem(transformObject); // Append the transform object to the square object, now the square object has inherited all the transform object's goodness.

    requestAnimationFrameID = requestAnimationFrame(doAnim); // Start the animation loop.
    function doAnim() {
      var transformObject;

      if (mySquare.currentTheta > angularLimit) {
        cancelAnimationFrame(requestAnimationFrameID); // Instruct the browser to stop calling requestAnimationFrame()'s callback.
        return;
      }

      mySquare.transform.baseVal.getItem(0).setRotate(mySquare.currentTheta, 0, 0); // Access the transform object (that was appended to mySquare in the init() function) and use its setRotate method to rotate the square about the point (0, 0) (which is at the center of the SVG viewport).
      mySquare.currentTheta += thetaDelta; // Place this line here so that the square isn't over rotated on the last call to doAnim().
      requestAnimationFrameID = requestAnimationFrame(doAnim); // Call the doAnim() function about every 60 times per second (i.e., 60 FPS).
    }
  </script>
</body>
</html>

예제 3의 가장 어려운 부분은 다음 두 줄일 것입니다.


transformObject = svgElement.createSVGTransform(); 
mySquare.transform.baseVal.appendItem(transformObject);

첫 번째 줄은 일반적인 변형 개체를 만듭니다. 두 번째 줄은 이 변형 개체를 mySquare 노드에 연결합니다. 이렇게 하면 mySquare 개체가 변형 개체와 관련된 모든 메서드와 속성, 특히 setRotate 메서드를 상속합니다. 이 작업이 완료된 후 다음과 같이 새로 얻은 setRotate 메서드를 호출하여 mySquare 사각형을 회전할 수 있습니다.


mySquare.transform.baseVal.getItem(0).setRotate(mySquare.currentTheta, 0, 0);

중요하지 않지만 "no-op" 변형 특성을 rect 요소(예: transform="matrix(1 0 0 1 0 0)")에 추가하는 방법으로 변형 개체의 생성 및 추가를 피할 수도 있습니다.


<rect id="mySquare" x="-100" y="-100" width="200" height="200" rx="5" ry="5" 
          transform="matrix(1 0 0 1 0 0)"
          style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;" />

이 코드는 먼저 즉석에서 변형 개체를 만들고 추가하지 않고 조작할 수 있는 직사각형 요소에서 변형 개체를 만듭니다. 전체 예제는 다음과 같습니다.

예제 4 - No-op 변형 개체

라이브 링크: 예제 4


<!DOCTYPE html>
<html>

<head>  
  <title>JavaScript SVG Animation</title>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge" /> <!-- Remove this line in production. -->
</head>

<body>
  <svg width="800px" height="800px" viewBox="0 0 800 800">

    <g transform="translate(400, 400)"> <!-- Create a Cartesian coordinate system (with the y-axis flipped) for the animated square. That is, place the origin at the center of the 800 x 800 SVG viewport: -->
  
      <!-- A 200 x 200 square with the upper left-hand corner at (-100, -100). This places the center of the square 
      at the origin (0, 0). Note that the no-op transform attribute is necessary to generate a transform object 
      such that the setRotate() method can be utilized in the doAnim() function: -->  
      <rect id="mySquare" x="-100" y="-100" width="200" height="200" rx="5" ry="5" 
            transform="matrix(1 0 0 1 0 0)"
            style=" fill: orange; stroke: black; stroke-width: 3; stroke-dasharray: 10, 5;" />

      <!-- Represents the x-axis: -->
      <line x1="-400" y1="0" x2="400" y2="0" style="stroke: black;" /> 
    
      <!-- Represents the y-axis (although up is negative and down is positive): -->  
      <line x1="0" y1="-400" x2="0" y2="400" style="stroke: black;" /> 
            
    </g>
  </svg>
  <script>
    "use strict";

    /* CONSTANTS */
    var initialTheta = 0; // The initial rotation angle, in degrees.
    var thetaDelta = 0.3; // The amount to rotate the square about every 16.7 milliseconds, in degrees.
    var angularLimit = 90; // The maximum number of degrees to rotate the square.

    /* GLOBALS */
    var mySquare = document.getElementById("mySquare");
    var requestAnimationFrameID;

    mySquare.currentTheta = initialTheta; // The initial rotation angle to use when the animation starts.
    requestAnimationFrameID = requestAnimationFrame(doAnim); // Start the animation loop.

    function doAnim() {
      if (mySquare.currentTheta > angularLimit) {
        clearInterval(requestAnimationFrameID);
        return;
      }

      mySquare.transform.baseVal.getItem(0).setRotate(mySquare.currentTheta, 0, 0); // Rotate the square about the point (0, 0), which is now at the center of the SVG viewport. Assumes a no-op transform attribute has been applied to the mySquare element, such as transform="matrix(1 0 0 1 0 0)".
      mySquare.currentTheta += thetaDelta; // Increase the angle that the square will be rotated to, by a small amount.
      requestAnimationFrameID = requestAnimationFrame(doAnim); // Call the doAnim() function about 60 time per second, or about once every 16.7 milliseconds until cancelRequestAnimation() is called.
    }
  </script>
</body>
</html>

예제 4에서는 ID 매트릭스를 "no-op" 변형 특성으로 사용하여 변형 개체를 생성합니다. 대신 transform=”rotate(0)”를 쉽게 사용할 수 있었습니다.

예제 3예제 4보다 나은 장점 하나는"no-op" 특성을 직사각형 요소에 추가해야 한다는 것을 기억할 필요가 없다는 것입니다.

이제 두 가지 스크립팅 스타일(DOM L2 및 SVG DOM)을 모두 이해했으므로 보다 흥미로운 애니메이션으로 넘어가겠습니다. 다음 예제에서는 회전 마찰 기어를 모델링합니다. 서로 연결되어 회전하는 두 개의 자전거 바퀴 고무를 상상해 보세요. 고무의 높은 마찰 계수 덕분에 한 바퀴가 회전하면 다른 바퀴도 회전합니다. 다음은 원형 마찰 기어 시스템의 이상적인 버전입니다.

두 개의 마찰 기어

다음 예제에서는 이 이미지를 생성하는 코드를 보여 줍니다.

예제 5 - 두 개의 기어

라이브 링크: 예제 5


<!DOCTYPE html>
<html>

<head>  
  <title>Two Animated Gears</title>
  <meta http-equiv="X-UA-Compatible" content="IE=Edge"/> <!--  Remove this line in production. -->
</head>

<body>
  <div align="center"> <!-- An inexpensive way to center everything. -->
    <div style=" margin-bottom: 8px;">
      <button id="startButton" type="button" onclick="startAnim();">
        Start Animation
      </button> 
    </div> 
    <svg id="svgElement" width="800px" height="800px" viewBox="0 0 800 800"> <!-- Give the svg element a name so that we can easily access it via JavaScript. -->
      <rect x="0" y="0" width="100%" height="100%" rx="16" ry="16" 
            style="fill: none; stroke: black; stroke-dasharray: 10, 5;" />
  
      <defs> <!-- Do not render the gear template, just define it. -->
        <g id="gearTemplate"> <!-- Give this group of graphic elements a name so that it can be "called" from the <use> element. -->
          <circle cx="0" cy="0" r="150" style="stroke: black;" />
          <line x1="0" y1="-150" x2="0" y2="150" style="stroke: white;"/> <!-- From top to bottom, draw the vertical wheel "spoke". -->        
          <line x1="-150" y1="0" x2="0" y2="0" style="stroke: white;"/> <!-- Draw left half of the horizontal "spoke". -->
          <line x1="0" y1="0" x2="150" y2="0" style="stroke: darkGreen;"/> <!-- Draw right half of the horizontal "spoke". -->
        </g>
      </defs>

      <g transform="translate(400, 400)"> <!-- Create a Cartesian coordinate system (with the y-axis flipped) for the animated gears. That is, place the origin at the center of the 800 x 800 SVG viewport: -->
        <use id="gear0" x="-150" y="0" xlink:href="#gearTemplate" style="fill: orange;" /> <!-- Use the previously defined gear template and position it appropriately. -->
        <use id="gear1" x="150" y="0" xlink:href="#gearTemplate" style="fill: mediumPurple;" /> <!-- Same as the previous line but give this circle a different color. -->                
      </g>
    </svg>
  </div>
  <script>
    "use strict";

    /* CONSTANTS */
    var initialTheta = 0; // The initial rotation angle, in degrees.
    var currentTheta = initialTheta; // The initial rotation angle to use when the animation starts.
    var thetaDelta = 0.5; // The amount to rotate the gears every ~16.7 milliseconds or so, in degrees.
    var angularLimit = 360; // The maximum number of degrees to rotate the gears.

    /* GLOBALS */
    var requestAnimationFrameID;
    var transformObject = svgElement.createSVGTransform(); // Create a generic SVG transform object so as to gain access to its methods and properties, such as setRotate().
    var gear0 = document.getElementById('gear0');
    var gear1 = document.getElementById('gear1');

    gear0.transform.baseVal.appendItem(transformObject); // Append the transform object to gear0, now the gear0 object has inherited all the transform object's goodness.
    gear1.transform.baseVal.appendItem(transformObject); // Append the same generic transform object to gear1 - we just want gear1 to inherit all of it's goodness.

    function startAnim() {
      if (!startButton.startButtonClicked) // Don't allow multiple instance of the function specified by requestAnimationFrame to be invoked by the browser. Note that button.startButtonClicked will be undefined on first use, which is effectively the same as false.
      {
        /* Only do the following once per full animation: */
        startButton.startButtonClicked = true; // A custom property is attached to the button object to track whether the button has been clicked or not.
        requestAnimationFrameID = requestAnimationFrame(doAnim); // Start the animation loop.
      }
    }

    function doAnim() {
      if (currentTheta > angularLimit) {
        startButton.startButtonClicked = false; // Let the user run the animation again if they choose.
        currentTheta = initialTheta; // If we let the user run the animation multiple times, be sure to set currentTheta back to an appropriate value.
        cancelAnimationFrame(requestAnimationFrameID); // Instruct the browser to stop calling requestAnimationFrame()'s callback.
        return; // We have completed our animation, time to quit.
      }

      gear0.transform.baseVal.getItem(0).setRotate(currentTheta, -150, 0); // Rotate the 0th gear about the point (-150, 0).
      gear1.transform.baseVal.getItem(0).setRotate(-currentTheta, 150, 0); // Rotate the 1st gear, note the minus sign on currentTheta, this rotates the gear in the opposite direction.
      // gear0.setAttribute("transform", "rotate(" + currentTheta + ", -150, 0)"); // More cross-browser friendly, slightly less performant. Note that you don't technically need to append a transform object to each gear object, in init(), when using this line.
      // gear1.setAttribute("transform", "rotate(" + -currentTheta + ", 150, 0)"); // More cross-browser friendly, slightly less performant. Note that you don't technically need to append a transform object to each gear object, in init(), when using this line.
      currentTheta += thetaDelta; // Place this line here so that the gears are not over rotated on the last call to doAnim().
      requestAnimationFrameID = requestAnimationFrame(doAnim); // Call the doAnim() function about every 16.7 milliseconds (i.e., about 60 frames per second).
    }
  </script>
</body>
</html>

예제 5에서는 두 개의 그래픽 개체를 애니메이션하고 애니메이션 시작 단추를 추가했습니다. 이로 인해 예제 4의 회전하는 사각형에 비해 많은 코드가 리팩터링되었습니다. 특히:

  • 두 개의 개별 "기어"에 대한 SVG 태그를 만들지 않고 use 요소를 사용하여 원하는 만큼 재사용할 수 있는 기어 템플릿을 만들었습니다.
  • 작업을 간소화하기 위해 값을 두 기어에 모두 적용할 수 있도록 currentTheta를 전역 변수로 만들었습니다.
  • 새 함수인 startAnim이 도입되었습니다. 애니메이션 시작 단추를 클릭하면 requestAnimationFrame에서 애니메이션 루프를 시작하는 이 함수가 호출됩니다.
  • 사용자가 애니메이션 시작 단추를 여러 번 클릭하면 doAnim의 여러 인스턴스가 보호 없이 호출되므로 애니메이션이 실제보다 훨씬 더 빠르게 실행되는 것처럼 보입니다. 원하지 않는 이 동작을 중지하기 위해 사용자 지정 속성 startButtonClicked를 단추 개체에 추가하고, 단추를 처음 클릭할 때 이를 true로 설정합니다.
  • 애니메이션이 완료된 후 사용자가 애니메이션을 다시 시작할 수 있도록 다음 두 줄이 doAnim에 추가되었습니다.

    
    startButton.startButtonClicked = false; 
    currentTheta = initialTheta; 
    
    

    이러한 코드 줄은 애니메이션이 완료될 경우(currentThetaangularLimit보다 큰 경우)에만 실행됩니다.

예제 5의 문제 중 하나는 각 기어에 대해 개별적으로 setRotate 메서드를 호출하고 각 기어의 중심이 있는 위치를 기억해야 한다는 것입니다. 이런 기어가 많은 경우 이 작업이 번거로울 수 있습니다. 이 문제의 한 가지 해결 방법은 예제 6과 같이 기어 배열을 사용하는 것입니다. 코드가 길어서 코드 예제는 이 문서에 표시되어 있지 않습니다. 라이브 예제를 확인하려면 Internet Explorer의 소스 보기 기능을 사용하세요. 이 예제의 스크린샷은 다음과 같습니다.

예제 6 - 17개의 기어

라이브 링크: 예제 6

17개의 과냉각 마찰 기어

예제 6의 코드에서는 17개 기어에 애니메이션 효과를 줍니다. 예제 5에서 두 개의 기어에 대해 수행한 것과 같이 모두 고유 색상과 (x, y) 값 정보가 포함된 17개의 use 요소를 입력하지 않고 프로그래밍 방식으로 이 정보와 일부 다른 유용한 정보가 포함된 기어 배열을 빌드합니다.

  • 각 기어의 반지름(무엇보다, 기어의 적절한 회전 속도를 계산할 수 있도록).
  • 각 기어(즉, currentAngle)의 현재 각 위치(도).
  • 각 기어의 이름 등.

또한 기어 배열을 사용하여 화면에 기어가 렌더링되도록 프로그래밍 방식으로 적절한 요소를 DOM에 추가합니다. 즉, JavaScript 코드는 기어 요소를 다음 “coordinateFrame” g 요소에 추가합니다.


<g id="coordinateFrame" transform="translate(400, 400)"> 
  <!-- Gear <g> elements will be appended here via JavaScript. -->
</g>

가장 큰 기어(위의 “#0”)는 구동 기어입니다. 구동 기어는 아이들러 기어인 나머지 기어를 모두 회전시킵니다. 구동 기어는 하나만 있으므로 이 기어의 회전 속도를 설정하고(constants.driveGearSpeed = 0.3) 다음과 같이 이를 기준으로 다른 기어의 필요 속도를 계산합니다.


gears[i].currentAngle += (gears[i].clockwise * constants.driveGearSpeed * (gears[constants.driveGearIndex].r/gears[i].r));

먼저 각 기어는 gears[i].currentAngle 속성을 통해 자체 각 위치를 추적합니다. 기어가 회전하는 방향은 gears[i].clockwise(1 또는 -1)로 결정됩니다. 지정된 기어의 현재 각 위치는 구동 기어의 각 속도를 구동 기어의 반지름으로 곱하고, 현재 기어의 반지름으로 나눈 값입니다. 구동 기어 자체의 경우 물론 gears[constants.driveGearIndex].r/gears[i].r은 1입니다.

이제 사용자 지정 단추 속성인 startButtonClicked의 사용을 살펴 보겠습니다. 이 속성을 통해 단추 개체는 자신의 현재 상태(클릭 여부)를 추적할 수 있습니다.

예제 6에 대한 추가 질문이 있으면 샘플 내 주석을 검토하세요. 모든 중요한 줄에 주석이 추가되어 있습니다.

다음 예제에서는 단일 애니메이션 시작 단추를 추가 UI(사용자 인터페이스) 요소로 대체하여 예제 6을 확장합니다.

예제 7 - 확장 UI가 있는 17개의 기어

라이브 링크: 예제 7

이 예제는 다음 UI 단추를 추가하여 예제 6을 확장합니다.

추가된 UI 단추 이미지

시작, 일시 중지원래대로 단추는 애니메이션을 시작, 일시 중지 및 다시 설정하고 "+" 및 "-" 단추는 애니메이션 속도를 높이거나 낮춥니다. 이 새로운 UI에는 다음을 비롯한 여러 코드 변경이 필요합니다.

  • 더 이상 각을 제한할 필요가 없습니다. 사용자가 일시 중지 또는 원래대로 단추를 클릭할 때까지 애니메이션이 실행되도록 합니다. 잠재적인 변수 오버플로를 방지하기 위해 다음 코드가 사용되었습니다.
    
    if (gears[i].currentAngle >= 360)
      gears[i].currentAngle -= 360;
    
    

    이 코드는 currentAngle의 의미에 영향을 미치지 않고 각 기어의 currentAngle 값을 작게 유지합니다. 즉, 362도 회전한 원은 2도 회전한 원과 동일하게 보입니다(예: 362 – 360 = 2도).

예제 8 - 오디오가 있는 17개의 기어

라이브 링크: 예제 8

이 예제에서는 HTML5 audio 요소를 사용하여 "+" 및 "-" 단추를 클릭할 때 박자가 선형으로 증가하거나 감소하는 사운드 효과를 추가하는 방법으로 이전 예제를 확장합니다. 또한 사운드 효과가 방해가 될 수 있으므로 편리한 사운드 끄기 단추도 추가했습니다.

추가된 UI 단추 이미지

예제 8 오디오 코드는 비교적 간단하며 주석이 잘 처리되어 있습니다. 추가적인 설명이 필요할 수 있는 유일한 함수는 현재 구동 기어의 회전 속도에 적합한 오디오 파일 재생 속도를 반환하는 calculatePlaybackRate입니다. 이 동작이 어떻게 수행되는지에 대한 설명을 돕기 위해 다음 그래프를 살펴봅니다.

구동 기어 대 오디오 재생 속도 그래프

x축은 구동 기어의 현재 회전 속도를 의미합니다(양수 또는 음수 가능). y축은 적절한 오디오 파일 재생 속도를 의미합니다(양수만 가능). 구동 기어 속도가 0이면 오디오 재생 속도도 0(사운드 없음)이어야 합니다. 또한 상수 초기화를 통해 구동 기어 속도가 constants.initialDriveGearSpeed이면 오디오 재생 속도가 constants.initialPlaybackRate임을 알고 있습니다. 이제 두 개의 점, (0, 0) 및 (constants.initialDriveGearSpeed, constants.initialPlaybackRate)가 있습니다. 두 점이 한 개의 선을 정의하므로(그리고 선형 반응을 원하므로), calculatePlaybackRate에 사용된 수식을 쉽게 파생시킬 수 있습니다. 선(m)의 경사를 계산하고 여기에 현재 구동 기어의 절대 값을 곱해 올바른 오디오 파일 재생 속도를 구하면 됩니다. 자세한 내용은 calculatePlaybackRate를 참조하세요.

다음 논리적 단계는 예제 8에 기어를 더 추가하는 작업이 될 수도 있습니다. 예제의 코드에는 기어 개체 배열이 사용되므로 적절한 크기로 적절하게 배치된 기어를 배열에 추가하여 애니메이션의 전체 기어 수를 늘리면 됩니다. 이 예제는 직접 연습해 보세요. 여기서 설명한 방법을 이해하는 데 크게 도움이 될 것입니다.

관련 항목

중급 SVG 애니메이션
고급 SVG 애니메이션
HTML5 그래픽
Scalable Vector Graphics (SVG)

 

 

표시:
© 2014 Microsoft