{"id":4878,"date":"2018-04-23T05:33:02","date_gmt":"2018-04-23T05:33:02","guid":{"rendered":"http:\/\/a1webdesignteam.com\/blog\/?p=4878"},"modified":"2018-04-23T05:36:47","modified_gmt":"2018-04-23T05:36:47","slug":"creating-a-panning-effect-for-svg","status":"publish","type":"post","link":"https:\/\/a1webdesignteam.com\/blog\/creating-a-panning-effect-for-svg\/","title":{"rendered":"Creating a Panning Effect for SVG"},"content":{"rendered":"<p>Earlier this month on the\u00a0Animation at Work Slack, we had a discussion about finding a way to let users pan inside an SVG.<\/p>\n<p>I made this demo below to show how I&#8217;d approach this question:<\/p>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_dmjYpR\" class=\"cp_embed_iframe \" title=\"Demo - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/dmjYpR?height=625&amp;theme-id=1&amp;slug-hash=dmjYpR&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Demo%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"625\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<p>Here are the four steps to make the above demo work:<\/p>\n<ol class=\"SVG-panning-summary\">\n<li><a href=\"https:\/\/css-tricks.com\/creating-a-panning-effect-for-svg\/#mouse-touch-events\">Get mouse and touch events from the user<\/a><\/li>\n<li><a href=\"https:\/\/css-tricks.com\/creating-a-panning-effect-for-svg\/#calculate-mouse-offsets\">Calculate the mouse offsets from its origin<\/a><\/li>\n<li><a href=\"https:\/\/css-tricks.com\/creating-a-panning-effect-for-svg\/#save-updated-viewbox\">Save the new\u00a0<code>viewBox<\/code>\u00a0coordinates<\/a><\/li>\n<li><a href=\"https:\/\/css-tricks.com\/creating-a-panning-effect-for-svg\/#handle-dynamic-viewport\">Handle dynamic viewport<\/a><\/li>\n<\/ol>\n<p>Let&#8217;s check those steps one by one more thoroughly.<\/p>\n<h3 id=\"mouse-touch-events\" class=\"has-header-link\">#1. Mouse &amp; Touch Events<\/h3>\n<p>To get the mouse or touch position, we first need to add event listeners on our SVG. We can use the\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Pointer_events\">Pointer Events<\/a>\u00a0to handle all kind of pointers (mouse\/touch\/stylus\/&#8230;) but those events are not yet supported by all browsers. We will need to add some fallback to make sure all users will be able to drag the SVG.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ We select the SVG into the page<\/span>\r\n<span class=\"token keyword\">var<\/span> svg <span class=\"token operator\">=<\/span> document<span class=\"token punctuation\">.<\/span><span class=\"token function\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'svg'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token comment\" spellcheck=\"true\">\/\/ If browser supports pointer events<\/span>\r\n<span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>window<span class=\"token punctuation\">.<\/span>PointerEvent<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'pointerdown'<\/span><span class=\"token punctuation\">,<\/span> onPointerDown<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Pointer is pressed<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'pointerup'<\/span><span class=\"token punctuation\">,<\/span> onPointerUp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Releasing the pointer<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'pointerleave'<\/span><span class=\"token punctuation\">,<\/span> onPointerUp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Pointer gets out of the SVG area<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'pointermove'<\/span><span class=\"token punctuation\">,<\/span> onPointerMove<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Pointer is moving<\/span>\r\n<span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Add all mouse events listeners fallback<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'mousedown'<\/span><span class=\"token punctuation\">,<\/span> onPointerDown<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Pressing the mouse<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'mouseup'<\/span><span class=\"token punctuation\">,<\/span> onPointerUp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Releasing the mouse<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'mouseleave'<\/span><span class=\"token punctuation\">,<\/span> onPointerUp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Mouse gets out of the SVG area<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'mousemove'<\/span><span class=\"token punctuation\">,<\/span> onPointerMove<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Mouse is moving<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Add all touch events listeners fallback<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'touchstart'<\/span><span class=\"token punctuation\">,<\/span> onPointerDown<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Finger is touching the screen<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'touchend'<\/span><span class=\"token punctuation\">,<\/span> onPointerUp<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Finger is no longer touching the screen<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'touchmove'<\/span><span class=\"token punctuation\">,<\/span> onPointerMove<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ Finger is moving<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>Because we could have touch events and pointer events, we need to create a tiny function to returns to coordinates either from the first finger either from a pointer.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ This function returns an object with X &amp; Y values from the pointer event<\/span>\r\n<span class=\"token keyword\">function<\/span> getPointFromEvent <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token keyword\">var<\/span> point <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>x<span class=\"token punctuation\">:<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span> y<span class=\"token punctuation\">:<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ If event is triggered by a touch event, we get the position of the first finger<\/span>\r\n  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">[<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>clientX<span class=\"token punctuation\">;<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">[<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>clientY<span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>clientX<span class=\"token punctuation\">;<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>clientY<span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span>\r\n  \r\n  <span class=\"token keyword\">return<\/span> point<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>Once the page is ready and waiting for any user interactions, we can start handling the mousedown\/touchstart events to save the original coordinates of the pointer and create a variable to let us know if the pointer is down or not.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ This variable will be used later for move events to check if pointer is down or not<\/span>\r\n<span class=\"token keyword\">var<\/span> isPointerDown <span class=\"token operator\">=<\/span> <span class=\"token boolean\">false<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token comment\" spellcheck=\"true\">\/\/ This variable will contain the original coordinates when the user start pressing the mouse or touching the screen<\/span>\r\n<span class=\"token keyword\">var<\/span> pointerOrigin <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\r\n  x<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span>\r\n  y<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span>\r\n<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token comment\" spellcheck=\"true\">\/\/ Function called by the event listeners when user start pressing\/touching<\/span>\r\n<span class=\"token keyword\">function<\/span> <span class=\"token function\">onPointerDown<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  isPointerDown <span class=\"token operator\">=<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ We set the pointer as down<\/span>\r\n  \r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We get the pointer position on click\/touchdown so we can get the value once the user starts to drag<\/span>\r\n  <span class=\"token keyword\">var<\/span> pointerPosition <span class=\"token operator\">=<\/span> <span class=\"token function\">getPointFromEvent<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  pointerOrigin<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> pointerPosition<span class=\"token punctuation\">.<\/span>x<span class=\"token punctuation\">;<\/span>\r\n  pointerOrigin<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> pointerPosition<span class=\"token punctuation\">.<\/span>y<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<h3 id=\"calculate-mouse-offsets\" class=\"has-header-link\">#2. Calculate Mouse Offsets<\/h3>\n<p>Now that we have the coordinates of the original position where the user started to drag inside the SVG, we can calculate the distance between the current pointer position and its origin. We do this for both the X and Y axis and we apply the calculated values on the\u00a0<code>viewBox<\/code>.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ We save the original values from the viewBox<\/span>\r\n<span class=\"token keyword\">var<\/span> viewBox <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\r\n  x<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span>\r\n  y<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span>\r\n  width<span class=\"token punctuation\">:<\/span> <span class=\"token number\">500<\/span><span class=\"token punctuation\">,<\/span>\r\n  height<span class=\"token punctuation\">:<\/span> <span class=\"token number\">500<\/span>\r\n<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token comment\" spellcheck=\"true\">\/\/ The distances calculated from the pointer will be stored here<\/span>\r\n<span class=\"token keyword\">var<\/span> newViewBox <span class=\"token operator\">=<\/span> <span class=\"token punctuation\">{<\/span>\r\n  x<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span><span class=\"token punctuation\">,<\/span>\r\n  y<span class=\"token punctuation\">:<\/span> <span class=\"token number\">0<\/span>\r\n<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token comment\" spellcheck=\"true\">\/\/ Function called by the event listeners when user start moving\/dragging<\/span>\r\n<span class=\"token keyword\">function<\/span> onPointerMove <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Only run this function if the pointer is down<\/span>\r\n  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token operator\">!<\/span>isPointerDown<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n    <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ This prevent user to do a selection on the page<\/span>\r\n  event<span class=\"token punctuation\">.<\/span><span class=\"token function\">preventDefault<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Get the pointer position<\/span>\r\n  <span class=\"token keyword\">var<\/span> pointerPosition <span class=\"token operator\">=<\/span> <span class=\"token function\">getPointFromEvent<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We calculate the distance between the pointer origin and the current position<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ The viewBox x &amp; y values must be calculated from the original values and the distances<\/span>\r\n  newViewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span> <span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>x<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  newViewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span> <span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>y<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We create a string with the new viewBox values<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ The X &amp; Y values are equal to the current viewBox minus the calculated distances<\/span>\r\n  <span class=\"token keyword\">var<\/span> viewBoxString <span class=\"token operator\">=<\/span> <span class=\"token template-string\"><span class=\"token string\">`<\/span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>newViewBox<span class=\"token punctuation\">.<\/span>x<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span> <span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>newViewBox<span class=\"token punctuation\">.<\/span>y<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span> <span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>viewBox<span class=\"token punctuation\">.<\/span>width<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span> <span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${<\/span>viewBox<span class=\"token punctuation\">.<\/span>height<span class=\"token interpolation-punctuation punctuation\">}<\/span><\/span><span class=\"token string\">`<\/span><\/span><span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We apply the new viewBox values onto the SVG<\/span>\r\n  svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">setAttribute<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'viewBox'<\/span><span class=\"token punctuation\">,<\/span> viewBoxString<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  \r\n  document<span class=\"token punctuation\">.<\/span><span class=\"token function\">querySelector<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'.viewbox'<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>innerHTML <span class=\"token operator\">=<\/span> viewBoxString<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p class=\"explanation\">If you don&#8217;t feel comfortable with the concept of\u00a0<code>viewBox<\/code>, I would suggest you first read this\u00a0great article\u00a0by Sara Soueidan.<\/p>\n<h3 id=\"save-updated-viewbox\" class=\"has-header-link\">#3. Save Updated viewBox<\/h3>\n<p>Now that the\u00a0<code>viewBox<\/code>\u00a0has been updated, we need to save its new values when the user stops dragging the SVG.<\/p>\n<p>This step is important because otherwise we would always calculate the pointer offsets from the original\u00a0<code>viewBox<\/code>\u00a0values and the user will drag the SVG from the starting point every time.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token keyword\">function<\/span> <span class=\"token function\">onPointerUp<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ The pointer is no longer considered as down<\/span>\r\n  isPointerDown <span class=\"token operator\">=<\/span> <span class=\"token boolean\">false<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We save the viewBox coordinates based on the last pointer offsets<\/span>\r\n  viewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> newViewBox<span class=\"token punctuation\">.<\/span>x<span class=\"token punctuation\">;<\/span>\r\n  viewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> newViewBox<span class=\"token punctuation\">.<\/span>y<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<h3 id=\"handle-dynamic-viewport\" class=\"has-header-link\">#4. Handle Dynamic Viewport<\/h3>\n<p>If we set a custom width on our SVG, you may notice while dragging on the demo below that the bird is moving either faster or slower than your pointer.<\/p>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_QmRbQZ\" class=\"cp_embed_iframe \" title=\"Dynamic viewport - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/QmRbQZ?height=500&amp;theme-id=1&amp;slug-hash=QmRbQZ&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Dynamic%20viewport%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"500\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<p>On the original demo, the SVG&#8217;s width is exactly matching its\u00a0<code>viewBox<\/code>\u00a0width. The\u00a0<i>actual size<\/i>\u00a0of your SVG may also be called\u00a0<code>viewport<\/code>. In a perfect situation, when the user is moving their pointer by\u00a0<code>1px<\/code>, we want the\u00a0<code>viewBox<\/code>\u00a0to translate by\u00a0<code>1px<\/code>.<\/p>\n<p>But, most of the time, the SVG has a responsive size and the\u00a0<code>viewBox<\/code>\u00a0will most likely not match the SVG\u00a0<code>viewport<\/code>. If the SVG&#8217;s width is twice as big than the\u00a0<code>viewBox<\/code>, when the user moves their pointer by\u00a0<code>1px<\/code>, the image inside the SVG will translate by\u00a0<code>2px<\/code>.<\/p>\n<p>To fix this, we need to calculate the\u00a0<b>ratio<\/b>\u00a0between the\u00a0<code>viewBox<\/code>\u00a0and the\u00a0<code>viewport<\/code>\u00a0and apply this ratio while calculating the new\u00a0<code>viewBox<\/code>. This ratio must also be updated whenever the SVG size may change.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ Calculate the ratio based on the viewBox width and the SVG width<\/span>\r\n<span class=\"token keyword\">var<\/span> ratio <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>width <span class=\"token operator\">\/<\/span> svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">getBoundingClientRect<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>width<span class=\"token punctuation\">;<\/span>\r\nwindow<span class=\"token punctuation\">.<\/span><span class=\"token function\">addEventListener<\/span><span class=\"token punctuation\">(<\/span><span class=\"token string\">'resize'<\/span><span class=\"token punctuation\">,<\/span> <span class=\"token keyword\">function<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  ratio <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>width <span class=\"token operator\">\/<\/span> svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">getBoundingClientRect<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span>width<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<p>Once we know the\u00a0<b>ratio<\/b>, we need to multiply the mouse offsets by the ratio to proportionally increase or reduce the offsets.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token keyword\">function<\/span> onMouseMove <span class=\"token punctuation\">(<\/span>e<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">]<\/span>\r\n  newViewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>x<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">*<\/span> ratio<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  newViewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> viewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>y<span class=\"token punctuation\">)<\/span> <span class=\"token operator\">*<\/span> ratio<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">[<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">.<\/span><span class=\"token punctuation\">]<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>Here&#8217;s how this works with a smaller\u00a0<code>viewport<\/code>\u00a0than the\u00a0<code>viewBox<\/code>\u00a0width:<\/p>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_qoyXaY\" class=\"cp_embed_iframe \" title=\"Smaller viewport - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/qoyXaY?height=340&amp;theme-id=1&amp;slug-hash=qoyXaY&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Smaller%20viewport%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"340\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<p>And another demo with a\u00a0<code>viewport<\/code>\u00a0bigger than the\u00a0<code>viewBox<\/code>\u00a0width:<\/p>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_ZxjJXy\" class=\"cp_embed_iframe \" title=\"Bigger viewport - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/ZxjJXy?height=700&amp;theme-id=1&amp;slug-hash=ZxjJXy&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Bigger%20viewport%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"700\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<h3 id=\"conclusion\" class=\"has-header-link\">#[Bonus] Optimizing the code<\/h3>\n<p>To make our code a bit shorter, there are two very useful concepts in SVG we could use.<\/p>\n<h4 id=\"svg-point\" class=\"has-header-link\">#SVG Points<\/h4>\n<p>The first concept is to use\u00a0<a href=\"https:\/\/www.w3.org\/TR\/SVG\/single-page.html#coords-InterfaceSVGPoint\">SVG Points<\/a>\u00a0instead of basic Javascript objects to save the pointer&#8217;s positions. After creating a new SVG Point variable, we can apply some Matrix Transformation on it to convert the position relative to the screen to a position relative to the current SVG user units.<\/p>\n<p>Check the code below to see how the functions\u00a0<code>getPointFromEvent()<\/code>\u00a0and\u00a0<code>onPointerDown()<\/code>\u00a0have changed.<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ Create an SVG point that contains x &amp; y values<\/span>\r\n<span class=\"token keyword\">var<\/span> point <span class=\"token operator\">=<\/span> svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">createSVGPoint<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token keyword\">function<\/span> getPointFromEvent <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">[<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>clientX<span class=\"token punctuation\">;<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>targetTouches<span class=\"token punctuation\">[<\/span><span class=\"token number\">0<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">.<\/span>clientY<span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>clientX<span class=\"token punctuation\">;<\/span>\r\n    point<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">=<\/span> event<span class=\"token punctuation\">.<\/span>clientY<span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span>\r\n  \r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We get the current transformation matrix of the SVG and we inverse it<\/span>\r\n  <span class=\"token keyword\">var<\/span> invertedSVGMatrix <span class=\"token operator\">=<\/span> svg<span class=\"token punctuation\">.<\/span><span class=\"token function\">getScreenCTM<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">.<\/span><span class=\"token function\">inverse<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  \r\n  <span class=\"token keyword\">return<\/span> point<span class=\"token punctuation\">.<\/span><span class=\"token function\">matrixTransform<\/span><span class=\"token punctuation\">(<\/span>invertedSVGMatrix<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span>\r\n\r\n<span class=\"token keyword\">var<\/span> pointerOrigin<span class=\"token punctuation\">;<\/span>\r\n<span class=\"token keyword\">function<\/span> <span class=\"token function\">onPointerDown<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  isPointerDown <span class=\"token operator\">=<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">;<\/span> <span class=\"token comment\" spellcheck=\"true\">\/\/ We set the pointer as down<\/span>\r\n  \r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We get the pointer position on click\/touchdown so we can get the value once the user starts to drag<\/span>\r\n  pointerOrigin <span class=\"token operator\">=<\/span> <span class=\"token function\">getPointFromEvent<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>By using SVG Points, you don&#8217;t even have to handle transformations applied on your SVG! Compare the following two examples where the first is broken when a rotation is applied on the SVG and the second example uses SVG Points.<\/p>\n<div class=\"svg-panning-demos-aside\">\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_MVNQrG\" class=\"cp_embed_iframe \" title=\"Demo + transformation - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/MVNQrG?height=400&amp;theme-id=1&amp;slug-hash=MVNQrG&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Demo%20%2B%20transformation%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"400\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_LdwQzK\" class=\"cp_embed_iframe \" title=\"Demo Bonus + transform - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/LdwQzK?height=400&amp;theme-id=1&amp;slug-hash=LdwQzK&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Demo%20Bonus%20%2B%20transform%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"400\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n<\/div>\n<h4 id=\"SVG-Animated-Rect\" class=\"has-header-link\">#SVG Animated Rect<\/h4>\n<p>The second unknown concept in SVG we can use to shorten our code, is the usage of\u00a0Animated Rect.<\/p>\n<p>Because the\u00a0<code>viewBox<\/code>\u00a0is actually considered as an SVG Rectangle (x, y, width, height), we can create a variable from its base value that will automatically update the\u00a0<code>viewBox<\/code>\u00a0if we update this variable.<\/p>\n<p>See how easier it is now to update the\u00a0<code>viewBox<\/code>\u00a0of our SVG!<\/p>\n<pre class=\" language-javascript\"><code class=\" language-javascript\"><span class=\"token comment\" spellcheck=\"true\">\/\/ We save the original values from the viewBox<\/span>\r\n<span class=\"token keyword\">var<\/span> viewBox <span class=\"token operator\">=<\/span> svg<span class=\"token punctuation\">.<\/span>viewBox<span class=\"token punctuation\">.<\/span>baseVal<span class=\"token punctuation\">;<\/span>\r\n\r\n<span class=\"token keyword\">function<\/span> onPointerMove <span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n  <span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token operator\">!<\/span>isPointerDown<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span>\r\n    <span class=\"token keyword\">return<\/span><span class=\"token punctuation\">;<\/span>\r\n  <span class=\"token punctuation\">}<\/span>\r\n  event<span class=\"token punctuation\">.<\/span><span class=\"token function\">preventDefault<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Get the pointer position as an SVG Point<\/span>\r\n  <span class=\"token keyword\">var<\/span> pointerPosition <span class=\"token operator\">=<\/span> <span class=\"token function\">getPointFromEvent<\/span><span class=\"token punctuation\">(<\/span>event<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ Update the viewBox variable with the distance from origin and current position<\/span>\r\n  <span class=\"token comment\" spellcheck=\"true\">\/\/ We don't need to take care of a ratio because this is handled in the getPointFromEvent function<\/span>\r\n  viewBox<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span><span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>x <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>x<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n  viewBox<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span><span class=\"token operator\">=<\/span> <span class=\"token punctuation\">(<\/span>pointerPosition<span class=\"token punctuation\">.<\/span>y <span class=\"token operator\">-<\/span> pointerOrigin<span class=\"token punctuation\">.<\/span>y<span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span>\r\n<span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>And here is the final demo. See how much shorter the code is now? \ud83d\ude00<\/p>\n<div class=\"cp_embed_wrapper resizable\">\n<p><iframe loading=\"lazy\" id=\"cp_embed_WzqmoY\" class=\"cp_embed_iframe \" title=\"Demo Bonus - SVG Panning\" src=\"https:\/\/codepen.io\/Mamboleoo\/embed\/WzqmoY?height=625&amp;theme-id=1&amp;slug-hash=WzqmoY&amp;default-tab=result&amp;user=Mamboleoo&amp;embed-version=2&amp;pen-title=Demo%20Bonus%20-%20SVG%20Panning\" name=\"CodePen Embed\" width=\"100%\" height=\"625\" frameborder=\"0\" scrolling=\"no\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n<div class=\"win-size-grip\"><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Earlier this month on the\u00a0Animation at Work Slack, we had a discussion about finding a way to let users pan inside an SVG. I made this demo below to show how I&#8217;d approach this question: Here are the four steps to make the above demo work: Get mouse and touch events from the user Calculate [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4879,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0},"categories":[5],"tags":[51],"_links":{"self":[{"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/posts\/4878"}],"collection":[{"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/comments?post=4878"}],"version-history":[{"count":0,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/posts\/4878\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/media\/4879"}],"wp:attachment":[{"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/media?parent=4878"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/categories?post=4878"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/a1webdesignteam.com\/blog\/wp-json\/wp\/v2\/tags?post=4878"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}