88 lines
1.8 KiB
Vue
88 lines
1.8 KiB
Vue
<template>
|
|
<v-footer :color="color" :dark="is_dark" fixed>
|
|
<span ref="marquee" class="text-h6">
|
|
<div ref="content" v-html="content" />
|
|
</span>
|
|
</v-footer>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Prop, Ref, Vue, Watch } from "vue-property-decorator";
|
|
import Color from "color";
|
|
|
|
@Component
|
|
export default class TickerBar extends Vue {
|
|
@Prop({ required: true })
|
|
private readonly content!: string;
|
|
|
|
@Prop({ default: "primary" })
|
|
private readonly color!: string;
|
|
|
|
@Ref("content")
|
|
private readonly _content!: HTMLDivElement;
|
|
|
|
@Ref("marquee")
|
|
private readonly _marquee!: HTMLSpanElement;
|
|
|
|
private get is_dark(): boolean {
|
|
return this.footer_color.isDark();
|
|
}
|
|
|
|
private get footer_color(): Color {
|
|
// try getting from vuetify theme
|
|
let color = this.$vuetify.theme.themes.light[this.color];
|
|
|
|
if (typeof color === "string") {
|
|
return Color(color);
|
|
}
|
|
|
|
// fallback: parse color directly
|
|
return Color(this.color);
|
|
}
|
|
|
|
@Watch("content", { immediate: true })
|
|
private set_marquee_duration(): void {
|
|
this.$nextTick((): void => {
|
|
const width = parseFloat(
|
|
window.getComputedStyle(this._content).getPropertyValue("width")
|
|
);
|
|
|
|
// 10 seconds + another second per 40px
|
|
const duration = 10 + Math.round(width / 40);
|
|
|
|
this._marquee.style.setProperty("animation-duration", `${duration}s`);
|
|
});
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@keyframes marquee {
|
|
0% {
|
|
transform: translate(0, 0);
|
|
}
|
|
100% {
|
|
transform: translate(-100%, 0);
|
|
}
|
|
}
|
|
|
|
.v-footer {
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
|
|
> span {
|
|
display: inline-block;
|
|
padding-left: 100%;
|
|
text-indent: 0;
|
|
animation: marquee 30s linear infinite;
|
|
|
|
&:hover {
|
|
animation-play-state: paused;
|
|
}
|
|
}
|
|
|
|
:deep() * {
|
|
margin: 0 !important;
|
|
}
|
|
}
|
|
</style>
|