Commit 28568645 authored by 15011343103's avatar 15011343103

feat:

parent 9d944887
{
"name": "h5-template-generator",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"bl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
}
},
"buffer-alloc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
"integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
"requires": {
"buffer-alloc-unsafe": "^1.1.0",
"buffer-fill": "^1.0.0"
}
},
"buffer-alloc-unsafe": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
"integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
},
"buffer-crc32": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
"integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
},
"buffer-fill": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
"integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
},
"compressing": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/compressing/-/compressing-1.5.0.tgz",
"integrity": "sha512-CPMzMd/MswJh7CPRo605FQhXQlP0JbwCjPteh9GZ0jYTQcDBrYJLEW7Fm1dpTdOHk0/2ZE2C8SO5EX4Dp7KZ8w==",
"requires": {
"flushwritable": "^1.0.0",
"get-ready": "^1.0.0",
"iconv-lite": "^0.5.0",
"mkdirp": "^0.5.1",
"pump": "^3.0.0",
"streamifier": "^0.1.1",
"tar-stream": "^1.5.2",
"yauzl": "^2.7.0",
"yazl": "^2.4.2"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
"integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=",
"requires": {
"pend": "~1.2.0"
}
},
"flushwritable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/flushwritable/-/flushwritable-1.0.0.tgz",
"integrity": "sha1-PjKNj95BKtR+c44751C00pAENJg="
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"get-ready": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/get-ready/-/get-ready-1.0.0.tgz",
"integrity": "sha1-+RgX8emt7P6hOlYq38jeiDqzR4I="
},
"iconv-lite": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.0.tgz",
"integrity": "sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
"integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"readable-stream": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"streamifier": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz",
"integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"tar-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz",
"integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==",
"requires": {
"bl": "^1.0.0",
"buffer-alloc": "^1.2.0",
"end-of-stream": "^1.0.0",
"fs-constants": "^1.0.0",
"readable-stream": "^2.3.0",
"to-buffer": "^1.1.1",
"xtend": "^4.0.0"
}
},
"to-buffer": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz",
"integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
"yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
"integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=",
"requires": {
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
},
"yazl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
"integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
"requires": {
"buffer-crc32": "~0.2.3"
}
}
}
}
This diff is collapsed.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
overflow: hidden;
background: #000;
}
#app {
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.loading {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #000;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
}
.loading i {
font-size: 40px;
margin-bottom: 20px;
}
.photo-section {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.photo-wrapper {
width: 100%;
height: 100%;
display: flex;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform;
}
.photo-item {
flex: 0 0 100%;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
.photo-item img {
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
object-fit: contain;
}
.point-area {
position: absolute;
border: 2px dashed rgba(64, 158, 255, 0.3);
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
}
.point-area:hover {
border-color: #409EFF;
background-color: rgba(64, 158, 255, 0.1);
}
.highlight-area {
position: absolute;
border: 3px solid #409EFF;
border-radius: 4px;
background-color: rgba(64, 158, 255, 0.1);
pointer-events: none;
transition: all 0.3s ease;
}
.controls {
position: fixed;
bottom: 30px;
left: 30px;
z-index: 1000;
display: flex;
align-items: center;
gap: 15px;
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(5px);
border-radius: 30px;
padding: 0;
width: 60px;
height: 60px;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
.controls.expanded {
width: 380px;
padding: 0 15px;
cursor: default;
}
.toggle-icon {
font-size: 24px;
color: #fff;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
}
.controls .el-button {
border-radius: 50%;
width: 50px;
height: 50px;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
flex-shrink: 0;
}
.controls .el-button.collapse-button {
width: 50px;
height: 50px;
margin-left: 5px;
background-color: #909399;
border-color: #909399;
}
.controls .el-button.collapse-button:hover {
background-color: #F56C6C;
border-color: #F56C6C;
}
.controls .el-button.collapse-button .button-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
line-height: 1;
}
body {
background: #fff;
.controls .el-button.collapse-button .button-text {
font-size: 12px;
margin-top: 2px;
color: #fff;
}
.controls .el-button.collapse-button i {
font-size: 18px;
margin-bottom: 2px;
}
.controls .el-button:hover {
transform: scale(1.1);
}
.controls .el-button:active {
transform: scale(0.95);
}
.controls .el-button[disabled] {
opacity: 0.5;
cursor: not-allowed;
}
.controls .el-button.is-point-mode {
background-color: #67C23A;
border-color: #67C23A;
}
.button-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
line-height: 1;
}
.button-text {
font-size: 12px;
margin-top: 2px;
}
.controls .el-button i {
font-size: 20px;
margin-bottom: 2px;
}
audio {
display: none;
}
/* 覆盖 Element UI 的默认样式 */
.el-container {
height: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
.el-main {
height: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
#test{
margin: 80px;
}
.book-player {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.text-section {
margin: 30px 0;
}
.text-content {
padding: 20px;
background: #f5f7fa;
border-radius: 4px;
}
.text-content p {
margin: 0 0 15px;
line-height: 1.6;
}
.translation {
color: #666;
font-style: italic;
}
.audio-section {
margin: 30px 0;
padding: 20px;
background: #f5f7fa;
border-radius: 4px;
}
[v-cloak] {
display: none;
}
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>h5-template-generator</title>
<link href="./index.css" rel="stylesheet" >
<script type="text/javascript" src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/jquery-1.12.3.min.js"></script>
<script type="text/javascript" src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
<script type="text/javascript" src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/vue.min.js"></script>
<!-- 引入样式 -->
<link href="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/element-ui/theme-chalk/index.css" rel="stylesheet">
<!-- 引入组件库 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Book Player</title>
<link rel="stylesheet" href="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/element-ui/theme-chalk/index.css">
<script src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/jquery-1.12.3.min.js"></script>
<script src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/air.js"></script>
<script src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/vue.min.js"></script>
<script src="//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/element-ui/index.js"></script>
<link rel="stylesheet" href="index.css">
</head>
<body>
<h1 id="test"></h1>
</body>
<script src="./index.js"></script>
<div id="app" v-cloak>
<div v-if="loading" class="loading">
<i class="el-icon-loading"></i>
<p>Loading...</p>
</div>
<template v-else>
<div class="photo-section">
<div class="photo-wrapper" :style="wrapperStyle">
<div v-for="(url, index) in bookData.photoUrls"
:key="index"
class="photo-item">
<img :src="url" :alt="'Page ' + (index + 1)">
<!-- 点读区域 -->
<template v-if="currentPage === index">
<div v-for="(point, pointIndex) in getCurrentPagePoints()"
:key="pointIndex"
class="point-area"
:style="getHighlightStyle(point.rect)"
@click="handlePointClick(point)">
</div>
<!-- 当前高亮区域 -->
<div v-if="currentHighlight"
class="highlight-area"
:style="getHighlightStyle(currentHighlight)">
</div>
</template>
</div>
</div>
</div>
<!-- 可展开的控制面板 -->
<div class="controls" :class="{ 'expanded': isPanelExpanded }" @click="!isPanelExpanded && togglePanel()">
<!-- 展开状态的图标 -->
<i v-if="!isPanelExpanded" class="el-icon-menu toggle-icon"></i>
<!-- 控制按钮,仅在展开状态显示 -->
<template v-if="isPanelExpanded">
<!-- 上一页按钮 -->
<el-button type="primary" circle @click.stop="prevPage" :disabled="currentPage === 0">
<i class="el-icon-arrow-left"></i>
</el-button>
<!-- 播放/暂停按钮 -->
<el-button type="primary" circle @click.stop="togglePlay" :disabled="isPointMode">
<i :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'"></i>
</el-button>
<!-- 点读模式切换按钮 -->
<el-button type="primary" circle @click.stop="togglePointMode" :class="{ 'is-point-mode': isPointMode }">
<div class="button-content">
<i class="el-icon-pointer"></i>
<span class="button-text">点读</span>
</div>
</el-button>
<!-- 下一页按钮 -->
<el-button type="primary" circle @click.stop="nextPage" :disabled="currentPage >= bookData.photoUrls.length - 1">
<i class="el-icon-arrow-right"></i>
</el-button>
<!-- 收起按钮 -->
<el-button type="info" circle class="collapse-button" @click.stop="togglePanel">
<div class="button-content">
<i class="el-icon-close"></i>
<span class="button-text">收起</span>
</div>
</el-button>
</template>
<audio ref="audioPlayer" :src="bookData.audioUrl" @timeupdate="onTimeUpdate" @ended="onAudioEnded"></audio>
</div>
</template>
</div>
<script src="index.js"></script>
</body>
</html>
\ No newline at end of file
/*
window.courseware.getData(callback,key); //用于获取数据
......@@ -7,37 +6,226 @@ window.air.hideAirClassLoading(key,data); //页面加载完成时,必须调
*/
// 对应模板的名称
const key = "bookplayer";
//对应模板的名称,这个可以不写,但是最好是能写上,同时开发多个模板的情况下如果不写这个,本地缓存可能会存在混乱
var key = "h5_test";
$(function(){
window.courseware.getData(function(data){
//数据加载完,才算页面加载完成
//获取数据
if(data&&data.test){
$("#test").html("Hello "+data.test);
} else{
//一般来讲,如果没有获取到数据的话,给一组默认数据,用于演示模板的展示效果,方便别人了解这个模板
$("#test").html("Hello 我是演示信息!");
}
//数据渲染完成后,添加各种交互或者动画效果
//我这里简单做一个渐入渐出效果的例子
var flag = true;
setInterval(function(){
if(flag){
$("#test").fadeIn(500);
}else{
$("#test").fadeOut(500);
}
flag = !flag;
},1000)
//在页面加载、事件、动画等都初始化完成后,一定要记得调用此方法,否则模板将是不可用的
window.air.hideAirClassLoading(key,data);
},key);
new Vue({
el: '#app',
data() {
return {
bookData: null,
loading: true,
currentPage: 0,
isPlaying: false,
currentHighlight: null,
isPointMode: false, // 点读模式状态
pointAudio: null, // 点读音频实例
isPanelExpanded: false // 控制面板展开状态
}
},
computed: {
wrapperStyle() {
return {
transform: `translateX(-${this.currentPage * 100}%)`
};
}
},
methods: {
togglePlay() {
const audio = this.$refs.audioPlayer;
if (this.isPlaying) {
audio.pause();
} else {
// 如果在点读模式下,切换到连续播放时停止点读
if (this.pointAudio) {
this.pointAudio.pause();
this.pointAudio = null;
}
audio.play();
}
this.isPlaying = !this.isPlaying;
},
// 切换点读模式
togglePointMode() {
this.isPointMode = !this.isPointMode;
if (!this.isPointMode && this.pointAudio) {
this.pointAudio.pause();
this.pointAudio = null;
}
// 进入点读模式时暂停连续播放
if (this.isPointMode && this.isPlaying) {
this.togglePlay();
}
},
// 处理点读区域点击
handlePointClick(item) {
if (!this.isPointMode) return;
// 如果有正在播放的点读音频,先停止并移除事件监听
if (this.pointAudio) {
const oldAudio = this.pointAudio;
this.pointAudio = null;
this.currentHighlight = null;
// 确保在创建新音频之前完全停止旧音频
oldAudio.pause();
oldAudio.removeEventListener('timeupdate', oldAudio._timeUpdateHandler);
}
})
\ No newline at end of file
// 创建新的音频实例用于点读
const audio = new Audio(this.bookData.audioUrl);
audio.currentTime = parseFloat(item.start);
// 创建timeupdate处理函数并保存引用,以便后续移除
audio._timeUpdateHandler = () => {
if (audio && audio.currentTime >= parseFloat(item.end)) {
audio.pause();
if (this.pointAudio === audio) {
this.pointAudio = null;
this.currentHighlight = null;
}
}
};
// 监听点读结束
audio.addEventListener('timeupdate', audio._timeUpdateHandler);
// 显示高亮并播放
this.pointAudio = audio;
this.currentHighlight = item.rect;
// 使用 Promise 处理播放
audio.play().catch(error => {
console.log('Audio playback failed:', error);
this.pointAudio = null;
this.currentHighlight = null;
});
},
onTimeUpdate() {
// 在点读模式下不更新连续播放的高亮
if (this.isPointMode) return;
const audio = this.$refs.audioPlayer;
const currentTime = audio.currentTime;
if (this.bookData.pointReads && this.bookData.pointReads.length) {
let foundHighlight = null;
this.bookData.pointReads.forEach((pageData, pageIndex) => {
if (pageData.items) {
pageData.items.forEach(item => {
const start = parseFloat(item.start);
const end = parseFloat(item.end);
if (currentTime >= start && currentTime < end) {
foundHighlight = item.rect;
if (pageIndex !== this.currentPage) {
this.currentPage = pageIndex;
}
}
});
}
});
this.currentHighlight = foundHighlight;
}
},
onAudioEnded() {
this.isPlaying = false;
this.currentHighlight = null;
this.currentPage = 0;
},
getHighlightStyle(rect) {
if (!rect) return {};
return {
left: rect.x * 100 + '%',
top: rect.y * 100 + '%',
width: rect.width * 100 + '%',
height: rect.height * 100 + '%',
cursor: this.isPointMode ? 'pointer' : 'default'
};
},
// 获取当前页面的所有点读区域
getCurrentPagePoints() {
if (!this.bookData || !this.bookData.pointReads) return [];
const currentPageData = this.bookData.pointReads[this.currentPage];
return currentPageData && currentPageData.items ? currentPageData.items : [];
},
// 上一页
prevPage() {
if (this.currentPage > 0) {
this.changePage(this.currentPage - 1);
}
},
// 下一页
nextPage() {
if (this.currentPage < this.bookData.photoUrls.length - 1) {
this.changePage(this.currentPage + 1);
}
},
// 切换页面
changePage(newPage) {
// 切换页面时停止点读
if (this.pointAudio) {
this.pointAudio.pause();
this.pointAudio = null;
this.currentHighlight = null;
}
if (this.isPlaying) {
const nextPageData = this.bookData.pointReads[newPage];
if (nextPageData && nextPageData.items && nextPageData.items.length > 0) {
const firstItem = nextPageData.items[0];
this.$refs.audioPlayer.currentTime = parseFloat(firstItem.start);
}
}
this.currentPage = newPage;
},
// 加载数据
loadData() {
// 先尝试加载默认数据
fetch('./defaultData.json')
.then(response => response.json())
.then(defaultData => {
this.bookData = defaultData;
this.loading = false;
if (window.air && window.air.hideAirClassLoading) {
window.air.hideAirClassLoading(key, this.bookData);
}
})
.catch(error => {
console.error('Error loading defaultData.json:', error);
this.loading = false;
});
},
togglePanel() {
this.isPanelExpanded = !this.isPanelExpanded;
}
},
created() {
// 检查 courseware API 是否可用
if (window.courseware && window.courseware.getData) {
window.courseware.getData((data) => {
if (data) {
this.bookData = data;
this.loading = false;
if (window.air && window.air.hideAirClassLoading) {
window.air.hideAirClassLoading(key, this.bookData);
}
} else {
this.loadData(); // 如果没有数据,加载默认数据
}
}, key);
} else {
this.loadData(); // 如果 API 不可用,加载默认数据
}
},
beforeDestroy() {
// 清理点读音频实例
if (this.pointAudio) {
this.pointAudio.pause();
this.pointAudio = null;
}
}
});
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment