|
|
@@ -0,0 +1,446 @@
|
|
|
+<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"></svg-icon>
|
|
|
+ <svg-icon v-else icon-class="play"></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"></svg-icon
|
|
|
+ ></a>
|
|
|
+ <span>{{ sentenceActive + 1 + "/" + data.length }}</span>
|
|
|
+ <a class="btn" @click="handlePage('+')"
|
|
|
+ ><svg-icon icon-class="arrow-right-s-line"></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].tokens">
|
|
|
+ <div
|
|
|
+ :key="indexC"
|
|
|
+ :class="[
|
|
|
+ 'content-item',
|
|
|
+ (activeWordIndex === null &&
|
|
|
+ currentTime * 1000 <= data[sentenceActive].e &&
|
|
|
+ currentTime * 1000 >= data[sentenceActive].tokens[indexC].s) ||
|
|
|
+ activeWordIndex === indexC
|
|
|
+ ? 'active'
|
|
|
+ : '',
|
|
|
+ ]"
|
|
|
+ :style="{
|
|
|
+ color:
|
|
|
+ (activeWordIndex === null &&
|
|
|
+ currentTime * 1000 <= data[sentenceActive].e &&
|
|
|
+ currentTime * 1000 >= data[sentenceActive].tokens[indexC].s) ||
|
|
|
+ activeWordIndex === indexC
|
|
|
+ ? themeList[sentenceTheme].sentenceActiveColor
|
|
|
+ : themeList[sentenceTheme].sentenceColor,
|
|
|
+ fontSize: fontSize + 'px',
|
|
|
+ lineHeight: fontSize + 8 + 'px',
|
|
|
+ marginRight: itemC.a === '' ? '0' : '',
|
|
|
+ }"
|
|
|
+ @click="palyWord(indexC)"
|
|
|
+ >
|
|
|
+ {{ itemC.w }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ <div class="sentence-bottom">
|
|
|
+ <div
|
|
|
+ class="fontsize-box"
|
|
|
+ :style="{ background: themeList[sentenceTheme].bottomBg }"
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ fontSize === 32
|
|
|
+ ? themeList[sentenceTheme].bottomBarActiveBtnBg
|
|
|
+ : '',
|
|
|
+ color:
|
|
|
+ fontSize === 32 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(32, 'fontSize')"
|
|
|
+ >小</span
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="border"
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ fontSize === 48 ? themeList[sentenceTheme].bottomBarBorder : '',
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ fontSize === 40
|
|
|
+ ? themeList[sentenceTheme].bottomBarActiveBtnBg
|
|
|
+ : '',
|
|
|
+ color:
|
|
|
+ fontSize === 40 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(40, 'fontSize')"
|
|
|
+ >中</span
|
|
|
+ >
|
|
|
+ <div
|
|
|
+ class="border"
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ fontSize === 32 ? themeList[sentenceTheme].bottomBarBorder : '',
|
|
|
+ }"
|
|
|
+ ></div>
|
|
|
+ <span
|
|
|
+ :style="{
|
|
|
+ background:
|
|
|
+ fontSize === 48
|
|
|
+ ? themeList[sentenceTheme].bottomBarActiveBtnBg
|
|
|
+ : '',
|
|
|
+ color:
|
|
|
+ fontSize === 48 ? themeList[sentenceTheme].bottomBarActive : '',
|
|
|
+ }"
|
|
|
+ @click="handleChangeBgColor(48, '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].s / 1000) {
|
|
|
+ _this.audio.currentTime = _this.data[_this.sentenceActive].s / 1000;
|
|
|
+ } else {
|
|
|
+ _this.audio.currentTime = _this.currentTime;
|
|
|
+ }
|
|
|
+ _this.ed = _this.data[_this.sentenceActive].e / 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].tokens[index].s / 1000;
|
|
|
+ _this.ed = _this.data[_this.sentenceActive].tokens[index].e / 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;
|
|
|
+ .sentence-top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ .play-btn {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 20px;
|
|
|
+ padding: 8px;
|
|
|
+ background: #175dff;
|
|
|
+ color: rgba(255, 255, 255, 0.96);
|
|
|
+ }
|
|
|
+ .sentence-right {
|
|
|
+ color: rgba(0, 0, 0, 0.96);
|
|
|
+ font-weight: 700;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ .btn {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+ span {
|
|
|
+ min-width: 40px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .el-icon-close {
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .svg-icon {
|
|
|
+ font-size: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .content-inner {
|
|
|
+ padding: 40px 45px;
|
|
|
+ border-radius: 8px;
|
|
|
+ margin: 16px 0;
|
|
|
+ display: flex;
|
|
|
+ flex-flow: wrap;
|
|
|
+ .content-item {
|
|
|
+ margin: 0 6px 0 0;
|
|
|
+ font-size: 32px;
|
|
|
+ line-height: 40px;
|
|
|
+ cursor: pointer;
|
|
|
+ &.active {
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .sentence-bottom {
|
|
|
+ display: flex;
|
|
|
+ .fontsize-box {
|
|
|
+ display: flex;
|
|
|
+ padding: 3px;
|
|
|
+ border-radius: 20px;
|
|
|
+ align-items: center;
|
|
|
+ span {
|
|
|
+ font-weight: 400;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 22px;
|
|
|
+ width: 38px;
|
|
|
+ padding: 2px 12px;
|
|
|
+ border-radius: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ &.active {
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .border {
|
|
|
+ width: 1px;
|
|
|
+ height: 14px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .article-color {
|
|
|
+ list-style: none;
|
|
|
+ display: flex;
|
|
|
+ width: 132px;
|
|
|
+ height: 32px;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 40px;
|
|
|
+ margin: 0 0 0 12px;
|
|
|
+ padding: 4px;
|
|
|
+ .color-item {
|
|
|
+ padding: 2px;
|
|
|
+ border: 2px solid transparent;
|
|
|
+ border-radius: 50%;
|
|
|
+ a {
|
|
|
+ width: 16px;
|
|
|
+ height: 16px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: block;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|