289 lines
8.6 KiB
Vue

<template>
<div class="marquee-box">
<div class="scroll-area">
<!-- 设置margin使内容 有从无到有的出现效果 -->
<div class="marquee-container">
<div class="icon">
<i
v-if="config.customize.icon.position === 'left'"
:class="config.customize.icon.name"
:style="{ color: config.customize.icon.color, fontSize: config.customize.fontSize + 'px' }"
/>
</div>
<svg class="svg-container">
<defs>
<linearGradient
:id="'backgroundGradient-'+config.code"
:x1="0"
:y1="['to top right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
:x2="['to right','to bottom right','to top right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
:y2="['to bottom','to bottom right'].includes(config.customize.bgGradientDirection) ? '100%' : '0'"
>
<stop
offset="0%"
:stop-color="config.customize.backgroundColorType === 'pure' ? config.customize.backgroundColor : config.customize.bgGradientColor0"
/>
<stop
offset="100%"
:stop-color="config.customize.backgroundColorType === 'pure' ? config.customize.backgroundColor : config.customize.bgGradientColor1"
/>
</linearGradient>
<linearGradient
:id="'textGradient-'+config.code"
:x1="0"
:y1="['to top right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
:x2="['to right','to bottom right','to top right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
:y2="['to bottom','to bottom right'].includes(config.customize.textGradientDirection) ? '100%' : '0'"
>
<stop
offset="0%"
:stop-color="config.customize.textColorType === 'pure' ? config.customize.textColor : config.customize.textGradientColor0"
/>
<stop
offset="100%"
:stop-color="config.customize.textColorType === 'pure' ? config.customize.textColor : config.customize.textGradientColor1"
/>
</linearGradient>
</defs>
<rect
v-if="config.customize.backgroundColorType !== 'transparent'"
width="100%"
height="100%"
:fill="`url(#backgroundGradient-${config.code})`"
/>
<text
:x="10"
:y="config.customize.fontSize"
:style="{ fontSize: config.customize.fontSize + 'px', fontWeight: config.customize.fontWeight }"
:fill="`url(#textGradient-${config.code})`"
>
<animate
v-if="isAnimate"
:attributeName="attributeName[config.customize.direction]"
:from="from[config.customize.direction]"
:to="to[config.customize.direction]"
:dur="config.customize.dur + 's'"
repeatCount="indefinite"
/>
{{ config.customize.title }}
</text>
</svg>
<div class="icon">
<i
v-if="config.customize.icon.position === 'right'"
:class="config.customize.icon.name"
:style="{ color: config.customize.icon.color, fontSize: config.customize.fontSize + 'px' }"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import Speech from 'speak-tts'
import { EventBus } from 'data-room-ui/js/utils/eventBus'
import commonMixins from 'data-room-ui/js/mixins/commonMixins'
import paramsMixins from 'data-room-ui/js/mixins/paramsMixins'
export default {
props: {
// 卡片的属性
config: {
type: Object,
default: () => ({})
}
},
data () {
return {
customClass: {},
attributeName: {
right: 'x',
left: 'x',
top: 'y',
bottom: 'y'
},
// 动画开始
from: {
left: '-100%',
right: '100%',
top: '-100%',
bottom: '100%'
},
// 动画结束
to: {
left: '100%',
right: '-100%',
top: '100%',
bottom: '-100%'
},
isAnimate: true,
// 组件内部数据
innerData: null,
// 音频播放
aduio: null,
// 语音播报
speech: null,
// 语音播报定时器
speechTimer: null
}
},
computed: {
},
mixins: [paramsMixins, commonMixins],
mounted () {
this.chartInit()
// 如果点击了生成图片,则先关闭动画
EventBus.$on('stopMarquee', () => {
this.isAnimate = false
})
// 图片生成完成后,再开启动画
EventBus.$on('startMarquee', () => {
this.isAnimate = true
})
document.addEventListener('visibilitychange', this.handleVisibilityChange)
},
beforeDestroy () {
EventBus.$off('stopMarquee')
EventBus.$off('startMarquee')
// 销毁语音播报定时器
if (this.speechTimer) {
clearInterval(this.speechTimer)
}
},
methods: {
dataFormatting (config, data) {
// 数据返回成功则赋值
if (data.success) {
data = data.data
// 获取到后端返回的数据,有则赋值
if (config.dataHandler) {
try {
// 此处函数处理data
eval(config.dataHandler)
} catch (e) {
console.error(e)
}
}
config.option.data = data
config.customize.title = config.option.data[config.dataSource.dimensionField] || config.customize.title
this.innerData = config
// 语音播报
} else {
// 数据返回失败则赋前端的模拟数据
config.option.data = []
}
return config
},
// 语音播报
voiceBroadcast (config) {
if (this.innerData) {
if (config.customize.voiceBroadcast) {
if (this.innerData.dataSource.businessKey && this.innerData.option.data[this.innerData.dataSource.metricField]) {
// 如果aduio存在先销毁这个实例或者替换它的URL
if (this.aduio) {
this.aduio.pause()
this.aduio = null
}
this.aduio = new Audio()
this.aduio.src = this.innerData.option.data[this.innerData.dataSource.metricField]
this.aduio.play()
} else if (config.customize.title) {
this.speechBroadcast(config.customize.title)
// 根据配置的时间,定时播报,第一次播报后,再定时播报
this.speechBroadcast(config.customize.title)
if (config.customize.dur) {
this.speechTimer = setInterval(() => {
this.speechBroadcast(config.customize.title)
}, config.customize.dur * 1000)
}
}
} else {
if (this.aduio) {
this.aduio.pause()
this.aduio = null
}
}
} else {
if (config.customize.voiceBroadcast) {
this.speech = new Speech()
if (config.customize.dur) {
this.speechBroadcast(config.customize.title)
this.speechTimer = setInterval(() => {
this.speechBroadcast(config.customize.title)
}, config.customize.dur * 1000)
}
}
}
},
// 语音播报
speechBroadcast (text) {
if (this.speech.hasBrowserSupport()) {
this.speech.setLanguage('zh-CN')
this.speech.pitch = 1
this.speech.init()
this.speech.speak({ text: text })
} else {
this.$message({
message: '您的浏览器不支持语音播报',
type: 'warning'
})
}
},
changeStyle (config) {
this.voiceBroadcast(config)
},
// 监听页面是否可见
handleVisibilityChange () {
if (document.visibilityState === 'hidden') {
if (this.aduio) {
this.aduio.pause()
}
if (this.speech) {
this.speech.pause()
}
} else {
if (this.aduio) {
this.aduio.play()
}
if (this.speech) {
this.speech.resume()
}
}
}
}
}
</script>
<style lang="scss" scoped>
.marquee-box {
width: 100%;
height: 100%;
white-space: nowrap;
overflow: hidden;
.scroll-area {
width: 100%;
height: 100%;
.marquee-container {
width: 100%;
height: 100%;
display: flex;
.svg-container {
width: 100%;
height: 100%;
}
}
}
.icon {
position: relative;
top: 0;
// 清除浮动
}
}
</style>