{"id":2402,"date":"2011-10-10T19:39:11","date_gmt":"2011-10-11T00:39:11","guid":{"rendered":"http:\/\/www.lonhosford.com\/lonblog\/?p=2402"},"modified":"2015-07-31T18:25:49","modified_gmt":"2015-07-31T23:25:49","slug":"html5-canvas-circular-rotation-animation-using-jquery-hotkeys-for-user-interaction","status":"publish","type":"post","link":"https:\/\/www.lonhosford.com\/lonblog\/2011\/10\/10\/html5-canvas-circular-rotation-animation-using-jquery-hotkeys-for-user-interaction\/","title":{"rendered":"HTML5 Canvas Circular Rotation Animation Example Using JQuery Hotkeys For User Interaction"},"content":{"rendered":"<p>This takes my first <a href=\"https:\/\/www.lonhosford.com\/lonblog\/2011\/10\/09\/html5-canvas-circular-rotation-animation\/\">example animating a filled circle rotating<\/a> around the center point to a user interaction level using <a href=\"https:\/\/github.com\/jeresig\/jquery.hotkeys\"  target = \"_blank\">JQuery Hotkeys<\/a>. <img loading=\"lazy\" decoding=\"async\" alt=\"\" src=\"https:\/\/lh3.googleusercontent.com\/-D0GmtVkl664\/TpIsHRo7uEI\/AAAAAAAAGJM\/N1sMmjSRBug\/s288\/Html5CanvasCircularMovement.jpg\" class=\"alignleft\" width=\"288\" height=\"288\" \/> <a href=\"https:\/\/github.com\/jeresig\/jquery.hotkeys\" target = \"_blank\">JQuery Hotkeys<\/a> is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination using JQuery.<br \/>\n<figure style=\"width: 100px\" class=\"wp-caption alignright\"><a href=\"http:\/\/www.amazon.com\/gp\/product\/1430236655\/ref=as_li_ss_il?ie=UTF8&#038;tag=hosfordusa&#038;linkCode=as2&#038;camp=217145&#038;creative=399373&#038;creativeASIN=1430236655\"><img decoding=\"async\" border=\"0\" src=\"http:\/\/ws.assoc-amazon.com\/widgets\/q?_encoding=UTF8&#038;Format=_SL110_&#038;ASIN=1430236655&#038;MarketPlace=US&#038;ID=AsinImage&#038;WS=1&#038;tag=hosfordusa&#038;ServiceVersion=20070822\" ><\/a><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.assoc-amazon.com\/e\/ir?t=hosfordusa&#038;l=as2&#038;o=1&#038;a=1430236655&#038;camp=217145&#038;creative=399373\" width=\"1\" height=\"1\" border=\"0\" alt=\"\" style=\"border:none !important; margin:0px !important;\" \/><a href=\"http:\/\/www.amazon.com\/gp\/product\/1430236655\/ref=as_li_ss_tl?ie=UTF8&#038;tag=hosfordusa&#038;linkCode=as2&#038;camp=217145&#038;creative=399373&#038;creativeASIN=1430236655\">Foundation HTML5 Animation with JavaScript<\/a><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/www.assoc-amazon.com\/e\/ir?t=hosfordusa&#038;l=as2&#038;o=1&#038;a=1430236655&#038;camp=217145&#038;creative=399373\" width=\"1\" height=\"1\" border=\"0\" alt=\"\" style=\"border:none !important; margin:0px !important;\" \/><br \/>\n<figcaption class=\"wp-caption-text\">Learn More<\/figcaption><\/figure><\/p>\n<p>This version of the animation allows for start and stop playing. It also includes reversing the direction of rotation from clockwise to counter clockwise and back again. Finally I decided to allow increasing and decreasing the speed of rotation.  <\/p>\n<p>Another change from the previous <a href=\"https:\/\/www.lonhosford.com\/lonblog\/2011\/10\/09\/html5-canvas-circular-rotation-animation\/\">example<\/a> is to move as much relevant functionality as possible into the animated object called player. For example the player object is responsible for its own data computations and for interpreting key events. <\/p>\n<p>The events in the animation are the timer and key events. The timer event calls the update and draw functions at the desired frame rate. The update and draw functions then call the same functions for all animation objects: we only have one animation object at this point. <\/p>\n<p><strong><a  href=\"\/content\/html5\/canvas\/circular_movement_ex02\/circular_movement.html\" target = \"_blank\">Demo<\/a><\/strong><\/p>\n<p><strong>Download<\/strong><\/p>\n<ul>\n<li>\n<a onclick=\"javascript: pageTracker._trackPageview('\/downloads\/html5\/canvas\/circular_movement_ex02.zip'); \" href=\"https:\/\/www.lonhosford.com\/content\/html5\/canvas\/circular_movement_ex02.zip\">Source<\/a>. This includes the 0.7.9 minimized version of JQuery Hotkeys. <\/li>\n<li>\n<a href=\"https:\/\/github.com\/jeresig\/jquery.hotkeys\"  target = \"_blank\">JQuery Hotkeys Github<\/a> in case you want the latest version.\n<\/li>\n<\/ul>\n<p>[ad name=&#8221;Google Adsense&#8221;]<br \/>\nThis is the html5 head section. Line 4 brings in JQuery from the Google CDN. Line 5 references a local copy of <a href=\"https:\/\/github.com\/jeresig\/jquery.hotkeys\"  target = \"_blank\">JQuery Hotkeys<\/a>. In this case the 0.7.9 minimized version.<\/p>\n<p>The html structure is expanded to include elements for a main title, a subtitle and keyboard instructions for the animation.  The internal style on lines 9 &#8211; 27 provide formatting for these new structural elements.<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;!DOCTYPE HTML&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;script language=&quot;javascript&quot; src=&quot;https:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/1.6.4\/jquery.min.js&quot; type=&quot;text\/javascript&quot;&gt;&lt;\/script&gt;\r\n&lt;script src=&quot;js\/jquery.hotkeys-0.7.9.min.js&quot;&gt;&lt;\/script&gt;\r\n&lt;title&gt;HTML5 Canvas Based Animation Circular Movement Start, Stop, Reverse, Faster, Slower&lt;\/title&gt;\r\n&lt;meta charset=&quot;UTF-8&quot;&gt;\r\n&lt;title&gt;Untitled Document&lt;\/title&gt;\r\n&lt;style type=&quot;text\/css&quot;&gt;\r\n\tbody {\r\n\t\tfont-family:Arial, Helvetica, sans-serif;\r\n\t\tfont-size:12px;\r\n\t}\r\n\t#titleDiv{\r\n\t\tfont-family:Verdana, Geneva, sans-serif;\r\n\t}\r\n\t#titleHeadDiv{\r\n\t\tfont-size:16px;\r\n\t}\r\n\t#titleSub1Div{\r\n\t\tfont-size:14px;\r\n\t}\r\n\t#instructionsDiv{\r\n\t\tmargin-top:10px;\r\n\t\tmargin-bottom:5px;\t\r\n\t}\r\n&lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;div id=&quot;titleDiv&quot;&gt;\r\n    &lt;div id=&quot;titleHeadDiv&quot;&gt;HTML5 Canvas Based Animation Circular Movement&lt;\/div&gt;\r\n    &lt;div id=&quot;titleSub1Div&quot;&gt;Key Controlled Start, Stop, Reverse, Faster, Slower&lt;\/div&gt;\r\n&lt;\/div&gt;\r\n&lt;div id=&quot;instructionsDiv&quot;&gt;Keys: P=Play, S=Stop, R=Reverse, Up=Faster, Down=Slower&lt;\/div&gt;\r\n\r\n&lt;script type='text\/javascript'&gt;\r\n<\/pre>\n<p>To keep the code readable, lines 38 &#8211; 57 define constants. <\/p>\n<p>Line 54 &#8211; 68 are dynamically appending the canvas element to the body element. Then the canvas variable on line 61 represents the drawing context for the code. <\/p>\n<pre class=\"brush: jscript; first-line: 37; title: ; notranslate\" title=\"\">\r\n\/\/ Canvas width\r\n\tvar CANVAS_WIDTH = 480;\t\r\n\t\/\/ Canvas height\t\r\n\tvar CANVAS_HEIGHT = 480;\t\r\n\t\/\/ Rotation direction constants\r\n\tvar CLOCKWISE = 'clockwise';\r\n\tvar COUNTER_CLOCKWISE = 'counter_clockwise';\r\n\t\/\/ Key name constants\r\n\tvar KEY_NAME_PLAY = 'p';\r\n\tvar KEY_NAME_STOP = 's';\r\n\tvar KEY_NAME_REVERSE = 'r';\r\n\tvar KEY_NAME_FASTER = &quot;up&quot;;\r\n\tvar KEY_NAME_SLOWER = &quot;down&quot;;\r\n\t\/\/ Constants to match JQuery Hotkeys event value type.\r\n\tvar KEY_TYPE_UP = &quot;keyup&quot;;\r\n\tvar KEY_TYPE_DOWN = &quot;keydown&quot;;\r\n\tvar FPS = 30;\t\t\t\t\/\/ Frames per second\r\n\t\/\/ Canvas center points adjusting for stroke.\r\n\tvar canvasCenterX = (CANVAS_WIDTH - 1) \/ 2;\r\n\tvar canvasCenterY = (CANVAS_HEIGHT - 1) \/ 2;\r\n\t\/\/ Create a canvas element variable.\r\n\tvar canvasElement = $(&quot;&lt;canvas width='&quot; + CANVAS_WIDTH + \r\n\t  &quot;' height='&quot; + CANVAS_HEIGHT + &quot;'&gt;&lt;\/canvas&quot;);\r\n\t\/\/ Reference to the canvas 2d context.\r\n\tvar canvas = canvasElement.get(0).getContext(&quot;2d&quot;);\r\n\t\/\/ Dynamically append a canvas element to the body tag.\r\n\tcanvasElement.appendTo('body');\t\r\n\t\/\/ Object defining the player we are rotating.\r\n<\/pre>\n<p>[ad name=&#8221;Google Adsense&#8221;]<br \/>\nThe player object is the bulk of the code to study.<\/p>\n<p>Lines 65 &#8211; 77 define a series of properties for the player object. The player simply rotates around a point.<\/p>\n<p>This example aimed at making the animation objects more independent. For example the speed property allows an independent speed.<\/p>\n<pre class=\"brush: jscript; first-line: 65; title: ; notranslate\" title=\"\">\r\n\tvar player = {\r\n\t\tcolor: &quot;#00A&quot;,\r\n\t\tx: 0,  \t\t\/\/ Starting x\r\n\t\ty: 0,\t\t\/\/ Starting y\r\n\t\tradius: 20,\t\/\/ Radius of the player\r\n\t\tdirection: CLOCKWISE,\t\/\/ CLOCKWISE or COUNTER_CLOCKWISE\r\n\t\tangle:0,\t\t\t\t\/\/ Degrees of rotation for sin and cosine\r\n\t\tspeed: .05,\t\t\t\t\/\/ Pixels per frame\r\n\t\tspeedChange: .001,\t\t\/\/ Pixels per frame\r\n\t\tspeedMax: .18,\t\t\t\/\/ Pixels per frame\r\n\t\tspeedMin: .005,\t\t\t\/\/ Pixels per frame\r\n\t\tradiusRotation:200,\t\t\/\/ Rotation circle radius\r\n\t\tisPlaying: false,\t\t\/\/ True false state animating\r\n<\/pre>\n<p>The player draw method is called from the main animation loop. The method uses the player object&#8217;s properties to draw the player. One bare number for lineWidth could have been added to properties.<\/p>\n<pre class=\"brush: jscript; first-line: 78; title: ; notranslate\" title=\"\">\r\n\t\tdraw: function() \/\/ Draw the player.\r\n\t\t{\r\n\t\t\tcanvas.fillStyle = this.color;\t\t\r\n\t\t\tcanvas.beginPath();  \r\n\t\t\tcanvas.arc(this.x,this.y,this.radius,0,Math.PI*2,true); \r\n\t\t\tcanvas.fill();\r\n\t\t\tcanvas.lineWidth = 5;\r\n    \t\tcanvas.strokeStyle = &quot;black&quot;;\r\n    \t\tcanvas.stroke();\r\n\t  \t},\r\n<\/pre>\n<p>The init method for the player object computes the starting x and y positions. The main animation init function calls the player init method.<\/p>\n<pre class=\"brush: jscript; first-line: 88; title: ; notranslate\" title=\"\">\r\n\t\tinit: function()\r\n\t\t{\r\n\t\t\tthis.x = canvasCenterX + Math.cos(this.angle) * this.radiusRotation;\r\n\t\t\tthis.y = canvasCenterY + Math.sin(this.angle) * this.radiusRotation;\r\n\t\t},\r\n<\/pre>\n<p>Handling keyboard input is central to this example. The main code captures the key input using JQuery Hotkeys. There the player object keyEvent method is called. It receives the keyType representing either a key down or key up state and the name of the key. <\/p>\n<p>The key down state is not checked but is the state for the play, stop, faster and slower key names. The key down state causes JQuery Hotkeys to repeat the key while it is down. This is acceptable for the play and stop key although we might want to limit those to a key up only state as you will see we did for the reverse key. The faster and slower keys we want to accept repetition if they are held down as we would with an accelerator or brake.<\/p>\n<p> For the reverse key, the key up state is used. If the key down state was used, the reverse key would repeat creating a chaotic result for direction.<\/p>\n<pre class=\"brush: jscript; first-line: 93; title: ; notranslate\" title=\"\">\r\n\t\tkeyEvent: function(keyType, keyName)\r\n\t\t{\r\n\t\t\t\/\/ Set to playing state.\r\n\t\t\tif (keyName == KEY_NAME_PLAY)\r\n\t\t\t{\r\n\t\t\t\tthis.isPlaying = true;\r\n\t\t\t}\r\n\t\t\t\/\/ Set to stopped state\r\n\t\t\telse if (keyName == KEY_NAME_STOP)\r\n\t\t\t{\r\n\t\t\t\tthis.isPlaying = false;\r\n\t\t\t}\r\n\t\t\t\/\/ Increase speed if playing\r\n\t\t\telse if (keyName == KEY_NAME_FASTER &amp;&amp; this.isPlaying)\r\n\t\t\t{\r\n\t\t\t\tthis.speed += this.speedChange;\r\n\t\t\t\tthis.speed = Math.min(this.speedMax, this.speed);\r\n\t\t\t}\r\n\t\t\t\/\/ Decrease speed if playing\r\n\t\t\telse if (keyName == KEY_NAME_SLOWER &amp;&amp; this.isPlaying)\r\n\t\t\t{\r\n\t\t\t\tthis.speed -= this.speedChange;\r\n\t\t\t\tthis.speed = Math.max(this.speedMin, this.speed);\r\n\t\t\t}\r\n\t\t\t\/\/ Up state of the key.\r\n\t\t\tif (keyType == KEY_TYPE_UP)\r\n\t\t\t{\r\n\t\t\t\t\/\/ Change from clockwise to counter clockwise rotation\r\n\t\t\t\tif (keyName == KEY_NAME_REVERSE &amp;&amp; this.direction == CLOCKWISE)\r\n\t\t\t\t{\r\n\t\t\t\t\tplayer.direction = COUNTER_CLOCKWISE;\r\n\t\t\t\t}\r\n\t\t\t\t\/\/ Change from counter clockwise rotation to clockwise rotation\r\n\t\t\t\telse if (keyName == KEY_NAME_REVERSE &amp;&amp; this.direction == COUNTER_CLOCKWISE)\r\n\t\t\t\t{\r\n\t\t\t\t\tplayer.direction = CLOCKWISE;\r\n\t\t\t\t}\r\n\t\t\t}\t\t\t\r\n\t\t},\r\n<\/pre>\n<p>The update method handles computation of the model data for the player object. Assuming the player is moving, identified by the isPlaying property, the new position on the rotation perimeter is computed using standard trigonometry. <\/p>\n<p>Also the direction is handled by simply incrementing or decrementing the angle property.<\/p>\n<pre class=\"brush: jscript; first-line: 132; title: ; notranslate\" title=\"\">\r\n\t\tupdate: function()\r\n\t\t{\r\n\t\t\t\/\/ Update player data model if playing.\r\n\t\t\tif (this.isPlaying)\r\n\t\t\t{\r\n\t\t\t\t\/\/ Compute the triangle coordinates from the center of rotation\r\n\t\t\t\tplayer.x = canvasCenterX + Math.cos(this.angle) * this.radiusRotation;\r\n\t\t\t\tplayer.y = canvasCenterY + Math.sin(this.angle) * this.radiusRotation;\r\n\t\t\t\t\/\/ Keep moving the rotation clockwise.\r\n\t\t\t\tif (this.direction == CLOCKWISE)\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.angle += this.speed;\r\n\t\t\t\t}\r\n\t\t\t\t\/\/ Keep moving the rotation counter clockwise.\r\n\t\t\t\telse if(this.direction == COUNTER_CLOCKWISE)\r\n\t\t\t\t{\r\n\t\t\t\t\tthis.angle -= this.speed;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t};\t\r\n<\/pre>\n<p>This is the main animation init function. The task is to initialize all components. The JQuery Hotkeys are initialized here. The player object has its own init method. <\/p>\n<p>The JQuery Hotkeys for the game are defined here. This is the syntax to listen for key events. Notice the distinction for listening to key down and key up states for each key. As mentioned above the key down states repeat so were useful for all but the reverse key where the key up state was captured.<\/p>\n<pre class=\"brush: jscript; first-line: 153; title: ; notranslate\" title=\"\">\r\n\t\/\/ Initialize game\r\n\tfunction init()\r\n\t{\r\n\t\t\/\/ Set keys for play\r\n\t\t$(document).bind(KEY_TYPE_DOWN, KEY_NAME_PLAY, keyEvent); \r\n\t\t$(document).bind(KEY_TYPE_DOWN, KEY_NAME_STOP, keyEvent); \r\n\t\t$(document).bind(KEY_TYPE_UP, KEY_NAME_REVERSE, keyEvent); \t\r\n\t\t$(document).bind(KEY_TYPE_DOWN, KEY_NAME_FASTER, keyEvent); \r\n\t\t$(document).bind(KEY_TYPE_DOWN, KEY_NAME_SLOWER, keyEvent); \r\n\t\tplayer.init();\r\n\t}\r\n<\/pre>\n<p>[ad name=&#8221;Google Adsense&#8221;]<br \/>\nThe init function registers the keyEvent function for the keys JQuery Hotkeys processes.<\/p>\n<p>We do a bit of conversion of the received event information to make it more readable to our code by creating a key name literal from the keyCode event property. <\/p>\n<p>The final step is to call the keyEvent method on our only player object and it handles the key information accordingly.<\/p>\n<pre class=\"brush: jscript; first-line: 164; title: ; notranslate\" title=\"\">\r\n\t\/\/ Evaluate key input\r\n\tfunction keyEvent(evt)\r\n\t{\r\n\t\t\/\/ Animation name for the key pressed assumed to be the char name.\r\n\t\tvar keyName = String.fromCharCode(evt.keyCode).toLowerCase();\r\n\t\t\/\/ Up arrow key\r\n\t\tif (evt.keyCode == 38)\r\n\t\t{\r\n\t\t\tkeyName = KEY_NAME_FASTER;\t\r\n\t\t}\r\n\t\t\/\/ Down arrow key\r\n\t\tif (evt.keyCode == 40)\r\n\t\t{\r\n\t\t\tkeyName = KEY_NAME_SLOWER;\t\r\n\t\t}\r\n\t\t\/\/ Call animation object keyEvent methods.\r\n\t\tplayer.keyEvent(evt.type,keyName);\r\n\t\t\r\n\t}\r\n<\/pre>\n<p>The main update and draw functions are called with the animation timer. <\/p>\n<p>The update function recomputes the model data. In this case there is just updating the player model data. <\/p>\n<p>For the draw method, the canvas draw operations are included and then the player draw method is called.<\/p>\n<p>The separation of these two allows for a different timing solution that would separate the processing timing of the data and the drawing.<\/p>\n<pre class=\"brush: jscript; first-line: 183; title: ; notranslate\" title=\"\">\r\n\t\/\/ Update the model data.\r\n\tfunction update() \r\n\t{  \r\n\t\t\/\/ Call the animation object update events.\r\n\t\tplayer.update();\r\n\t}\r\n\t\/\/ Draw the views\r\n\tfunction draw() \r\n\t{\r\n\t\t\/\/Canvas object clear and draw background and border.\r\n\t\tcanvas.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);\r\n\t\tcanvas.strokeStyle = &quot;red&quot;;\r\n\t\tcanvas.strokeRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);\r\n\t\tcanvas.fillStyle = &quot;#ccc&quot;;\r\n\t\tcanvas.fillRect(.5, .5, CANVAS_WIDTH-1, CANVAS_HEIGHT-1);\r\n\t\t\/\/ Call the animation object update events.\r\n\t\tplayer.draw();\r\n\t}\r\n<\/pre>\n<p>This is were the animation starts. Once JQuery fires its ready function, the animation init method is called and then a timer interval is calls the update and draw functions to drive the animation.<\/p>\n<pre class=\"brush: jscript; first-line: 201; title: ; notranslate\" title=\"\">\r\n\t\/\/ JQuery ready\r\n\t$(function() \r\n\t{\r\n\t\t\/\/ Start here.\r\n\t\t\r\n\t\t\/\/ Initialize components\r\n\t\tinit();\r\n\t\t\/\/ Timer for animation.\r\n\t\tsetInterval(function() \r\n\t\t{\r\n\t\t\tupdate();\r\n\t\t\tdraw();\r\n\t\t}, 1000\/FPS);\r\n\t\t\t\r\n\t});\r\n\r\n<\/pre>\n<pre class=\"brush: xml; first-line: 216; title: ; notranslate\" title=\"\">\r\n&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n\r\n<\/pre>\n<div id=\"fb-root\"><\/div>\n<p><script src=\"https:\/\/connect.facebook.net\/en_US\/all.js#appId=105467682877384&amp;xfbml=1\"><\/script><fb:like href=\"https:\/\/www.lonhosford.com\/lonblog\/2011\/10\/10\/html5-canvas-circular-rotation-animation-using-jquery-hotkeys-for-user-interaction\/\" send=\"true\" width=\"450\" show_faces=\"true\" font=\"\"><\/fb:like><\/p>\n<div id=\"fb-root\"><\/div>\n<p><script src=\"https:\/\/connect.facebook.net\/en_US\/all.js#xfbml=1\"><\/script><fb:comments href=\"https:\/\/www.lonhosford.com\/lonblog\/2011\/10\/10\/html5-canvas-circular-rotation-animation-using-jquery-hotkeys-for-user-interaction\/\" num_posts=\"3\" width=\"500\"><\/fb:comments><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This takes my first example animating a filled circle rotating around the center point to a user interaction level using JQuery Hotkeys. JQuery Hotkeys is a plug-in that lets you easily add and remove handlers for keyboard events anywhere in your code supporting almost any key combination using JQuery. This version of the animation allows [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[74,75,76],"class_list":["post-2402","post","type-post","status-publish","format-standard","hentry","category-general","tag-html","tag-html5-2","tag-html5-canvas"],"_links":{"self":[{"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/posts\/2402","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/comments?post=2402"}],"version-history":[{"count":30,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/posts\/2402\/revisions"}],"predecessor-version":[{"id":3670,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/posts\/2402\/revisions\/3670"}],"wp:attachment":[{"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/media?parent=2402"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/categories?post=2402"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lonhosford.com\/lonblog\/wp-json\/wp\/v2\/tags?post=2402"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}