{"id":1638,"date":"2012-04-16T20:36:13","date_gmt":"2012-04-17T01:36:13","guid":{"rendered":"http:\/\/www.wiredprairie.us\/blog\/?p=1638"},"modified":"2012-04-16T20:39:23","modified_gmt":"2012-04-17T01:39:23","slug":"spinning-svg-animation","status":"publish","type":"post","link":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1638","title":{"rendered":"Spinning SVG Animation"},"content":{"rendered":"

Interested in learning a few capabilities of SVG animation this evening from JavaScript in a web page, I put together a simple demonstration.<\/p>\n

Click on the image to launch the demo.<\/p>\n

\"image\"<\/a><\/p>\n

Animating SVG in a modern browser (including IE9) is generally easy enough. This example was slightly more interesting in that I wanted the thicker line to rotate based on the center point of the image, rather than the location of the line itself.<\/p>\n

Not what I wanted:<\/p>\n

\"image\"<\/p>\n

Desired rotation:<\/p>\n

\"image\"<\/p>\n

The white bar would rotate around the center point (marked by the red arrow above).<\/p>\n

I\u2019ve included all of the code at the bottom of the post. (There\u2019s quite a lot of path information).<\/p>\n

The JavaScript just used an interval to move the line around the point:<\/p>\n

(function <\/span>() {\n    window.onload = loaded;\n    function <\/span>loaded() {\n        var <\/span>colorTemp = document.getElementById("color-temp"<\/span>);\n        var <\/span>reading = document.getElementById('current-reading'<\/span>);\n        var <\/span>currentAngle = 0;\n        var <\/span>fill;\n\n        var <\/span>direction = 1;\n        setInterval(function <\/span>() {\n            currentAngle += direction;\n            if <\/span>(currentAngle >= 120 || currentAngle <= -120) {\n                direction *= -1;\n            } else if <\/span>(currentAngle === 0) {\n                fill = direction === 1 ? "#BE1E2D" <\/span>: "#10A2DC"<\/span>;\n                colorTemp.setAttribute("fill"<\/span>, fill);\n            }\n            \/\/ adjust the opacity\n            <\/span>colorTemp.setAttribute("opacity"<\/span>, Math.abs(currentAngle) \/ 120.0);\n\n            reading.setAttribute("transform"<\/span>, "rotate(" <\/span>+ currentAngle + ")"<\/span>);\n\n        }, 25);\n    }\n})();<\/pre>\n

The angle is fixed between 120 and \u2013120 degrees. When the angle reaches zero, the fill color is toggled. <\/p>\n

As the angle adjusts, the line is updated as well as the opacity of the fill.<\/p>\n

The overall size of the SVG drawing is 600×600. Knowing that (and wanting the exact center), I translated a group to an offset of 300x, 300y containing the line:<\/p>\n

<<\/span>g <\/span>id<\/span>="temp-transform" <\/span>transform<\/span>="translate(300,300)">\n        <<\/span>line <\/span>id<\/span>="current-reading" <\/span>fill<\/span>="none" <\/span>stroke<\/span>="#FFFFFF"\n              <\/span>stroke-width<\/span>="5" <\/span>stroke-linecap<\/span>="round"\n              <\/span>stroke-miterlimit<\/span>="10" <\/span>x1<\/span>="0" <\/span>y1<\/span>="-180" <\/span>x2<\/span>="0" <\/span>y2<\/span>="-120"\/>\n<\/<\/span>g<\/span>>\n<\/span><\/pre>\n

This effectively made the rotation now work from the center, once the line coordinates were adjusted to reflect the new translation. <\/p>\n

This works because the contents of the outer group caused the new starting point (origin) to be 300x, 300y rather than the default 0x, 0y. You can see the line is from y \u2013120 to y \u2013180. That\u2019s because I wanted the line to start in the top middle (as the x is set to 0 for the line).<\/p>\n

I used Adobe Illustrator CS 5.5 to create the SVG image. Illustrator typically does not always handle fonts well when exporting as SVG. The final text node was this:<\/p>\n

\n
<text transform="matrix(1 0 0 1 254.1387 83.3198)"<\/span> fill="#D1D3D4"<\/span> font-family="'Arial'"<\/span>
font-weight="bold"<\/span> font-size="29.4616"<\/span>>thermo<\/text>
<\/pre>\n

<\/div>\n

To make it work across browsers, I needed to modify the text node below slightly. Note the font name and missing bold attribute. It\u2019s not a hard switch, but it\u2019s annoying if you need to make changes to your SVG multiple times.<\/p>\n

\n
<text transform="matrix(1 0 0 1 254.1387 83.3198)"<\/span> fill="#D1D3D4"<\/span> 
font-family="'Arial-BoldMT'"<\/span> font-size="29.4616"<\/span>>thermo<\/text><\/pre>\n

<\/div>\n

If you have any questions, please leave a comment!<\/p>\n

<!<\/span>DOCTYPE <\/span>HTML<\/span>>\n<<\/span>html<\/span>>\n<<\/span>head<\/span>>\n    <<\/span>title<\/span>><\/span>Svg Spinner Demo<\/<\/span>title<\/span>>\n    <<\/span>style<\/span>>\n        <\/span>#demo1 svg <\/span>{\n            width<\/span>: 300px<\/span>;\n            height<\/span>: 300px<\/span>;\n        }\n\n    <\/<\/span>style<\/span>>\n<\/<\/span>head<\/span>>\n<<\/span>body<\/span>>\n\n<<\/span>div <\/span>id<\/span>="demo1">\n<<\/span>svg <\/span>version<\/span>="1.0" <\/span>id<\/span>="Layer_1" <\/span>xmlns<\/span>="http:\/\/www.w3.org\/2000\/svg" <\/span>xmlns<\/span>:<\/span>xlink<\/span>="http:\/\/www.w3.org\/1999\/xlink" <\/span>x<\/span>="0px" <\/span>y<\/span>="0px"\n     <\/span>width<\/span>="600px" <\/span>height<\/span>="600px" <\/span>viewBox<\/span>="0 0 600 600" <\/span>enable-background<\/span>="new 0 0 600 600" <\/span>xml<\/span>:<\/span>space<\/span>="preserve">\n<<\/span>g<\/span>>\n    <<\/span>radialGradient <\/span>id<\/span>="SVGID_1_" <\/span>cx<\/span>="298.9106" <\/span>cy<\/span>="196.146" <\/span>r<\/span>="399.267" <\/span>gradientUnits<\/span>="userSpaceOnUse">\n        <<\/span>stop  <\/span>offset<\/span>="0" <\/span>style<\/span>="<\/span>stop-color<\/span>:#D9D9D9"\/>\n        <<\/span>stop  <\/span>offset<\/span>="1" <\/span>style<\/span>="<\/span>stop-color<\/span>:#C2C2C2"\/>\n    <\/<\/span>radialGradient<\/span>>\n    <<\/span>path <\/span>fill<\/span>="url(#SVGID_1_)" <\/span>d<\/span>="M300.646,595.41c-78.957,0-153.188-30.747-209.02-86.579C35.795,453,5.048,378.77,5.048,299.812\n        c0-78.957,30.748-153.188,86.579-209.02S221.689,4.214,300.646,4.214c78.958,0,153.188,30.748,209.019,86.579\n        c55.832,55.831,86.579,130.063,86.579,209.02c0,78.958-30.747,153.188-86.579,209.019\n        C453.834,564.663,379.604,595.41,300.646,595.41z"\/>\n    <<\/span>path <\/span>fill<\/span>="#808285" <\/span>d<\/span>="M300.646,6.214c39.637,0,78.086,7.762,114.281,23.071c34.961,14.787,66.359,35.957,93.324,62.922\n        s48.135,58.364,62.923,93.325c15.309,36.193,23.07,74.643,23.07,114.28s-7.762,78.086-23.07,114.281\n        c-14.788,34.961-35.958,66.359-62.923,93.324s-58.363,48.135-93.324,62.923c-36.194,15.309-74.644,23.07-114.281,23.07\n        s-78.087-7.762-114.28-23.07c-34.961-14.788-66.36-35.958-93.325-62.923s-48.135-58.363-62.922-93.324\n        C14.81,377.898,7.048,339.449,7.048,299.812s7.762-78.087,23.071-114.28c14.787-34.961,35.957-66.36,62.922-93.325\n        s58.364-48.135,93.325-62.922C222.559,13.976,261.009,6.214,300.646,6.214 M300.646,2.214\n        C136.287,2.214,3.048,135.453,3.048,299.812S136.287,597.41,300.646,597.41s297.598-133.239,297.598-297.598\n        S465.005,2.214,300.646,2.214L300.646,2.214z"\/>\n<\/<\/span>g<\/span>>\n<<\/span>g<\/span>>\n    <<\/span>radialGradient <\/span>id<\/span>="SVGID_2_" <\/span>cx<\/span>="299.0464" <\/span>cy<\/span>="204.2632" <\/span>r<\/span>="368.0053" <\/span>gradientUnits<\/span>="userSpaceOnUse">\n        <<\/span>stop  <\/span>offset<\/span>="0" <\/span>style<\/span>="<\/span>stop-color<\/span>:#141414"\/>\n        <<\/span>stop  <\/span>offset<\/span>="1" <\/span>style<\/span>="<\/span>stop-color<\/span>:#080808"\/>\n    <\/<\/span>radialGradient<\/span>>\n    <<\/span>path <\/span>fill<\/span>="url(#SVGID_2_)" <\/span>d<\/span>="M300.646,572.266c-72.775,0-141.194-28.34-192.654-79.8c-51.459-51.46-79.8-119.879-79.8-192.654\n        s28.34-141.194,79.8-192.654s119.879-79.8,192.654-79.8s141.194,28.34,192.654,79.8c51.46,51.46,79.8,119.879,79.8,192.654\n        s-28.34,141.194-79.8,192.654S373.421,572.266,300.646,572.266z"\/>\n    <<\/span>path <\/span>d<\/span>="M300.646,29.358c36.512,0,71.931,7.15,105.271,21.252c32.205,13.622,61.129,33.123,85.968,57.962\n        c24.84,24.839,44.341,53.763,57.963,85.968c14.102,33.34,21.252,68.759,21.252,105.271c0,36.512-7.15,71.931-21.252,105.271\n        c-13.622,32.205-33.123,61.129-57.963,85.968c-24.839,24.84-53.763,44.341-85.968,57.963\n        c-33.34,14.102-68.759,21.252-105.271,21.252c-36.513,0-71.931-7.15-105.271-21.252c-32.205-13.622-61.129-33.123-85.968-57.963\n        c-24.839-24.839-44.34-53.763-57.962-85.968c-14.102-33.34-21.252-68.759-21.252-105.271c0-36.513,7.15-71.931,21.252-105.271\n        c13.622-32.205,33.123-61.129,57.962-85.968s53.763-44.34,85.968-57.962C228.715,36.509,264.133,29.358,300.646,29.358\n         M300.646,25.358c-151.577,0-274.454,122.877-274.454,274.454c0,151.576,122.877,274.454,274.454,274.454\n        c151.576,0,274.454-122.878,274.454-274.454C575.1,148.235,452.222,25.358,300.646,25.358L300.646,25.358z"\/>\n<\/<\/span>g<\/span>>\n<<\/span>g<\/span>>\n    <<\/span>radialGradient <\/span>id<\/span>="SVGID_3_" <\/span>cx<\/span>="299.5176" <\/span>cy<\/span>="232.4409" <\/span>r<\/span>="259.4771" <\/span>gradientUnits<\/span>="userSpaceOnUse">\n        <<\/span>stop  <\/span>offset<\/span>="0" <\/span>style<\/span>="<\/span>stop-color<\/span>:#212121"\/>\n        <<\/span>stop  <\/span>offset<\/span>="1" <\/span>style<\/span>="<\/span>stop-color<\/span>:#0D0D0D"\/>\n    <\/<\/span>radialGradient<\/span>>\n    <<\/span>path <\/span>fill<\/span>="url(#SVGID_3_)" <\/span>d<\/span>="M300.646,491.916c-105.927,0-192.104-86.178-192.104-192.104\n        c0-105.927,86.178-192.104,192.104-192.104c105.926,0,192.104,86.178,192.104,192.104\n        C492.75,405.738,406.572,491.916,300.646,491.916z"\/>\n    <<\/span>path <\/span>d<\/span>="M300.646,109.708c25.666,0,50.561,5.026,73.996,14.938c22.638,9.575,42.968,23.283,60.429,40.743\n        c17.46,17.46,31.167,37.791,40.742,60.428c9.912,23.435,14.938,48.331,14.938,73.996s-5.025,50.561-14.938,73.996\n        c-9.575,22.638-23.282,42.968-40.742,60.429c-17.461,17.46-37.791,31.167-60.429,40.742c-23.435,9.912-48.33,14.938-73.996,14.938\n        s-50.562-5.025-73.996-14.938c-22.637-9.575-42.968-23.282-60.428-40.742c-17.46-17.461-31.168-37.791-40.743-60.429\n        c-9.912-23.435-14.938-48.33-14.938-73.996s5.026-50.562,14.938-73.996c9.575-22.637,23.283-42.968,40.743-60.428\n        s37.791-31.168,60.428-40.743C250.084,114.733,274.98,109.708,300.646,109.708 M300.646,105.708\n        c-107.201,0-194.104,86.903-194.104,194.104c0,107.201,86.903,194.104,194.104,194.104c107.201,0,194.104-86.903,194.104-194.104\n        C494.75,192.611,407.847,105.708,300.646,105.708L300.646,105.708z"\/>\n<\/<\/span>g<\/span>>\n<<\/span>g <\/span>opacity<\/span>="0.8">\n    <<\/span>path <\/span>id<\/span>="color-temp" <\/span>fill<\/span>="#BE1E2D" <\/span>opacity<\/span>="0" <\/span>d<\/span>="M300.646,489.309c-104.489,0-189.497-85.008-189.497-189.497c0-104.489,85.008-189.497,189.497-189.497\n        c104.489,0,189.497,85.008,189.497,189.497C490.143,404.301,405.135,489.309,300.646,489.309z"\/>\n    <<\/span>path <\/span>d<\/span>="M300.646,110.815c25.515,0,50.266,4.997,73.566,14.852c22.505,9.519,42.718,23.146,60.075,40.504\n        s30.984,37.57,40.504,60.075c9.854,23.3,14.852,48.051,14.852,73.566s-4.997,50.266-14.852,73.566\n        c-9.52,22.505-23.146,42.718-40.504,60.075s-37.57,30.984-60.075,40.504c-23.3,9.854-48.051,14.852-73.566,14.852\n        s-50.266-4.997-73.566-14.852c-22.505-9.52-42.718-23.146-60.075-40.504s-30.985-37.57-40.504-60.075\n        c-9.855-23.3-14.852-48.051-14.852-73.566s4.997-50.266,14.852-73.566c9.519-22.505,23.146-42.718,40.504-60.075\n        s37.57-30.985,60.075-40.504C250.38,115.812,275.131,110.815,300.646,110.815 M300.646,109.815\n        c-104.933,0-189.997,85.064-189.997,189.997c0,104.932,85.064,189.997,189.997,189.997c104.932,0,189.997-85.064,189.997-189.997\n        C490.643,194.879,405.578,109.815,300.646,109.815L300.646,109.815z"\/>\n<\/<\/span>g<\/span>>\n<<\/span>g <\/span>id<\/span>="temperature-markings">\n        <<\/span>line <\/span>fill<\/span>="none" <\/span>stroke<\/span>="#C7C8CA" <\/span>stroke-width<\/span>="5" <\/span>stroke-miterlimit<\/span>="10"\n              <\/span>x1<\/span>="300.646" <\/span>y1<\/span>="123.751" <\/span>x2<\/span>="300.646" <\/span>y2<\/span>="161.603"\/>\n        <<\/span>line <\/span>fill<\/span>="none" <\/span>stroke<\/span>="#C7C8CA" <\/span>stroke-width<\/span>="5" <\/span>stroke-miterlimit<\/span>="10"\n              <\/span>x1<\/span>="422.102" <\/span>y1<\/span>="371.971" <\/span>x2<\/span>="454.881" <\/span>y2<\/span>="390.896"\/>\n        <<\/span>line <\/span>fill<\/span>="none" <\/span>stroke<\/span>="#C7C8CA" <\/span>stroke-width<\/span>="5" <\/span>stroke-miterlimit<\/span>="10"\n              <\/span>x1<\/span>="146.41" <\/span>y1<\/span>="390.896" <\/span>x2<\/span>="179.191" <\/span>y2<\/span>="371.97"\/>\n<\/<\/span>g<\/span>>\n<<\/span>g <\/span>id<\/span>="temp-transform" <\/span>transform<\/span>="translate(300,300)">\n        <<\/span>line <\/span>id<\/span>="current-reading" <\/span>fill<\/span>="none" <\/span>stroke<\/span>="#FFFFFF"\n              <\/span>stroke-width<\/span>="5" <\/span>stroke-linecap<\/span>="round"\n              <\/span>stroke-miterlimit<\/span>="10" <\/span>x1<\/span>="0" <\/span>y1<\/span>="-180" <\/span>x2<\/span>="0" <\/span>y2<\/span>="-120"\/>\n<\/<\/span>g<\/span>>\n    <<\/span>text <\/span>transform<\/span>="matrix(1 0 0 1 254.1387 83.3198)" <\/span>fill<\/span>="#D1D3D4" <\/span>font-family<\/span>="'Arial'"\n          <\/span>font-weight<\/span>="bold" <\/span>font-size<\/span>="29.4616"><\/span>thermo<\/<\/span>text<\/span>>\n<\/<\/span>svg<\/span>>\n\n\n<\/<\/span>div<\/span>>\n\n\n<<\/span>script <\/span>type<\/span>="text\/javascript">\n\n    <\/span>(function <\/span>() {\n\n        window.onload = loaded;\n\n        function <\/span>loaded() {\n            var <\/span>colorTemp = document.getElementById("color-temp"<\/span>);\n            var <\/span>reading = document.getElementById('current-reading'<\/span>);\n            var <\/span>currentAngle = 0;\n            var <\/span>fill;\n\n            var <\/span>direction = 1;\n            setInterval(function <\/span>() {\n                currentAngle += direction;\n                if <\/span>(currentAngle >= 120 || currentAngle <= -120) {\n                    direction *= -1;\n                } else if <\/span>(currentAngle === 0) {\n                    fill = direction === 1 ? "#BE1E2D" <\/span>: "#10A2DC"<\/span>;\n                    colorTemp.setAttribute("fill"<\/span>, fill);\n                }\n                \/\/ adjust the opacity\n                <\/span>colorTemp.setAttribute("opacity"<\/span>, Math.abs(currentAngle) \/ 120.0);\n\n                reading.setAttribute("transform"<\/span>, "rotate(" <\/span>+ currentAngle + ")"<\/span>);\n\n            }, 25);\n        }\n    })();\n\n\n<\/<\/span>script<\/span>>\n<\/<\/span>body<\/span>>\n<\/<\/span>html<\/span>>\n<\/span><\/pre>\n","protected":false},"excerpt":{"rendered":"

Interested in learning a few capabilities of SVG animation this evening from JavaScript in a web page, I put together a simple demonstration. Click on the image to launch the demo. Animating SVG in a modern browser (including IE9) is generally easy enough. This example was slightly more interesting in that I wanted the thicker […]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[4],"tags":[88,38,89,59],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pd5QIe-qq","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":1643,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1643","url_meta":{"origin":1638,"position":0},"title":"SVG Setting Text Content Dynamically","date":"April 17, 2012","format":false,"excerpt":"Continuing on a theme of animating SVG, I\u2019ve added a label in the center of the animation which contains the current angle of the ever-rotating marker (in fact in a centered horizontally and vertically SVG text node). (Click image below to try it.) At the bottom of the file from\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.wiredprairie.us\/blog\/wp-content\/uploads\/2012\/04\/image26.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1563,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1563","url_meta":{"origin":1638,"position":1},"title":"Knockout.JS: AsDictionary","date":"March 9, 2012","format":false,"excerpt":"I frequently find that I have an array of objects in JavaScript that I want to display in a particular order and also have the ability to quickly locate an object by an ID or a key (and not use the indexOf function). As my recent project is using Knockout.JS,\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.wiredprairie.us\/blog\/wp-content\/uploads\/2012\/03\/image3.png?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":1570,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1570","url_meta":{"origin":1638,"position":2},"title":"JavaScript: isScrolledIntoView","date":"March 15, 2012","format":false,"excerpt":"I needed a simple way to detect when a placeholder DIV (that would contain an image) had scrolled into the current viewport of the browser. I\u2019ve seen a few solutions that worked, and a few that didn\u2019t (hello? test your code!). Here\u2019s my simple solution: function isScrolledIntoView(elem) { elem =\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"SmugMupBrowser-live","src":"https:\/\/i0.wp.com\/www.wiredprairie.us\/blog\/wp-content\/uploads\/2012\/03\/SmugMupBrowser-live.gif?resize=350%2C200","width":350,"height":200},"classes":[]},{"id":885,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/885","url_meta":{"origin":1638,"position":3},"title":"Microsoft Ajax Library Declarative Command Alternatives","date":"January 3, 2010","format":false,"excerpt":"There may be another way to accomplish this, but I wanted to have a simple commanding system in the Microsoft Ajax Library which worked outside of the templates. After a number of false starts and frustration brought about by very limited documentation, I discovered a reasonable implementation. I created a\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1565,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1565","url_meta":{"origin":1638,"position":4},"title":"Knockout.JS: Dictionary\/Index and ObservableArray, Part 2","date":"March 10, 2012","format":false,"excerpt":"Ryan suggested an alternative in a comment (and corresponding jsFiddle) to the technique that I\u2019d used in my previous Knockout.JS post. Following his suggestion, I made a few tweaks to my original function (and renamed it yet again): ko.observableArray.fn.withIndex = function (keyName) { var index = ko.computed(function () { var\u2026","rel":"","context":"In "Coding"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":890,"url":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/890","url_meta":{"origin":1638,"position":5},"title":"Disabling automatic Sys.UI.Control attachment","date":"January 17, 2010","format":false,"excerpt":"If you\u2019re using the Microsoft Ajax Library (learn), you may not always want to start the automatic \u201cattach\u201d process that takes place when the page loads. It\u2019s easy to disable, but not yet documented any place I could find easily.