在svg中逆时针旋转齿轮

rotatation of a gear counter-clockwise in svg

我想要一个简单的齿轮,能够顺时针、逆时针旋转并暂停动画。当我只有这两个按钮时,我的代码工作正常:顺时针和暂停;但是添加了逆时针按钮及其相关功能后,没有任何效果!
Fiddle

    <!DOCTYPE html>
<html>

<head>  
  <title> 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="clockwise" type="button" onclick="clockwise();">
        clockwise
      </button> 
              <button id="pause" type="button" onclick="pauseAnim();">
        Pause
      </button> 
<button id="counterclockwise" type="button" onclick="counterclockwise();">
        Counterclockwise
      </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: white;"/> <!-- 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. -->

      </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 = 1080; // 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');

gear0.transform.baseVal.appendItem(transformObject); // Append the transform object to gear0, now the gear0 object has inherited all the transform object's goodness.


     //...........................................     


function clockwise() {

if (!clockwise.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 animation: */

        clockwise.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 counterclockwise() {

if (!counterclockwise.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 animation: */

        counterclockwise.startButtonClicked = true; // A custom property is attached to the button object to track whether the button has been clicked or not.
        requestAnimationFrameID = requestAnimationFrame(doAnim2); // Start the animation loop.
      }
    }



     //........................
    function pauseAnim() {
      cancelAnimationFrame(requestAnimationFrameID); // Stop calling the doAnim() function.

      clockwise.startButtonClicked = false; // Allow
     requestAnimationFrame() to be called if the Start button is clicked again. And disable the "+" or "-" buttons when paused.
     counterclockwise.startButtonClicked = false;

    }

         function doAnim() {
      if (currentTheta > angularLimit) {
        clockwise.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).

      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).     
     }
     //.................................................................

     function doAnim2() {
      if (currentTheta > angularLimit) {
        counterclockwise.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).

      currentTheta += thetaDelta; // Place this line here so that the gears are not over rotated on the last call to doAnim().
      requestAnimationFrameID = requestAnimationFrame(doAnim2); // Call the doAnim() function about every 16.7 milliseconds (i.e., about 60 frames per second).     
     }



     </script>
</body>
</html>    

编辑 2015 年 1 月 19 日

既然您发布了关于 counterclockwise 函数的尝试,看来您在 pause 函数内部的评论有问题(requestAnimationFrame() to be called if the Start button is… 没有评论,导致语法错误。)
此外,我注意到您正在将 -currentTheta 应用于转换,如果您从 'clockwise' 转到 'counterclockwise'。

会导致间隙

您的代码段中缺少 counterclockwise 函数。所以很难告诉你哪里出了问题。
但我承认它与 clockwise 相同,但在另一方面:

var initialTheta = 0;
var currentTheta = initialTheta;
var thetaDelta = 0.5;
var angularLimit = 1080;
var angularLimitMin = -1080;

var requestAnimationFrameID;
var transformObject = svgElement.createSVGTransform();
var gear0 = document.getElementById('gear0');

gear0.transform.baseVal.appendItem(transformObject);


function clockwise() {
  if (!clockwise.startButtonClicked) {
    clockwise.startButtonClicked = true;
    requestAnimationFrameID = requestAnimationFrame(doAnim);
  }
}


function counterclockwise() {
  if (!counterclockwise.startButtonClicked) {
    counterclockwise.startButtonClicked = true;
    requestAnimationFrameID = requestAnimationFrame(undoAnim);
  }
}


function pauseAnim() {
  cancelAnimationFrame(requestAnimationFrameID);
  clockwise.startButtonClicked = false;
  counterclockwise.startButtonClicked = false;
}

function doAnim() {
  if (currentTheta > angularLimit) {
    clockwise.startButtonClicked = false;
    currentTheta = initialTheta;
    cancelAnimationFrame(requestAnimationFrameID);
    return;
  }

  gear0.transform.baseVal.getItem(0).setRotate(currentTheta, -150, 0);
  currentTheta += thetaDelta;
  requestAnimationFrameID = requestAnimationFrame(doAnim);
}

function undoAnim() {
  if (currentTheta < angularLimitMin) {
    counterclockwise.startButtonClicked = false;
    currentTheta = initialTheta;
    cancelAnimationFrame(requestAnimationFrameID);
    return;
  }
  gear0.transform.baseVal.getItem(0).setRotate(currentTheta, -150, 0);
  currentTheta -= thetaDelta;
  requestAnimationFrameID = requestAnimationFrame(undoAnim);
}
<div align="center">
  <!-- An inexpensive way to center everything. -->
  <div style=" margin-bottom: 8px;">
    <button id="clockwise" type="button" onclick="clockwise();">clockwise</button>
    <button id="pause" type="button" onclick="pauseAnim();">Pause</button>
    <button id="counterclockwise" type="button" onclick="counterclockwise();">Counterclockwise</button>
  </div>
  <svg id="svgElement" width="800px" height="800px" viewBox="0 0 800 800">
    <rect x="0" y="0" width="100%" height="100%" rx="16" ry="16" style="fill: none; stroke: black; stroke-dasharray: 10, 5;" />
    <defs>
      <g id="gearTemplate">
        <circle cx="0" cy="0" r="150" style="stroke: black;" />
        <line x1="0" y1="-150" x2="0" y2="150" style="stroke: white;" />
        <line x1="-150" y1="0" x2="0" y2="0" style="stroke: white;" />
        <line x1="0" y1="0" x2="150" y2="0" style="stroke: white;" />
      </g>
    </defs>
    <g transform="translate(400, 400)">
      <use id="gear0" x="-150" y="0" xlink:href="#gearTemplate" style="fill: orange;" />
    </g>
  </svg>
</div>

以及 updated snippet 和您的评论