SVG Interactivity
When it comes to UI, SVG interactivity is a powerful tool for creating dynamic and engaging components. And while this feature is already on our long-term roadmap – at the time of writing this, our SDK does not support SVG interactivity natively. This post outlines a practical solution to implement SVG interactions(e.g. hover) in Gameface/Prysm using an approach that leverages the Raphael.js library.
Overview
To achieve interactivity within different sections of an SVG, you can follow these general steps:
- Obtain the paths defining the sectors.
- Check if the received mouse event is inside one of them.
- Execute the desired logic based on the interaction.
That said, let’s delve into the specifics of this approach, highlighting the necessary adjustments and considerations to make it work seamlessly.
Implementation
Step 1: Obtain the Paths
First, ensure you have the paths or shapes that define the interactive sectors of your SVG. These paths will be used to detect mouse events.
let paths = [];
const svg = document.getElementById('svg');
let pathElements = svg.children;
for (let i = 0; i < pathElements.length; ++i) {
let path = {};
path.color = i == 0 ? 'red' : i == 1 ? 'green' : i == 2 ? 'blue' : 'yellow';
path.element = pathElements[i];
path.decl = pathElements[i].getAttribute('d');
paths.push(path);
}
Step 2: Check the Mouse Events
To determine if a mouse event occurs inside a specific sector, we will use the Raphael.js library, which simplifies the hit test.
svg.onmousemove = (e) => {
paths.forEach((p) => {
if (Raphael.isPointInsidePath(p.decl, e.clientX - offset.x, e.clientY - offset.y)) {
p.element.setAttribute('fill', p.color);
} else {
p.element.setAttribute('fill', 'black');
}
});
};
If isPointInsidePath returns true for a specific sector - you can execute the desired logic. In this case, we simply change the fill color of the hovered SVG element.
Important adjustments
Library Compatibility: At the moment, Raphael.js is not fully tested & supported with Gameface/Prysm, and there are some adjustments needed to make it compatible. There are a few options:
- Modify this line to R.type = "SVG" to ensure compatibility.
- Alternatively, you can use a polyfill before including Raphael.js:
<script>
// Polyfill for Raphael JS library
window.SVGAngle = {};
</script>
<script src="raphael.min.js"></script>
Notable specifics
- Mouse Coordinates: In the example, we use the clientX and clientY coordinates of the mouse. This would mean that if we move the SVG element from the 0,0 coordinate of the screen, or if there are other elements before it - it won't work as expected, since it will check the mouse coordinates against the screen, not the element. For this reason, we need to take into account whether the SVG element has been moved and calculate the specific offset.
- Transforms: To be able to obtain the correct sector - ensure that the SVG does not contain any transform values except translate(). This assumption simplifies the calculation of mouse positions relative to the SVG.
- SVG Dimensions: Lastly, keep in mind that some changes to the size of the SVG can result in incorrect behavior. For example, if the viewBox is different from the SVG element's dimensions - e.g. svg width = 150, height = 150, viewBox = 0 0 300 300.
The full example can be downloaded from this link. Happy hacking!
Please sign in to leave a comment.
Comments
0 comments