CMS Version
4.5.3
Player Type
Any
Marquee scroll too fast
I’m analyzing the jquery.marquee plugin block in the bundle and the two functions that call it (xiboElementsRender and xiboTextRender). .
What I verified in the code
Vertical scrolling is handled by the jquery.marquee plugin (module 59080), with this flow:
- Xibo sets
data-speed = speed / 25 * 1000, in bothxiboElementsRenderandxiboTextRender. Ifspeedis 0 it is forced to 1. - As soon as the plugin reads the options, it computes
duration = c.width() / speed * 1000. It always uses the container’s width, even when the direction isupordown. The vertical-direction flag (i) is only set after this line. - In the vertical branch the duration is then rescaled:
duration = (h + O) / O * duration, whereOis the container height andhis the content height plus the gap. - With
seamlessenabled (data-duplicated) the duration is divided by 2.
Why speed doesn’t behave as you’d expect
For vertical scrolling, the actual speed in pixels per second works out to speed * 40 * (container height / container width), rather than speed * 40. The result therefore depends on the container’s aspect ratio. A tall, narrow ticker scrolls much faster than it should, while a wide, short one scrolls much slower. This is an inference from the code, and something that can be observed at runtime in anyplayer.
Why duration has no effect
In the marqueeUp and marqueeDown branches of both functions, the widget’s duration parameter is never used. It only appears in the cycle branch (e.duration / numItems), i.e. for slide effects. For the marquee, the scroll time depends on speed and the content height, while the widget duration only determines when the layout moves on to the next widget. If the text is longer than the scroll can complete in the configured time, it gets cut off. This is the current code’s behavior, not necessarily the intended one.
By example with a 240 × 740 container, the formula puts the vertical scroll at about 3.1 times the speed it should have. This is a calculation from the code, not a runtime measurement. I’m assuming those are the dimensions of the element the marquee runs on (.scroll or its parent) and that O in the code equals 740.
The calculation
- The plugin computes the base duration from the width:
240 / (speed × 40) × 1000ms. For example, withspeed = 2(the default in the elements renderer) this is about 2.99 s. - It then multiplies by
(h + 740) / 740, wherehis the content height plus gap. The distance travelled ish + 740, so the actual velocity is740 / base duration, which cancels outh. - With
speed = 2that gives about 248 px/s instead of the intended 80 px/s. - In general, the actual velocity is
speed × 40 × (740 / 239), which isspeed × 40 × 3.096. That is about 124 px/s per speed unit instead of 40.
I’m treating data-speed as pixels per second because that is what the horizontal case produces: there, width / duration works out to exactly data-speed. This is my reading of the intended behavior, not something documented.
Consequences
- Since
speed = 0is forced to 1, the slowest scroll you can get with a similar aspect ratio is about 124 px/s. - The vertical scroll will never match the horizontal one, because the correction factor depends on the aspect ratio.
I have prepared a small python script to patch bundle.min.js to take into account height instead of width for vertical scolling plugins:
#!/usr/bin/env python3
"""Patch jquery.marquee in Xibo bundle: use height for vertical scroll."""
import sys
OLD = 's.speed&&(s.duration=parseInt(c.width(),10)/s.speed*1e3),i="up"===s.direction||"down"===s.direction,'
NEW = 'i="up"===s.direction||"down"===s.direction,s.speed&&(s.duration=parseInt(i?c.height():c.width(),10)/s.speed*1e3),'
src, dst = sys.argv[1], sys.argv[2]
data = open(src, encoding="utf-8").read()
n = data.count(OLD)
if n != 1:
sys.exit(f"ERROR: Expected 1 occurrences, found {n}. Not modifying.")
open(dst, "w", encoding="utf-8").write(data.replace(OLD, NEW))
print("Patch have been applied:", dst)
Usage: python3 apply_marquee_patch.py bundle_min.js bundle_min.patched.js
And then substitute the bundle…