2025-06-16 1:47 AM
So, I'm trying to have a text that scroll automatically if the container width is too small.
My costume container will set the text and then calculate the width with
textLightSoundSettings.setTypedText(TypedText(T_AVVSONORITEXT));
if (textLightSoundSettings.getTextWidth() > scrollTextLightSoundSettings.getWidth()) {
scrollText = true;
pixelToScroll = textLightSoundSettings.getTextWidth() - scrollTextLightSoundSettings.getWidth();
}
Then, in the container's tick handle event
void lightSoundItem::handleTickEvent()
{
lightSoundItemBase::handleTickEvent();
if(scrollText)
{
static int16_t pixelScrolled = 0;
static bool scrollRight = true;
if (scrollRight) {
if (pixelScrolled < pixelToScroll) {
scrollTextLightSoundSettings.doScroll(1, 0);
pixelScrolled += 1;
} else {
scrollRight = false;
}
} else {
if (pixelScrolled > 0) {
scrollTextLightSoundSettings.doScroll(-1, 0);
pixelScrolled -= 1;
} else {
scrollRight = true;
}
}
}
scrollTextLightSoundSettings.invalidate();
}
}
For some reason, even if the pixelToScroll is calculated correctly, when the text scrolls, it doesn't reach the end of the text, but it stops too early.
Am I missing something?
Solved! Go to Solution.
2025-06-16 5:28 AM
Hi @GaetanGodart ,
it turned out it was an issue with my logic and I was able to fix it (anyway, thanks a lot for the link to the custom widget)
void lightSoundItem::handleTickEvent()
{
lightSoundItemBase::handleTickEvent();
if (!scrollText) {
return; // No scrolling needed
}
switch (scrollState) {
case SCROLL_RIGHT:
{
animationTime++;
// Calculate new position using sine easing
int16_t newPosition = EasingEquations::sineEaseInOut(
animationTime, // t: current time
0, // b: beginning value (left position)
pixelToScroll, // c: change (total distance to scroll)
animationDuration // d: duration
);
// Calculate delta from current position
int16_t deltaX = newPosition - currentScrollPosition;
// Apply scroll
if (deltaX != 0) {
scrollTextLightSoundSettings.doScroll(deltaX, 0);
currentScrollPosition = newPosition;
}
// Check if animation is complete
if (animationTime >= animationDuration) {
scrollState = PAUSE_RIGHT;
pauseCounter = 0;
animationTime = 0;
}
}
break;
case PAUSE_RIGHT:
pauseCounter++;
if (pauseCounter >= pauseDuration) {
scrollState = SCROLL_LEFT;
pauseCounter = 0;
}
break;
case SCROLL_LEFT:
{
animationTime++;
// Calculate new position using sine easing (going back to 0)
int16_t newPosition = EasingEquations::sineEaseInOut(
animationTime, // t: current time
pixelToScroll, // b: beginning value (right position)
-pixelToScroll, // c: change (negative distance back to start)
animationDuration // d: duration
);
// Calculate delta from current position
int16_t deltaX = newPosition - currentScrollPosition;
// Apply scroll
if (deltaX != 0) {
scrollTextLightSoundSettings.doScroll(deltaX, 0);
currentScrollPosition = newPosition;
}
// Check if animation is complete
if (animationTime >= animationDuration) {
scrollState = PAUSE_LEFT;
pauseCounter = 0;
animationTime = 0;
}
}
break;
case PAUSE_LEFT:
pauseCounter++;
if (pauseCounter >= pauseDuration) {
scrollState = SCROLL_RIGHT;
pauseCounter = 0;
}
break;
}
}
The header file contains
int16_t tickCounter;
const int duration = 240;
bool scrollForward = true;
int16_t prevPosAnimation = 0;
// Animation state variables
uint16_t animationTime = 0;
uint16_t animationDuration = 120; // Total animation duration (ticks)
uint16_t pauseDuration = 60; // Pause duration at each end (ticks)
uint16_t pauseCounter = 0;
enum ScrollState {
SCROLL_RIGHT, // Moving right (positive deltaX)
PAUSE_RIGHT, // Paused at right position
SCROLL_LEFT, // Moving left (negative deltaX)
PAUSE_LEFT // Paused at left position
};
ScrollState scrollState = SCROLL_RIGHT;
int16_t currentScrollPosition = 0;
The only thing I came across is that, for some reason, on the first access of the view where my container is, before the animation starts I have to wait some seconds.
It's like I have to wait a full "ghost" execution of the animation state machine before I actually see the text moving.
2025-06-16 4:01 AM
Hello @nico23 ,
Perhaps this custom widget is doing what you want to do.
You could either use it directly or look at the code and do the same thing.
If that is not good enough for you, could you please share your project so I can try it myself and try to see where the issue is?
Regards,
2025-06-16 5:28 AM
Hi @GaetanGodart ,
it turned out it was an issue with my logic and I was able to fix it (anyway, thanks a lot for the link to the custom widget)
void lightSoundItem::handleTickEvent()
{
lightSoundItemBase::handleTickEvent();
if (!scrollText) {
return; // No scrolling needed
}
switch (scrollState) {
case SCROLL_RIGHT:
{
animationTime++;
// Calculate new position using sine easing
int16_t newPosition = EasingEquations::sineEaseInOut(
animationTime, // t: current time
0, // b: beginning value (left position)
pixelToScroll, // c: change (total distance to scroll)
animationDuration // d: duration
);
// Calculate delta from current position
int16_t deltaX = newPosition - currentScrollPosition;
// Apply scroll
if (deltaX != 0) {
scrollTextLightSoundSettings.doScroll(deltaX, 0);
currentScrollPosition = newPosition;
}
// Check if animation is complete
if (animationTime >= animationDuration) {
scrollState = PAUSE_RIGHT;
pauseCounter = 0;
animationTime = 0;
}
}
break;
case PAUSE_RIGHT:
pauseCounter++;
if (pauseCounter >= pauseDuration) {
scrollState = SCROLL_LEFT;
pauseCounter = 0;
}
break;
case SCROLL_LEFT:
{
animationTime++;
// Calculate new position using sine easing (going back to 0)
int16_t newPosition = EasingEquations::sineEaseInOut(
animationTime, // t: current time
pixelToScroll, // b: beginning value (right position)
-pixelToScroll, // c: change (negative distance back to start)
animationDuration // d: duration
);
// Calculate delta from current position
int16_t deltaX = newPosition - currentScrollPosition;
// Apply scroll
if (deltaX != 0) {
scrollTextLightSoundSettings.doScroll(deltaX, 0);
currentScrollPosition = newPosition;
}
// Check if animation is complete
if (animationTime >= animationDuration) {
scrollState = PAUSE_LEFT;
pauseCounter = 0;
animationTime = 0;
}
}
break;
case PAUSE_LEFT:
pauseCounter++;
if (pauseCounter >= pauseDuration) {
scrollState = SCROLL_RIGHT;
pauseCounter = 0;
}
break;
}
}
The header file contains
int16_t tickCounter;
const int duration = 240;
bool scrollForward = true;
int16_t prevPosAnimation = 0;
// Animation state variables
uint16_t animationTime = 0;
uint16_t animationDuration = 120; // Total animation duration (ticks)
uint16_t pauseDuration = 60; // Pause duration at each end (ticks)
uint16_t pauseCounter = 0;
enum ScrollState {
SCROLL_RIGHT, // Moving right (positive deltaX)
PAUSE_RIGHT, // Paused at right position
SCROLL_LEFT, // Moving left (negative deltaX)
PAUSE_LEFT // Paused at left position
};
ScrollState scrollState = SCROLL_RIGHT;
int16_t currentScrollPosition = 0;
The only thing I came across is that, for some reason, on the first access of the view where my container is, before the animation starts I have to wait some seconds.
It's like I have to wait a full "ghost" execution of the animation state machine before I actually see the text moving.