- Jump To … +
  ./demo/modules/canvas-minimap.js ./demo/modules/canvas-scene-editor.js ./demo/modules/demo-m006-c1.js ./demo/modules/demo-m006-c2.js ./demo/modules/demo-m006-c3.js ./demo/modules/demo-m006-c4.js ./demo/modules/demo-m006-utils.js ./demo/modules/dom-entity-editor.js ./demo/modules/entity-copy-paste.js ./demo/modules/entity-manipulation-gui.js ./demo/modules/entity-navigation.js ./demo/modules/entity-ring-builder.js ./demo/modules/london-crime-lines.js ./demo/modules/london-crime-stacked-bars.js ./demo/modules/lottie-loader.js ./demo/modules/simple-chart-frame.js ./demo/modules/simple-graph-lines.js ./demo/modules/simple-graph-stacked-bars.js ./demo/modules/wikipedia-views-spiral-chart.js ./demo/snippets/animated-highlight-gradient-text-snippet.js ./demo/snippets/animated-hover-gradient-snippet.js ./demo/snippets/animated-word-gradient-snippet.js ./demo/snippets/before-after-slider-infographic.js ./demo/snippets/bubbles-text-snippet.js ./demo/snippets/green-box-snippet.js ./demo/snippets/jazzy-button-snippet.js ./demo/snippets/page-performance-snippet-test.js ./demo/snippets/page-performance-snippet.js ./demo/snippets/pan-image-snippet.js ./demo/snippets/placeholder-effect-snippet.js ./demo/snippets/ripple-effect-snippet.js ./demo/snippets/risograph-text-gradient-snippet.js ./demo/snippets/spotlight-text-snippet-test.js ./demo/snippets/spotlight-text-snippet.js ./demo/snippets/swirling-stripes-text-snippet.js ./demo/snippets/text-snippet-helper.js ./demo/snippets/word-highlighter-snippet.js ./demo/snippets/worley-text-gradient-snippet.js

- §

  # Demo Snippets 004

  Snippets included in the Scrawl-canvas demo/snippets folder

  Related files:

  - Snippets included in the Scrawl-canvas demo/snippets folder
  - Animated hover gradient snippet
  - Animated word gradient snippet
  - Green box snippet
  - Jazzy button snippet
  - Page performance snippet
  - Pan image snippet
  - Placeholder effect snippet
  - Ripple effect snippet
  - Spotlight text snippet
  - Word highlighter snippet
- §

  ### ‘Pan image’ snippet

  Purpose: Displays a pan effect on an image, where users can click and drag the image to explore it

  Function input:

  - the DOM element - a block element - usually a <div> element - containing one or more <img> elements

  Function output: a Javascript object will be returned, containing the following attributes

      {
          element     // the Scrawl-canvas wrapper for the DOM element supplied to the function
          canvas      // the Scrawl-canvas wrapper for the snippet's canvas
          animation   // the Scrawl-canvas animation object
          demolish    // remove the snippet from the Scrawl-canvas library
      }

  ##### Usage example:

      import panImage from './relative/or/absolute/path/to/this/file.js';

      let myElements = document.querySelectorAll('.some-class');

      myElements.forEach(el => panImage(scrawl, el));

