@airhang/vue-book-reader 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3378 @@
1
+ /******/ (function() { // webpackBootstrap
2
+ /******/ "use strict";
3
+ /******/ // The require scope
4
+ /******/ var __webpack_require__ = {};
5
+ /******/
6
+ /************************************************************************/
7
+ /******/ /* webpack/runtime/define property getters */
8
+ /******/ !function() {
9
+ /******/ // define getter functions for harmony exports
10
+ /******/ __webpack_require__.d = function(exports, definition) {
11
+ /******/ for(var key in definition) {
12
+ /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
13
+ /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
14
+ /******/ }
15
+ /******/ }
16
+ /******/ };
17
+ /******/ }();
18
+ /******/
19
+ /******/ /* webpack/runtime/hasOwnProperty shorthand */
20
+ /******/ !function() {
21
+ /******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
22
+ /******/ }();
23
+ /******/
24
+ /******/ /* webpack/runtime/make namespace object */
25
+ /******/ !function() {
26
+ /******/ // define __esModule on exports
27
+ /******/ __webpack_require__.r = function(exports) {
28
+ /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
29
+ /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
30
+ /******/ }
31
+ /******/ Object.defineProperty(exports, '__esModule', { value: true });
32
+ /******/ };
33
+ /******/ }();
34
+ /******/
35
+ /******/ /* webpack/runtime/publicPath */
36
+ /******/ !function() {
37
+ /******/ __webpack_require__.p = "";
38
+ /******/ }();
39
+ /******/
40
+ /************************************************************************/
41
+ var __webpack_exports__ = {};
42
+ // ESM COMPAT FLAG
43
+ __webpack_require__.r(__webpack_exports__);
44
+
45
+ // EXPORTS
46
+ __webpack_require__.d(__webpack_exports__, {
47
+ BookCatalogueDrawer: function() { return /* reexport */ BookCatalogueDrawer; },
48
+ BookReader: function() { return /* reexport */ BookReader; },
49
+ "default": function() { return /* binding */ entry_lib; }
50
+ });
51
+
52
+ ;// ./node_modules/@vue/cli-service/lib/commands/build/setPublicPath.js
53
+ /* eslint-disable no-var */
54
+ // This file is imported into lib/wc client bundles.
55
+
56
+ if (typeof window !== 'undefined') {
57
+ var currentScript = window.document.currentScript
58
+ if (false) // removed by dead control flow
59
+ { var getCurrentScript; }
60
+
61
+ var src = currentScript && currentScript.src.match(/(.+\/)[^/]+\.js(\?.*)?$/)
62
+ if (src) {
63
+ __webpack_require__.p = src[1] // eslint-disable-line
64
+ }
65
+ }
66
+
67
+ // Indicate to webpack that this file can be concatenated
68
+ /* harmony default export */ var setPublicPath = (null);
69
+
70
+ ;// ./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookReader.vue?vue&type=template&id=eaca7004&scoped=true
71
+ var render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"photo-album-container",style:({ transform: `scale(${_vm.zoomScale}) translate(${_vm.translateX}px, ${_vm.translateY}px)` }),on:{"touchstart":_vm.onContainerTouchStart,"touchmove":_vm.onContainerTouchMove,"touchend":_vm.onContainerTouchEnd,"dblclick":_vm.onContainerDoubleClick,"click":_vm.onContentClick}},[( false)?0:_vm._e(),(_vm.contentReady)?_c('div',{staticClass:"flipbook-wrapper",on:{"dblclick":_vm.onFlipbookDoubleClick,"!touchstart":function($event){return _vm.onFlipbookTouchStart.apply(null, arguments)},"!touchend":function($event){return _vm.onFlipbookTouchEnd.apply(null, arguments)}}},[(_vm.flipMode === 'flip' && _vm.flag)?_c('flipbook',{ref:"flipbook",class:['flipbook', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized'],attrs:{"pages":_vm.pages,"pagesHiRes":_vm.pagesHiRes,"startPage":_vm.startPage,"zooms":null,"ambient-light":0.6,"gloss":0.4,"single-page":_vm.isMobile,"click-to-flip":false,"wheel-to-flip":!_vm.isMobile,"swipe-to-flip":true,"center-pages":true},on:{"flip-left-end":_vm.onFlipLeftEnd,"flip-right-end":_vm.onFlipRightEnd},scopedSlots:_vm._u([{key:"default",fn:function({ page, canFlipLeft, canFlipRight }){return undefined}}],null,false,2449366912)}):(_vm.flipMode === 'slide')?_c('div',{ref:"slideViewer",class:['slide-viewer', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized'],on:{"touchstart":_vm.onSlideViewerTouchStart,"touchmove":_vm.onSlideViewerTouchMove,"touchend":_vm.onSlideViewerTouchEnd}},[_c('div',{staticClass:"slide-container",style:(_vm.slideContainerStyle)},_vm._l((_vm.pages),function(page,index){return _c('div',{key:index,staticClass:"slide-page",class:{ 'slide-page-active': index + 1 === _vm.currentPage }},[(page)?_c('img',{staticClass:"slide-page-image",attrs:{"src":page,"alt":""}}):_c('div',{staticClass:"slide-page-placeholder"},[_vm._v("封面/封底")])])}),0)]):(_vm.flipMode === 'fade')?_c('div',{ref:"fadeViewer",class:['fade-viewer', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized']},[_c('transition',{attrs:{"name":"fade-page","mode":"out-in"}},[_c('div',{key:_vm.currentPage,staticClass:"fade-page-container"},[(_vm.pages[_vm.currentPage - 1])?_c('img',{staticClass:"fade-page-image",attrs:{"src":_vm.pages[_vm.currentPage - 1],"alt":""}}):_c('div',{staticClass:"fade-page-placeholder"},[_vm._v("封面/封底")])])])],1):(_vm.flipMode === 'scroll')?_c('div',{ref:"scrollViewer",class:['scroll-viewer', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized'],on:{"scroll":_vm.onScrollViewerScroll}},[_c('div',{staticClass:"scroll-container"},_vm._l((_vm.pages),function(page,index){return _c('div',{key:index,ref:'scrollPage' + index,refInFor:true,staticClass:"scroll-page"},[(page)?_c('img',{staticClass:"scroll-page-image",attrs:{"src":page,"alt":""}}):_c('div',{staticClass:"scroll-page-placeholder"},[_vm._v("封面/封底")])])}),0)]):(_vm.flipMode === 'clip')?_c('div',{ref:"clipViewer",class:['clip-viewer', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized']},[_c('div',{staticClass:"clip-pages-wrapper"},[_c('transition',{attrs:{"name":"clip-current"}},[_c('div',{key:'current-' + _vm.currentPage,staticClass:"clip-page clip-page-current"},[(_vm.pages[_vm.currentPage - 1])?_c('img',{staticClass:"clip-page-image",attrs:{"src":_vm.pages[_vm.currentPage - 1],"alt":""}}):_c('div',{staticClass:"clip-page-placeholder"},[_vm._v("封面/封底")])])]),(_vm.currentPage < _vm.totalPages)?_c('div',{staticClass:"clip-page clip-page-next"},[(_vm.pages[_vm.currentPage])?_c('img',{staticClass:"clip-page-image",attrs:{"src":_vm.pages[_vm.currentPage],"alt":""}}):_c('div',{staticClass:"clip-page-placeholder"},[_vm._v("封面/封底")])]):_vm._e()],1)]):(_vm.flipMode === 'card')?_c('div',{ref:"cardViewer",class:['card-viewer', _vm.isMobile ? 'mobile-mode mobile-optimized' : 'desktop-mode desktop-optimized']},[_c('div',{staticClass:"card-stack"},[_c('transition-group',{staticClass:"card-transition-group",attrs:{"name":"card-flip","tag":"div"}},_vm._l((_vm.visibleCards),function(page,index){return _c('div',{key:'card-' + page.originalIndex,staticClass:"card-item",class:{
72
+ 'card-item-prev': page.position === 'prev',
73
+ 'card-item-current': page.position === 'current',
74
+ 'card-item-next': page.position === 'next'
75
+ },style:(_vm.getCardStyle(page.position))},[_c('div',{staticClass:"card-content"},[(page.src)?_c('img',{staticClass:"card-image",attrs:{"src":page.src,"alt":""}}):_c('div',{staticClass:"card-placeholder"},[_vm._v("封面/封底")])]),_c('div',{staticClass:"card-page-number"},[_vm._v(_vm._s(page.originalIndex + 1)+" / "+_vm._s(_vm.totalPages))])])}),0)],1)]):_vm._e()],1):_vm._e(),_c('div',{staticClass:"controls",class:{ 'mobile-controls': _vm.isMobile, 'desktop-controls': !_vm.isMobile, 'controls-visible': _vm.showControls },on:{"click":function($event){$event.stopPropagation();}}},[_c('button',{staticClass:"btn btn-prev",on:{"click":_vm.flipLeft}},[_vm._v(" ← 上一页 ")]),_c('span',{staticClass:"page-indicator"},[(_vm.totalPages === 0)?_c('span',[_vm._v("加载中...")]):(_vm.currentPage === 1 && _vm.totalPages > 1)?_c('span',[_vm._v("封面")]):(_vm.currentPage === _vm.totalPages && _vm.totalPages > 1)?_c('span',[_vm._v("封底")]):(!_vm.isMobile && _vm.totalPages > 2)?_c('span',[_vm._v("第 "+_vm._s(Math.ceil((_vm.currentPage - 1) / 2))+" 组 / 共 "+_vm._s(Math.ceil((_vm.totalPages - 2) / 2))+" 组")]):(_vm.isMobile && _vm.totalPages > 2)?_c('span',[_vm._v("第 "+_vm._s(Math.max(1, _vm.currentPage ))+" 页 / 共 "+_vm._s(Math.max(0, _vm.totalPages - 2))+" 页")]):(_vm.totalPages === 1)?_c('span',[_vm._v("第 1 页 / 共 1 页")]):_c('span',[_vm._v("第 "+_vm._s(_vm.currentPage)+" 页 / 共 "+_vm._s(_vm.totalPages)+" 页")])]),_c('button',{staticClass:"btn btn-next",on:{"click":_vm.flipRight}},[_vm._v(" 下一页 → ")])]),(_vm.showFlipModeSelector)?_c('div',{staticClass:"flip-mode-selector",class:{ 'mobile-flip-mode-selector': _vm.isMobile }},[_c('button',{staticClass:"btn btn-flip-mode",on:{"click":_vm.toggleFlipModeMenu}},[_c('span',{staticClass:"flip-mode-icon"},[_vm._v(_vm._s(_vm.flipModeIcon))]),_c('span',{staticClass:"flip-mode-text"},[_vm._v(_vm._s(_vm.flipModeLabel))])]),(_vm.showFlipModeMenu)?_c('div',{staticClass:"flip-mode-menu"},_vm._l((_vm.flipModes),function(mode){return _c('div',{key:mode.value,staticClass:"flip-mode-item",class:{ 'flip-mode-item-active': _vm.flipMode === mode.value },on:{"click":function($event){return _vm.selectFlipMode(mode.value)}}},[_c('span',{staticClass:"flip-mode-item-icon"},[_vm._v(_vm._s(mode.icon))]),_c('span',{staticClass:"flip-mode-item-label"},[_vm._v(_vm._s(mode.label))])])}),0):_vm._e()]):_vm._e(),_c('div',{staticClass:"catalogue-button",class:{
76
+ 'mobile-catalogue-button': _vm.isMobile,
77
+ 'dragging': _vm.catalogueButtonDragging,
78
+ 'catalogue-visible': _vm.showControls
79
+ },style:({
80
+ left: _vm.catalogueButtonPosition.x + 'px',
81
+ top: _vm.catalogueButtonPosition.y + 'px'
82
+ }),on:{"mousedown":_vm.onCatalogueMouseDown,"touchstart":_vm.onCatalogueTouchStart,"touchmove":_vm.onCatalogueTouchMove,"touchend":_vm.onCatalogueTouchEnd,"click":function($event){$event.stopPropagation();}}},[_c('button',{staticClass:"btn btn-catalogue",on:{"click":_vm.openCatalogue}},[_c('span',{staticClass:"catalogue-text"},[_vm._v("目录")])])]),_c('div',{staticClass:"zoom-hint",class:{ 'show': _vm.zoomScale !== 1 }},[_c('div',{staticClass:"zoom-info"},[_c('span',[_vm._v("缩放: "+_vm._s(Math.round(_vm.zoomScale * 100))+"%")]),_c('button',{staticClass:"btn-reset-zoom",on:{"click":_vm.resetZoom}},[_vm._v("重置")])])]),(_vm.loading)?_c('div',{staticClass:"loading-overlay"},[_vm._m(0)]):_vm._e(),(_vm.error && !_vm.loading)?_c('div',{staticClass:"error-container"},[_c('div',{staticClass:"error-content"},[_c('div',{staticClass:"error-icon"},[_vm._v("⚠️")]),_c('h3',[_vm._v("加载失败")]),_c('p',[_vm._v(_vm._s(_vm.error.message || '网络连接异常,请检查网络后重试'))]),_c('button',{staticClass:"retry-btn",on:{"click":_vm.fetchBooksData}},[_c('span',{staticClass:"retry-icon"},[_vm._v("🔄")]),_vm._v(" 重新加载 ")])])]):_vm._e(),(_vm.booksData && !_vm.loading && !_vm.error)?_c('div',{staticClass:"success-toast",class:{ 'show': _vm.showSuccessToast }},[_vm._v(" 书籍加载完成 ")]):_vm._e(),_c('book-catalogue-drawer',{attrs:{"book-id":_vm.bookId,"catalogue":_vm.catalogue,"loading":_vm.catalogueLoading},on:{"catalogue-click":_vm.onCatalogueClick,"page-jump":_vm.onPageJump,"Focus":_vm.onFocus,"fetch-catalogue":_vm.onFetchCatalogue},model:{value:(_vm.showCatalogueDrawer),callback:function ($$v) {_vm.showCatalogueDrawer=$$v},expression:"showCatalogueDrawer"}})],1)
83
+ }
84
+ var staticRenderFns = [function (){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"loading-container"},[_c('div',{staticClass:"loading-spinner"},[_c('div',{staticClass:"spinner-ring"}),_c('div',{staticClass:"spinner-ring"}),_c('div',{staticClass:"spinner-ring"})]),_c('div',{staticClass:"loading-text"},[_c('h3',[_vm._v("正在加载书籍")]),_c('p',[_vm._v("请稍候,正在获取精彩内容...")]),_c('div',{staticClass:"loading-progress"},[_c('div',{staticClass:"progress-bar"})])])])
85
+ }]
86
+
87
+
88
+ ;// ./src/components/BookReader.vue?vue&type=template&id=eaca7004&scoped=true
89
+
90
+ ;// ./node_modules/rematrix/dist/rematrix.es.js
91
+ /*! @license Rematrix v0.7.2
92
+
93
+ Copyright 2021 Julian Lloyd.
94
+
95
+ Permission is hereby granted, free of charge, to any person obtaining a copy
96
+ of this software and associated documentation files (the "Software"), to deal
97
+ in the Software without restriction, including without limitation the rights
98
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
+ copies of the Software, and to permit persons to whom the Software is
100
+ furnished to do so, subject to the following conditions:
101
+
102
+ The above copyright notice and this permission notice shall be included in
103
+ all copies or substantial portions of the Software.
104
+
105
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
108
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
110
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
111
+ THE SOFTWARE.
112
+ */
113
+ function format(source) {
114
+ if (source && source.constructor === Array) {
115
+ var values = source
116
+ .filter(function (value) { return typeof value === 'number'; })
117
+ .filter(function (value) { return !isNaN(value); });
118
+
119
+ if (source.length === 6 && values.length === 6) {
120
+ var matrix = identity();
121
+ matrix[0] = values[0];
122
+ matrix[1] = values[1];
123
+ matrix[4] = values[2];
124
+ matrix[5] = values[3];
125
+ matrix[12] = values[4];
126
+ matrix[13] = values[5];
127
+ return matrix
128
+ } else if (source.length === 16 && values.length === 16) {
129
+ return source
130
+ }
131
+ }
132
+ throw new TypeError('Expected a `number[]` with length 6 or 16.')
133
+ }
134
+
135
+ function fromString(source) {
136
+ if (typeof source === 'string') {
137
+ var match = source.match(/matrix(3d)?\(([^)]+)\)/);
138
+ if (match) {
139
+ var raw = match[2].split(',').map(parseFloat);
140
+ return format(raw)
141
+ }
142
+ if (source === 'none' || source === '') {
143
+ return identity()
144
+ }
145
+ }
146
+ throw new TypeError('Expected a string containing `matrix()` or `matrix3d()')
147
+ }
148
+
149
+ function identity() {
150
+ var matrix = [];
151
+ for (var i = 0; i < 16; i++) {
152
+ i % 5 == 0 ? matrix.push(1) : matrix.push(0);
153
+ }
154
+ return matrix
155
+ }
156
+
157
+ function inverse(source) {
158
+ var m = format(source);
159
+
160
+ var s0 = m[0] * m[5] - m[4] * m[1];
161
+ var s1 = m[0] * m[6] - m[4] * m[2];
162
+ var s2 = m[0] * m[7] - m[4] * m[3];
163
+ var s3 = m[1] * m[6] - m[5] * m[2];
164
+ var s4 = m[1] * m[7] - m[5] * m[3];
165
+ var s5 = m[2] * m[7] - m[6] * m[3];
166
+
167
+ var c5 = m[10] * m[15] - m[14] * m[11];
168
+ var c4 = m[9] * m[15] - m[13] * m[11];
169
+ var c3 = m[9] * m[14] - m[13] * m[10];
170
+ var c2 = m[8] * m[15] - m[12] * m[11];
171
+ var c1 = m[8] * m[14] - m[12] * m[10];
172
+ var c0 = m[8] * m[13] - m[12] * m[9];
173
+
174
+ var determinant = 1 / (s0 * c5 - s1 * c4 + s2 * c3 + s3 * c2 - s4 * c1 + s5 * c0);
175
+
176
+ if (isNaN(determinant) || determinant === Infinity) {
177
+ throw new Error('Inverse determinant attempted to divide by zero.')
178
+ }
179
+
180
+ return [
181
+ (m[5] * c5 - m[6] * c4 + m[7] * c3) * determinant,
182
+ (-m[1] * c5 + m[2] * c4 - m[3] * c3) * determinant,
183
+ (m[13] * s5 - m[14] * s4 + m[15] * s3) * determinant,
184
+ (-m[9] * s5 + m[10] * s4 - m[11] * s3) * determinant,
185
+
186
+ (-m[4] * c5 + m[6] * c2 - m[7] * c1) * determinant,
187
+ (m[0] * c5 - m[2] * c2 + m[3] * c1) * determinant,
188
+ (-m[12] * s5 + m[14] * s2 - m[15] * s1) * determinant,
189
+ (m[8] * s5 - m[10] * s2 + m[11] * s1) * determinant,
190
+
191
+ (m[4] * c4 - m[5] * c2 + m[7] * c0) * determinant,
192
+ (-m[0] * c4 + m[1] * c2 - m[3] * c0) * determinant,
193
+ (m[12] * s4 - m[13] * s2 + m[15] * s0) * determinant,
194
+ (-m[8] * s4 + m[9] * s2 - m[11] * s0) * determinant,
195
+
196
+ (-m[4] * c3 + m[5] * c1 - m[6] * c0) * determinant,
197
+ (m[0] * c3 - m[1] * c1 + m[2] * c0) * determinant,
198
+ (-m[12] * s3 + m[13] * s1 - m[14] * s0) * determinant,
199
+ (m[8] * s3 - m[9] * s1 + m[10] * s0) * determinant ]
200
+ }
201
+
202
+ function multiply(matrixA, matrixB) {
203
+ var fma = format(matrixA);
204
+ var fmb = format(matrixB);
205
+ var product = [];
206
+
207
+ for (var i = 0; i < 4; i++) {
208
+ var row = [fma[i], fma[i + 4], fma[i + 8], fma[i + 12]];
209
+ for (var j = 0; j < 4; j++) {
210
+ var k = j * 4;
211
+ var col = [fmb[k], fmb[k + 1], fmb[k + 2], fmb[k + 3]];
212
+ var result = row[0] * col[0] + row[1] * col[1] + row[2] * col[2] + row[3] * col[3];
213
+
214
+ product[i + k] = result;
215
+ }
216
+ }
217
+
218
+ return product
219
+ }
220
+
221
+ function perspective(distance) {
222
+ var matrix = identity();
223
+ matrix[11] = -1 / distance;
224
+ return matrix
225
+ }
226
+
227
+ function rotate(angle) {
228
+ return rotateZ(angle)
229
+ }
230
+
231
+ function rotateX(angle) {
232
+ var theta = (Math.PI / 180) * angle;
233
+ var matrix = identity();
234
+
235
+ matrix[5] = matrix[10] = Math.cos(theta);
236
+ matrix[6] = matrix[9] = Math.sin(theta);
237
+ matrix[9] *= -1;
238
+
239
+ return matrix
240
+ }
241
+
242
+ function rotateY(angle) {
243
+ var theta = (Math.PI / 180) * angle;
244
+ var matrix = identity();
245
+
246
+ matrix[0] = matrix[10] = Math.cos(theta);
247
+ matrix[2] = matrix[8] = Math.sin(theta);
248
+ matrix[2] *= -1;
249
+
250
+ return matrix
251
+ }
252
+
253
+ function rotateZ(angle) {
254
+ var theta = (Math.PI / 180) * angle;
255
+ var matrix = identity();
256
+
257
+ matrix[0] = matrix[5] = Math.cos(theta);
258
+ matrix[1] = matrix[4] = Math.sin(theta);
259
+ matrix[4] *= -1;
260
+
261
+ return matrix
262
+ }
263
+
264
+ function scale(scalar, scalarY) {
265
+ var matrix = identity();
266
+
267
+ matrix[0] = scalar;
268
+ matrix[5] = typeof scalarY === 'number' ? scalarY : scalar;
269
+
270
+ return matrix
271
+ }
272
+
273
+ function scaleX(scalar) {
274
+ var matrix = identity();
275
+ matrix[0] = scalar;
276
+ return matrix
277
+ }
278
+
279
+ function scaleY(scalar) {
280
+ var matrix = identity();
281
+ matrix[5] = scalar;
282
+ return matrix
283
+ }
284
+
285
+ function scaleZ(scalar) {
286
+ var matrix = identity();
287
+ matrix[10] = scalar;
288
+ return matrix
289
+ }
290
+
291
+ function skew(angleX, angleY) {
292
+ var thetaX = (Math.PI / 180) * angleX;
293
+ var matrix = identity();
294
+
295
+ matrix[4] = Math.tan(thetaX);
296
+
297
+ if (angleY) {
298
+ var thetaY = (Math.PI / 180) * angleY;
299
+ matrix[1] = Math.tan(thetaY);
300
+ }
301
+
302
+ return matrix
303
+ }
304
+
305
+ function skewX(angle) {
306
+ var theta = (Math.PI / 180) * angle;
307
+ var matrix = identity();
308
+
309
+ matrix[4] = Math.tan(theta);
310
+
311
+ return matrix
312
+ }
313
+
314
+ function skewY(angle) {
315
+ var theta = (Math.PI / 180) * angle;
316
+ var matrix = identity();
317
+
318
+ matrix[1] = Math.tan(theta);
319
+
320
+ return matrix
321
+ }
322
+
323
+ function rematrix_es_toString(source) {
324
+ return ("matrix3d(" + (format(source).join(', ')) + ")")
325
+ }
326
+
327
+ function translate(distanceX, distanceY) {
328
+ var matrix = identity();
329
+ matrix[12] = distanceX;
330
+
331
+ if (distanceY) {
332
+ matrix[13] = distanceY;
333
+ }
334
+
335
+ return matrix
336
+ }
337
+
338
+ function translate3d(distanceX, distanceY, distanceZ) {
339
+ var matrix = identity();
340
+ if (distanceX !== undefined && distanceY !== undefined && distanceZ !== undefined) {
341
+ matrix[12] = distanceX;
342
+ matrix[13] = distanceY;
343
+ matrix[14] = distanceZ;
344
+ }
345
+ return matrix
346
+ }
347
+
348
+ function translateX(distance) {
349
+ var matrix = identity();
350
+ matrix[12] = distance;
351
+ return matrix
352
+ }
353
+
354
+ function translateY(distance) {
355
+ var matrix = identity();
356
+ matrix[13] = distance;
357
+ return matrix
358
+ }
359
+
360
+ function translateZ(distance) {
361
+ var matrix = identity();
362
+ matrix[14] = distance;
363
+ return matrix
364
+ }
365
+
366
+
367
+
368
+ ;// ./node_modules/flipbook-vue/dist/vue2/flipbook.mjs
369
+ /*!
370
+ * @license
371
+ * flipbook-vue v1.0.0-beta.4
372
+ * Copyright © 2023 Takeshi Sone.
373
+ * Released under the MIT License.
374
+ */
375
+
376
+
377
+
378
+ var Matrix = /*@__PURE__*/(function () {
379
+ function Matrix(arg) {
380
+ if (arg) {
381
+ if (arg.m) {
382
+ this.m = [].concat( arg.m );
383
+ } else {
384
+ this.m = [].concat( arg );
385
+ }
386
+ } else {
387
+ this.m = identity();
388
+ }
389
+ }
390
+
391
+ Matrix.prototype.clone = function clone () {
392
+ return new Matrix(this);
393
+ };
394
+
395
+ Matrix.prototype.multiply = function multiply$1 (m) {
396
+ return this.m = multiply(this.m, m);
397
+ };
398
+
399
+ Matrix.prototype.perspective = function perspective$1 (d) {
400
+ return this.multiply(perspective(d));
401
+ };
402
+
403
+ Matrix.prototype.transformX = function transformX (x) {
404
+ return (x * this.m[0] + this.m[12]) / (x * this.m[3] + this.m[15]);
405
+ };
406
+
407
+ Matrix.prototype.translate = function translate$1 (x, y) {
408
+ return this.multiply(translate(x, y));
409
+ };
410
+
411
+ Matrix.prototype.translate3d = function translate3d$1 (x, y, z) {
412
+ return this.multiply(translate3d(x, y, z));
413
+ };
414
+
415
+ Matrix.prototype.rotateY = function rotateY$1 (deg) {
416
+ return this.multiply(rotateY(deg));
417
+ };
418
+
419
+ Matrix.prototype.toString = function toString$1 () {
420
+ return rematrix_es_toString(this.m);
421
+ };
422
+
423
+ return Matrix;
424
+ }());
425
+
426
+ var spinner = "data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22500%22%20height%3D%22500%22%20viewBox%3D%220%200%20500%20500%22%20fill%3D%22transparent%22%20style%3D%22background-color%3A%20%23fff%22%3E%20%20%3Ccircle%20%20%20%20cx%3D%22250%22%20%20%20%20cy%3D%22250%22%20%20%20%20r%3D%2248%22%20%20%20%20stroke%3D%22%23333%22%20%20%20%20stroke-width%3D%222%22%20%20%20%20stroke-dasharray%3D%22271%2030%22%20%20%3E%20%20%20%20%3CanimateTransform%20%20%20%20%20%20attributeName%3D%22transform%22%20%20%20%20%20%20attributeType%3D%22XML%22%20%20%20%20%20%20type%3D%22rotate%22%20%20%20%20%20%20from%3D%220%20250%20250%22%20%20%20%20%20%20to%3D%22360%20250%20250%22%20%20%20%20%20%20dur%3D%221s%22%20%20%20%20%20%20repeatCount%3D%22indefinite%22%20%20%20%20%2F%3E%20%20%3C%2Fcircle%3E%3C%2Fsvg%3E";
427
+
428
+ var easeIn, easeInOut, easeOut;
429
+
430
+ easeIn = function(x) {
431
+ return Math.pow(x, 2);
432
+ };
433
+
434
+ easeOut = function(x) {
435
+ return 1 - easeIn(1 - x);
436
+ };
437
+
438
+ easeInOut = function(x) {
439
+ if (x < 0.5) {
440
+ return easeIn(x * 2) / 2;
441
+ } else {
442
+ return 0.5 + easeOut((x - 0.5) * 2) / 2;
443
+ }
444
+ };
445
+
446
+ var script = {
447
+ props: {
448
+ pages: {
449
+ type: Array,
450
+ required: true
451
+ },
452
+ pagesHiRes: {
453
+ type: Array,
454
+ default: function() {
455
+ return [];
456
+ }
457
+ },
458
+ flipDuration: {
459
+ type: Number,
460
+ default: 1000
461
+ },
462
+ zoomDuration: {
463
+ type: Number,
464
+ default: 500
465
+ },
466
+ zooms: {
467
+ type: Array,
468
+ default: function() {
469
+ return [1, 2, 4];
470
+ }
471
+ },
472
+ perspective: {
473
+ type: Number,
474
+ default: 2400
475
+ },
476
+ nPolygons: {
477
+ type: Number,
478
+ default: 10
479
+ },
480
+ ambient: {
481
+ type: Number,
482
+ default: 0.4
483
+ },
484
+ gloss: {
485
+ type: Number,
486
+ default: 0.6
487
+ },
488
+ swipeMin: {
489
+ type: Number,
490
+ default: 3
491
+ },
492
+ singlePage: {
493
+ type: Boolean,
494
+ default: false
495
+ },
496
+ forwardDirection: {
497
+ validator: function(val) {
498
+ return val === 'right' || val === 'left';
499
+ },
500
+ default: 'right'
501
+ },
502
+ centering: {
503
+ type: Boolean,
504
+ default: true
505
+ },
506
+ startPage: {
507
+ type: Number,
508
+ default: null
509
+ },
510
+ loadingImage: {
511
+ type: String,
512
+ default: spinner
513
+ },
514
+ clickToZoom: {
515
+ type: Boolean,
516
+ default: true
517
+ },
518
+ dragToFlip: {
519
+ type: Boolean,
520
+ default: true
521
+ },
522
+ wheel: {
523
+ type: String,
524
+ default: 'scroll'
525
+ }
526
+ },
527
+ data: function() {
528
+ return {
529
+ viewWidth: 0,
530
+ viewHeight: 0,
531
+ imageWidth: null,
532
+ imageHeight: null,
533
+ displayedPages: 1,
534
+ nImageLoad: 0,
535
+ nImageLoadTrigger: 0,
536
+ imageLoadCallback: null,
537
+ currentPage: 0,
538
+ firstPage: 0,
539
+ secondPage: 1,
540
+ zoomIndex: 0,
541
+ zoom: 1,
542
+ zooming: false,
543
+ touchStartX: null,
544
+ touchStartY: null,
545
+ maxMove: 0,
546
+ activeCursor: null,
547
+ hasTouchEvents: false,
548
+ hasPointerEvents: false,
549
+ minX: 2e308,
550
+ maxX: -2e308,
551
+ preloadedImages: {},
552
+ flip: {
553
+ progress: 0,
554
+ direction: null,
555
+ frontImage: null,
556
+ backImage: null,
557
+ auto: false,
558
+ opacity: 1
559
+ },
560
+ currentCenterOffset: null,
561
+ animatingCenter: false,
562
+ startScrollLeft: 0,
563
+ startScrollTop: 0,
564
+ scrollLeft: 0,
565
+ scrollTop: 0,
566
+ loadedImages: {}
567
+ };
568
+ },
569
+ computed: {
570
+ IE: function() {
571
+ return typeof navigator !== 'undefined' && /Trident/.test(navigator.userAgent);
572
+ },
573
+ canFlipLeft: function() {
574
+ if (this.forwardDirection === 'left') {
575
+ return this.canGoForward;
576
+ } else {
577
+ return this.canGoBack;
578
+ }
579
+ },
580
+ canFlipRight: function() {
581
+ if (this.forwardDirection === 'right') {
582
+ return this.canGoForward;
583
+ } else {
584
+ return this.canGoBack;
585
+ }
586
+ },
587
+ canZoomIn: function() {
588
+ return !this.zooming && this.zoomIndex < this.zooms_.length - 1;
589
+ },
590
+ canZoomOut: function() {
591
+ return !this.zooming && this.zoomIndex > 0;
592
+ },
593
+ numPages: function() {
594
+ if (this.pages[0] === null) {
595
+ return this.pages.length - 1;
596
+ } else {
597
+ return this.pages.length;
598
+ }
599
+ },
600
+ page: function() {
601
+ if (this.pages[0] !== null) {
602
+ return this.currentPage + 1;
603
+ } else {
604
+ return Math.max(1, this.currentPage);
605
+ }
606
+ },
607
+ zooms_: function() {
608
+ return this.zooms || [1];
609
+ },
610
+ canGoForward: function() {
611
+ return !this.flip.direction && this.currentPage < this.pages.length - this.displayedPages;
612
+ },
613
+ canGoBack: function() {
614
+ return !this.flip.direction && this.currentPage >= this.displayedPages && !(this.displayedPages === 1 && !this.pageUrl(this.firstPage - 1));
615
+ },
616
+ leftPage: function() {
617
+ if (this.forwardDirection === 'right' || this.displayedPages === 1) {
618
+ return this.firstPage;
619
+ } else {
620
+ return this.secondPage;
621
+ }
622
+ },
623
+ rightPage: function() {
624
+ if (this.forwardDirection === 'left') {
625
+ return this.firstPage;
626
+ } else {
627
+ return this.secondPage;
628
+ }
629
+ },
630
+ showLeftPage: function() {
631
+ return this.pageUrl(this.leftPage);
632
+ },
633
+ showRightPage: function() {
634
+ return this.pageUrl(this.rightPage) && this.displayedPages === 2;
635
+ },
636
+ cursor: function() {
637
+ if (this.activeCursor) {
638
+ return this.activeCursor;
639
+ } else if (this.IE) {
640
+ return 'auto';
641
+ } else if (this.clickToZoom && this.canZoomIn) {
642
+ return 'zoom-in';
643
+ } else if (this.clickToZoom && this.canZoomOut) {
644
+ return 'zoom-out';
645
+ } else if (this.dragToFlip) {
646
+ return 'grab';
647
+ } else {
648
+ return 'auto';
649
+ }
650
+ },
651
+ pageScale: function() {
652
+ var scale, vw, xScale, yScale;
653
+ vw = this.viewWidth / this.displayedPages;
654
+ xScale = vw / this.imageWidth;
655
+ yScale = this.viewHeight / this.imageHeight;
656
+ scale = xScale < yScale ? xScale : yScale;
657
+ if (scale < 1) {
658
+ return scale;
659
+ } else {
660
+ return 1;
661
+ }
662
+ },
663
+ pageWidth: function() {
664
+ return Math.round(this.imageWidth * this.pageScale);
665
+ },
666
+ pageHeight: function() {
667
+ return Math.round(this.imageHeight * this.pageScale);
668
+ },
669
+ xMargin: function() {
670
+ return (this.viewWidth - this.pageWidth * this.displayedPages) / 2;
671
+ },
672
+ yMargin: function() {
673
+ return (this.viewHeight - this.pageHeight) / 2;
674
+ },
675
+ polygonWidth: function() {
676
+ var w;
677
+ w = this.pageWidth / this.nPolygons;
678
+ w = Math.ceil(w + 1 / this.zoom);
679
+ return w + 'px';
680
+ },
681
+ polygonHeight: function() {
682
+ return this.pageHeight + 'px';
683
+ },
684
+ polygonBgSize: function() {
685
+ return ((this.pageWidth) + "px " + (this.pageHeight) + "px");
686
+ },
687
+ polygonArray: function() {
688
+ return this.makePolygonArray('front').concat(this.makePolygonArray('back'));
689
+ },
690
+ boundingLeft: function() {
691
+ var x;
692
+ if (this.displayedPages === 1) {
693
+ return this.xMargin;
694
+ } else {
695
+ x = this.pageUrl(this.leftPage) ? this.xMargin : this.viewWidth / 2;
696
+ if (x < this.minX) {
697
+ return x;
698
+ } else {
699
+ return this.minX;
700
+ }
701
+ }
702
+ },
703
+ boundingRight: function() {
704
+ var x;
705
+ if (this.displayedPages === 1) {
706
+ return this.viewWidth - this.xMargin;
707
+ } else {
708
+ x = this.pageUrl(this.rightPage) ? this.viewWidth - this.xMargin : this.viewWidth / 2;
709
+ if (x > this.maxX) {
710
+ return x;
711
+ } else {
712
+ return this.maxX;
713
+ }
714
+ }
715
+ },
716
+ centerOffset: function() {
717
+ var retval;
718
+ retval = this.centering ? Math.round(this.viewWidth / 2 - (this.boundingLeft + this.boundingRight) / 2) : 0;
719
+ if (this.currentCenterOffset === null && this.imageWidth !== null) {
720
+ this.currentCenterOffset = retval;
721
+ }
722
+ return retval;
723
+ },
724
+ centerOffsetSmoothed: function() {
725
+ return Math.round(this.currentCenterOffset);
726
+ },
727
+ dragToScroll: function() {
728
+ return !this.hasTouchEvents;
729
+ },
730
+ scrollLeftMin: function() {
731
+ var w;
732
+ w = (this.boundingRight - this.boundingLeft) * this.zoom;
733
+ if (w < this.viewWidth) {
734
+ return (this.boundingLeft + this.centerOffsetSmoothed) * this.zoom - (this.viewWidth - w) / 2;
735
+ } else {
736
+ return (this.boundingLeft + this.centerOffsetSmoothed) * this.zoom;
737
+ }
738
+ },
739
+ scrollLeftMax: function() {
740
+ var w;
741
+ w = (this.boundingRight - this.boundingLeft) * this.zoom;
742
+ if (w < this.viewWidth) {
743
+ return (this.boundingLeft + this.centerOffsetSmoothed) * this.zoom - (this.viewWidth - w) / 2;
744
+ } else {
745
+ return (this.boundingRight + this.centerOffsetSmoothed) * this.zoom - this.viewWidth;
746
+ }
747
+ },
748
+ scrollTopMin: function() {
749
+ var h;
750
+ h = this.pageHeight * this.zoom;
751
+ if (h < this.viewHeight) {
752
+ return this.yMargin * this.zoom - (this.viewHeight - h) / 2;
753
+ } else {
754
+ return this.yMargin * this.zoom;
755
+ }
756
+ },
757
+ scrollTopMax: function() {
758
+ var h;
759
+ h = this.pageHeight * this.zoom;
760
+ if (h < this.viewHeight) {
761
+ return this.yMargin * this.zoom - (this.viewHeight - h) / 2;
762
+ } else {
763
+ return (this.yMargin + this.pageHeight) * this.zoom - this.viewHeight;
764
+ }
765
+ },
766
+ scrollLeftLimited: function() {
767
+ return Math.min(this.scrollLeftMax, Math.max(this.scrollLeftMin, this.scrollLeft));
768
+ },
769
+ scrollTopLimited: function() {
770
+ return Math.min(this.scrollTopMax, Math.max(this.scrollTopMin, this.scrollTop));
771
+ }
772
+ },
773
+ mounted: function() {
774
+ window.addEventListener('resize', this.onResize, {
775
+ passive: true
776
+ });
777
+ this.onResize();
778
+ this.zoom = this.zooms_[0];
779
+ return this.goToPage(this.startPage);
780
+ },
781
+ beforeDestroy: function() {
782
+ return window.removeEventListener('resize', this.onResize, {
783
+ passive: true
784
+ });
785
+ },
786
+ methods: {
787
+ onResize: function() {
788
+ var viewport;
789
+ viewport = this.$refs.viewport;
790
+ if (!viewport) {
791
+ return;
792
+ }
793
+ this.viewWidth = viewport.clientWidth;
794
+ this.viewHeight = viewport.clientHeight;
795
+ this.displayedPages = this.viewWidth > this.viewHeight && !this.singlePage ? 2 : 1;
796
+ if (this.displayedPages === 2) {
797
+ this.currentPage &= ~1;
798
+ }
799
+ this.fixFirstPage();
800
+ this.minX = 2e308;
801
+ return this.maxX = -2e308;
802
+ },
803
+ fixFirstPage: function() {
804
+ if (this.displayedPages === 1 && this.currentPage === 0 && this.pages.length && !this.pageUrl(0)) {
805
+ return this.currentPage++;
806
+ }
807
+ },
808
+ pageUrl: function(page, hiRes) {
809
+ if ( hiRes === void 0 ) hiRes = false;
810
+
811
+ var url;
812
+ if (hiRes && this.zoom > 1 && !this.zooming) {
813
+ url = this.pagesHiRes[page];
814
+ if (url) {
815
+ return url;
816
+ }
817
+ }
818
+ return this.pages[page] || null;
819
+ },
820
+ pageUrlLoading: function(page, hiRes) {
821
+ if ( hiRes === void 0 ) hiRes = false;
822
+
823
+ var url;
824
+ url = this.pageUrl(page, hiRes);
825
+ if (hiRes && this.zoom > 1 && !this.zooming) {
826
+ // High-res image doesn't use 'loading'
827
+ return url;
828
+ }
829
+ return url && this.loadImage(url);
830
+ },
831
+ flipLeft: function() {
832
+ if (!this.canFlipLeft) {
833
+ return;
834
+ }
835
+ return this.flipStart('left', true);
836
+ },
837
+ flipRight: function() {
838
+ if (!this.canFlipRight) {
839
+ return;
840
+ }
841
+ return this.flipStart('right', true);
842
+ },
843
+ makePolygonArray: function(face) {
844
+ var bgPos, dRadian, dRotate, direction, i, image, j, lighting, m, originRight, pageMatrix, pageRotation, pageX, polygonWidth, progress, rad, radian, radius, ref, results, rotate, theta, x, x0, x1, z;
845
+ if (!this.flip.direction) {
846
+ return [];
847
+ }
848
+ progress = this.flip.progress;
849
+ direction = this.flip.direction;
850
+ if (this.displayedPages === 1 && direction !== this.forwardDirection) {
851
+ progress = 1 - progress;
852
+ direction = this.forwardDirection;
853
+ }
854
+ this.flip.opacity = this.displayedPages === 1 && progress > .7 ? 1 - (progress - .7) / .3 : 1;
855
+ image = face === 'front' ? this.flip.frontImage : this.flip.backImage;
856
+ polygonWidth = this.pageWidth / this.nPolygons;
857
+ pageX = this.xMargin;
858
+ originRight = false;
859
+ if (this.displayedPages === 1) {
860
+ if (this.forwardDirection === 'right') {
861
+ if (face === 'back') {
862
+ originRight = true;
863
+ pageX = this.xMargin - this.pageWidth;
864
+ }
865
+ } else {
866
+ if (direction === 'left') {
867
+ if (face === 'back') {
868
+ pageX = this.pageWidth - this.xMargin;
869
+ } else {
870
+ originRight = true;
871
+ }
872
+ } else {
873
+ if (face === 'front') {
874
+ pageX = this.pageWidth - this.xMargin;
875
+ } else {
876
+ originRight = true;
877
+ }
878
+ }
879
+ }
880
+ } else {
881
+ if (direction === 'left') {
882
+ if (face === 'back') {
883
+ pageX = this.viewWidth / 2;
884
+ } else {
885
+ originRight = true;
886
+ }
887
+ } else {
888
+ if (face === 'front') {
889
+ pageX = this.viewWidth / 2;
890
+ } else {
891
+ originRight = true;
892
+ }
893
+ }
894
+ }
895
+ pageMatrix = new Matrix();
896
+ pageMatrix.translate(this.viewWidth / 2);
897
+ pageMatrix.perspective(this.perspective);
898
+ pageMatrix.translate(-this.viewWidth / 2);
899
+ pageMatrix.translate(pageX, this.yMargin);
900
+ pageRotation = 0;
901
+ if (progress > 0.5) {
902
+ pageRotation = -(progress - 0.5) * 2 * 180;
903
+ }
904
+ if (direction === 'left') {
905
+ pageRotation = -pageRotation;
906
+ }
907
+ if (face === 'back') {
908
+ pageRotation += 180;
909
+ }
910
+ if (pageRotation) {
911
+ if (originRight) {
912
+ pageMatrix.translate(this.pageWidth);
913
+ }
914
+ pageMatrix.rotateY(pageRotation);
915
+ if (originRight) {
916
+ pageMatrix.translate(-this.pageWidth);
917
+ }
918
+ }
919
+ if (progress < 0.5) {
920
+ theta = progress * 2 * Math.PI;
921
+ } else {
922
+ theta = (1 - (progress - 0.5) * 2) * Math.PI;
923
+ }
924
+ if (theta === 0) {
925
+ theta = 1e-9;
926
+ }
927
+ radius = this.pageWidth / theta;
928
+ radian = 0;
929
+ dRadian = theta / this.nPolygons;
930
+ rotate = dRadian / 2 / Math.PI * 180;
931
+ dRotate = dRadian / Math.PI * 180;
932
+ if (originRight) {
933
+ rotate = -theta / Math.PI * 180 + dRotate / 2;
934
+ }
935
+ if (face === 'back') {
936
+ rotate = -rotate;
937
+ dRotate = -dRotate;
938
+ }
939
+ this.minX = 2e308;
940
+ this.maxX = -2e308;
941
+ results = [];
942
+ for (i = j = 0, ref = this.nPolygons; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
943
+ bgPos = (i / (this.nPolygons - 1) * 100) + "% 0px";
944
+ m = pageMatrix.clone();
945
+ rad = originRight ? theta - radian : radian;
946
+ x = Math.sin(rad) * radius;
947
+ if (originRight) {
948
+ x = this.pageWidth - x;
949
+ }
950
+ z = (1 - Math.cos(rad)) * radius;
951
+ if (face === 'back') {
952
+ z = -z;
953
+ }
954
+ m.translate3d(x, 0, z);
955
+ m.rotateY(-rotate);
956
+ x0 = m.transformX(0);
957
+ x1 = m.transformX(polygonWidth);
958
+ this.maxX = Math.max(Math.max(x0, x1), this.maxX);
959
+ this.minX = Math.min(Math.min(x0, x1), this.minX);
960
+ lighting = this.computeLighting(pageRotation - rotate, dRotate);
961
+ radian += dRadian;
962
+ rotate += dRotate;
963
+ results.push([face + i, image, lighting, bgPos, m.toString(), Math.abs(Math.round(z))]);
964
+ }
965
+ return results;
966
+ },
967
+ computeLighting: function(rot, dRotate) {
968
+ var DEG, POW, blackness, diffuse, gradients, lightingPoints, specular;
969
+ gradients = [];
970
+ lightingPoints = [-0.5, -0.25, 0, 0.25, 0.5];
971
+ if (this.ambient < 1) {
972
+ blackness = 1 - this.ambient;
973
+ diffuse = lightingPoints.map(function (d) {
974
+ return (1 - Math.cos((rot - dRotate * d) / 180 * Math.PI)) * blackness;
975
+ });
976
+ gradients.push(("linear-gradient(to right,\n rgba(0, 0, 0, " + (diffuse[0]) + "),\n rgba(0, 0, 0, " + (diffuse[1]) + ") 25%,\n rgba(0, 0, 0, " + (diffuse[2]) + ") 50%,\n rgba(0, 0, 0, " + (diffuse[3]) + ") 75%,\n rgba(0, 0, 0, " + (diffuse[4]) + "))"));
977
+ }
978
+ if (this.gloss > 0 && !this.IE) {
979
+ DEG = 30;
980
+ POW = 200;
981
+ specular = lightingPoints.map(function (d) {
982
+ return Math.max(Math.pow( Math.cos((rot + DEG - dRotate * d) / 180 * Math.PI), POW ), Math.pow( Math.cos((rot - DEG - dRotate * d) / 180 * Math.PI), POW ));
983
+ });
984
+ gradients.push(("linear-gradient(to right,\n rgba(255, 255, 255, " + (specular[0] * this.gloss) + "),\n rgba(255, 255, 255, " + (specular[1] * this.gloss) + ") 25%,\n rgba(255, 255, 255, " + (specular[2] * this.gloss) + ") 50%,\n rgba(255, 255, 255, " + (specular[3] * this.gloss) + ") 75%,\n rgba(255, 255, 255, " + (specular[4] * this.gloss) + "))"));
985
+ }
986
+ return gradients.join(',');
987
+ },
988
+ flipStart: function(direction, auto) {
989
+ var this$1$1 = this;
990
+
991
+ if (direction !== this.forwardDirection) {
992
+ if (this.displayedPages === 1) {
993
+ this.flip.frontImage = this.pageUrl(this.currentPage - 1);
994
+ this.flip.backImage = null;
995
+ } else {
996
+ this.flip.frontImage = this.pageUrl(this.firstPage);
997
+ this.flip.backImage = this.pageUrl(this.currentPage - this.displayedPages + 1);
998
+ }
999
+ } else {
1000
+ if (this.displayedPages === 1) {
1001
+ this.flip.frontImage = this.pageUrl(this.currentPage);
1002
+ this.flip.backImage = null;
1003
+ } else {
1004
+ this.flip.frontImage = this.pageUrl(this.secondPage);
1005
+ this.flip.backImage = this.pageUrl(this.currentPage + this.displayedPages);
1006
+ }
1007
+ }
1008
+ this.flip.direction = direction;
1009
+ this.flip.progress = 0;
1010
+ return requestAnimationFrame(function () {
1011
+ return requestAnimationFrame(function () {
1012
+ if (this$1$1.flip.direction !== this$1$1.forwardDirection) {
1013
+ if (this$1$1.displayedPages === 2) {
1014
+ this$1$1.firstPage = this$1$1.currentPage - this$1$1.displayedPages;
1015
+ }
1016
+ } else {
1017
+ if (this$1$1.displayedPages === 1) {
1018
+ this$1$1.firstPage = this$1$1.currentPage + this$1$1.displayedPages;
1019
+ } else {
1020
+ this$1$1.secondPage = this$1$1.currentPage + 1 + this$1$1.displayedPages;
1021
+ }
1022
+ }
1023
+ if (auto) {
1024
+ return this$1$1.flipAuto(true);
1025
+ }
1026
+ });
1027
+ });
1028
+ },
1029
+ flipAuto: function(ease) {
1030
+ var this$1$1 = this;
1031
+
1032
+ var animate, duration, startRatio, t0;
1033
+ t0 = Date.now();
1034
+ duration = this.flipDuration * (1 - this.flip.progress);
1035
+ startRatio = this.flip.progress;
1036
+ this.flip.auto = true;
1037
+ this.$emit(("flip-" + (this.flip.direction) + "-start"), this.page);
1038
+ animate = function () {
1039
+ return requestAnimationFrame(function () {
1040
+ var ratio, t;
1041
+ t = Date.now() - t0;
1042
+ ratio = startRatio + t / duration;
1043
+ if (ratio > 1) {
1044
+ ratio = 1;
1045
+ }
1046
+ this$1$1.flip.progress = ease ? easeInOut(ratio) : ratio;
1047
+ if (ratio < 1) {
1048
+ return animate();
1049
+ } else {
1050
+ if (this$1$1.flip.direction !== this$1$1.forwardDirection) {
1051
+ this$1$1.currentPage -= this$1$1.displayedPages;
1052
+ } else {
1053
+ this$1$1.currentPage += this$1$1.displayedPages;
1054
+ }
1055
+ this$1$1.$emit(("flip-" + (this$1$1.flip.direction) + "-end"), this$1$1.page);
1056
+ if (this$1$1.displayedPages === 1 && this$1$1.flip.direction === this$1$1.forwardDirection) {
1057
+ this$1$1.flip.direction = null;
1058
+ } else {
1059
+ this$1$1.onImageLoad(1, function () {
1060
+ return this$1$1.flip.direction = null;
1061
+ });
1062
+ }
1063
+ return this$1$1.flip.auto = false;
1064
+ }
1065
+ });
1066
+ };
1067
+ return animate();
1068
+ },
1069
+ flipRevert: function() {
1070
+ var this$1$1 = this;
1071
+
1072
+ var animate, duration, startRatio, t0;
1073
+ t0 = Date.now();
1074
+ duration = this.flipDuration * this.flip.progress;
1075
+ startRatio = this.flip.progress;
1076
+ this.flip.auto = true;
1077
+ animate = function () {
1078
+ return requestAnimationFrame(function () {
1079
+ var ratio, t;
1080
+ t = Date.now() - t0;
1081
+ ratio = startRatio - startRatio * t / duration;
1082
+ if (ratio < 0) {
1083
+ ratio = 0;
1084
+ }
1085
+ this$1$1.flip.progress = ratio;
1086
+ if (ratio > 0) {
1087
+ return animate();
1088
+ } else {
1089
+ this$1$1.firstPage = this$1$1.currentPage;
1090
+ this$1$1.secondPage = this$1$1.currentPage + 1;
1091
+ if (this$1$1.displayedPages === 1 && this$1$1.flip.direction !== this$1$1.forwardDirection) {
1092
+ this$1$1.flip.direction = null;
1093
+ } else {
1094
+ this$1$1.onImageLoad(1, function () {
1095
+ return this$1$1.flip.direction = null;
1096
+ });
1097
+ }
1098
+ return this$1$1.flip.auto = false;
1099
+ }
1100
+ });
1101
+ };
1102
+ return animate();
1103
+ },
1104
+ onImageLoad: function(trigger, cb) {
1105
+ this.nImageLoad = 0;
1106
+ this.nImageLoadTrigger = trigger;
1107
+ return this.imageLoadCallback = cb;
1108
+ },
1109
+ didLoadImage: function(ev) {
1110
+ if (this.imageWidth === null) {
1111
+ this.imageWidth = (ev.target || ev.path[0]).naturalWidth;
1112
+ this.imageHeight = (ev.target || ev.path[0]).naturalHeight;
1113
+ this.preloadImages();
1114
+ }
1115
+ if (!this.imageLoadCallback) {
1116
+ return;
1117
+ }
1118
+ if (++this.nImageLoad >= this.nImageLoadTrigger) {
1119
+ this.imageLoadCallback();
1120
+ return this.imageLoadCallback = null;
1121
+ }
1122
+ },
1123
+ zoomIn: function(zoomAt) {
1124
+ if ( zoomAt === void 0 ) zoomAt = null;
1125
+
1126
+ if (!this.canZoomIn) {
1127
+ return;
1128
+ }
1129
+ this.zoomIndex += 1;
1130
+ return this.zoomTo(this.zooms_[this.zoomIndex], zoomAt);
1131
+ },
1132
+ zoomOut: function(zoomAt) {
1133
+ if ( zoomAt === void 0 ) zoomAt = null;
1134
+
1135
+ if (!this.canZoomOut) {
1136
+ return;
1137
+ }
1138
+ this.zoomIndex -= 1;
1139
+ return this.zoomTo(this.zooms_[this.zoomIndex], zoomAt);
1140
+ },
1141
+ zoomTo: function(zoom, zoomAt) {
1142
+ var this$1$1 = this;
1143
+ if ( zoomAt === void 0 ) zoomAt = null;
1144
+
1145
+ var animate, containerFixedX, containerFixedY, end, endX, endY, fixedX, fixedY, rect, start, startX, startY, t0, viewport;
1146
+ viewport = this.$refs.viewport;
1147
+ if (zoomAt) {
1148
+ rect = viewport.getBoundingClientRect();
1149
+ fixedX = zoomAt.pageX - rect.left;
1150
+ fixedY = zoomAt.pageY - rect.top;
1151
+ } else {
1152
+ fixedX = viewport.clientWidth / 2;
1153
+ fixedY = viewport.clientHeight / 2;
1154
+ }
1155
+ start = this.zoom;
1156
+ end = zoom;
1157
+ startX = viewport.scrollLeft;
1158
+ startY = viewport.scrollTop;
1159
+ containerFixedX = fixedX + startX;
1160
+ containerFixedY = fixedY + startY;
1161
+ endX = containerFixedX / start * end - fixedX;
1162
+ endY = containerFixedY / start * end - fixedY;
1163
+ t0 = Date.now();
1164
+ this.zooming = true;
1165
+ this.$emit('zoom-start', zoom);
1166
+ animate = function () {
1167
+ return requestAnimationFrame(function () {
1168
+ var ratio, t;
1169
+ t = Date.now() - t0;
1170
+ ratio = t / this$1$1.zoomDuration;
1171
+ if (ratio > 1 || this$1$1.IE) {
1172
+ ratio = 1;
1173
+ }
1174
+ ratio = easeInOut(ratio);
1175
+ this$1$1.zoom = start + (end - start) * ratio;
1176
+ this$1$1.scrollLeft = startX + (endX - startX) * ratio;
1177
+ this$1$1.scrollTop = startY + (endY - startY) * ratio;
1178
+ if (t < this$1$1.zoomDuration) {
1179
+ return animate();
1180
+ } else {
1181
+ this$1$1.$emit('zoom-end', zoom);
1182
+ this$1$1.zooming = false;
1183
+ this$1$1.zoom = zoom;
1184
+ this$1$1.scrollLeft = endX;
1185
+ return this$1$1.scrollTop = endY;
1186
+ }
1187
+ });
1188
+ };
1189
+ animate();
1190
+ if (end > 1) {
1191
+ return this.preloadImages(true);
1192
+ }
1193
+ },
1194
+ zoomAt: function(zoomAt) {
1195
+ this.zoomIndex = (this.zoomIndex + 1) % this.zooms_.length;
1196
+ return this.zoomTo(this.zooms_[this.zoomIndex], zoomAt);
1197
+ },
1198
+ swipeStart: function(touch) {
1199
+ this.touchStartX = touch.pageX;
1200
+ this.touchStartY = touch.pageY;
1201
+ this.maxMove = 0;
1202
+ if (this.zoom <= 1) {
1203
+ if (this.dragToFlip) {
1204
+ return this.activeCursor = 'grab';
1205
+ }
1206
+ } else {
1207
+ this.startScrollLeft = this.$refs.viewport.scrollLeft;
1208
+ this.startScrollTop = this.$refs.viewport.scrollTop;
1209
+ return this.activeCursor = 'all-scroll';
1210
+ }
1211
+ },
1212
+ swipeMove: function(touch) {
1213
+ var x, y;
1214
+ if (this.touchStartX == null) {
1215
+ return;
1216
+ }
1217
+ x = touch.pageX - this.touchStartX;
1218
+ y = touch.pageY - this.touchStartY;
1219
+ this.maxMove = Math.max(this.maxMove, Math.abs(x));
1220
+ this.maxMove = Math.max(this.maxMove, Math.abs(y));
1221
+ if (this.zoom > 1) {
1222
+ if (this.dragToScroll) {
1223
+ this.dragScroll(x, y);
1224
+ }
1225
+ return;
1226
+ }
1227
+ if (!this.dragToFlip) {
1228
+ return;
1229
+ }
1230
+ if (Math.abs(y) > Math.abs(x)) {
1231
+ return;
1232
+ }
1233
+ this.activeCursor = 'grabbing';
1234
+ if (x > 0) {
1235
+ if (this.flip.direction === null && this.canFlipLeft && x >= this.swipeMin) {
1236
+ this.flipStart('left', false);
1237
+ }
1238
+ if (this.flip.direction === 'left') {
1239
+ this.flip.progress = x / this.pageWidth;
1240
+ if (this.flip.progress > 1) {
1241
+ this.flip.progress = 1;
1242
+ }
1243
+ }
1244
+ } else {
1245
+ if (this.flip.direction === null && this.canFlipRight && x <= -this.swipeMin) {
1246
+ this.flipStart('right', false);
1247
+ }
1248
+ if (this.flip.direction === 'right') {
1249
+ this.flip.progress = -x / this.pageWidth;
1250
+ if (this.flip.progress > 1) {
1251
+ this.flip.progress = 1;
1252
+ }
1253
+ }
1254
+ }
1255
+ return true;
1256
+ },
1257
+ swipeEnd: function(touch) {
1258
+ if (this.touchStartX == null) {
1259
+ return;
1260
+ }
1261
+ if (this.clickToZoom && this.maxMove < this.swipeMin) {
1262
+ this.zoomAt(touch);
1263
+ }
1264
+ if (this.flip.direction !== null && !this.flip.auto) {
1265
+ if (this.flip.progress > 1 / 4) {
1266
+ this.flipAuto(false);
1267
+ } else {
1268
+ this.flipRevert();
1269
+ }
1270
+ }
1271
+ this.touchStartX = null;
1272
+ return this.activeCursor = null;
1273
+ },
1274
+ onTouchStart: function(ev) {
1275
+ this.hasTouchEvents = true;
1276
+ return this.swipeStart(ev.changedTouches[0]);
1277
+ },
1278
+ onTouchMove: function(ev) {
1279
+ if (this.swipeMove(ev.changedTouches[0])) {
1280
+ if (ev.cancelable) {
1281
+ return ev.preventDefault();
1282
+ }
1283
+ }
1284
+ },
1285
+ onTouchEnd: function(ev) {
1286
+ return this.swipeEnd(ev.changedTouches[0]);
1287
+ },
1288
+ onPointerDown: function(ev) {
1289
+ this.hasPointerEvents = true;
1290
+ if (this.hasTouchEvents) {
1291
+ return;
1292
+ }
1293
+ if (ev.which && ev.which !== 1) { // Ignore right-click
1294
+ return;
1295
+ }
1296
+ this.swipeStart(ev);
1297
+ try {
1298
+ return ev.target.setPointerCapture(ev.pointerId);
1299
+ } catch (error) {
1300
+
1301
+ }
1302
+ },
1303
+ onPointerMove: function(ev) {
1304
+ if (!this.hasTouchEvents) {
1305
+ return this.swipeMove(ev);
1306
+ }
1307
+ },
1308
+ onPointerUp: function(ev) {
1309
+ if (this.hasTouchEvents) {
1310
+ return;
1311
+ }
1312
+ this.swipeEnd(ev);
1313
+ try {
1314
+ return ev.target.releasePointerCapture(ev.pointerId);
1315
+ } catch (error) {
1316
+
1317
+ }
1318
+ },
1319
+ onMouseDown: function(ev) {
1320
+ if (this.hasTouchEvents || this.hasPointerEvents) {
1321
+ return;
1322
+ }
1323
+ if (ev.which && ev.which !== 1) { // Ignore right-click
1324
+ return;
1325
+ }
1326
+ return this.swipeStart(ev);
1327
+ },
1328
+ onMouseMove: function(ev) {
1329
+ if (!(this.hasTouchEvents || this.hasPointerEvents)) {
1330
+ return this.swipeMove(ev);
1331
+ }
1332
+ },
1333
+ onMouseUp: function(ev) {
1334
+ if (!(this.hasTouchEvents || this.hasPointerEvents)) {
1335
+ return this.swipeEnd(ev);
1336
+ }
1337
+ },
1338
+ dragScroll: function(x, y) {
1339
+ this.scrollLeft = this.startScrollLeft - x;
1340
+ return this.scrollTop = this.startScrollTop - y;
1341
+ },
1342
+ onWheel: function(ev) {
1343
+ if (this.wheel === 'scroll' && this.zoom > 1 && this.dragToScroll) {
1344
+ this.scrollLeft = this.$refs.viewport.scrollLeft + ev.deltaX;
1345
+ this.scrollTop = this.$refs.viewport.scrollTop + ev.deltaY;
1346
+ if (ev.cancelable) {
1347
+ ev.preventDefault();
1348
+ }
1349
+ }
1350
+ if (this.wheel === 'zoom') {
1351
+ if (ev.deltaY >= 100) {
1352
+ this.zoomOut(ev);
1353
+ return ev.preventDefault();
1354
+ } else if (ev.deltaY <= -100) {
1355
+ this.zoomIn(ev);
1356
+ return ev.preventDefault();
1357
+ }
1358
+ }
1359
+ },
1360
+ preloadImages: function(hiRes) {
1361
+ if ( hiRes === void 0 ) hiRes = false;
1362
+
1363
+ var i, j, k, ref, ref1, ref2, ref3, src;
1364
+ for (i = j = ref = this.currentPage - 3, ref1 = this.currentPage + 3; (ref <= ref1 ? j <= ref1 : j >= ref1); i = ref <= ref1 ? ++j : --j) {
1365
+ this.pageUrlLoading(i); // this preloads image
1366
+ }
1367
+ if (hiRes) {
1368
+ for (i = k = ref2 = this.currentPage, ref3 = this.currentPage + this.displayedPages; (ref2 <= ref3 ? k < ref3 : k > ref3); i = ref2 <= ref3 ? ++k : --k) {
1369
+ src = this.pagesHiRes[i];
1370
+ if (src) {
1371
+ (new Image()).src = src;
1372
+ }
1373
+ }
1374
+ }
1375
+ },
1376
+ goToPage: function(p) {
1377
+ if (p === null || p === this.page) {
1378
+ return;
1379
+ }
1380
+ if (this.pages[0] === null) {
1381
+ if (this.displayedPages === 2 && p === 1) {
1382
+ this.currentPage = 0;
1383
+ } else {
1384
+ this.currentPage = p;
1385
+ }
1386
+ } else {
1387
+ this.currentPage = p - 1;
1388
+ }
1389
+ this.minX = 2e308;
1390
+ this.maxX = -2e308;
1391
+ return this.currentCenterOffset = this.centerOffset;
1392
+ },
1393
+ loadImage: function(url) {
1394
+ var this$1$1 = this;
1395
+
1396
+ var img;
1397
+ if (this.imageWidth === null) {
1398
+ // First loaded image defines the image width and height.
1399
+ // So it must be true image, not 'loading' image.
1400
+ return url;
1401
+ } else {
1402
+ if (this.loadedImages[url]) {
1403
+ return url;
1404
+ } else {
1405
+ img = new Image();
1406
+ img.onload = function () {
1407
+ if (this$1$1.$set) {
1408
+ return this$1$1.$set(this$1$1.loadedImages, url, true);
1409
+ } else {
1410
+ return this$1$1.loadedImages[url] = true;
1411
+ }
1412
+ };
1413
+ img.src = url;
1414
+ return this.loadingImage;
1415
+ }
1416
+ }
1417
+ }
1418
+ },
1419
+ watch: {
1420
+ currentPage: function() {
1421
+ this.firstPage = this.currentPage;
1422
+ this.secondPage = this.currentPage + 1;
1423
+ return this.preloadImages();
1424
+ },
1425
+ centerOffset: function() {
1426
+ var this$1$1 = this;
1427
+
1428
+ var animate;
1429
+ if (this.animatingCenter) {
1430
+ return;
1431
+ }
1432
+ animate = function () {
1433
+ return requestAnimationFrame(function () {
1434
+ var diff, rate;
1435
+ rate = 0.1;
1436
+ diff = this$1$1.centerOffset - this$1$1.currentCenterOffset;
1437
+ if (Math.abs(diff) < 0.5) {
1438
+ this$1$1.currentCenterOffset = this$1$1.centerOffset;
1439
+ return this$1$1.animatingCenter = false;
1440
+ } else {
1441
+ this$1$1.currentCenterOffset += diff * rate;
1442
+ return animate();
1443
+ }
1444
+ });
1445
+ };
1446
+ this.animatingCenter = true;
1447
+ return animate();
1448
+ },
1449
+ scrollLeftLimited: function(val) {
1450
+ var this$1$1 = this;
1451
+
1452
+ if (this.IE) {
1453
+ return requestAnimationFrame(function () {
1454
+ return this$1$1.$refs.viewport.scrollLeft = val;
1455
+ });
1456
+ } else {
1457
+ return this.$refs.viewport.scrollLeft = val;
1458
+ }
1459
+ },
1460
+ scrollTopLimited: function(val) {
1461
+ var this$1$1 = this;
1462
+
1463
+ if (this.IE) {
1464
+ return requestAnimationFrame(function () {
1465
+ return this$1$1.$refs.viewport.scrollTop = val;
1466
+ });
1467
+ } else {
1468
+ return this.$refs.viewport.scrollTop = val;
1469
+ }
1470
+ },
1471
+ pages: function(after, before) {
1472
+ this.fixFirstPage();
1473
+ if (!(before != null ? before.length : void 0) && (after != null ? after.length : void 0)) {
1474
+ if (this.startPage > 1 && after[0] === null) {
1475
+ return this.currentPage++;
1476
+ }
1477
+ }
1478
+ },
1479
+ startPage: function(p) {
1480
+ return this.goToPage(p);
1481
+ }
1482
+ }
1483
+ };
1484
+
1485
+ function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) {
1486
+ if (typeof shadowMode !== 'boolean') {
1487
+ createInjectorSSR = createInjector;
1488
+ createInjector = shadowMode;
1489
+ shadowMode = false;
1490
+ }
1491
+ // Vue.extend constructor export interop.
1492
+ var options = typeof script === 'function' ? script.options : script;
1493
+ // render functions
1494
+ if (template && template.render) {
1495
+ options.render = template.render;
1496
+ options.staticRenderFns = template.staticRenderFns;
1497
+ options._compiled = true;
1498
+ // functional template
1499
+ if (isFunctionalTemplate) {
1500
+ options.functional = true;
1501
+ }
1502
+ }
1503
+ // scopedId
1504
+ if (scopeId) {
1505
+ options._scopeId = scopeId;
1506
+ }
1507
+ var hook;
1508
+ if (moduleIdentifier) {
1509
+ // server build
1510
+ hook = function (context) {
1511
+ // 2.3 injection
1512
+ context =
1513
+ context || // cached call
1514
+ (this.$vnode && this.$vnode.ssrContext) || // stateful
1515
+ (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional
1516
+ // 2.2 with runInNewContext: true
1517
+ if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
1518
+ context = __VUE_SSR_CONTEXT__;
1519
+ }
1520
+ // inject component styles
1521
+ if (style) {
1522
+ style.call(this, createInjectorSSR(context));
1523
+ }
1524
+ // register component module identifier for async chunk inference
1525
+ if (context && context._registeredComponents) {
1526
+ context._registeredComponents.add(moduleIdentifier);
1527
+ }
1528
+ };
1529
+ // used by ssr in case component is cached and beforeCreate
1530
+ // never gets called
1531
+ options._ssrRegister = hook;
1532
+ }
1533
+ else if (style) {
1534
+ hook = shadowMode
1535
+ ? function (context) {
1536
+ style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot));
1537
+ }
1538
+ : function (context) {
1539
+ style.call(this, createInjector(context));
1540
+ };
1541
+ }
1542
+ if (hook) {
1543
+ if (options.functional) {
1544
+ // register for functional component in vue file
1545
+ var originalRender = options.render;
1546
+ options.render = function renderWithStyleInjection(h, context) {
1547
+ hook.call(context);
1548
+ return originalRender(h, context);
1549
+ };
1550
+ }
1551
+ else {
1552
+ // inject component registration as beforeCreate hook
1553
+ var existing = options.beforeCreate;
1554
+ options.beforeCreate = existing ? [].concat(existing, hook) : [hook];
1555
+ }
1556
+ }
1557
+ return script;
1558
+ }
1559
+
1560
+ var isOldIE = typeof navigator !== 'undefined' &&
1561
+ /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase());
1562
+ function createInjector(context) {
1563
+ return function (id, style) { return addStyle(id, style); };
1564
+ }
1565
+ var HEAD;
1566
+ var styles = {};
1567
+ function addStyle(id, css) {
1568
+ var group = isOldIE ? css.media || 'default' : id;
1569
+ var style = styles[group] || (styles[group] = { ids: new Set(), styles: [] });
1570
+ if (!style.ids.has(id)) {
1571
+ style.ids.add(id);
1572
+ var code = css.source;
1573
+ if (css.map) {
1574
+ // https://developer.chrome.com/devtools/docs/javascript-debugging
1575
+ // this makes source maps inside style tags work properly in Chrome
1576
+ code += '\n/*# sourceURL=' + css.map.sources[0] + ' */';
1577
+ // http://stackoverflow.com/a/26603875
1578
+ code +=
1579
+ '\n/*# sourceMappingURL=data:application/json;base64,' +
1580
+ btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) +
1581
+ ' */';
1582
+ }
1583
+ if (!style.element) {
1584
+ style.element = document.createElement('style');
1585
+ style.element.type = 'text/css';
1586
+ if (css.media)
1587
+ { style.element.setAttribute('media', css.media); }
1588
+ if (HEAD === undefined) {
1589
+ HEAD = document.head || document.getElementsByTagName('head')[0];
1590
+ }
1591
+ HEAD.appendChild(style.element);
1592
+ }
1593
+ if ('styleSheet' in style.element) {
1594
+ style.styles.push(code);
1595
+ style.element.styleSheet.cssText = style.styles
1596
+ .filter(Boolean)
1597
+ .join('\n');
1598
+ }
1599
+ else {
1600
+ var index = style.ids.size - 1;
1601
+ var textNode = document.createTextNode(code);
1602
+ var nodes = style.element.childNodes;
1603
+ if (nodes[index])
1604
+ { style.element.removeChild(nodes[index]); }
1605
+ if (nodes.length)
1606
+ { style.element.insertBefore(textNode, nodes[index]); }
1607
+ else
1608
+ { style.element.appendChild(textNode); }
1609
+ }
1610
+ }
1611
+ }
1612
+
1613
+ /* script */
1614
+ var __vue_script__ = script;
1615
+
1616
+ /* template */
1617
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_vm._t("default",null,null,{
1618
+ canFlipLeft: _vm.canFlipLeft,
1619
+ canFlipRight: _vm.canFlipRight,
1620
+ canZoomIn: _vm.canZoomIn,
1621
+ canZoomOut: _vm.canZoomOut,
1622
+ page: _vm.page,
1623
+ numPages: _vm.numPages,
1624
+ flipLeft: _vm.flipLeft,
1625
+ flipRight: _vm.flipRight,
1626
+ zoomIn: _vm.zoomIn,
1627
+ zoomOut: _vm.zoomOut,
1628
+ }),_vm._v(" "),_c('div',{ref:"viewport",staticClass:"viewport",class:{
1629
+ zoom: _vm.zooming || _vm.zoom > 1,
1630
+ 'drag-to-scroll': _vm.dragToScroll,
1631
+ },style:({ cursor: _vm.cursor == 'grabbing' ? 'grabbing' : 'auto' }),on:{"touchmove":_vm.onTouchMove,"pointermove":_vm.onPointerMove,"mousemove":_vm.onMouseMove,"touchend":_vm.onTouchEnd,"touchcancel":_vm.onTouchEnd,"pointerup":_vm.onPointerUp,"pointercancel":_vm.onPointerUp,"mouseup":_vm.onMouseUp,"wheel":_vm.onWheel}},[_c('div',{staticClass:"flipbook-container",style:({ transform: ("scale(" + _vm.zoom + ")") })},[_c('div',{staticClass:"click-to-flip left",style:({ cursor: _vm.canFlipLeft ? 'pointer' : 'auto' }),on:{"click":_vm.flipLeft}}),_vm._v(" "),_c('div',{staticClass:"click-to-flip right",style:({ cursor: _vm.canFlipRight ? 'pointer' : 'auto' }),on:{"click":_vm.flipRight}}),_vm._v(" "),_c('div',{style:({ transform: ("translateX(" + _vm.centerOffsetSmoothed + "px)") })},[(_vm.showLeftPage)?_c('img',{staticClass:"page fixed",style:({
1632
+ width: _vm.pageWidth + 'px',
1633
+ height: _vm.pageHeight + 'px',
1634
+ left: _vm.xMargin + 'px',
1635
+ top: _vm.yMargin + 'px',
1636
+ }),attrs:{"src":_vm.pageUrlLoading(_vm.leftPage, true)},on:{"load":function($event){return _vm.didLoadImage($event)}}}):_vm._e(),_vm._v(" "),(_vm.showRightPage)?_c('img',{staticClass:"page fixed",style:({
1637
+ width: _vm.pageWidth + 'px',
1638
+ height: _vm.pageHeight + 'px',
1639
+ left: _vm.viewWidth / 2 + 'px',
1640
+ top: _vm.yMargin + 'px',
1641
+ }),attrs:{"src":_vm.pageUrlLoading(_vm.rightPage, true)},on:{"load":function($event){return _vm.didLoadImage($event)}}}):_vm._e(),_vm._v(" "),_c('div',{style:({ opacity: _vm.flip.opacity })},_vm._l((_vm.polygonArray),function(ref){
1642
+ var key = ref[0];
1643
+ var bgImage = ref[1];
1644
+ var lighting = ref[2];
1645
+ var bgPos = ref[3];
1646
+ var transform = ref[4];
1647
+ var z = ref[5];
1648
+ return _c('div',{key:key,staticClass:"polygon",class:{ blank: !bgImage },style:({
1649
+ backgroundImage: bgImage && ("url(" + (_vm.loadImage(bgImage)) + ")"),
1650
+ backgroundSize: _vm.polygonBgSize,
1651
+ backgroundPosition: bgPos,
1652
+ width: _vm.polygonWidth,
1653
+ height: _vm.polygonHeight,
1654
+ transform: transform,
1655
+ zIndex: z,
1656
+ })},[_c('div',{directives:[{name:"show",rawName:"v-show",value:(lighting.length),expression:"lighting.length"}],staticClass:"lighting",style:({ backgroundImage: lighting })})])}),0),_vm._v(" "),_c('div',{staticClass:"bounding-box",style:({
1657
+ left: _vm.boundingLeft + 'px',
1658
+ top: _vm.yMargin + 'px',
1659
+ width: _vm.boundingRight - _vm.boundingLeft + 'px',
1660
+ height: _vm.pageHeight + 'px',
1661
+ cursor: _vm.cursor,
1662
+ }),on:{"touchstart":_vm.onTouchStart,"pointerdown":_vm.onPointerDown,"mousedown":_vm.onMouseDown}})])])])],2)};
1663
+ var __vue_staticRenderFns__ = [];
1664
+
1665
+ /* style */
1666
+ var __vue_inject_styles__ = function (inject) {
1667
+ if (!inject) { return }
1668
+ inject("data-v-e3f0fbe2_0", { source: ".viewport[data-v-e3f0fbe2]{-webkit-overflow-scrolling:touch;width:100%;height:100%}.viewport.zoom[data-v-e3f0fbe2]{overflow:scroll}.viewport.zoom.drag-to-scroll[data-v-e3f0fbe2]{overflow:hidden}.flipbook-container[data-v-e3f0fbe2]{position:relative;width:100%;height:100%;transform-origin:top left;user-select:none}.click-to-flip[data-v-e3f0fbe2]{position:absolute;width:50%;height:100%;top:0;user-select:none}.click-to-flip.left[data-v-e3f0fbe2]{left:0}.click-to-flip.right[data-v-e3f0fbe2]{right:0}.bounding-box[data-v-e3f0fbe2]{position:absolute;user-select:none}.page[data-v-e3f0fbe2]{position:absolute;backface-visibility:hidden}.polygon[data-v-e3f0fbe2]{position:absolute;top:0;left:0;background-repeat:no-repeat;backface-visibility:hidden;transform-origin:center left}.polygon.blank[data-v-e3f0fbe2]{background-color:#ddd}.polygon .lighting[data-v-e3f0fbe2]{width:100%;height:100%}", map: undefined, media: undefined });
1669
+
1670
+ };
1671
+ /* scoped */
1672
+ var __vue_scope_id__ = "data-v-e3f0fbe2";
1673
+ /* module identifier */
1674
+ var __vue_module_identifier__ = undefined;
1675
+ /* functional template */
1676
+ var __vue_is_functional_template__ = false;
1677
+ /* style inject SSR */
1678
+
1679
+ /* style inject shadow dom */
1680
+
1681
+
1682
+
1683
+ var __vue_component__ = /*#__PURE__*/normalizeComponent(
1684
+ { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
1685
+ __vue_inject_styles__,
1686
+ __vue_script__,
1687
+ __vue_scope_id__,
1688
+ __vue_is_functional_template__,
1689
+ __vue_module_identifier__,
1690
+ false,
1691
+ createInjector,
1692
+ undefined,
1693
+ undefined
1694
+ );
1695
+
1696
+
1697
+
1698
+ ;// ./node_modules/@vue/vue-loader-v15/lib/loaders/templateLoader.js??ruleSet[1].rules[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookCatalogueDrawer.vue?vue&type=template&id=3930be62&scoped=true
1699
+ var BookCatalogueDrawervue_type_template_id_3930be62_scoped_true_render = function render(){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:"catalogue-drawer-container"},[(_vm.visible)?_c('div',{staticClass:"drawer-overlay",on:{"click":_vm.closeDrawer}}):_vm._e(),_c('div',{staticClass:"catalogue-drawer",class:{ 'drawer-open': _vm.visible },style:({ width: _vm.drawerWidth })},[_c('div',{staticClass:"drawer-header"},[_c('h3',{staticClass:"drawer-title"},[_vm._v("目录")]),_c('button',{staticClass:"close-button",on:{"click":_vm.closeDrawer}},[_c('span',{staticClass:"close-icon"},[_vm._v("×")])])]),_c('div',{staticClass:"page-jump-section"},[_c('input',{directives:[{name:"model",rawName:"v-model",value:(_vm.jumpPageInput),expression:"jumpPageInput"}],staticClass:"page-jump-input",attrs:{"type":"text","placeholder":"请输入页码"},domProps:{"value":(_vm.jumpPageInput)},on:{"input":[function($event){if($event.target.composing)return;_vm.jumpPageInput=$event.target.value},_vm.handlePageInput],"focus":_vm.onInputFocus,"blur":_vm.onInputBlur}}),_c('button',{staticClass:"btn-jump",on:{"click":_vm.handleJumpToPage}},[_vm._v(" 跳转 ")])]),_c('div',{staticClass:"drawer-content"},[(_vm.loading)?_c('div',{staticClass:"loading-container"},[_c('div',{staticClass:"loading-spinner"}),_c('p',[_vm._v("加载目录中...")])]):(_vm.error)?_c('div',{staticClass:"error-container"},[_c('div',{staticClass:"error-icon"},[_vm._v("⚠️")]),_c('p',[_vm._v("目录加载失败")]),_c('button',{staticClass:"retry-button",on:{"click":_vm.fetchCatalogue}},[_vm._v(" 重新加载 ")])]):(_vm.catalogueData && _vm.catalogueData.length > 0)?_c('div',{staticClass:"catalogue-list"},_vm._l((_vm.catalogueData),function(item,index){return _c('catalogue-item',{key:`catalogue-${index}`,attrs:{"item":item || {},"level":0},on:{"item-click":_vm.onItemClick}})}),1):_c('div',{staticClass:"empty-container"},[_c('p',[_vm._v("暂无目录")])])])])])
1700
+ }
1701
+ var BookCatalogueDrawervue_type_template_id_3930be62_scoped_true_staticRenderFns = []
1702
+
1703
+
1704
+ ;// ./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookCatalogueDrawer.vue?vue&type=script&lang=js
1705
+
1706
+ // 目录项组件
1707
+ const CatalogueItem = {
1708
+ name: 'CatalogueItem',
1709
+ props: {
1710
+ item: {
1711
+ type: Object,
1712
+ required: true
1713
+ },
1714
+ level: {
1715
+ type: Number,
1716
+ default: 0
1717
+ }
1718
+ },
1719
+ data() {
1720
+ return {
1721
+ expanded: false
1722
+ }
1723
+ },
1724
+ computed: {
1725
+ hasChildren() {
1726
+ const children = this.item.childrenList || this.item.children || this.item.subItems || this.item.items
1727
+ return children && children.length > 0
1728
+ },
1729
+ childrenData() {
1730
+ return this.item.childrenList || this.item.children || this.item.subItems || this.item.items || []
1731
+ },
1732
+ itemStyle() {
1733
+ // 移除缩进,所有层级都左对齐
1734
+ return {
1735
+ paddingLeft: '12px',
1736
+ paddingRight: '12px'
1737
+ }
1738
+ },
1739
+ levelClass() {
1740
+ return `level-${this.level}`
1741
+ }
1742
+ },
1743
+ methods: {
1744
+ toggleExpand() {
1745
+ if (this.hasChildren) {
1746
+ this.expanded = !this.expanded
1747
+ } else {
1748
+ this.handleClick()
1749
+ }
1750
+ },
1751
+ handleClick() {
1752
+ this.$emit('item-click', this.item)
1753
+ }
1754
+ },
1755
+ render(h) {
1756
+ const item = this.item
1757
+ const titleText = item.titleName || item.title || item.name || item.text || item.label || '未命名章节'
1758
+ const pageNum = item.startPageNum || item.pageNumber || item.page || item.pageNum
1759
+
1760
+ // 创建拖拽手柄
1761
+ const dragHandle = h('span', {
1762
+ class: 'drag-handle'
1763
+ }, '')
1764
+
1765
+ // 创建展开图标
1766
+ const expandIcon = this.hasChildren ? h('span', {
1767
+ class: 'expand-icon'
1768
+ }, this.expanded ? '▼' : '▶') : null
1769
+
1770
+ // 创建层级指示器
1771
+ const levelIndicator = this.level > 0 ? h('span', {
1772
+ class: `level-indicator level-${this.level}`
1773
+ }, '•'.repeat(this.level)) : null
1774
+
1775
+ // 创建标题
1776
+ const titleSpan = h('span', {
1777
+ class: 'item-title'
1778
+ }, titleText)
1779
+
1780
+ // 创建页码(放在标题后面)
1781
+ // const pageSpan = pageNum ? h('span', {
1782
+ // class: 'page-number'
1783
+ // }, `第${pageNum}页`) : null
1784
+ const pageSpan = null
1785
+ // 创建子级目录
1786
+ const childrenDiv = (this.hasChildren && this.expanded) ? h('div', {
1787
+ class: 'children-container'
1788
+ }, this.childrenData.map((child, index) => {
1789
+ return h('catalogue-item', {
1790
+ key: index,
1791
+ props: {
1792
+ item: child,
1793
+ level: this.level + 1
1794
+ },
1795
+ on: {
1796
+ 'item-click': (item) => this.$emit('item-click', item)
1797
+ }
1798
+ })
1799
+ })) : null
1800
+
1801
+ return h('div', {
1802
+ class: 'catalogue-item-wrapper'
1803
+ }, [
1804
+ h('div', {
1805
+ class: [
1806
+ 'catalogue-item',
1807
+ this.levelClass,
1808
+ {
1809
+ 'has-children': this.hasChildren,
1810
+ 'expanded': this.expanded
1811
+ }
1812
+ ],
1813
+ style: this.itemStyle,
1814
+ on: {
1815
+ click: this.toggleExpand
1816
+ }
1817
+ }, [
1818
+ h('div', {
1819
+ class: 'item-content'
1820
+ }, [dragHandle, expandIcon, levelIndicator, titleSpan, pageSpan].filter(Boolean))
1821
+ ]),
1822
+ childrenDiv
1823
+ ].filter(Boolean))
1824
+ }
1825
+ }
1826
+
1827
+ /* harmony default export */ var BookCatalogueDrawervue_type_script_lang_js = ({
1828
+ name: 'BookCatalogueDrawer',
1829
+ components: {
1830
+ CatalogueItem
1831
+ },
1832
+ props: {
1833
+ value: {
1834
+ type: Boolean,
1835
+ default: false
1836
+ },
1837
+ bookId: {
1838
+ type: String,
1839
+ default: ''
1840
+ },
1841
+ catalogue: {
1842
+ type: Array,
1843
+ default: () => []
1844
+ },
1845
+ loading: {
1846
+ type: Boolean,
1847
+ default: false
1848
+ }
1849
+ },
1850
+ data() {
1851
+ return {
1852
+ catalogueData: [],
1853
+ error: null,
1854
+ jumpPageInput: ''
1855
+ }
1856
+ },
1857
+ computed: {
1858
+ visible: {
1859
+ get() {
1860
+ return this.value
1861
+ },
1862
+ set(val) {
1863
+ this.$emit('input', val)
1864
+ }
1865
+ },
1866
+ drawerWidth() {
1867
+ // 响应式宽度
1868
+ return window.innerWidth <= 768 ? '80%' : '400px'
1869
+ }
1870
+ },
1871
+ watch: {
1872
+ visible(newVal) {
1873
+ if (newVal && this.bookId && !this.catalogueData.length) {
1874
+ this.$emit('fetch-catalogue', this.bookId)
1875
+ }
1876
+ },
1877
+ bookId(newVal) {
1878
+ if (newVal && this.visible) {
1879
+ this.$emit('fetch-catalogue', newVal)
1880
+ }
1881
+ },
1882
+ catalogue: {
1883
+ handler(newVal) {
1884
+ if (newVal && newVal.length > 0) {
1885
+ this.catalogueData = this.processCatalogueData(newVal)
1886
+ }
1887
+ },
1888
+ immediate: true
1889
+ }
1890
+ },
1891
+ methods: {
1892
+ /**
1893
+ * 获取目录数据 - 触发事件让父组件处理
1894
+ */
1895
+ fetchCatalogue() {
1896
+ if (!this.bookId) {
1897
+ console.warn('缺少 bookId,无法获取目录')
1898
+ return
1899
+ }
1900
+ this.$emit('fetch-catalogue', this.bookId)
1901
+ },
1902
+
1903
+ /**
1904
+ * 处理目录数据,确保数据结构正确
1905
+ * @param {Array} data - 原始目录数据
1906
+ * @returns {Array} 处理后的目录数据
1907
+ */
1908
+ processCatalogueData(data) {
1909
+ if (!Array.isArray(data)) {
1910
+ return []
1911
+ }
1912
+
1913
+ return data.map(item => {
1914
+ if (!item || typeof item !== 'object') {
1915
+ return {
1916
+ titleName: '未知章节',
1917
+ startPageNum: null,
1918
+ childrenList: []
1919
+ }
1920
+ }
1921
+
1922
+ // 确保必要的字段存在
1923
+ const processedItem = {
1924
+ ...item,
1925
+ titleName: item.titleName || item.title || item.name || '未命名章节',
1926
+ startPageNum: item.startPageNum || item.pageNumber || item.page || item.pageNum || null,
1927
+ childrenList: item.childrenList || item.children || item.subItems || item.items || []
1928
+ }
1929
+
1930
+ // 递归处理子级
1931
+ if (processedItem.childrenList && processedItem.childrenList.length > 0) {
1932
+ processedItem.childrenList = this.processCatalogueData(processedItem.childrenList)
1933
+ }
1934
+
1935
+ return processedItem
1936
+ })
1937
+ },
1938
+
1939
+ /**
1940
+ * 目录项点击事件
1941
+ * @param {Object} item - 点击的目录项
1942
+ */
1943
+ onItemClick(item) {
1944
+ console.log('目录项被点击:', item)
1945
+
1946
+ // 发送事件给父组件
1947
+ this.$emit('catalogue-click', item)
1948
+
1949
+ // 如果有页码信息,可以进行跳转
1950
+ const pageNumber = item.startPageNum || item.pageNumber || item.page || item.pageNum
1951
+ if (pageNumber) {
1952
+ this.$emit('page-jump', pageNumber)
1953
+ }
1954
+ },
1955
+
1956
+ /**
1957
+ * 关闭抽屉
1958
+ */
1959
+ closeDrawer() {
1960
+ this.$emit('input', false)
1961
+ },
1962
+
1963
+ /**
1964
+ * 处理页码输入
1965
+ */
1966
+ handlePageInput(e) {
1967
+ const value = e.target.value
1968
+ const numValue = value.replace(/[^\d]/g, '')
1969
+ this.jumpPageInput = numValue
1970
+ },
1971
+
1972
+ /**
1973
+ * 执行页码跳转
1974
+ */
1975
+ handleJumpToPage() {
1976
+ const pageNum = parseInt(this.jumpPageInput)
1977
+ if (pageNum && pageNum >= 1) {
1978
+ this.$emit('page-jump', pageNum)
1979
+ this.jumpPageInput = ''
1980
+ this.closeDrawer()
1981
+ }
1982
+ },
1983
+
1984
+ /**
1985
+ * 输入框获得焦点
1986
+ */
1987
+ onInputFocus() {
1988
+ // 可以在这里添加焦点处理逻辑
1989
+ },
1990
+
1991
+ /**
1992
+ * 输入框失去焦点
1993
+ */
1994
+ onInputBlur() {
1995
+ this.$emit('Focus')
1996
+ },
1997
+
1998
+ /**
1999
+ * 重置数据
2000
+ */
2001
+ resetData() {
2002
+ this.catalogueData = []
2003
+ this.error = null
2004
+ this.loading = false
2005
+ },
2006
+
2007
+ /**
2008
+ * 处理窗口大小变化
2009
+ */
2010
+ handleResize() {
2011
+ // 可以在这里添加响应式逻辑
2012
+ }
2013
+ },
2014
+
2015
+ mounted() {
2016
+ // 监听窗口大小变化
2017
+ window.addEventListener('resize', this.handleResize)
2018
+ },
2019
+
2020
+ beforeDestroy() {
2021
+ window.removeEventListener('resize', this.handleResize)
2022
+ }
2023
+ });
2024
+
2025
+ ;// ./src/components/BookCatalogueDrawer.vue?vue&type=script&lang=js
2026
+ /* harmony default export */ var components_BookCatalogueDrawervue_type_script_lang_js = (BookCatalogueDrawervue_type_script_lang_js);
2027
+ ;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookCatalogueDrawer.vue?vue&type=style&index=0&id=3930be62&prod&scoped=true&lang=css
2028
+ // extracted by mini-css-extract-plugin
2029
+
2030
+ ;// ./src/components/BookCatalogueDrawer.vue?vue&type=style&index=0&id=3930be62&prod&scoped=true&lang=css
2031
+
2032
+ ;// ./node_modules/@vue/vue-loader-v15/lib/runtime/componentNormalizer.js
2033
+ /* globals __VUE_SSR_CONTEXT__ */
2034
+
2035
+ // IMPORTANT: Do NOT use ES2015 features in this file (except for modules).
2036
+ // This module is a runtime utility for cleaner component module output and will
2037
+ // be included in the final webpack user bundle.
2038
+
2039
+ function componentNormalizer_normalizeComponent(
2040
+ scriptExports,
2041
+ render,
2042
+ staticRenderFns,
2043
+ functionalTemplate,
2044
+ injectStyles,
2045
+ scopeId,
2046
+ moduleIdentifier /* server only */,
2047
+ shadowMode /* vue-cli only */
2048
+ ) {
2049
+ // Vue.extend constructor export interop
2050
+ var options =
2051
+ typeof scriptExports === 'function' ? scriptExports.options : scriptExports
2052
+
2053
+ // render functions
2054
+ if (render) {
2055
+ options.render = render
2056
+ options.staticRenderFns = staticRenderFns
2057
+ options._compiled = true
2058
+ }
2059
+
2060
+ // functional template
2061
+ if (functionalTemplate) {
2062
+ options.functional = true
2063
+ }
2064
+
2065
+ // scopedId
2066
+ if (scopeId) {
2067
+ options._scopeId = 'data-v-' + scopeId
2068
+ }
2069
+
2070
+ var hook
2071
+ if (moduleIdentifier) {
2072
+ // server build
2073
+ hook = function (context) {
2074
+ // 2.3 injection
2075
+ context =
2076
+ context || // cached call
2077
+ (this.$vnode && this.$vnode.ssrContext) || // stateful
2078
+ (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional
2079
+ // 2.2 with runInNewContext: true
2080
+ if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {
2081
+ context = __VUE_SSR_CONTEXT__
2082
+ }
2083
+ // inject component styles
2084
+ if (injectStyles) {
2085
+ injectStyles.call(this, context)
2086
+ }
2087
+ // register component module identifier for async chunk inferrence
2088
+ if (context && context._registeredComponents) {
2089
+ context._registeredComponents.add(moduleIdentifier)
2090
+ }
2091
+ }
2092
+ // used by ssr in case component is cached and beforeCreate
2093
+ // never gets called
2094
+ options._ssrRegister = hook
2095
+ } else if (injectStyles) {
2096
+ hook = shadowMode
2097
+ ? function () {
2098
+ injectStyles.call(
2099
+ this,
2100
+ (options.functional ? this.parent : this).$root.$options.shadowRoot
2101
+ )
2102
+ }
2103
+ : injectStyles
2104
+ }
2105
+
2106
+ if (hook) {
2107
+ if (options.functional) {
2108
+ // for template-only hot-reload because in that case the render fn doesn't
2109
+ // go through the normalizer
2110
+ options._injectStyles = hook
2111
+ // register for functional component in vue file
2112
+ var originalRender = options.render
2113
+ options.render = function renderWithStyleInjection(h, context) {
2114
+ hook.call(context)
2115
+ return originalRender(h, context)
2116
+ }
2117
+ } else {
2118
+ // inject component registration as beforeCreate hook
2119
+ var existing = options.beforeCreate
2120
+ options.beforeCreate = existing ? [].concat(existing, hook) : [hook]
2121
+ }
2122
+ }
2123
+
2124
+ return {
2125
+ exports: scriptExports,
2126
+ options: options
2127
+ }
2128
+ }
2129
+
2130
+ ;// ./src/components/BookCatalogueDrawer.vue
2131
+
2132
+
2133
+
2134
+ ;
2135
+
2136
+
2137
+ /* normalize component */
2138
+
2139
+ var component = componentNormalizer_normalizeComponent(
2140
+ components_BookCatalogueDrawervue_type_script_lang_js,
2141
+ BookCatalogueDrawervue_type_template_id_3930be62_scoped_true_render,
2142
+ BookCatalogueDrawervue_type_template_id_3930be62_scoped_true_staticRenderFns,
2143
+ false,
2144
+ null,
2145
+ "3930be62",
2146
+ null
2147
+
2148
+ )
2149
+
2150
+ /* harmony default export */ var BookCatalogueDrawer = (component.exports);
2151
+ ;// ./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookReader.vue?vue&type=script&lang=js
2152
+
2153
+
2154
+
2155
+
2156
+ /* harmony default export */ var BookReadervue_type_script_lang_js = ({
2157
+ name: 'PhotoAlbumView',
2158
+ components: {
2159
+ Flipbook: __vue_component__,
2160
+ BookCatalogueDrawer: BookCatalogueDrawer
2161
+ },
2162
+ props: {
2163
+ pages: {
2164
+ type: Array,
2165
+ default: () => []
2166
+ },
2167
+ bookId: {
2168
+ type: String,
2169
+ default: ''
2170
+ },
2171
+ startPage: {
2172
+ type: Number,
2173
+ default: 1
2174
+ },
2175
+ catalogue: {
2176
+ type: Array,
2177
+ default: () => []
2178
+ },
2179
+ catalogueLoading: {
2180
+ type: Boolean,
2181
+ default: false
2182
+ }
2183
+ },
2184
+ data() {
2185
+ return {
2186
+ currentPage: 1,
2187
+ flag: true,
2188
+ // 设备检测
2189
+ isMobile: false,
2190
+ // 接口相关数据
2191
+ booksData: null,
2192
+ loading: false,
2193
+ error: null,
2194
+ showSuccessToast: false,
2195
+ // 内容就绪状态
2196
+ contentReady: false,
2197
+ // 手势缩放相关
2198
+ zoomScale: 1,
2199
+ translateX: 0,
2200
+ translateY: 0,
2201
+ lastTouchDistance: 0,
2202
+ lastTouchCenter: { x: 0, y: 0 },
2203
+ isZooming: false,
2204
+ isPanning: false,
2205
+ touchStartTime: 0,
2206
+ initialTouches: [],
2207
+ // Flipbook区域双击检测
2208
+ flipbookTouchStartTime: 0,
2209
+ flipbookLastTapTime: 0,
2210
+ // 目录相关
2211
+ showCatalogueDrawer: false,
2212
+ // 目录按钮拖拽相关
2213
+ catalogueButtonDragging: false,
2214
+ catalogueButtonPosition: { x: 20, y: 20 },
2215
+ dragStartPosition: { x: 0, y: 0 },
2216
+ dragOffset: { x: 0, y: 0 },
2217
+ // 翻页模式相关
2218
+ flipMode: 'flip', // flip: 仿真翻页, slide: 滑动翻页, fade: 淡入淡出, scroll: 垂直滚动
2219
+ showFlipModeMenu: false,
2220
+ flipModes: [
2221
+ { value: 'flip', label: '仿真翻页', icon: '📖' },
2222
+ { value: 'slide', label: '滑动翻页', icon: '↔️' },
2223
+ { value: 'fade', label: '淡入淡出', icon: '✨' },
2224
+ { value: 'scroll', label: '垂直滚动', icon: '📜' },
2225
+ { value: 'clip', label: '截断特效', icon: '✂️' },
2226
+ { value: 'card', label: '卡片风格', icon: '🃏' }
2227
+ ],
2228
+ // 滑动模式相关
2229
+ slideStartX: 0,
2230
+ slideCurrentX: 0,
2231
+ slideIsDragging: false,
2232
+ // URL参数控制
2233
+ flipModeFromUrl: false, // 翻页模式是否由URL参数指定
2234
+ // 控件自动隐藏相关
2235
+ showControls: false,
2236
+ hideControlsTimer: null,
2237
+ // 页码跳转相关
2238
+ jumpPageInput: ''
2239
+ }
2240
+ },
2241
+ computed: {
2242
+ totalPages() {
2243
+ return this.pages.length
2244
+ },
2245
+ pagesHiRes() {
2246
+ return this.pages
2247
+ },
2248
+ /**
2249
+ * 翻页模式图标
2250
+ */
2251
+ flipModeIcon() {
2252
+ const mode = this.flipModes.find(m => m.value === this.flipMode)
2253
+ return mode ? mode.icon : '📖'
2254
+ },
2255
+ /**
2256
+ * 翻页模式标签
2257
+ */
2258
+ flipModeLabel() {
2259
+ const mode = this.flipModes.find(m => m.value === this.flipMode)
2260
+ return mode ? mode.label : '仿真翻页'
2261
+ },
2262
+ /**
2263
+ * 滑动容器样式
2264
+ */
2265
+ slideContainerStyle() {
2266
+ const offset = -(this.currentPage - 1) * 100
2267
+ const dragOffset = this.slideIsDragging ? (this.slideCurrentX - this.slideStartX) / 5 : 0
2268
+ return {
2269
+ transform: `translateX(calc(${offset}% + ${dragOffset}px))`,
2270
+ transition: this.slideIsDragging ? 'none' : 'transform 0.3s ease-out'
2271
+ }
2272
+ },
2273
+ /**
2274
+ * 是否显示翻页模式选择器
2275
+ */
2276
+ showFlipModeSelector() {
2277
+ // 如果翻页模式是由URL参数指定的,则隐藏选择器
2278
+ return !this.flipModeFromUrl
2279
+ },
2280
+ /**
2281
+ * 卡片模式可见卡片
2282
+ */
2283
+ visibleCards() {
2284
+ const cards = []
2285
+ // 上一页
2286
+ if (this.currentPage > 1) {
2287
+ cards.push({
2288
+ src: this.pages[this.currentPage - 2],
2289
+ originalIndex: this.currentPage - 2,
2290
+ position: 'prev'
2291
+ })
2292
+ }
2293
+ // 当前页
2294
+ cards.push({
2295
+ src: this.pages[this.currentPage - 1],
2296
+ originalIndex: this.currentPage - 1,
2297
+ position: 'current'
2298
+ })
2299
+ // 下一页
2300
+ if (this.currentPage < this.totalPages) {
2301
+ cards.push({
2302
+ src: this.pages[this.currentPage],
2303
+ originalIndex: this.currentPage,
2304
+ position: 'next'
2305
+ })
2306
+ }
2307
+ return cards
2308
+ },
2309
+ /**
2310
+ * 计算当前页面显示文本
2311
+ */
2312
+ pageDisplayText() {
2313
+ if (this.totalPages === 0) return '加载中...'
2314
+ if (this.currentPage === 1 && this.totalPages > 1) return '封面'
2315
+ if (this.currentPage === this.totalPages && this.totalPages > 1) return '封底'
2316
+
2317
+ if (this.isMobile) {
2318
+ if (this.totalPages <= 2) return `第 ${this.currentPage} 页 / 共 ${this.totalPages} 页`
2319
+ return `第 ${Math.max(1, this.currentPage - 1)} 页 / 共 ${Math.max(0, this.totalPages - 2)} 页`
2320
+ } else {
2321
+ if (this.totalPages <= 2) return `第 ${this.currentPage} 页 / 共 ${this.totalPages} 页`
2322
+ return `第 ${Math.ceil((this.currentPage - 1) / 2)} 组 / 共 ${Math.ceil((this.totalPages - 2) / 2)} 组`
2323
+ }
2324
+ }
2325
+ },
2326
+ methods: {
2327
+ /**
2328
+ * 获取书籍数据
2329
+ */
2330
+ async fetchBooksData() {
2331
+ try {
2332
+ this.loading = true
2333
+ this.error = null
2334
+
2335
+ // 注释:原来从URL参数获取fileManagementId,现在改为通过props传入
2336
+ // const fileManagementId = this.$route.params.id || this.$route.query.id || ''
2337
+ // this.bookId = fileManagementId
2338
+
2339
+ // 注释:原来调用API获取数据,现在改为通过props传入
2340
+ // const response = await booksApi.getBooksSlice(fileManagementId, textContent, 1, 9999)
2341
+
2342
+ // 触发事件让父组件处理数据获取
2343
+ this.$emit('fetch-books-data', this.bookId)
2344
+
2345
+ // 如果通过props传入了pages数据,直接使用
2346
+ if (this.pages && this.pages.length > 0) {
2347
+ this.booksData = { data: { records: this.pages.map(url => ({ imageUrl: url })) } }
2348
+
2349
+ // 显示成功提示
2350
+ this.showSuccessToast = true
2351
+ setTimeout(() => {
2352
+ this.showSuccessToast = false
2353
+ }, 3000)
2354
+
2355
+ // 检查URL参数并跳转到指定页码
2356
+ this.checkUrlPageParameter()
2357
+ }
2358
+
2359
+ } catch (error) {
2360
+ console.error('获取书籍数据失败:', error)
2361
+ this.error = error
2362
+
2363
+ // 显示错误信息
2364
+ if (error.response) {
2365
+ console.error('API错误响应:', error.response.data)
2366
+ } else if (error.request) {
2367
+ console.error('网络请求失败:', error.request)
2368
+ } else {
2369
+ console.error('请求配置错误:', error.message)
2370
+ }
2371
+ } finally {
2372
+ this.loading = false
2373
+ }
2374
+ },
2375
+
2376
+ /**
2377
+ * 根据API数据更新页面
2378
+ * @param {Array} apiData - API返回的数据
2379
+ */
2380
+ updatePagesFromApiData(apiData) {
2381
+ if (!Array.isArray(apiData)) {
2382
+ console.warn('API数据格式不正确,期望数组格式')
2383
+ return
2384
+ }
2385
+
2386
+ console.log('开始更新页面数据,数据长度:', apiData.length)
2387
+
2388
+ // 这里可以根据实际的API数据结构来处理
2389
+ // 假设API返回的数据包含图片URL
2390
+ const newPages = [null] // 保留封面页
2391
+
2392
+ apiData.forEach((item, index) => {
2393
+ if (item.imageUrl) {
2394
+ newPages.push(item.imageUrl)
2395
+ console.log(`添加页面 ${index + 1}:`, item.imageUrl)
2396
+ }
2397
+ })
2398
+
2399
+ if (newPages.length > 1) {
2400
+ this.pages = newPages
2401
+ console.log('页面数据更新完成,总页数:', this.pages.length)
2402
+ }
2403
+ },
2404
+
2405
+ /**
2406
+ * 检测是否为移动设备
2407
+ */
2408
+ detectMobile() {
2409
+ const userAgent = navigator.userAgent.toLowerCase()
2410
+ const mobileKeywords = ['mobile', 'android', 'iphone', 'ipad', 'ipod', 'blackberry', 'windows phone']
2411
+
2412
+ // 检测 User Agent
2413
+ const isMobileUA = mobileKeywords.some(keyword => userAgent.includes(keyword))
2414
+
2415
+ // 检测屏幕尺寸
2416
+ const isMobileScreen = window.innerWidth <= 768
2417
+
2418
+ // 检测触摸支持
2419
+ const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0
2420
+
2421
+ // 综合判断
2422
+ this.isMobile = isMobileUA || (isMobileScreen && isTouchDevice)
2423
+
2424
+ console.log('设备检测结果:', {
2425
+ userAgent: isMobileUA,
2426
+ screenSize: isMobileScreen,
2427
+ touchSupport: isTouchDevice,
2428
+ finalResult: this.isMobile
2429
+ })
2430
+ },
2431
+
2432
+ /**
2433
+ * 预加载首页图片
2434
+ */
2435
+ async preloadFirstPage() {
2436
+ if (!this.pages || this.pages.length === 0) {
2437
+ this.contentReady = true
2438
+ return
2439
+ }
2440
+
2441
+ const firstPageIndex = this.startPage - 1
2442
+ const firstPageUrl = this.pages[firstPageIndex]
2443
+
2444
+ if (!firstPageUrl) {
2445
+ this.contentReady = true
2446
+ return
2447
+ }
2448
+
2449
+ try {
2450
+ await new Promise((resolve, reject) => {
2451
+ const img = new Image()
2452
+ img.onload = () => resolve()
2453
+ img.onerror = () => resolve() // 即使失败也继续
2454
+ img.src = firstPageUrl
2455
+
2456
+ // 超时保护
2457
+ setTimeout(() => resolve(), 3000)
2458
+ })
2459
+ } catch (error) {
2460
+ console.warn('首页图片预加载失败:', error)
2461
+ } finally {
2462
+ this.contentReady = true
2463
+ }
2464
+ },
2465
+
2466
+ /**
2467
+ * 监听窗口尺寸变化
2468
+ */
2469
+ handleResize() {
2470
+ const wasMobile = this.isMobile
2471
+ this.detectMobile()
2472
+
2473
+ // 如果设备类型发生变化,重新初始化flipbook
2474
+ if (wasMobile !== this.isMobile) {
2475
+ console.log('设备类型变化,重新初始化')
2476
+ this.$nextTick(() => {
2477
+ if (this.$refs.flipbook) {
2478
+ // 重置到第一页
2479
+ this.currentPage = 1
2480
+ this.startPage = 1
2481
+ }
2482
+ })
2483
+ }
2484
+ },
2485
+ flipLeft() {
2486
+ console.log('尝试向左翻页,当前页:', this.currentPage)
2487
+ if (this.flipMode === 'flip') {
2488
+ // 仿真翻页模式
2489
+ if (this.$refs.flipbook && this.currentPage > 1) {
2490
+ try {
2491
+ this.$refs.flipbook.flipLeft()
2492
+ console.log('向左翻页命令已发送')
2493
+ } catch (error) {
2494
+ console.error('向左翻页失败:', error)
2495
+ }
2496
+ } else {
2497
+ console.log('无法向左翻页 - 已在第一页或组件未就绪')
2498
+ }
2499
+ } else {
2500
+ // 其他模式直接切换页码
2501
+ if (this.currentPage > 1) {
2502
+ this.currentPage--
2503
+ this.onFlipLeftEnd(this.currentPage)
2504
+ }
2505
+ }
2506
+ },
2507
+ flipRight() {
2508
+ console.log('尝试向右翻页,当前页:', this.currentPage)
2509
+ if (this.flipMode === 'flip') {
2510
+ // 仿真翻页模式
2511
+ if (this.$refs.flipbook && this.currentPage < this.totalPages) {
2512
+ try {
2513
+ this.$refs.flipbook.flipRight()
2514
+ console.log('向右翻页命令已发送')
2515
+ } catch (error) {
2516
+ console.error('向右翻页失败:', error)
2517
+ }
2518
+ } else {
2519
+ console.log('无法向右翻页 - 已在最后一页或组件未就绪')
2520
+ }
2521
+ } else {
2522
+ // 其他模式直接切换页码
2523
+ if (this.currentPage < this.totalPages) {
2524
+ this.currentPage++
2525
+ this.onFlipRightEnd(this.currentPage)
2526
+ }
2527
+ }
2528
+ },
2529
+ onFlipLeftEnd(page) {
2530
+ console.log('向左翻页完成,新页面:', page)
2531
+ this.currentPage = page
2532
+ // 触发页码变化事件
2533
+ this.$emit('page-change', page)
2534
+ // 记录阅读进度
2535
+ this.recordReadingProgress(page)
2536
+ },
2537
+ onFlipRightEnd(page) {
2538
+ console.log('向右翻页完成,新页面:', page)
2539
+ this.currentPage = page
2540
+ // 触发页码变化事件
2541
+ this.$emit('page-change', page)
2542
+ // 记录阅读进度
2543
+ this.recordReadingProgress(page)
2544
+ },
2545
+
2546
+ /**
2547
+ * 容器触摸开始事件
2548
+ */
2549
+ onContainerTouchStart(e) {
2550
+ this.touchStartTime = Date.now()
2551
+ this.initialTouches = Array.from(e.touches)
2552
+
2553
+ if (e.touches.length === 2) {
2554
+ // 双指缩放
2555
+ this.isZooming = true
2556
+ this.lastTouchDistance = this.getTouchDistance(e.touches[0], e.touches[1])
2557
+ this.lastTouchCenter = this.getTouchCenter(e.touches[0], e.touches[1])
2558
+ e.preventDefault()
2559
+ } else if (e.touches.length === 1 && this.zoomScale > 1) {
2560
+ // 单指拖拽(仅在放大状态下)
2561
+ this.isPanning = true
2562
+ this.lastTouchCenter = { x: e.touches[0].clientX, y: e.touches[0].clientY }
2563
+ }
2564
+ },
2565
+
2566
+ /**
2567
+ * 容器触摸移动事件
2568
+ */
2569
+ onContainerTouchMove(e) {
2570
+ if (this.isZooming && e.touches.length === 2) {
2571
+ // 双指缩放
2572
+ const currentDistance = this.getTouchDistance(e.touches[0], e.touches[1])
2573
+ const currentCenter = this.getTouchCenter(e.touches[0], e.touches[1])
2574
+
2575
+ // 计算缩放比例
2576
+ const scaleChange = currentDistance / this.lastTouchDistance
2577
+ let newScale = this.zoomScale * scaleChange
2578
+
2579
+ // 限制缩放范围
2580
+ newScale = Math.max(0.5, Math.min(3, newScale))
2581
+
2582
+ // 计算缩放中心点的偏移
2583
+ const deltaX = currentCenter.x - this.lastTouchCenter.x
2584
+ const deltaY = currentCenter.y - this.lastTouchCenter.y
2585
+
2586
+ this.zoomScale = newScale
2587
+ this.translateX += deltaX
2588
+ this.translateY += deltaY
2589
+
2590
+ this.lastTouchDistance = currentDistance
2591
+ this.lastTouchCenter = currentCenter
2592
+
2593
+ e.preventDefault()
2594
+ } else if (this.isPanning && e.touches.length === 1 && this.zoomScale > 1) {
2595
+ // 单指拖拽
2596
+ const deltaX = e.touches[0].clientX - this.lastTouchCenter.x
2597
+ const deltaY = e.touches[0].clientY - this.lastTouchCenter.y
2598
+
2599
+ this.translateX += deltaX
2600
+ this.translateY += deltaY
2601
+
2602
+ this.lastTouchCenter = { x: e.touches[0].clientX, y: e.touches[0].clientY }
2603
+
2604
+ e.preventDefault()
2605
+ }
2606
+ },
2607
+
2608
+ /**
2609
+ * 容器触摸结束事件
2610
+ */
2611
+ onContainerTouchEnd(e) {
2612
+ const touchDuration = Date.now() - this.touchStartTime
2613
+ const wasZooming = this.isZooming
2614
+ const wasPanning = this.isPanning
2615
+
2616
+ // 先重置状态
2617
+ this.isZooming = false
2618
+ this.isPanning = false
2619
+
2620
+ // 双击重置缩放 - 只在单指快速点击时检测
2621
+ if (e.changedTouches.length === 1 && touchDuration < 300 && !wasZooming && !wasPanning) {
2622
+ const now = Date.now()
2623
+ if (this.lastTapTime && now - this.lastTapTime < 400) {
2624
+ // 检测到双击
2625
+ this.resetZoom()
2626
+ console.log('移动端双击重置显示比例')
2627
+ this.lastTapTime = 0 // 重置,避免三击触发
2628
+ } else {
2629
+ this.lastTapTime = now
2630
+ }
2631
+ }
2632
+
2633
+ // 边界检查和回弹
2634
+ this.constrainPosition()
2635
+ },
2636
+
2637
+ /**
2638
+ * 获取两点间距离
2639
+ */
2640
+ getTouchDistance(touch1, touch2) {
2641
+ const dx = touch1.clientX - touch2.clientX
2642
+ const dy = touch1.clientY - touch2.clientY
2643
+ return Math.sqrt(dx * dx + dy * dy)
2644
+ },
2645
+
2646
+ /**
2647
+ * 获取两点中心
2648
+ */
2649
+ getTouchCenter(touch1, touch2) {
2650
+ return {
2651
+ x: (touch1.clientX + touch2.clientX) / 2,
2652
+ y: (touch1.clientY + touch2.clientY) / 2
2653
+ }
2654
+ },
2655
+
2656
+ /**
2657
+ * 重置缩放
2658
+ */
2659
+ resetZoom() {
2660
+ this.zoomScale = 1
2661
+ this.translateX = 0
2662
+ this.translateY = 0
2663
+ },
2664
+
2665
+ /**
2666
+ * PC端双击事件处理
2667
+ */
2668
+ onContainerDoubleClick(e) {
2669
+ // 阻止事件冒泡和默认行为,避免触发翻页
2670
+ e.preventDefault()
2671
+ e.stopPropagation()
2672
+
2673
+ // 重置缩放比例
2674
+ this.resetZoom()
2675
+ console.log('容器双击重置显示比例,当前缩放:', this.zoomScale)
2676
+ },
2677
+
2678
+ /**
2679
+ * Flipbook区域双击事件处理
2680
+ */
2681
+ onFlipbookDoubleClick(e) {
2682
+ // 阻止事件冒泡和默认行为,避免触发翻页
2683
+ e.preventDefault()
2684
+ e.stopPropagation()
2685
+
2686
+ // 重置缩放比例
2687
+ this.resetZoom()
2688
+ console.log('Flipbook双击重置显示比例,当前缩放:', this.zoomScale)
2689
+ },
2690
+
2691
+ /**
2692
+ * Flipbook区域触摸开始事件
2693
+ */
2694
+ onFlipbookTouchStart(e) {
2695
+ this.flipbookTouchStartTime = Date.now()
2696
+ },
2697
+
2698
+ /**
2699
+ * Flipbook区域触摸结束事件(移动端双击检测)
2700
+ */
2701
+ onFlipbookTouchEnd(e) {
2702
+ const touchDuration = Date.now() - this.flipbookTouchStartTime
2703
+
2704
+ // 双击重置缩放 - 只在单指快速点击时检测
2705
+ if (e.changedTouches.length === 1 && touchDuration < 300) {
2706
+ const now = Date.now()
2707
+ if (this.flipbookLastTapTime && now - this.flipbookLastTapTime < 400) {
2708
+ // 检测到双击
2709
+ e.preventDefault()
2710
+ e.stopPropagation()
2711
+ this.resetZoom()
2712
+ console.log('Flipbook区域移动端双击重置显示比例')
2713
+ this.flipbookLastTapTime = 0 // 重置,避免三击触发
2714
+ } else {
2715
+ this.flipbookLastTapTime = now
2716
+ }
2717
+ }
2718
+ },
2719
+
2720
+ /**
2721
+ * 约束位置在边界内
2722
+ */
2723
+ constrainPosition() {
2724
+ if (this.zoomScale <= 1) {
2725
+ this.translateX = 0
2726
+ this.translateY = 0
2727
+ return
2728
+ }
2729
+
2730
+ const maxTranslate = (this.zoomScale - 1) * 200
2731
+ this.translateX = Math.max(-maxTranslate, Math.min(maxTranslate, this.translateX))
2732
+ this.translateY = Math.max(-maxTranslate, Math.min(maxTranslate, this.translateY))
2733
+ },
2734
+
2735
+
2736
+ /**
2737
+ * 打开目录抽屉
2738
+ */
2739
+ openCatalogue() {
2740
+ console.log('点击目录按钮', {
2741
+ dragging: this.catalogueButtonDragging,
2742
+ showDrawer: this.showCatalogueDrawer
2743
+ })
2744
+
2745
+ // 只有在不是拖拽状态下才打开目录
2746
+ if (!this.catalogueButtonDragging) {
2747
+ this.showCatalogueDrawer = true
2748
+ console.log('目录抽屉已打开')
2749
+ } else {
2750
+ console.log('拖拽状态中,不打开目录')
2751
+ }
2752
+ },
2753
+
2754
+ /**
2755
+ * 目录按钮拖拽开始
2756
+ */
2757
+ onCatalogueMouseDown(e) {
2758
+ // 不立即设置为拖拽状态,等待移动再判断
2759
+ this.catalogueButtonDragging = false
2760
+
2761
+ const rect = e.target.closest('.catalogue-button').getBoundingClientRect()
2762
+ this.dragStartPosition = {
2763
+ x: e.clientX,
2764
+ y: e.clientY
2765
+ }
2766
+ this.dragOffset = {
2767
+ x: e.clientX - rect.left,
2768
+ y: e.clientY - rect.top
2769
+ }
2770
+
2771
+ document.addEventListener('mousemove', this.onCatalogueMouseMove)
2772
+ document.addEventListener('mouseup', this.onCatalogueMouseUp)
2773
+ },
2774
+
2775
+ /**
2776
+ * 目录按钮拖拽移动
2777
+ */
2778
+ onCatalogueMouseMove(e) {
2779
+ // 检查是否开始拖拽
2780
+ const dragDistance = Math.sqrt(
2781
+ Math.pow(e.clientX - this.dragStartPosition.x, 2) +
2782
+ Math.pow(e.clientY - this.dragStartPosition.y, 2)
2783
+ )
2784
+
2785
+ // 只有移动距离超过5px才认为是拖拽
2786
+ if (dragDistance > 5) {
2787
+ this.catalogueButtonDragging = true
2788
+ }
2789
+
2790
+ if (!this.catalogueButtonDragging) return
2791
+
2792
+ e.preventDefault()
2793
+
2794
+ const newX = e.clientX - this.dragOffset.x
2795
+ const newY = e.clientY - this.dragOffset.y
2796
+
2797
+ // 限制在视窗范围内
2798
+ const maxX = window.innerWidth - 120 // 按钮宽度约120px
2799
+ const maxY = window.innerHeight - 50 // 按钮高度约50px
2800
+
2801
+ this.catalogueButtonPosition = {
2802
+ x: Math.max(0, Math.min(maxX, newX)),
2803
+ y: Math.max(0, Math.min(maxY, newY))
2804
+ }
2805
+ },
2806
+
2807
+ /**
2808
+ * 目录按钮拖拽结束
2809
+ */
2810
+ onCatalogueMouseUp(e) {
2811
+ // 立即重置拖拽状态
2812
+ this.catalogueButtonDragging = false
2813
+
2814
+ document.removeEventListener('mousemove', this.onCatalogueMouseMove)
2815
+ document.removeEventListener('mouseup', this.onCatalogueMouseUp)
2816
+ },
2817
+
2818
+ /**
2819
+ * 目录按钮触摸开始(移动端)
2820
+ */
2821
+ onCatalogueTouchStart(e) {
2822
+ // 不立即设置为拖拽状态
2823
+ this.catalogueButtonDragging = false
2824
+
2825
+ const touch = e.touches[0]
2826
+ const rect = e.target.closest('.catalogue-button').getBoundingClientRect()
2827
+
2828
+ this.dragStartPosition = {
2829
+ x: touch.clientX,
2830
+ y: touch.clientY
2831
+ }
2832
+ this.dragOffset = {
2833
+ x: touch.clientX - rect.left,
2834
+ y: touch.clientY - rect.top
2835
+ }
2836
+ },
2837
+
2838
+ /**
2839
+ * 目录按钮触摸移动(移动端)
2840
+ */
2841
+ onCatalogueTouchMove(e) {
2842
+ const touch = e.touches[0]
2843
+
2844
+ // 检查是否开始拖拽
2845
+ const dragDistance = Math.sqrt(
2846
+ Math.pow(touch.clientX - this.dragStartPosition.x, 2) +
2847
+ Math.pow(touch.clientY - this.dragStartPosition.y, 2)
2848
+ )
2849
+
2850
+ // 只有移动距离超过5px才认为是拖拽
2851
+ if (dragDistance > 5) {
2852
+ this.catalogueButtonDragging = true
2853
+ }
2854
+
2855
+ if (!this.catalogueButtonDragging) return
2856
+
2857
+ e.preventDefault()
2858
+
2859
+ const newX = touch.clientX - this.dragOffset.x
2860
+ const newY = touch.clientY - this.dragOffset.y
2861
+
2862
+ // 限制在视窗范围内,考虑安全区域
2863
+ const maxX = window.innerWidth - 120
2864
+ const maxY = window.innerHeight - 80
2865
+
2866
+ this.catalogueButtonPosition = {
2867
+ x: Math.max(15, Math.min(maxX, newX)),
2868
+ y: Math.max(15, Math.min(maxY, newY))
2869
+ }
2870
+ },
2871
+
2872
+ /**
2873
+ * 目录按钮触摸结束(移动端)
2874
+ */
2875
+ onCatalogueTouchEnd(e) {
2876
+ // 立即重置拖拽状态
2877
+ this.catalogueButtonDragging = false
2878
+ },
2879
+
2880
+ /**
2881
+ * 切换翻页模式菜单显示
2882
+ */
2883
+ toggleFlipModeMenu() {
2884
+ this.showFlipModeMenu = !this.showFlipModeMenu
2885
+ },
2886
+
2887
+ /**
2888
+ * 选择翻页模式
2889
+ * @param {string} mode - 翻页模式
2890
+ */
2891
+ selectFlipMode(mode) {
2892
+ const oldMode = this.flipMode
2893
+ this.flipMode = mode
2894
+ this.showFlipModeMenu = false
2895
+
2896
+ // 保存翻页模式到本地存储
2897
+ localStorage.setItem('book-viewer-flip-mode', mode)
2898
+
2899
+ console.log('切换翻页模式:', { from: oldMode, to: mode })
2900
+
2901
+ // 如果切换到滚动模式,需要滚动到当前页
2902
+ if (mode === 'scroll') {
2903
+ this.$nextTick(() => {
2904
+ this.scrollToCurrentPage()
2905
+ })
2906
+ }
2907
+ },
2908
+
2909
+ /**
2910
+ * 滑动模式触摸开始
2911
+ */
2912
+ onSlideViewerTouchStart(e) {
2913
+ if (e.touches.length === 1) {
2914
+ this.slideStartX = e.touches[0].clientX
2915
+ this.slideCurrentX = e.touches[0].clientX
2916
+ this.slideIsDragging = true
2917
+ }
2918
+ },
2919
+
2920
+ /**
2921
+ * 滑动模式触摸移动
2922
+ */
2923
+ onSlideViewerTouchMove(e) {
2924
+ if (this.slideIsDragging && e.touches.length === 1) {
2925
+ this.slideCurrentX = e.touches[0].clientX
2926
+ e.preventDefault()
2927
+ }
2928
+ },
2929
+
2930
+ /**
2931
+ * 滑动模式触摸结束
2932
+ */
2933
+ onSlideViewerTouchEnd(e) {
2934
+ if (this.slideIsDragging) {
2935
+ const deltaX = this.slideCurrentX - this.slideStartX
2936
+ const threshold = 50 // 滑动阈值
2937
+
2938
+ if (deltaX > threshold && this.currentPage > 1) {
2939
+ // 向右滑动,上一页
2940
+ this.currentPage--
2941
+ this.recordReadingProgress(this.currentPage)
2942
+ } else if (deltaX < -threshold && this.currentPage < this.totalPages) {
2943
+ // 向左滑动,下一页
2944
+ this.currentPage++
2945
+ this.recordReadingProgress(this.currentPage)
2946
+ }
2947
+
2948
+ this.slideIsDragging = false
2949
+ }
2950
+ },
2951
+
2952
+ /**
2953
+ * 滚动模式滚动事件
2954
+ */
2955
+ onScrollViewerScroll(e) {
2956
+ const container = this.$refs.scrollViewer
2957
+ if (!container) return
2958
+
2959
+ const scrollTop = container.scrollTop
2960
+ const containerHeight = container.clientHeight
2961
+
2962
+ // 计算当前页码
2963
+ for (let i = 0; i < this.pages.length; i++) {
2964
+ const pageRef = this.$refs['scrollPage' + i]
2965
+ if (pageRef && pageRef[0]) {
2966
+ const pageTop = pageRef[0].offsetTop
2967
+ const pageBottom = pageTop + pageRef[0].offsetHeight
2968
+
2969
+ if (scrollTop >= pageTop - containerHeight / 2 && scrollTop < pageBottom - containerHeight / 2) {
2970
+ if (this.currentPage !== i + 1) {
2971
+ this.currentPage = i + 1
2972
+ this.recordReadingProgress(this.currentPage)
2973
+ }
2974
+ break
2975
+ }
2976
+ }
2977
+ }
2978
+ },
2979
+
2980
+ /**
2981
+ * 滚动到当前页
2982
+ */
2983
+ scrollToCurrentPage() {
2984
+ if (this.flipMode !== 'scroll') return
2985
+
2986
+ const pageRef = this.$refs['scrollPage' + (this.currentPage - 1)]
2987
+ if (pageRef && pageRef[0]) {
2988
+ pageRef[0].scrollIntoView({ behavior: 'smooth', block: 'start' })
2989
+ }
2990
+ },
2991
+
2992
+ /**
2993
+ * 关闭翻页模式菜单(点击外部)
2994
+ */
2995
+ closeFlipModeMenu(e) {
2996
+ if (!e.target.closest('.flip-mode-selector')) {
2997
+ this.showFlipModeMenu = false
2998
+ }
2999
+ },
3000
+
3001
+ /**
3002
+ * 获取卡片样式
3003
+ * @param {string} position - 卡片位置:prev, current, next
3004
+ */
3005
+ getCardStyle(position) {
3006
+ switch (position) {
3007
+ case 'prev':
3008
+ return {
3009
+ transform: 'translateX(-70%) scale(0.85) rotateY(15deg)',
3010
+ zIndex: 1,
3011
+ opacity: 0.6,
3012
+ filter: 'brightness(0.8)'
3013
+ }
3014
+ case 'current':
3015
+ return {
3016
+ transform: 'translateX(0) scale(1) rotateY(0deg)',
3017
+ zIndex: 3,
3018
+ opacity: 1,
3019
+ filter: 'brightness(1)'
3020
+ }
3021
+ case 'next':
3022
+ return {
3023
+ transform: 'translateX(70%) scale(0.85) rotateY(-15deg)',
3024
+ zIndex: 1,
3025
+ opacity: 0.6,
3026
+ filter: 'brightness(0.8)'
3027
+ }
3028
+ default:
3029
+ return {}
3030
+ }
3031
+ },
3032
+
3033
+ /**
3034
+ * 目录项点击事件
3035
+ * @param {Object} item - 点击的目录项
3036
+ */
3037
+ onCatalogueClick(item) {
3038
+ console.log('目录项被点击:', item)
3039
+ this.$emit('catalogue-click', item)
3040
+ },
3041
+
3042
+ /**
3043
+ * 获取目录数据事件
3044
+ * @param {String} bookId - 书籍ID
3045
+ */
3046
+ onFetchCatalogue(bookId) {
3047
+ this.$emit('fetch-catalogue', bookId)
3048
+ },
3049
+
3050
+ /**
3051
+ * 页面跳转事件
3052
+ * @param {number} pageNumber - 目标页码
3053
+ */
3054
+ onFocus() {
3055
+ // 使用flipbook的方法跳转到指定页面
3056
+ if (this.flipMode === 'flip') {
3057
+ this.flag = false
3058
+ setTimeout(() => {
3059
+ this.flag = true
3060
+ }, 500)
3061
+ } else if (this.flipMode === 'scroll') {
3062
+ this.scrollToCurrentPage()
3063
+ }
3064
+ },
3065
+ onPageJump(pageNumber) {
3066
+ console.log('跳转到页面:', pageNumber)
3067
+
3068
+ // 关闭目录抽屉
3069
+ this.showCatalogueDrawer = false
3070
+
3071
+ // 跳转到指定页面
3072
+ if (pageNumber && pageNumber >= 1 && pageNumber <= this.totalPages) {
3073
+ this.currentPage = pageNumber
3074
+ this.startPage = pageNumber
3075
+
3076
+ // 记录阅读进度
3077
+ this.recordReadingProgress(pageNumber)
3078
+
3079
+ // 使用flipbook的方法跳转到指定页面
3080
+ if (this.flipMode === 'flip') {
3081
+ this.flag = false
3082
+ setTimeout(() => {
3083
+ this.flag = true
3084
+ this.$nextTick(() => {
3085
+ this.$refs.flipbook.goToPage(pageNumber)
3086
+ })
3087
+ }, 500)
3088
+ } else if (this.flipMode === 'scroll') {
3089
+ this.scrollToCurrentPage()
3090
+ }
3091
+ } else {
3092
+ console.warn('无效的页码:', pageNumber)
3093
+ }
3094
+ },
3095
+
3096
+ /**
3097
+ * 检查URL参数并跳转到指定页码
3098
+ */
3099
+ checkUrlPageParameter() {
3100
+ // 从URL查询参数中获取页码
3101
+ const pageParam = this.$route.query.page
3102
+
3103
+ if (pageParam) {
3104
+ const targetPage = parseInt(pageParam, 10)
3105
+
3106
+ console.log('检测到URL页码参数:', {
3107
+ pageParam,
3108
+ targetPage,
3109
+ totalPages: this.totalPages
3110
+ })
3111
+
3112
+ // 验证页码是否有效
3113
+ if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= this.totalPages) {
3114
+ console.log('准备跳转到页码:', targetPage)
3115
+
3116
+ // 使用nextTick确保DOM已更新
3117
+ this.$nextTick(() => {
3118
+ this.jumpToPage(targetPage)
3119
+ })
3120
+ } else {
3121
+ console.warn('无效的页码参数:', {
3122
+ pageParam,
3123
+ targetPage,
3124
+ totalPages: this.totalPages,
3125
+ isValid: !isNaN(targetPage) && targetPage >= 1 && targetPage <= this.totalPages
3126
+ })
3127
+ }
3128
+ } else {
3129
+ console.log('URL中未检测到页码参数')
3130
+ }
3131
+ },
3132
+
3133
+ /**
3134
+ * 跳转到指定页码
3135
+ * @param {number} pageNumber - 目标页码
3136
+ */
3137
+ jumpToPage(pageNumber) {
3138
+ if (!pageNumber || pageNumber < 1 || pageNumber > this.totalPages) {
3139
+ console.warn('无效的页码:', pageNumber)
3140
+ return
3141
+ }
3142
+
3143
+ console.log('跳转到页面:', pageNumber)
3144
+
3145
+ // 更新当前页码和起始页码
3146
+ this.currentPage = pageNumber
3147
+ this.startPage = pageNumber
3148
+
3149
+ // 记录阅读进度
3150
+ this.recordReadingProgress(pageNumber)
3151
+
3152
+ // 使用flipbook的方法跳转到指定页面
3153
+ if (this.flipMode === 'flip') {
3154
+ if (this.$refs.flipbook && this.$refs.flipbook.goToPage) {
3155
+ try {
3156
+ this.$refs.flipbook.goToPage(pageNumber)
3157
+ console.log('页面跳转成功:', pageNumber)
3158
+ } catch (error) {
3159
+ console.error('页面跳转失败:', error)
3160
+ }
3161
+ } else {
3162
+ console.warn('Flipbook组件未就绪,无法跳转页面')
3163
+ }
3164
+ } else if (this.flipMode === 'scroll') {
3165
+ this.scrollToCurrentPage()
3166
+ }
3167
+ // slide 和 fade 模式直接通过 currentPage 变化来切换
3168
+ },
3169
+
3170
+ /**
3171
+ * 记录阅读进度
3172
+ * @param {number} pageNumber - 当前页码
3173
+ */
3174
+ async recordReadingProgress(pageNumber) {
3175
+ if (!this.bookId || !pageNumber) {
3176
+ console.warn('缺少书籍ID或页码,无法记录阅读进度')
3177
+ return
3178
+ }
3179
+
3180
+ try {
3181
+ console.log('记录阅读进度:', {
3182
+ bookId: this.bookId,
3183
+ pageNumber: pageNumber
3184
+ })
3185
+
3186
+ // 注释:原来调用API记录进度,现在改为触发事件让父组件处理
3187
+ // await booksApi.recordReadProgress(this.bookId, pageNumber)
3188
+ this.$emit('record-progress', { bookId: this.bookId, pageNumber })
3189
+
3190
+ console.log('阅读进度记录成功')
3191
+
3192
+ } catch (error) {
3193
+ console.error('记录阅读进度失败:', error)
3194
+ // 阅读记录失败不影响用户体验,只记录日志
3195
+ }
3196
+ },
3197
+
3198
+ /**
3199
+ * 显示控件
3200
+ */
3201
+ showControlsTemporarily() {
3202
+ this.showControls = true
3203
+
3204
+ // 清除之前的定时器
3205
+ if (this.hideControlsTimer) {
3206
+ clearTimeout(this.hideControlsTimer)
3207
+ }
3208
+
3209
+ // 3秒后自动隐藏
3210
+ this.hideControlsTimer = setTimeout(() => {
3211
+ this.showControls = false
3212
+ }, 3000)
3213
+ },
3214
+
3215
+ /**
3216
+ * 内容区域点击事件
3217
+ */
3218
+ onContentClick() {
3219
+ this.showControlsTemporarily()
3220
+ },
3221
+
3222
+ /**
3223
+ * 处理页码输入
3224
+ */
3225
+ handlePageInput(e) {
3226
+ const value = e.target.value
3227
+ // 只允许输入数字
3228
+ const numValue = value.replace(/[^\d]/g, '')
3229
+ // 限制不超过总页数
3230
+ if (numValue && parseInt(numValue) > this.totalPages) {
3231
+ this.jumpPageInput = this.totalPages.toString()
3232
+ } else {
3233
+ this.jumpPageInput = numValue
3234
+ }
3235
+ },
3236
+
3237
+ /**
3238
+ * 执行页码跳转
3239
+ */
3240
+ handleJumpToPage() {
3241
+ const pageNum = parseInt(this.jumpPageInput)
3242
+ if (pageNum && pageNum >= 1 && pageNum <= this.totalPages) {
3243
+ // 重置缩放和位移,避免内容区域缩小
3244
+ this.resetZoom()
3245
+ this.jumpToPage(pageNum)
3246
+ this.jumpPageInput = ''
3247
+ }
3248
+ }
3249
+ },
3250
+ watch: {
3251
+ async pages(newPages) {
3252
+ if (newPages && newPages.length > 0 && this.isMobile) {
3253
+ this.contentReady = false
3254
+ await this.$nextTick()
3255
+ await this.preloadFirstPage()
3256
+ } else if (newPages && newPages.length > 0) {
3257
+ this.contentReady = true
3258
+ }
3259
+ }
3260
+ },
3261
+ async mounted() {
3262
+ console.log('组件已挂载,总页数:', this.totalPages)
3263
+
3264
+ // 检测设备类型
3265
+ this.detectMobile()
3266
+
3267
+ // 检查URL参数中的翻页模式
3268
+ const urlFlipMode = this.$route.query.mode || this.$route.query.flipMode
3269
+ if (urlFlipMode && this.flipModes.some(m => m.value === urlFlipMode)) {
3270
+ // URL参数指定的翻页模式优先级最高,并隐藏切换按钮
3271
+ this.flipMode = urlFlipMode
3272
+ this.flipModeFromUrl = true
3273
+ console.log('从URL参数读取翻页模式:', urlFlipMode)
3274
+ } else {
3275
+ // 否则从本地存储恢复
3276
+ const savedFlipMode = localStorage.getItem('book-viewer-flip-mode')
3277
+ if (savedFlipMode && this.flipModes.some(m => m.value === savedFlipMode)) {
3278
+ this.flipMode = savedFlipMode
3279
+ }
3280
+ }
3281
+
3282
+ // 预加载首页图片(移动端优化)
3283
+ if (this.isMobile && this.pages && this.pages.length > 0) {
3284
+ await this.$nextTick()
3285
+ await this.preloadFirstPage()
3286
+ } else if (this.pages && this.pages.length > 0) {
3287
+ this.contentReady = true
3288
+ }
3289
+
3290
+ // 监听窗口尺寸变化
3291
+ window.addEventListener('resize', this.handleResize)
3292
+
3293
+ // 监听点击事件,用于关闭翻页模式菜单
3294
+ document.addEventListener('click', this.closeFlipModeMenu)
3295
+ },
3296
+
3297
+ beforeDestroy() {
3298
+ // 清理事件监听
3299
+ window.removeEventListener('resize', this.handleResize)
3300
+ // 清理拖拽事件监听
3301
+ document.removeEventListener('mousemove', this.onCatalogueMouseMove)
3302
+ document.removeEventListener('mouseup', this.onCatalogueMouseUp)
3303
+ // 清理翻页模式菜单事件监听
3304
+ document.removeEventListener('click', this.closeFlipModeMenu)
3305
+ // 清理控件自动隐藏定时器
3306
+ if (this.hideControlsTimer) {
3307
+ clearTimeout(this.hideControlsTimer)
3308
+ }
3309
+ }
3310
+ });
3311
+
3312
+ ;// ./src/components/BookReader.vue?vue&type=script&lang=js
3313
+ /* harmony default export */ var components_BookReadervue_type_script_lang_js = (BookReadervue_type_script_lang_js);
3314
+ ;// ./node_modules/mini-css-extract-plugin/dist/loader.js??clonedRuleSet-12.use[0]!./node_modules/css-loader/dist/cjs.js??clonedRuleSet-12.use[1]!./node_modules/@vue/vue-loader-v15/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/dist/cjs.js??clonedRuleSet-12.use[2]!./node_modules/@vue/vue-loader-v15/lib/index.js??vue-loader-options!./src/components/BookReader.vue?vue&type=style&index=0&id=eaca7004&prod&scoped=true&lang=css
3315
+ // extracted by mini-css-extract-plugin
3316
+
3317
+ ;// ./src/components/BookReader.vue?vue&type=style&index=0&id=eaca7004&prod&scoped=true&lang=css
3318
+
3319
+ ;// ./src/components/BookReader.vue
3320
+
3321
+
3322
+
3323
+ ;
3324
+
3325
+
3326
+ /* normalize component */
3327
+
3328
+ var BookReader_component = componentNormalizer_normalizeComponent(
3329
+ components_BookReadervue_type_script_lang_js,
3330
+ render,
3331
+ staticRenderFns,
3332
+ false,
3333
+ null,
3334
+ "eaca7004",
3335
+ null
3336
+
3337
+ )
3338
+
3339
+ /* harmony default export */ var BookReader = (BookReader_component.exports);
3340
+ ;// ./src/index.js
3341
+
3342
+
3343
+
3344
+ const components = {
3345
+ BookReader: BookReader,
3346
+ BookCatalogueDrawer: BookCatalogueDrawer
3347
+ }
3348
+
3349
+ const install = function(Vue) {
3350
+ if (install.installed) return
3351
+ install.installed = true
3352
+ Object.keys(components).forEach(key => {
3353
+ Vue.component(key, components[key])
3354
+ })
3355
+ }
3356
+
3357
+ if (typeof window !== 'undefined' && window.Vue) {
3358
+ install(window.Vue)
3359
+ }
3360
+
3361
+ /* harmony default export */ var src_0 = ({
3362
+ install,
3363
+ BookReader: BookReader,
3364
+ BookCatalogueDrawer: BookCatalogueDrawer
3365
+ });
3366
+
3367
+
3368
+
3369
+ ;// ./node_modules/@vue/cli-service/lib/commands/build/entry-lib.js
3370
+
3371
+
3372
+ /* harmony default export */ var entry_lib = (src_0);
3373
+
3374
+
3375
+ module.exports = __webpack_exports__;
3376
+ /******/ })()
3377
+ ;
3378
+ //# sourceMappingURL=vue-book-reader.common.js.map