best way to 'scroll' a name on a button, and remain within padding #1117
Replies: 5 comments 41 replies
-
|
Hello. |
Beta Was this translation helpful? Give feedback.
-
|
Something like this should do the trick. It's not "simple" but can be in a config template and then you just define the type: custom:button-card
entity: switch.skylight
name: Super long text that should scroll when it is too long to fit in the available space
show_label: true
label: Short
styles:
card:
- padding: 5px
variables:
toScroll:
- '#name'
- '#label'
scrollFunction: |
[[[
return () => {
if (!variables.toScroll || variables.toScroll.length === 0) return;
variables.toScroll.forEach( ( selector ) => {
const el = this.shadowRoot.querySelector( selector );
if ( el ) {
if ( el.scrollWidth > el.clientWidth ) {
el.classList.add('scrolling-text');
el.style.setProperty('--scroll-speed', (el.scrollWidth / 40) + 's' );
el.style.setProperty('--scroll-max', el.clientWidth + 'px' );
if (!el.firstElementChild || el.firstElementChild.tagName.toLowerCase() !== 'span' ) {
el.innerHTML = `<span>${el.innerHTML}</span>`;
}
} else {
el.classList.remove('scrolling-text');
el.style.removeProperty('--scroll-speed');
el.style.removeProperty('--scroll-max');
}
}
});
};
]]]
resizeObserver:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
this._resizeObserver = this._resizeObserver ||
new ResizeObserver( ( entries ) => {
this.updateComplete.then( () => {
variables.scrollFunction();
});
});
this._resizeObserver.observe(this);
]]]
updateComplete:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
this.updateComplete.then( () => {
variables.scrollFunction();
});
]]]
extra_styles: |
.scrolling-text {
container-type: normal;
}
ha-card:hover .scrolling-text > span {
animation: scroll-text var(--scroll-speed) alternate ease-in-out infinite;
text-overflow: unset !important;
display: inline-block ;
}
@keyframes scroll-text {
from {
transform: translateX( 0% );
}
to {
transform: translateX( min(calc(-100% + var(--scroll-max)), 0px) );
}
} |
Beta Was this translation helpful? Give feedback.
-
|
using this on my much wider marquee 'button' to show more text, and text only, which was my attempt to adjust the speed to the length of the text. It has worked out fine, but I figured it was too complex for my smaller button in this discussion. Seeing where you take us now, it starts to be more and more comparable: Ill take out the blink to make it clearer in the screen vid, you'll notice it shows the exact length of the string correctly and then starts all over: |
Beta Was this translation helpful? Give feedback.
-
|
New version of the config template with some more configuration options. Setting scrolling_text:
variables:
toScroll: [] # array of CSS querySelector of elements to scroll
scrollCurve: 'linear' # animation effect (eg: ease-in-out) only valid if scrollLoop is not true
scrollSpeed: 70 # speed of the animation
scrollLoop: true # whether to generate a never ending text scroll
scrollLoopSeparator: '•' # never ending text scroll separator
scrollAlternate: false # if the text should go back and forth
scrollAlways: false # if true the text will always scroll, not only on hover
## Main stuff
scrollFunction: |
[[[
return () => {
if (!variables.toScroll || variables.toScroll.length === 0) return;
variables.toScroll.forEach( ( selector ) => {
const el = this.shadowRoot.querySelector( selector );
if ( el ) {
if ( el.scrollWidth > el.clientWidth ) {
el.classList.add('scrolling-text');
el.style.setProperty('--scroll-speed', (el.scrollWidth / variables.scrollSpeed ) + 's' );
el.style.setProperty('--scroll-max', (variables.scrollLoop ? '50%' : (variables.scrollAlternate ? el.clientWidth + 'px' : '0px')) );
if (!el.firstElementChild || el.firstElementChild.tagName.toLowerCase() !== 'span' ) {
if ( variables.scrollLoop ) {
const content = `${el.innerHTML} ${variables.scrollLoopSeparator} `;
el.innerHTML = `<span>${content}${content}</span>`;
} else {
el.innerHTML = `<span>${el.innerHTML}</span>`;
}
}
} else {
el.classList.remove('scrolling-text');
el.style.removeProperty('--scroll-speed');
}
}
});
};
]]]
resizeObserver:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
if (!this._resizeObserver ) {
this._resizeObserver =
new ResizeObserver( ( entries ) => {
this.updateComplete.then( () => {
window.setTimeout( () => {
variables.scrollFunction();
}, 100 );
});
});
this._resizeObserver.observe(this);
}
]]]
updateComplete:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
this.updateComplete.then( () => {
variables.scrollFunction();
});
]]]
extra_styles: |
[[[
return `
.scrolling-text {
container-type: normal;
}
${variables.scrollAlways ? '' : 'ha-card:hover'} .scrolling-text > span {
animation: scroll-text var(--scroll-speed) ${!variables.scrollLoop && variables.scrollAlternate ? 'alternate' : '' } ${variables.scrollLoop ? 'linear' : (variables.scrollCurve || 'linear')} infinite;
text-overflow: unset !important;
display: inline-block ;
}
@keyframes scroll-text {
from {
transform: translateX( 0% );
}
to {
transform: translateX( min(calc(-100% + var(--scroll-max)), 0px) );
}
}
`;
]]] |
Beta Was this translation helpful? Give feedback.
-
|
Alright... this one should work for all use cases now :-) scrolling_text:
variables:
toScroll: [] # array of CSS querySelector of elements to scroll
scrollCurve: 'linear' # animation effect (eg: ease-in-out) only valid if scrollLoop is not true
scrollSpeed: 70 # speed of the animation
scrollLoop: true # whether to generate a never ending text scroll
scrollLoopSeparator: '•' # never ending text scroll separator
scrollAlternate: false # if the text should go back and forth
scrollMode: hover # hover | always | hover-pause
scrollFunction: |
[[[
return () => {
if (!variables.toScroll || variables.toScroll.length === 0) return;
variables.toScroll.forEach( ( selector ) => {
const el = this.shadowRoot.querySelector( selector );
if ( el ) {
const content = el.querySelector('span.scroll > span.scroll-content');
let scrollWidth = el.scrollWidth;
if (content) {
scrollWidth = content.getBoundingClientRect().width;
}
if ( scrollWidth > el.clientWidth ) {
el.classList.add('scrolling-text');
let span = el.querySelector('span.scroll');
if (!span) {
const spanWrapper = document.createElement('span');
spanWrapper.classList.add('scroll');
const innerSpan = document.createElement('span');
innerSpan.classList.add('scroll-content');
while (el.firstChild) {
innerSpan.appendChild(el.firstChild);
}
spanWrapper.appendChild(innerSpan);
el.appendChild(spanWrapper);
span = spanWrapper;
}
const innerSpan = el.querySelector('span.scroll > span.scroll-content');
if ( variables.scrollLoop ) {
const oldDupSpan = span.querySelector('span.scroll-dup-content');
const duplicateSpan = innerSpan.cloneNode(true);
duplicateSpan.classList.add('scroll-dup-content');
duplicateSpan.classList.add('scroll-duplicate');
if (!oldDupSpan) {
const separator = document.createElement('span');
separator.classList.add('scroll-separator');
separator.classList.add('scroll-duplicate');
separator.innerHTML = ` ${variables.scrollLoopSeparator} `;
span.appendChild(separator);
span.appendChild(duplicateSpan);
span.appendChild(separator.cloneNode(true));
} else {
oldDupSpan.replaceWith(duplicateSpan);
}
}
el.style.setProperty('--scroll-speed', (el.scrollWidth / variables.scrollSpeed ) + 's' );
el.style.setProperty('--scroll-max', (variables.scrollLoop ? '50%' : (variables.scrollAlternate ? el.clientWidth + 'px' : '0px')) );
} else {
el.classList.remove('scrolling-text');
el.style.removeProperty('--scroll-speed');
const span = el.querySelector('span.scroll');
const innerSpan = el.querySelector('span.scroll-content');
if (innerSpan) {
while (innerSpan.firstChild) {
el.appendChild(innerSpan.firstChild);
}
el.removeChild(span);
}
}
}
});
};
]]]
resizeObserver:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
if (!this._resizeObserver ) {
this._resizeObserver =
new ResizeObserver( ( entries ) => {
this.updateComplete.then( () => {
window.setTimeout( () => {
variables.scrollFunction();
}, 100 );
});
});
this._resizeObserver.observe(this);
}
]]]
updateComplete:
force_eval: true
value: |
[[[
if (!variables.toScroll || variables.toScroll.length === 0) return;
this.updateComplete.then( () => {
variables.scrollFunction();
});
]]]
extra_styles: |
[[[
return `
.scrolling-text {
container-type: normal;
}
${variables.scrollMode !== 'hover' ? '' : 'ha-card:hover'} .scrolling-text > span.scroll {
animation: scroll-text var(--scroll-speed) ${!variables.scrollLoop && variables.scrollAlternate ? 'alternate' : '' } ${variables.scrollLoop ? 'linear' : (variables.scrollCurve || 'linear')} infinite;
text-overflow: unset !important;
display: inline-block ;
}
${variables.scrollMode === 'hover-pause' ? `
ha-card:hover .scrolling-text > span.scroll {
animation-play-state: paused !important;
}
` : ''}
@keyframes scroll-text {
from {
transform: translateX( 0% );
}
to {
transform: translateX( min(calc(-100% + var(--scroll-max)), 0px) );
}
}
`;
]]] |
Beta Was this translation helpful? Give feedback.












Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
an image immediately clarifies what I need...
my name is
and I havent really set a style to it yet, other than the color and font etc, which are ok as is.
I just want to make in scroll in when the text is too long to display, and not cut-off like this.
using other 'marquee's that take into account the length and available space in wider buttons, that would be overkill here I think. Hope to have some generic css trick that can make his happen
where that var is:
so yeah a bit much for a simple button like this which is always of the same proportions
and, maybe activate than on hover ;-)
this is what a generic styling does, (so not yet limited to names that stay within the card), already overflowing the 5px padding
this will scroll only on hard coded 100 px, but how lovely would it be if we had an actual button-width variable we could use ;-)
Beta Was this translation helpful? Give feedback.
All reactions