- §

  Effects on the element:

  - Sets the element background to `transparent`
  - Imports any <img> elements it finds in the element, switching their display styling to ‘none’

      export default function (scrawl, el) {

- §

  Apply the snippet to the DOM element

          const snippet = scrawl.makeSnippet({
              domElement: el,
          });

          if (snippet) {

- §

  Set some convenience variables

              const canvas = snippet.canvas,
                  animation = snippet.animation,
                  group = canvas.base.name,
                  name = snippet.element.name;

- §

  Set the DOM element’s background color to transparent so we can see the new canvas

              el.style.backgroundColor = 'transparent';

- §

  Grab all the child <img> elements

              const images = el.querySelectorAll('img');

- §

  If the coder gave the DOM element a sane id attribute value then all our names will be sensible names. If not, then Scrawl-canvas will have computer-generated an id for the element. These generated names often include characters that do not sit well with the CSS/JS querySelector string requirements. So we have to make the name safe to be used as a search string

              let imgName = `${name}-image`;
              imgName = imgName.replace('.', '');

- §

  Update all the <img> elements we found. In particular:

  - add the name to the element’s class
  - set the indexed name as the element’s id value
  - set the element’s display styling attrinute to ‘none’ - we don’t weant it displaying in the DOM, we need it to display in the new canvas

              images.forEach((img, index) => {

                  let classes = img.getAttribute('class')

                  if (!classes) classes = '';
                  classes += ` ${imgName}`;

                  img.setAttribute('class', classes);
                  img.id = `${imgName}-${index}`;

                  img.style.display = 'none';
              });

- §

  Import all the images we found into Scrawl-canvas

              scrawl.importDomImage(`.${imgName}`);

- §

  Configuration - coders can set ‘data-x’ and ‘data-y’ attributes on the DOM element to indicate which area of the image should be displayed in the new canvas when the page first loads

              const copyStartX = el.dataset.x || 0,
                  copyStartY = el.dataset.y || 0;

- §

  We will display the first image we found in a Picture entity

              const [w, h] = canvas.get('dimensions');

              const asset = scrawl.library.asset[`${imgName}-0`];

              const panImage = scrawl.makePicture({

                  name: `${imgName}-panimage`,
                  group,
                  asset,
                  width: w,
                  height: h,

                  copyWidth: w,
                  copyHeight: h,
                  copyStartX,
                  copyStartY,
              });

              let cursor = 'grab',
                  lastX = 0,
                  lastY = 0,
                  aspectWidth = 1,
                  aspectHeight = 1;

- §

  The pan effect, where the user can click and drag the image within its container to explore large images in small areas. To achieve the effect we split the functionality across three separate event listeners, which capture the three key actions associated with the effect: starting a drag; continuing a drag; and ending a drag.

              scrawl.addListener('down', () => {

                  const here = canvas.here;

                  if (here.active && 'grab' === cursor) {

                      cursor = 'grabbing';
                      el.style.cursor = cursor;

                      const {x, y} = here;

                      lastX = x;
                      lastY = y;
                  }
              }, el);

              scrawl.addListener('move', () => {

                  if ('grabbing' === cursor) {

                      const {x, y} = canvas.here;

                      const dx = lastX - x,
                          dy = lastY - y;

                      panImage.setDelta({
                          copyStartX: dx * aspectWidth,
                          copyStartY: dy * aspectHeight,
                      });

                      lastX = x;
                      lastY = y;
                   }
              }, el);

              scrawl.addListener(['up', 'leave'], () => {

                  if ('grabbing' === cursor) {

                      cursor = 'grab';
                      el.style.cursor = cursor;

                      const {x, y} = canvas.here;

                      const dx = lastX - x,
                          dy = lastY - y;

                      panImage.setDelta({
                          copyStartX: dx * aspectWidth,
                          copyStartY: dy * aspectHeight,
                      });

                      lastX = 0;
                      lastY = 0;
                  }
              }, el);

- §

  We’re checking dimensions here partly because images load asynchronously and during the early part of that process their <img> elements will supply incorrect information to the script which is trying to set up the canvas element which will (eventually) host their image data. But we also need to check because the script has no way of knowing what sort of image it is dealing with - for instance an <img> element with a “srcset” attribute will load new images if the user decides to expand their browser’s window - which would have an impact on the assumptions we make in our scaling and dragging calculations.

              const checkDimensions = function () {

                  const [canvasWidth, canvasHeight] = canvas.get('dimensions');
                  const [imageWidth, imageHeight] = panImage.get('dimensions');

- §

  check to see if there’s been any resize browser resize or CSS layout change that affects our DOM element

                  if (canvasWidth !== imageWidth || canvasHeight !== imageHeight) {

                      const [sourceWidth, sourceHeight] = panImage.get('sourceDimensions');

- §

  We want the image we paint in our canvas to keep its asset’s original aspect ratio

                      aspectWidth = canvasWidth / sourceWidth;
                      aspectHeight = canvasHeight / sourceHeight;

- §

  We want the zoom level to be x3 (along the width) with respect to the DOM element’s dimensions

                      const setToHalfWidth = 50 / (aspectWidth * 100);

                      const ratioWidth = aspectWidth * setToHalfWidth * 100,
                          ratioHeight = aspectHeight * setToHalfWidth * 100;

                      panImage.set({
                          width: canvasWidth,
                          height: canvasHeight,
                          copyWidth: ratioWidth + '%',
                          copyHeight: ratioHeight + '%',
                      });
                  }
              };

- §

  Invoke our ‘checkDimensions’ function at the start of every Display cycle

              animation.set({
                  commence: checkDimensions,
              });
          }

- §

  Return the snippet, so coders can access the snippet’s parts - in case they need to tweak the output to meet the web page’s specific requirements

          return snippet;
      }