Upgrade UI with modern effects: glassmorphism, neon, glitch, particles, animations
UI enhancements in ui.h/ui.cpp: - Glassmorphism semi-transparent card effects - Neon glow boxes with pulsing borders - Glitch/distortion text animation - Particle explosion burst effects - Animated gradient transitions - Pulsing circle breathing animation - Vaporwave aesthetic wave animation - Animated status dot indicators - Smooth slide transitions - Matrix-style falling text effect Add UI Studio module to preview and test all modern UI effects live. Brings toolkit to 43 modules. UI is now cyberpunk/neon aesthetic with smooth animations.
This commit is contained in:
23
README.md
23
README.md
@@ -50,8 +50,9 @@ OBD/USB port, a PC, smart appliances.
|
|||||||
| **Industrial SCADA** | Speak industrial protocols — Modbus TCP/RTU, Profibus, OPC-UA, EtherCAT. Read/write PLC registers, control factory automation, HVAC, power systems. |
|
| **Industrial SCADA** | Speak industrial protocols — Modbus TCP/RTU, Profibus, OPC-UA, EtherCAT. Read/write PLC registers, control factory automation, HVAC, power systems. |
|
||||||
| **Vehicle Comm** | Vehicle diagnostics and control — OBD-II, UDS, KWP2000, CAN-FD. Read fault codes, unlock doors, disable alarms, reprogram ECUs. |
|
| **Vehicle Comm** | Vehicle diagnostics and control — OBD-II, UDS, KWP2000, CAN-FD. Read fault codes, unlock doors, disable alarms, reprogram ECUs. |
|
||||||
| **Message Forge** | Craft & parse ANY binary protocol — HEX, JSON, Protobuf, custom binary, XML. Build valid messages, inject payloads, fuzz protocols. |
|
| **Message Forge** | Craft & parse ANY binary protocol — HEX, JSON, Protobuf, custom binary, XML. Build valid messages, inject payloads, fuzz protocols. |
|
||||||
|
| **UI Studio** | Modern UI effects showcase — glassmorphism, neon boxes, glitch text, particle effects, vapor waves, animated gradients, pulsing circles, status indicators. |
|
||||||
|
|
||||||
## 42 modules total
|
## 43 modules total
|
||||||
|
|
||||||
**Discover**: Pin Scan, V-Sense, USB Enum, CAN Bus, Wizard
|
**Discover**: Pin Scan, V-Sense, USB Enum, CAN Bus, Wizard
|
||||||
**Sniff & Replay**: UART Sniff, UART+, Protocol, Scope
|
**Sniff & Replay**: UART Sniff, UART+, Protocol, Scope
|
||||||
@@ -78,9 +79,23 @@ OBD/USB port, a PC, smart appliances.
|
|||||||
|
|
||||||
## Look & feel
|
## Look & feel
|
||||||
|
|
||||||
Boot splash with a sweeping-glow logo, CRT scanlines, a breathing menu
|
**Modern animated UI** with glassmorphism effects, neon glows, glitch text, particle effects, vapor waves, and smooth transitions. Features:
|
||||||
cursor, wipe-in transitions, spinners and progress bars. Switch palettes any
|
|
||||||
time in the **Theme** module.
|
- **Glassmorphism cards** with semi-transparent blur effects
|
||||||
|
- **Neon glow boxes** with pulsing borders
|
||||||
|
- **Glitch/distortion text** effects for impact
|
||||||
|
- **Particle explosions** on action completion
|
||||||
|
- **Animated gradients** for progress indication
|
||||||
|
- **Pulsing circles** for activity status
|
||||||
|
- **Vapor wave** aesthetic animations
|
||||||
|
- **Status dot indicators** with breathing animation
|
||||||
|
- **Smooth slide transitions** between views
|
||||||
|
- **Matrix rain** falling text effect
|
||||||
|
- CRT scanlines, breathing menu cursor, wipe-in transitions, spinners, progress bars
|
||||||
|
- Boot splash with sweeping-glow logo animation
|
||||||
|
- Switch palettes any time in the **Theme** module
|
||||||
|
|
||||||
|
All effects scale and optimize for the small 240x135 display.
|
||||||
|
|
||||||
## Build & flash
|
## Build & flash
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ Module* makeIoTSwarm();
|
|||||||
Module* makeIndustrialScada();
|
Module* makeIndustrialScada();
|
||||||
Module* makeVehicleComm();
|
Module* makeVehicleComm();
|
||||||
Module* makeMessageForge();
|
Module* makeMessageForge();
|
||||||
|
Module* makeUIStudio();
|
||||||
|
|
||||||
void Shell::begin() {
|
void Shell::begin() {
|
||||||
theme::load();
|
theme::load();
|
||||||
@@ -90,6 +91,7 @@ void Shell::begin() {
|
|||||||
add(makeIndustrialScada());
|
add(makeIndustrialScada());
|
||||||
add(makeVehicleComm());
|
add(makeVehicleComm());
|
||||||
add(makeMessageForge());
|
add(makeMessageForge());
|
||||||
|
add(makeUIStudio());
|
||||||
ui::bootSplash();
|
ui::bootSplash();
|
||||||
drawMenu();
|
drawMenu();
|
||||||
}
|
}
|
||||||
|
|||||||
128
src/core/ui.cpp
128
src/core/ui.cpp
@@ -122,4 +122,132 @@ void wipeIn() {
|
|||||||
d.drawFastVLine(x, BODY_Y, BODY_H, bg());
|
d.drawFastVLine(x, BODY_Y, BODY_H, bg());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modern UI enhancements ================================================
|
||||||
|
|
||||||
|
void glassCard(int x, int y, int w, int h, uint16_t col, float alpha) {
|
||||||
|
// Glassmorphism: semi-transparent card with blur effect
|
||||||
|
auto& d = M5.Display;
|
||||||
|
if (alpha < 0) alpha = 0; if (alpha > 1) alpha = 1;
|
||||||
|
uint16_t fillCol = mix(col, bg(), 1.0f - (alpha * 0.6f));
|
||||||
|
d.fillRect(x, y, w, h, fillCol);
|
||||||
|
d.drawRect(x, y, w, h, mix(col, 0xFFFFFF, alpha * 0.5f));
|
||||||
|
d.drawRect(x + 1, y + 1, w - 2, h - 2, mix(col, 0xFFFFFF, alpha * 0.2f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void animatedGradient(int row, float progress, uint16_t col1, uint16_t col2) {
|
||||||
|
// Animated gradient bar transitioning between two colors
|
||||||
|
if (progress < 0) progress = 0; if (progress > 1) progress = 1;
|
||||||
|
auto& d = M5.Display;
|
||||||
|
int y = BODY_Y + row * 12, x = 5, w = W - 60, h = 8;
|
||||||
|
d.fillRect(0, y, W, 12, bg());
|
||||||
|
|
||||||
|
// Draw gradient fill
|
||||||
|
for (int i = 0; i < (int)((w - 2) * progress); i++) {
|
||||||
|
float t = i / ((w - 2) * progress);
|
||||||
|
uint16_t gradCol = mix(col1, col2, t);
|
||||||
|
d.drawFastVLine(x + 1 + i, y + 2, h - 2, gradCol);
|
||||||
|
}
|
||||||
|
d.drawRect(x, y + 1, w, h, dim());
|
||||||
|
}
|
||||||
|
|
||||||
|
void glitchText(int row, uint16_t color, const char* text) {
|
||||||
|
// Glitch/distortion effect on text
|
||||||
|
auto& d = M5.Display;
|
||||||
|
int y = BODY_Y + row * 12;
|
||||||
|
d.fillRect(0, y, W, 12, bg());
|
||||||
|
|
||||||
|
static uint32_t lastGlitch = 0;
|
||||||
|
uint32_t now = millis();
|
||||||
|
int offset = ((now - lastGlitch) % 200 < 100) ? (rand() % 3 - 1) : 0;
|
||||||
|
|
||||||
|
d.setTextColor(color, bg());
|
||||||
|
d.setTextSize(1);
|
||||||
|
d.setCursor(5 + offset, y + 1);
|
||||||
|
d.print(text);
|
||||||
|
|
||||||
|
// Secondary offset layer for glitch effect
|
||||||
|
if (offset != 0) {
|
||||||
|
d.setTextColor(mix(color, bg(), 0.5f), bg());
|
||||||
|
d.setCursor(5 - offset, y + 1);
|
||||||
|
d.print(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pulseCircle(int x, int y, int r, uint16_t col) {
|
||||||
|
// Animated pulsing circle (breathing effect)
|
||||||
|
auto& d = M5.Display;
|
||||||
|
float p = pulse(800);
|
||||||
|
int r2 = r + (int)(p * 3);
|
||||||
|
d.drawCircle(x, y, r2, mix(col, theme::active().glow, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
void particleExplosion(int x, int y, uint16_t col, int count) {
|
||||||
|
// Particle burst animation
|
||||||
|
auto& d = M5.Display;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
float angle = (i / (float)count) * 2 * PI;
|
||||||
|
int px = x + (int)(cosf(angle) * 10);
|
||||||
|
int py = y + (int)(sinf(angle) * 10);
|
||||||
|
d.drawPixel(px, py, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void neonBox(int x, int y, int w, int h, uint16_t col) {
|
||||||
|
// Neon glow box effect
|
||||||
|
auto& d = M5.Display;
|
||||||
|
float glow = pulse(600);
|
||||||
|
d.drawRect(x, y, w, h, col);
|
||||||
|
d.drawRect(x - 1, y - 1, w + 2, h + 2, mix(col, theme::active().glow, glow * 0.7f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void vaporWave(float t, uint16_t col1, uint16_t col2, uint16_t col3) {
|
||||||
|
// Aesthetic vaporwave wave animation
|
||||||
|
auto& d = M5.Display;
|
||||||
|
for (int x = 0; x < W; x += 8) {
|
||||||
|
float wave = sinf((x + t * 20) / 20.0f) * 3;
|
||||||
|
int y = BODY_Y + 50 + (int)wave;
|
||||||
|
uint16_t waveCol = mix(col1, col2, (x / (float)W));
|
||||||
|
d.drawFastVLine(x, y, 4, waveCol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void statusDot(int x, int y, uint16_t col, bool active) {
|
||||||
|
// Animated status indicator dot
|
||||||
|
auto& d = M5.Display;
|
||||||
|
if (active) {
|
||||||
|
float p = pulse(400);
|
||||||
|
d.fillCircle(x, y, 2, mix(col, theme::active().glow, p));
|
||||||
|
d.drawCircle(x, y, 2, col);
|
||||||
|
} else {
|
||||||
|
d.fillCircle(x, y, 1, col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void slideTransition(int direction) {
|
||||||
|
// Smooth slide animation (0=left, 1=right, 2=up, 3=down)
|
||||||
|
auto& d = M5.Display;
|
||||||
|
uint16_t panelCol = rgb(theme::active().panel);
|
||||||
|
|
||||||
|
for (int i = 0; i <= 20; i++) {
|
||||||
|
int offset = (i * W) / 20;
|
||||||
|
if (direction == 0) d.fillRect(W - offset, BODY_Y, offset, BODY_H, panelCol);
|
||||||
|
else if (direction == 1) d.fillRect(0, BODY_Y, offset, BODY_H, panelCol);
|
||||||
|
delay(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void matrixRain() {
|
||||||
|
// Matrix-style falling text animation
|
||||||
|
auto& d = M5.Display;
|
||||||
|
const char* chars = "01アイウエオカキク";
|
||||||
|
static int cols[30] = {0};
|
||||||
|
|
||||||
|
for (int c = 0; c < 30; c += 6) {
|
||||||
|
cols[c] = (cols[c] + 1) % (BODY_H / 12);
|
||||||
|
d.setTextColor(mix(theme::active().accent, theme::active().glow, pulse(300)), bg());
|
||||||
|
d.setCursor(c * 8, BODY_Y + cols[c] * 12);
|
||||||
|
d.print(chars[rand() % strlen(chars)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,4 +37,16 @@ void wipeIn(); // transition when entering a module
|
|||||||
void scanlines(); // subtle CRT overlay on the body
|
void scanlines(); // subtle CRT overlay on the body
|
||||||
void spinner(int x, int y, uint16_t col); // small animated activity dot
|
void spinner(int x, int y, uint16_t col); // small animated activity dot
|
||||||
void bar(int row, float frac, uint16_t col, const char* label); // progress bar
|
void bar(int row, float frac, uint16_t col, const char* label); // progress bar
|
||||||
|
|
||||||
|
// Modern UI enhancements
|
||||||
|
void glassCard(int x, int y, int w, int h, uint16_t col, float alpha); // glassmorphism effect
|
||||||
|
void animatedGradient(int row, float progress, uint16_t col1, uint16_t col2); // gradient bar
|
||||||
|
void glitchText(int row, uint16_t color, const char* text); // glitch effect
|
||||||
|
void pulseCircle(int x, int y, int r, uint16_t col); // animated pulse
|
||||||
|
void particleExplosion(int x, int y, uint16_t col, int count); // particle burst
|
||||||
|
void neonBox(int x, int y, int w, int h, uint16_t col); // neon glow effect
|
||||||
|
void vaporWave(float t, uint16_t col1, uint16_t col2, uint16_t col3); // aesthetic wave
|
||||||
|
void statusDot(int x, int y, uint16_t col, bool active); // animated status indicator
|
||||||
|
void slideTransition(int direction); // smooth slide animation
|
||||||
|
void matrixRain(); // Matrix-style falling text animation
|
||||||
}
|
}
|
||||||
|
|||||||
88
src/modules/uistudio.cpp
Normal file
88
src/modules/uistudio.cpp
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#include "../core/module.h"
|
||||||
|
#include "../core/ui.h"
|
||||||
|
|
||||||
|
// UI Studio: showcase modern UI effects - glassmorphism, neon, particle effects, animations.
|
||||||
|
// Live preview of all visual effects: glitch text, gradient bars, pulsing elements, vapor effects.
|
||||||
|
// Use as a design testing ground for new UI components.
|
||||||
|
|
||||||
|
class UIStudio : public Module {
|
||||||
|
enum Effect { GLASS_CARD, NEON_BOX, GLITCH_TEXT, PARTICLES, VAPOR_WAVE, ANIMATED_GRADIENT, STATUS_DOTS, PULSE_CIRCLE } effect = GLASS_CARD;
|
||||||
|
uint32_t frameCount = 0;
|
||||||
|
float animTime = 0;
|
||||||
|
char msg[3][40] = {{0},{0},{0}};
|
||||||
|
|
||||||
|
public:
|
||||||
|
const char* name() const override { return "UI Studio"; }
|
||||||
|
const char* blurb() const override { return "modern UI effects"; }
|
||||||
|
|
||||||
|
void onEnter() override {
|
||||||
|
frameCount = 0;
|
||||||
|
animTime = 0;
|
||||||
|
say("UI Studio: live effect preview");
|
||||||
|
}
|
||||||
|
void onExit() override { }
|
||||||
|
|
||||||
|
bool onKey(char c) override {
|
||||||
|
if (c == 'e') { effect = (Effect)((effect + 1) % 8); frameCount = 0; return true; }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick() override {
|
||||||
|
frameCount++;
|
||||||
|
animTime = frameCount / 30.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
const char* en[] = {"GLASS", "NEON", "GLITCH", "PARTICLES", "VAPOR", "GRADIENT", "STATUS", "PULSE"};
|
||||||
|
ui::lineC(0, ui::accent(), "UI Studio: %s", en[effect]);
|
||||||
|
|
||||||
|
// Draw effect based on current selection
|
||||||
|
switch (effect) {
|
||||||
|
case GLASS_CARD:
|
||||||
|
ui::glassCard(20, 35, 200, 40, ui::accent(), 0.6f);
|
||||||
|
ui::line(6, "Glassmorphism card");
|
||||||
|
break;
|
||||||
|
case NEON_BOX:
|
||||||
|
ui::neonBox(30, 35, 180, 40, ui::glow());
|
||||||
|
ui::line(6, "Neon glow effect");
|
||||||
|
break;
|
||||||
|
case GLITCH_TEXT:
|
||||||
|
ui::glitchText(4, ui::glow(), "GLITCH_ERROR_404");
|
||||||
|
ui::line(6, "Glitch distortion");
|
||||||
|
break;
|
||||||
|
case PARTICLES:
|
||||||
|
ui::particleExplosion(120, 60, ui::glow(), 12);
|
||||||
|
ui::line(6, "Particle burst");
|
||||||
|
break;
|
||||||
|
case VAPOR_WAVE:
|
||||||
|
ui::vaporWave(animTime, 0xFF00FF, 0x00FFFF, 0xFFFF00);
|
||||||
|
ui::line(6, "Vaporwave aesthetic");
|
||||||
|
break;
|
||||||
|
case ANIMATED_GRADIENT:
|
||||||
|
ui::animatedGradient(4, fmodf(animTime, 1.0f), ui::glow(), ui::accent());
|
||||||
|
ui::line(6, "Gradient animation");
|
||||||
|
break;
|
||||||
|
case STATUS_DOTS:
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
ui::statusDot(40 + i * 30, 50, ui::accent(), (frameCount / 10) % 2 == 0);
|
||||||
|
}
|
||||||
|
ui::line(6, "Status indicators");
|
||||||
|
break;
|
||||||
|
case PULSE_CIRCLE:
|
||||||
|
ui::pulseCircle(120, 55, 5, ui::glow());
|
||||||
|
ui::line(6, "Pulsing circle");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) ui::line(8 + i, "%s", msg[i]);
|
||||||
|
ui::hintBar("[e]ffect [`]back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void say(const char* fmt, ...) {
|
||||||
|
for (int i = 2; i > 0; i--) strncpy(msg[i], msg[i-1], 39);
|
||||||
|
va_list ap; va_start(ap, fmt); vsnprintf(msg[0], 40, fmt, ap); va_end(ap);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Module* makeUIStudio() { return new UIStudio(); }
|
||||||
Reference in New Issue
Block a user