|
@@ -0,0 +1,426 @@
|
|
|
+<template>
|
|
|
+ <div class="sentence-box" :style="{ background: themeList[sentenceTheme].bg }">
|
|
|
+ <div class="sentence-top">
|
|
|
+ <a class="play-btn" @click="handlePlay" :style="{ background: themeList[sentenceTheme].playBtnBg }">
|
|
|
+ <svg-icon v-if="isPlay" icon-class="pause" size="24"></svg-icon>
|
|
|
+ <svg-icon v-else icon-class="play" size="24"></svg-icon>
|
|
|
+ </a>
|
|
|
+ <div class="sentence-right" :style="{ color: themeList[sentenceTheme].rightBtnColor }">
|
|
|
+ <a class="btn" @click="handlePage('-')"><svg-icon icon-class="arrow-left-s-line" size="24"></svg-icon></a>
|
|
|
+ <span>{{ sentenceActive + 1 + '/' + data.length }}</span>
|
|
|
+ <a class="btn" @click="handlePage('+')"><svg-icon icon-class="arrow-right-s-line" size="24"></svg-icon></a>
|
|
|
+ <i class="el-icon-close" @click="closeWord"></i>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="content-inner" :style="{ background: themeList[sentenceTheme].contentBg }">
|
|
|
+ <template v-for="(itemC, indexC) in data[sentenceActive].wordsResultList">
|
|
|
+ <div
|
|
|
+ :key="indexC"
|
|
|
+ :class="[
|
|
|
+ 'content-item',
|
|
|
+ (activeWordIndex === null &&
|
|
|
+ currentTime * 1000 <= data[sentenceActive].ed &&
|
|
|
+ currentTime * 1000 >= data[sentenceActive].wordsResultList[indexC].wordBg) ||
|
|
|
+ activeWordIndex === indexC
|
|
|
+ ? 'active'
|
|
|
+ : '',
|
|
|
+ ]"
|
|
|
+ :style="{
|
|
|
+ color:
|
|
|
+ (activeWordIndex === null &&
|
|
|
+ currentTime * 1000 <= data[sentenceActive].ed &&
|
|
|
+ currentTime * 1000 >= data[sentenceActive].wordsResultList[indexC].wordBg) ||
|
|
|
+ activeWordIndex === indexC
|
|
|
+ ? themeList[sentenceTheme].sentenceActiveColor
|
|
|
+ : themeList[sentenceTheme].sentenceColor,
|
|
|
+ fontSize: fontSize + 'px',
|
|
|
+ lineHeight: fontSize + 8 + 'px',
|
|
|
+ }"
|
|
|
+ @click="palyWord(indexC)"
|
|
|
+ >
|
|
|
+ {{ itemC.wordsName || itemC.onebest }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <div class="sentence-bottom">
|
|
|
+ <div class="fontsize-box" :style="{ background: themeList[sentenceTheme].bottomBg }">
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background: fontSize === 20 ? themeList[sentenceTheme].bottomBarActiveBtnBg : '',
|
|
|
+ color: fontSize === 20 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(20, 'fontSize')"
|
|
|
+ >小</span
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="border"
|
|
|
+ :style="{
|
|
|
+ background: fontSize === 28 ? themeList[sentenceTheme].bottomBarBorder : '',
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background: fontSize === 24 ? themeList[sentenceTheme].bottomBarActiveBtnBg : '',
|
|
|
+ color: fontSize === 24 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(24, 'fontSize')"
|
|
|
+ >中</span
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="border"
|
|
|
+ :style="{
|
|
|
+ background: fontSize === 20 ? themeList[sentenceTheme].bottomBarBorder : '',
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background: fontSize === 28 ? themeList[sentenceTheme].bottomBarActiveBtnBg : '',
|
|
|
+ color: fontSize === 28 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(28, 'fontSize')"
|
|
|
+ >大</span
|
|
|
+ >
|
|
|
+ </div>
|
|
|
+ <ul class="article-color" :style="{ background: themeList[sentenceTheme].bottomBg }">
|
|
|
+ <li
|
|
|
+ :class="['color-item', sentenceTheme === indexC ? 'active' : '']"
|
|
|
+ v-for="(itemC, indexC) in themeList"
|
|
|
+ :key="indexC"
|
|
|
+ @click="handleChangeBgColor(indexC, 'theme')"
|
|
|
+ :style="{
|
|
|
+ borderColor: sentenceTheme === indexC ? itemC.boxBorder : '',
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <a
|
|
|
+ :style="{
|
|
|
+ background: itemC.themeBg,
|
|
|
+ borderColor: sentenceTheme === indexC ? itemC.themeActiveBorder : '',
|
|
|
+ }"
|
|
|
+ ></a>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+//这里可以导入其它文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
|
|
|
+//例如:import 《组件名称》from ‘《组件路径》';
|
|
|
+
|
|
|
+export default {
|
|
|
+ //import引入的组件需要注入到对象中才能使用
|
|
|
+ components: {},
|
|
|
+ props: ['fontSize', 'sentenceTheme', 'data', 'activeIndex', 'mp3Url'],
|
|
|
+ data() {
|
|
|
+ //这里存放数据
|
|
|
+ return {
|
|
|
+ isPlay: false, // 音频是否在播放
|
|
|
+ sentenceActive: this.activeIndex,
|
|
|
+ themeList: [
|
|
|
+ {
|
|
|
+ type: 'white',
|
|
|
+ bg: '#E5E6EB',
|
|
|
+ playBtnBg: '#175DFF', // 播放按钮背景色
|
|
|
+ rightBtnColor: 'rgba(0, 0, 0, 0.96)', // 右侧按钮颜色
|
|
|
+ contentBg: '#F7F8FA',
|
|
|
+ sentenceColor: 'rgba(0, 0, 0, 0.96)',
|
|
|
+ sentenceActiveColor: '#175DFF',
|
|
|
+ bottomBg: '#F2F3F5',
|
|
|
+ bottomBarActiveBtnBg: '#FFFFFF',
|
|
|
+ bottomBarColor: '#4E5969',
|
|
|
+ bottomBarActive: '#165DFF',
|
|
|
+ bottomBarBorder: '#E5E6EB',
|
|
|
+ themeBg: '#FFFFFF',
|
|
|
+ themeActiveBorder: '#E5E6EB',
|
|
|
+ boxBorder: '#3459D2', // 选中时高亮的外圈边框
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'darkGreen',
|
|
|
+ bg: '#C2C9C6',
|
|
|
+ playBtnBg: '#236E55', // 播放按钮背景色
|
|
|
+ rightBtnColor: 'rgba(0, 0, 0, 0.96)', // 右侧按钮颜色
|
|
|
+ contentBg: '#DFE4E2',
|
|
|
+ sentenceColor: 'rgba(0, 0, 0, 0.96)',
|
|
|
+ sentenceActiveColor: '#236E55',
|
|
|
+ bottomBg: '#DFE4E2',
|
|
|
+ bottomBarActiveBtnBg: '#FFFFFF',
|
|
|
+ bottomBarColor: '#4E5969',
|
|
|
+ bottomBarActive: '#236E55',
|
|
|
+ bottomBarBorder: '#C2C9C6',
|
|
|
+ themeBg: '#5BB99A',
|
|
|
+ themeActiveBorder: '#5BB99A',
|
|
|
+ boxBorder: '#fff', // 选中时高亮的外圈边框
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'darkBlue',
|
|
|
+ bg: '#1C2129',
|
|
|
+ playBtnBg: '#5373E7', // 播放按钮背景色
|
|
|
+ rightBtnColor: '#fff', // 右侧按钮颜色
|
|
|
+ contentBg: '#2F3742',
|
|
|
+ sentenceColor: '#C1C5CD',
|
|
|
+ sentenceActiveColor: '#5373E7',
|
|
|
+ bottomBg: '#2F3742',
|
|
|
+ bottomBarActiveBtnBg: '#1C2129',
|
|
|
+ bottomBarColor: '#929CA8',
|
|
|
+ bottomBarActive: '#5373E7',
|
|
|
+ bottomBarBorder: '#1C2129',
|
|
|
+ themeBg: '#1F2C5C',
|
|
|
+ themeActiveBorder: '#1F2C5C',
|
|
|
+ boxBorder: '#fff', // 选中时高亮的外圈边框
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'armyGreen',
|
|
|
+ bg: '#2A2F2C',
|
|
|
+ playBtnBg: '#30A47D', // 播放按钮背景色
|
|
|
+ rightBtnColor: '#fff', // 右侧按钮颜色
|
|
|
+ contentBg: '#393F3C',
|
|
|
+ sentenceColor: '#C1C5CD',
|
|
|
+ sentenceActiveColor: '#30A47D',
|
|
|
+ bottomBg: '#393F3C',
|
|
|
+ bottomBarActiveBtnBg: '#2A2F2C',
|
|
|
+ bottomBarColor: '#C1C5CD',
|
|
|
+ bottomBarActive: '#30A47D',
|
|
|
+ bottomBarBorder: '#2A2F2C',
|
|
|
+ themeBg: '#13392E',
|
|
|
+ themeActiveBorder: '#13392E',
|
|
|
+ boxBorder: '#fff', // 选中时高亮的外圈边框
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ currentTime: 0,
|
|
|
+ audio: new Audio(),
|
|
|
+ ed: null,
|
|
|
+ activeWordIndex: null,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ //计算属性 类似于data概念
|
|
|
+ computed: {},
|
|
|
+ //监控data中数据变化
|
|
|
+ watch: {},
|
|
|
+ //方法集合
|
|
|
+ methods: {
|
|
|
+ // 播放、暂停
|
|
|
+ handlePlay() {
|
|
|
+ let _this = this;
|
|
|
+ _this.activeWordIndex = null;
|
|
|
+ _this.isPlay = !_this.isPlay;
|
|
|
+ if (!_this.isPlay) {
|
|
|
+ _this.audio.pause();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ _this.audio.pause();
|
|
|
+ _this.audio.load();
|
|
|
+ _this.audio.src = _this.mp3Url;
|
|
|
+ if (_this.currentTime <= _this.data[_this.sentenceActive].bg / 1000) {
|
|
|
+ _this.audio.currentTime = _this.data[_this.sentenceActive].bg / 1000;
|
|
|
+ } else {
|
|
|
+ _this.audio.currentTime = _this.currentTime;
|
|
|
+ }
|
|
|
+ _this.ed = _this.data[_this.sentenceActive].ed / 1000;
|
|
|
+ _this.audio.loop = false;
|
|
|
+ _this.audio.play();
|
|
|
+ },
|
|
|
+ palyWord(index) {
|
|
|
+ let _this = this;
|
|
|
+ _this.activeWordIndex = index;
|
|
|
+ _this.audio.pause();
|
|
|
+ _this.audio.load();
|
|
|
+ _this.audio.src = _this.mp3Url;
|
|
|
+ _this.audio.currentTime = _this.data[_this.sentenceActive].wordsResultList[index].wordBg / 1000;
|
|
|
+ _this.ed = _this.data[_this.sentenceActive].wordsResultList[index].wordEd / 1000;
|
|
|
+ _this.audio.loop = false;
|
|
|
+ _this.audio.play();
|
|
|
+ },
|
|
|
+ // 关闭
|
|
|
+ closeWord() {
|
|
|
+ this.$emit('closeWord');
|
|
|
+ },
|
|
|
+ handlePage(type) {
|
|
|
+ if (type === '-') {
|
|
|
+ if (this.sentenceActive > 0) {
|
|
|
+ this.audio.pause();
|
|
|
+ this.isPlay = false;
|
|
|
+ this.currentTime = 0;
|
|
|
+ this.sentenceActive--;
|
|
|
+ } else {
|
|
|
+ this.$message.warning('已经是第一句');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (this.sentenceActive < this.data.length - 1) {
|
|
|
+ this.audio.pause();
|
|
|
+ this.isPlay = false;
|
|
|
+ this.currentTime = 0;
|
|
|
+ this.sentenceActive++;
|
|
|
+ } else {
|
|
|
+ this.$message.warning('已经是最后一句');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 切换主题颜色
|
|
|
+ handleChangeBgColor(index, type) {
|
|
|
+ if (type === 'fontSize') {
|
|
|
+ this.$emit('changeTheme', '', index);
|
|
|
+ } else {
|
|
|
+ this.$emit('changeTheme', index);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+ //生命周期 - 创建完成(可以访问当前this实例)
|
|
|
+ created() {},
|
|
|
+ //生命周期 - 挂载完成(可以访问DOM元素)
|
|
|
+ mounted() {
|
|
|
+ let _this = this;
|
|
|
+ _this.audio.addEventListener('timeupdate', function () {
|
|
|
+ _this.currentTime = _this.audio.currentTime;
|
|
|
+ const currentTime = _this.audio.currentTime;
|
|
|
+ if (_this.ed && currentTime >= _this.ed) {
|
|
|
+ _this.audio.pause();
|
|
|
+ _this.isPlay = false;
|
|
|
+ _this.currentTime = 0;
|
|
|
+ _this.activeWordIndex = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ //生命周期-创建之前
|
|
|
+ beforeCreated() {},
|
|
|
+ //生命周期-挂载之前
|
|
|
+ beforeMount() {},
|
|
|
+ //生命周期-更新之前
|
|
|
+ beforUpdate() {},
|
|
|
+ //生命周期-更新之后
|
|
|
+ updated() {},
|
|
|
+ //生命周期-销毁之前
|
|
|
+ beforeDestory() {},
|
|
|
+ //生命周期-销毁完成
|
|
|
+ destoryed() {},
|
|
|
+ //如果页面有keep-alive缓存功能,这个函数会触发
|
|
|
+ activated() {},
|
|
|
+};
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+/* @import url(); 引入css类 */
|
|
|
+.sentence-box {
|
|
|
+ padding: 16px;
|
|
|
+ border-radius: 16px;
|
|
|
+
|
|
|
+ .sentence-top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .play-btn {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ padding: 8px;
|
|
|
+ color: rgba(255, 255, 255, 96%);
|
|
|
+ background: #175dff;
|
|
|
+ border-radius: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sentence-right {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 20px;
|
|
|
+ color: rgba(0, 0, 0, 96%);
|
|
|
+
|
|
|
+ .btn {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ span {
|
|
|
+ min-width: 40px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-icon-close {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .svg-icon {
|
|
|
+ font-size: 24px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .content-inner {
|
|
|
+ display: flex;
|
|
|
+ flex-flow: wrap;
|
|
|
+ padding: 40px 45px;
|
|
|
+ margin: 16px 0;
|
|
|
+ border-radius: 8px;
|
|
|
+
|
|
|
+ .content-item {
|
|
|
+ // margin: 0 6px 0 0;
|
|
|
+ font-family: '楷体';
|
|
|
+ font-size: 20px;
|
|
|
+ line-height: 40px;
|
|
|
+ cursor: pointer;
|
|
|
+
|
|
|
+ // &.active {
|
|
|
+ // font-weight: 700;
|
|
|
+ // }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .sentence-bottom {
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ .fontsize-box {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 3px;
|
|
|
+ border-radius: 20px;
|
|
|
+
|
|
|
+ span {
|
|
|
+ width: 38px;
|
|
|
+ padding: 2px 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 22px;
|
|
|
+ cursor: pointer;
|
|
|
+ border-radius: 20px;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .border {
|
|
|
+ width: 1px;
|
|
|
+ height: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .article-color {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 132px;
|
|
|
+ height: 32px;
|
|
|
+ padding: 4px;
|
|
|
+ margin-left: 12px;
|
|
|
+ border-radius: 40px;
|
|
|
+
|
|
|
+ .color-item {
|
|
|
+ padding: 2px;
|
|
|
+ border: 2px solid transparent;
|
|
|
+ border-radius: 50%;
|
|
|
+
|
|
|
+ a {
|
|
|
+ display: block;
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ padding: 0;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|