Forum Discussion

JohnCooper-be3c's avatar
JohnCooper-be3c
Community Member
5 months ago
Solved

Drawing two intersecting lines on a graph in Storyline

So I have  a graph say, with x and y-axes from zero to 100, I have two variables - let's call them x_value and y_value that I have calculated in my Storyline course.  I want to draw two intersecting ...
  • BombardiloCroc's avatar
    5 months ago

    Hello John,

    I don’t recommend using the Storyline lines for a few reasons. Instead, you can create a 100% transparent shape (don't use a shape with no fill! add a fill and set the transparency to 100%) and use the code below. 

    Don’t forget to change the data-model-id and execute the JavaScript when the timeline starts.

    How the code works

    Overlay injection – A full‑size overlay (.line-overlay) is appended inside the 100% transparent shape container (data-model-id="6Wm53ht8QUh").

    Dynamic lines – Two absolutely positioned <div> elements (vertical and horizontal) are placed inside the overlay. Their left and top are set with percentages, so they stay in the correct spot even when the slide scales.

    Polling loop – Every pollRateMs milliseconds the script compares Storyline variables x_value and y_value to the previous values. When either changes, the lines are redrawn and animated once (GSAP if available, CSS transition otherwise).

    How to customise

    Update frequency – Edit the pollRateMs constant (e.g., 100 for 10 updates per second).

    Line thickness – Change width (vertical line) or height (horizontal line) in the marked “thickness” style lines.

    Line colour – Adjust the background property in the marked “color” style lines.

    Animation speed / easing – Modify duration and ease in the GSAP block, or the transition shorthand in the CSS‑fallback block.

    /*  Live tracking + single animation   */
    (function () {
    
      /* EDIT → change how often the script checks for new values (ms) */
      const pollRateMs = 200;             // 5× per second
    
      /* internal refs */
      const graphSelector = '[data-model-id="6cPIlzq1zZH"]';
      const player        = GetPlayer();
      let   lastX         = player.GetVar('x_value');
      let   lastY         = player.GetVar('y_value');
    
      /* first draw */
      drawLines(lastX, lastY);
    
      /* watch Storyline variables */
      setInterval(() => {
        const x = player.GetVar('x_value');
        const y = player.GetVar('y_value');
        if (x !== lastX || y !== lastY) {
          drawLines(x, y);
          lastX = x;
          lastY = y;
        }
      }, pollRateMs);
    
      /* ---------- core ---------- */
      function drawLines(x_value, y_value) {
        const graph = document.querySelector(graphSelector);
        if (!graph) return;
    
        /* overlay that stretches 100 % of the graph */
        let overlay = graph.querySelector('.line-overlay');
        if (!overlay) {
          overlay = Object.assign(document.createElement('div'), {
            className: 'line-overlay'
          });
          Object.assign(overlay.style, {
            position: 'absolute',
            inset: '0',
            pointerEvents: 'none'
          });
          graph.appendChild(overlay);
        }
    
        /* vertical line */
        let v = overlay.querySelector('.v');
        if (!v) {
          v = Object.assign(document.createElement('div'), { className: 'v' });
          Object.assign(v.style, {
            position: 'absolute',
            top: '0',
            bottom: '0',
            /* EDIT → vertical line thickness  */
            width: '1px',
            /* EDIT → vertical line color      */
            background: '#d61f27',
            transformOrigin: 'bottom center'
          });
          overlay.appendChild(v);
        }
        v.style.left = x_value + '%';
    
        /* horizontal line */
        let h = overlay.querySelector('.h');
        if (!h) {
          h = Object.assign(document.createElement('div'), { className: 'h' });
          Object.assign(h.style, {
            position: 'absolute',
            left: '0',
            right: '0',
            /* EDIT → horizontal line thickness */
            height: '1px',
            /* EDIT → horizontal line color     */
            background: '#d61f27',
            transformOrigin: 'left center'
          });
          overlay.appendChild(h);
        }
        h.style.top = (100 - y_value) + '%';  // invert Y so 0 % is bottom
    
        /* animate once per update */
        if (typeof gsap !== 'undefined') {
          /* EDIT → animation duration & easing */
          const anim = { duration: 0.4, ease: 'power2.out' };
          gsap.fromTo(v, { scaleY: 0 }, { scaleY: 1, ...anim });
          gsap.fromTo(h, { scaleX: 0 }, { scaleX: 1, ...anim });
        } else {
          /* CSS‑only fallback */
          [v, h].forEach(el => {
            /* EDIT → fallback animation duration & easing */
            el.style.transition = 'transform 0.4s ease-out';
            el.style.transform  = 'scale(0)';
            requestAnimationFrame(() => { el.style.transform = 'scale(1)'; });
          });
        }
      }
    })();
    


    I tested it and works great, but unfortunately I don't know how to share a .story file here. 🙃