Forum Discussion
Drawing two intersecting lines on a graph in Storyline
- 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. 🙃
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. 🙃
Awesome! Looks great - can't wait to try it out. I love the idea of live tracking the variables and the transparent overlay over the graph saves a lot of messing about working out max and min x and y co-ordinates. Thanks. I will let you know how I get on....
Related Content
- 6 months ago
- 5 months ago
- 8 months ago
- 9 months ago