- Create the goal bar assets with midjourney, photoshop and texture packer
Midjourney Prompt: neon progress bar, black tube with neon fill -s 25
Seperate the different parts of your goal bar such as the bar and fill using masking, use smoothing, feathering and shift edges as needed. Use the content-aware-fill feature in photoshop to clean up the bar after you seperate the fill layer.
Use the photoshop to spline script to export all of your layers. You can download it from here: https://github.com/EsotericSoftware/spine-scripts/tree/master/photoshop
Open Texture Packer and drag and drop a folder with the assets you just exported from photoshop. Make sure its set to Phaser 3 and click publish sprite sheet. You will get a png image and a json file.
Host your png image and JSON file on a server somewhere. I'm hosting mine in a public directory on a personal site on netlify. You may want to set the access-control-origin in your headers to avoid CORS issues.
/neon-bar/*
Access-Control-Allow-Origin: *
Create a custom widget on stream elements and write the goal bar logic
HTML
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
<script src="
https://cdn.jsdelivr.net/npm/phaser@3.70.0/dist/phaser.min.js
" unsafe-inline></script>
<div id="game-container"></div>
JS
// Global state object
let state = {
"goal": {
"value": 0, // Goal amount set in custom field by user
},
"desiredWidth": 0, // Track the desired width
"count": 0
};
// Configuration for the Phaser game
const config = {
type: Phaser.AUTO,
width: 400,
height: 300,
parent: 'game-container',
transparent: true,
scene: {
preload: preload,
create: create,
update: update
}
};
// Initialize the Phaser game
const game = new Phaser.Game(config);
function preload() {
//load the goal bar assets
this.load.atlas('ui', 'https://rowberry.dev/neon-bar/neon-bar.png', 'https://rowberry.dev/neon-bar/neon-bar.json');
}
function create() {
// Get center position of phaser screen
const centerX = this.cameras.main.width / 2;
const centerY = this.cameras.main.height / 2;
// Create the goal bar
const block = this.add.nineslice(centerX, centerY, 'ui', 'neon-bar-block.png', 402, 60, 10, 10, 10, 10);
this.fill = this.add.nineslice(centerX, centerY, 'ui', 'neon-bar-fill.png', 339, 29, 10, 10, 10, 10);
this.fill.setOrigin(0, 0.5);
this.fill.x = centerX - this.fill.width / 2 - 10;
// Create a Graphics object for the background
let bg = this.add.graphics();
bg.fillStyle(0x000000, 0.5); // Black with some transparency
bg.fillRect(0, 0, 200, 90); // Adjust the size as needed
// Create text objects
this.goalText = this.add.text(10, 10, `Goal: ${state.goal.value}`, { font: '16px Arial', fill: '#ffffff' });
this.countText = this.add.text(10, 30, `Count: ${state.count}`, { font: '16px Arial', fill: '#ffffff' });
this.widthText = this.add.text(10, 50, `Width: ${state.desiredWidth}`, { font: '16px Arial', fill: '#ffffff' });
// Create a container and add the background and text objects
this.infoGroup = this.add.container(0, 0);
this.infoGroup.add(bg);
this.infoGroup.add(this.goalText);
this.infoGroup.add(this.countText);
this.infoGroup.add(this.widthText);
// Position the container as needed
this.infoGroup.setPosition(0, 0);
}
function update() {
if (this.fill && this.fill.width !== state.desiredWidth) {
this.tweens.add({
targets: this.fill,
width: state.desiredWidth,
duration: 500,
ease: 'Linear',
});
this.widthText.setText(`Width: ${state.desiredWidth}`);
this.countText.setText(`Count: ${state.count}`);
}
}
window.addEventListener('onWidgetLoad', function (obj) {
let data = obj.detail.session.data;
let fieldData = obj.detail.fieldData;
state.goal.value = fieldData.goal_total;
progress(data);
});
window.addEventListener('onSessionUpdate', function (obj) {
let data = obj.detail.session;
progress(data);
});
const progress = (data) => {
let count = data["follower-goal"]["amount"];
state.count = count;
let p = (count) / (state.goal.value) * 100;
state.desiredWidth = (p / 100) * 339; // Assuming 339 is the full width
state.desiredWidth = Math.max(0, Math.min(state.desiredWidth, 339));
console.log(`Count: ${count}, Goal: ${state.goal.value}, p: ${p}, newWidth: ${state.desiredWidth}`);
};
FIELDS
{
"goal_total": {
"type": "number",
"label": "Goal amount",
"value": 100
}
}