Coding a simple follower goal bar widget in streamelements

Typically, I like to use SVG graphics when creating live-stream widgets. This allows us to use a graphics editor like Adobe illustrator to create appealing UI graphics and still be able to manipulate the SVG element's CSS with JavaScript to reflect changes in data. We can export the SVG code from illustrator and paste it as an inline-svg into our HTML markdown.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 530 80">
<g id="bar">
<rect id="fill" class="cls-6" x="15" y="19" width="500" height="46" rx="15.08"/>
<rect id="fill-overlay" class="cls-9" x="15" y="19" width="500" height="46" rx="15.08"/>
</g>
</svg>
In this example, we will be animating the CSS width attribute of the SVG rectangle element based on the percentage completed of the users follower goal. First, we will add a transition in the CSS to tell the browser to animate the width of the fill rectangle over 3 seconds when its value is changed.
#fill{
transition: 3s width;
}
Next, we will create a custom field to allow the user to set a goal amount total. (add this to the fields tab in your stream elements code editor)
{
"goal_total": {
"type": "number",
"label": "Goal amount",
"value": 100
}
}
Lastly, we will add the JavaScript. We will create two event listeners. One for when the widget loads and the other for when an event fires. You can pass an obj to these listeners that is data built right into the stream elements sandbox.
//runs code on widget load
window.addEventListener('onWidgetLoad', function (obj) {
let data = obj.detail.session.data;
let fieldData = obj.detail.fieldData;
});
//runs code on event
window.addEventListener('onSessionUpdate', function (obj) {
let data = obj.detail.session;
});
We will need to access the goal_total custom field data set by the user but it is only available within the 'onwidgetload' event listener. To get access to this data from within the 'onSessionUpdate' listener we will create a global state object and set that equal to the custom field.
//Global state object
let state = {
"goal": {
"value": 0
}
};
//runs code on widget load
window.addEventListener('onWidgetLoad', function (obj) {
let data = obj.detail.session.data;
let fieldData = obj.detail.fieldData;
state.goal.value = fieldData.goal_total;
});
//runs code on event
window.addEventListener('onSessionUpdate', function (obj) {
let data = obj.detail.session;
});
Next, we will create a progress function that will be called inside both event listeners and passed the data object.
//runs code on widget load
window.addEventListener('onWidgetLoad', function (obj) {
let data = obj.detail.session.data;
let fieldData = obj.detail.fieldData;
state.goal.value = fieldData.goal_total;
progress(data);
});
//runs code on event
window.addEventListener('onSessionUpdate', function (obj) {
let data = obj.detail.session;
progress(data);
});
let progress = (data) => {
};
Last, we will set all the variables for our goal percentage and finally set the width of our SVG element to our percentage.
//Global state object
let state = {
"goal": {
"value": 0
}
};
//runs code on widget load
window.addEventListener('onWidgetLoad', function (obj) {
let data = obj.detail.session.data;
let fieldData = obj.detail.fieldData;
state.goal.value = fieldData.goal_total;
progress(data);
});
//runs code on event
window.addEventListener('onSessionUpdate', function (obj) {
let data = obj.detail.session;
progress(data);
});
let progress = (data) => {
const fill = document.querySelector("#fill");
const fillOverlay = document.querySelector("#fill-overlay");
const maxWidth = fillOverlay.getBoundingClientRect().width;
let count = data["follower-goal"]["amount"];
let p = (count) / (state.goal.value) * maxWidth;
if(count >= state.goal.value){
p = maxWidth;
}
console.log(maxWidth);
fill.style.setProperty('width', p);
};