Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
B
bookplayer
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
template admin
bookplayer
Commits
28568645
Commit
28568645
authored
Mar 27, 2025
by
15011343103
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat:
parent
9d944887
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
1291 additions
and
302 deletions
+1291
-302
package-lock.json
package-lock.json
+0
-253
defaultData.json
play/defaultData.json
+716
-0
index.css
play/index.css
+270
-2
index.html
play/index.html
+84
-14
index.js
play/index.js
+221
-33
No files found.
package-lock.json
deleted
100644 → 0
View file @
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"
}
}
}
}
play/defaultData.json
0 → 100644
View file @
28568645
This diff is collapsed.
Click to expand it.
play/index.css
View file @
28568645
*
{
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
{
.controls
.el-button.collapse-button
.button-text
{
background
:
#fff
;
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
{
#test
{
margin
:
80px
;
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
play/index.html
View file @
28568645
<!DOCTYPE html>
<!DOCTYPE html>
<html
lang=
"en"
>
<html>
<head>
<head>
<meta
charset=
"UTF-8"
>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
>
<meta
http-equiv=
"X-UA-Compatible"
content=
"ie=edge"
>
<title>
Book Player
</title>
<title>
h5-template-generator
</title>
<link
rel=
"stylesheet"
href=
"//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/element-ui/theme-chalk/index.css"
>
<link
href=
"./index.css"
rel=
"stylesheet"
>
<script
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/jquery-1.12.3.min.js"
></script>
<script
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/air.js"
></script>
<script
src=
"//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/vue.min.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"
>
<!-- 引入组件库 -->
<script
src=
"//teach.cdn.ireadabc.com/h5template/h5-static-lib/js/element-ui/index.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>
</head>
<body>
<body>
<h1
id=
"test"
></h1>
<div
id=
"app"
v-cloak
>
</body>
<div
v-if=
"loading"
class=
"loading"
>
<script
src=
"./index.js"
></script>
<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>
</html>
\ No newline at end of file
play/index.js
View file @
28568645
/*
/*
window.courseware.getData(callback,key); //用于获取数据
window.courseware.getData(callback,key); //用于获取数据
...
@@ -7,37 +6,226 @@ window.air.hideAirClassLoading(key,data); //页面加载完成时,必须调
...
@@ -7,37 +6,226 @@ window.air.hideAirClassLoading(key,data); //页面加载完成时,必须调
*/
*/
// 对应模板的名称
const
key
=
"
bookplayer
"
;
//对应模板的名称,这个可以不写,但是最好是能写上,同时开发多个模板的情况下如果不写这个,本地缓存可能会存在混乱
new
Vue
({
var
key
=
"
h5_test
"
;
el
:
'
#app
'
,
data
()
{
$
(
function
(){
return
{
bookData
:
null
,
window
.
courseware
.
getData
(
function
(
data
){
loading
:
true
,
//数据加载完,才算页面加载完成
currentPage
:
0
,
//获取数据
isPlaying
:
false
,
if
(
data
&&
data
.
test
){
currentHighlight
:
null
,
$
(
"
#test
"
).
html
(
"
Hello
"
+
data
.
test
);
isPointMode
:
false
,
// 点读模式状态
}
else
{
pointAudio
:
null
,
// 点读音频实例
//一般来讲,如果没有获取到数据的话,给一组默认数据,用于演示模板的展示效果,方便别人了解这个模板
isPanelExpanded
:
false
// 控制面板展开状态
$
(
"
#test
"
).
html
(
"
Hello 我是演示信息!
"
);
}
}
},
computed
:
{
//数据渲染完成后,添加各种交互或者动画效果
wrapperStyle
()
{
//我这里简单做一个渐入渐出效果的例子
return
{
var
flag
=
true
;
transform
:
`translateX(-
${
this
.
currentPage
*
100
}
%)`
setInterval
(
function
(){
};
if
(
flag
){
}
$
(
"
#test
"
).
fadeIn
(
500
);
},
}
else
{
methods
:
{
$
(
"
#test
"
).
fadeOut
(
500
);
togglePlay
()
{
}
const
audio
=
this
.
$refs
.
audioPlayer
;
flag
=
!
flag
;
if
(
this
.
isPlaying
)
{
},
1000
)
audio
.
pause
();
}
else
{
//在页面加载、事件、动画等都初始化完成后,一定要记得调用此方法,否则模板将是不可用的
// 如果在点读模式下,切换到连续播放时停止点读
window
.
air
.
hideAirClassLoading
(
key
,
data
);
if
(
this
.
pointAudio
)
{
this
.
pointAudio
.
pause
();
},
key
);
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment