pdfh5.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212
  1. (function() {
  2. 'use strict';
  3. var definePinchZoom = function($) {
  4. var PinchZoom = function(el, options, viewerContainer) {
  5. this.el = $(el);
  6. this.viewerContainer = viewerContainer;
  7. this.zoomFactor = 1;
  8. this.lastScale = 1;
  9. this.offset = {
  10. x: 0,
  11. y: 0
  12. };
  13. this.options = $.extend({}, this.defaults, options);
  14. this.setupMarkup();
  15. this.bindEvents();
  16. this.update();
  17. // default enable.
  18. this.enable();
  19. this.height = 0;
  20. this.load = false;
  21. this.direction = null;
  22. this.clientY = null;
  23. this.lastclientY = null;
  24. },
  25. sum = function(a, b) {
  26. return a + b;
  27. },
  28. isCloseTo = function(value, expected) {
  29. return value > expected - 0.01 && value < expected + 0.01;
  30. };
  31. PinchZoom.prototype = {
  32. defaults: {
  33. tapZoomFactor: 2,
  34. zoomOutFactor: 1.2,
  35. animationDuration: 300,
  36. maxZoom: 4,
  37. minZoom: 0.8,
  38. lockDragAxis: false,
  39. use2d: true,
  40. zoomStartEventName: 'pz_zoomstart',
  41. zoomEndEventName: 'pz_zoomend',
  42. dragStartEventName: 'pz_dragstart',
  43. dragEndEventName: 'pz_dragend',
  44. doubleTapEventName: 'pz_doubletap'
  45. },
  46. /**
  47. * Event handler for 'dragstart'
  48. * @param event
  49. */
  50. handleDragStart: function(event) {
  51. this.el.trigger(this.options.dragStartEventName);
  52. this.stopAnimation();
  53. this.lastDragPosition = false;
  54. this.hasInteraction = true;
  55. this.handleDrag(event);
  56. },
  57. /**
  58. * Event handler for 'drag'
  59. * @param event
  60. */
  61. handleDrag: function(event) {
  62. if (this.zoomFactor > 1.0) {
  63. var touch = this.getTouches(event)[0];
  64. this.drag(touch, this.lastDragPosition, event);
  65. this.offset = this.sanitizeOffset(this.offset);
  66. this.lastDragPosition = touch;
  67. }
  68. },
  69. handleDragEnd: function() {
  70. this.el.trigger(this.options.dragEndEventName);
  71. this.end();
  72. },
  73. /**
  74. * Event handler for 'zoomstart'
  75. * @param event
  76. */
  77. handleZoomStart: function(event) {
  78. this.el.trigger(this.options.zoomStartEventName);
  79. this.stopAnimation();
  80. this.lastScale = 1;
  81. this.nthZoom = 0;
  82. this.lastZoomCenter = false;
  83. this.hasInteraction = true;
  84. },
  85. /**
  86. * Event handler for 'zoom'
  87. * @param event
  88. */
  89. handleZoom: function(event, newScale) {
  90. // a relative scale factor is used
  91. var touchCenter = this.getTouchCenter(this.getTouches(event)),
  92. scale = newScale / this.lastScale;
  93. this.lastScale = newScale;
  94. // the first touch events are thrown away since they are not precise
  95. this.nthZoom += 1;
  96. if (this.nthZoom > 3) {
  97. this.scale(scale, touchCenter);
  98. this.drag(touchCenter, this.lastZoomCenter);
  99. }
  100. this.lastZoomCenter = touchCenter;
  101. },
  102. handleZoomEnd: function() {
  103. this.el.trigger(this.options.zoomEndEventName);
  104. this.end();
  105. },
  106. /**
  107. * Event handler for 'doubletap'
  108. * @param event
  109. */
  110. handleDoubleTap: function(event) {
  111. var center = this.getTouches(event)[0],
  112. zoomFactor = this.zoomFactor > 1 ? 1 : this.options.tapZoomFactor,
  113. startZoomFactor = this.zoomFactor,
  114. updateProgress = (function(progress) {
  115. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
  116. }).bind(this);
  117. if (this.hasInteraction) {
  118. return;
  119. }
  120. if (startZoomFactor > zoomFactor) {
  121. center = this.getCurrentZoomCenter();
  122. }
  123. this.animate(this.options.animationDuration, updateProgress, this.swing);
  124. this.el.trigger(this.options.doubleTapEventName);
  125. },
  126. /**
  127. * Max / min values for the offset
  128. * @param offset
  129. * @return {Object} the sanitized offset
  130. */
  131. sanitizeOffset: function(offset) {
  132. var maxX = (this.zoomFactor - 1) * this.getContainerX(),
  133. maxY = (this.zoomFactor - 1) * this.getContainerY(),
  134. maxOffsetX = Math.max(maxX, 0),
  135. maxOffsetY = Math.max(maxY, 0),
  136. minOffsetX = Math.min(maxX, 0),
  137. minOffsetY = Math.min(maxY, 0);
  138. var x = Math.min(Math.max(offset.x, minOffsetX), maxOffsetX),
  139. y = Math.min(Math.max(offset.y, minOffsetY), maxOffsetY);
  140. return {
  141. x: x,
  142. y: y
  143. };
  144. },
  145. /**
  146. * Scale to a specific zoom factor (not relative)
  147. * @param zoomFactor
  148. * @param center
  149. */
  150. scaleTo: function(zoomFactor, center) {
  151. this.scale(zoomFactor / this.zoomFactor, center);
  152. },
  153. /**
  154. * Scales the element from specified center
  155. * @param scale
  156. * @param center
  157. */
  158. scale: function(scale, center) {
  159. scale = this.scaleZoomFactor(scale);
  160. this.addOffset({
  161. x: (scale - 1) * (center.x + this.offset.x),
  162. y: (scale - 1) * (center.y + this.offset.y)
  163. });
  164. },
  165. /**
  166. * Scales the zoom factor relative to current state
  167. * @param scale
  168. * @return the actual scale (can differ because of max min zoom factor)
  169. */
  170. scaleZoomFactor: function(scale) {
  171. var originalZoomFactor = this.zoomFactor;
  172. this.zoomFactor *= scale;
  173. this.zoomFactor = Math.min(this.options.maxZoom, Math.max(this.zoomFactor, this.options.minZoom));
  174. return this.zoomFactor / originalZoomFactor;
  175. },
  176. /**
  177. * Drags the element
  178. * @param center
  179. * @param lastCenter
  180. */
  181. drag: function(center, lastCenter, event) {
  182. if (lastCenter) {
  183. if (this.options.lockDragAxis) {
  184. // lock scroll to position that was changed the most
  185. if (Math.abs(center.x - lastCenter.x) > Math.abs(center.y - lastCenter.y)) {
  186. this.addOffset({
  187. x: -(center.x - lastCenter.x),
  188. y: 0
  189. });
  190. } else {
  191. this.addOffset({
  192. y: -(center.y - lastCenter.y),
  193. x: 0
  194. });
  195. }
  196. } else {
  197. if (center.y - lastCenter.y < 0) {
  198. this.direction = "down";
  199. } else if (center.y - lastCenter.y > 10) {
  200. this.direction = "up";
  201. }
  202. this.addOffset({
  203. y: -(center.y - lastCenter.y),
  204. x: -(center.x - lastCenter.x)
  205. });
  206. }
  207. }
  208. },
  209. /**
  210. * Calculates the touch center of multiple touches
  211. * @param touches
  212. * @return {Object}
  213. */
  214. getTouchCenter: function(touches) {
  215. return this.getVectorAvg(touches);
  216. },
  217. /**
  218. * Calculates the average of multiple vectors (x, y values)
  219. */
  220. getVectorAvg: function(vectors) {
  221. return {
  222. x: vectors.map(function(v) {
  223. return v.x;
  224. }).reduce(sum) / vectors.length,
  225. y: vectors.map(function(v) {
  226. return v.y;
  227. }).reduce(sum) / vectors.length
  228. };
  229. },
  230. /**
  231. * Adds an offset
  232. * @param offset the offset to add
  233. * @return return true when the offset change was accepted
  234. */
  235. addOffset: function(offset) {
  236. this.offset = {
  237. x: this.offset.x + offset.x,
  238. y: this.offset.y + offset.y
  239. };
  240. },
  241. sanitize: function() {
  242. if (this.zoomFactor < this.options.zoomOutFactor) {
  243. this.zoomOutAnimation();
  244. } else if (this.isInsaneOffset(this.offset)) {
  245. this.sanitizeOffsetAnimation();
  246. }
  247. },
  248. /**
  249. * Checks if the offset is ok with the current zoom factor
  250. * @param offset
  251. * @return {Boolean}
  252. */
  253. isInsaneOffset: function(offset) {
  254. var sanitizedOffset = this.sanitizeOffset(offset);
  255. return sanitizedOffset.x !== offset.x ||
  256. sanitizedOffset.y !== offset.y;
  257. },
  258. /**
  259. * Creates an animation moving to a sane offset
  260. */
  261. sanitizeOffsetAnimation: function() {
  262. var targetOffset = this.sanitizeOffset(this.offset),
  263. startOffset = {
  264. x: this.offset.x,
  265. y: this.offset.y
  266. },
  267. updateProgress = (function(progress) {
  268. this.offset.x = startOffset.x + progress * (targetOffset.x - startOffset.x);
  269. this.offset.y = startOffset.y + progress * (targetOffset.y - startOffset.y);
  270. this.update();
  271. }).bind(this);
  272. this.animate(
  273. this.options.animationDuration,
  274. updateProgress,
  275. this.swing
  276. );
  277. },
  278. /**
  279. * Zooms back to the original position,
  280. * (no offset and zoom factor 1)
  281. */
  282. zoomOutAnimation: function() {
  283. var startZoomFactor = this.zoomFactor,
  284. zoomFactor = 1,
  285. center = this.getCurrentZoomCenter(),
  286. updateProgress = (function(progress) {
  287. this.scaleTo(startZoomFactor + progress * (zoomFactor - startZoomFactor), center);
  288. }).bind(this);
  289. this.animate(
  290. this.options.animationDuration,
  291. updateProgress,
  292. this.swing
  293. );
  294. },
  295. /**
  296. * Updates the aspect ratio
  297. */
  298. updateAspectRatio: function() {
  299. this.setContainerY(this.getContainerX() / this.getAspectRatio());
  300. },
  301. /**
  302. * Calculates the initial zoom factor (for the element to fit into the container)
  303. * @return the initial zoom factor
  304. */
  305. getInitialZoomFactor: function() {
  306. // use .offsetWidth instead of width()
  307. // because jQuery-width() return the original width but Zepto-width() will calculate width with transform.
  308. // the same as .height()
  309. if (this.container[0] && this.el[0]) {
  310. return this.container[0].offsetWidth / this.el[0].offsetWidth;
  311. } else {
  312. return 0
  313. }
  314. },
  315. /**
  316. * Calculates the aspect ratio of the element
  317. * @return the aspect ratio
  318. */
  319. getAspectRatio: function() {
  320. if (this.el[0]) {
  321. var offsetHeight = this.el[0].offsetHeight;
  322. return this.container[0].offsetWidth / offsetHeight;
  323. } else {
  324. return 0
  325. }
  326. },
  327. /**
  328. * Calculates the virtual zoom center for the current offset and zoom factor
  329. * (used for reverse zoom)
  330. * @return {Object} the current zoom center
  331. */
  332. getCurrentZoomCenter: function() {
  333. // uses following formula to calculate the zoom center x value
  334. // offset_left / offset_right = zoomcenter_x / (container_x - zoomcenter_x)
  335. var length = this.container[0].offsetWidth * this.zoomFactor,
  336. offsetLeft = this.offset.x,
  337. offsetRight = length - offsetLeft - this.container[0].offsetWidth,
  338. widthOffsetRatio = offsetLeft / offsetRight,
  339. centerX = widthOffsetRatio * this.container[0].offsetWidth / (widthOffsetRatio + 1),
  340. // the same for the zoomcenter y
  341. height = this.container[0].offsetHeight * this.zoomFactor,
  342. offsetTop = this.offset.y,
  343. offsetBottom = height - offsetTop - this.container[0].offsetHeight,
  344. heightOffsetRatio = offsetTop / offsetBottom,
  345. centerY = heightOffsetRatio * this.container[0].offsetHeight / (heightOffsetRatio + 1);
  346. // prevents division by zero
  347. if (offsetRight === 0) {
  348. centerX = this.container[0].offsetWidth;
  349. }
  350. if (offsetBottom === 0) {
  351. centerY = this.container[0].offsetHeight;
  352. }
  353. return {
  354. x: centerX,
  355. y: centerY
  356. };
  357. },
  358. canDrag: function() {
  359. return !isCloseTo(this.zoomFactor, 1);
  360. },
  361. /**
  362. * Returns the touches of an event relative to the container offset
  363. * @param event
  364. * @return array touches
  365. */
  366. getTouches: function(event) {
  367. var position = this.container.offset();
  368. return Array.prototype.slice.call(event.touches).map(function(touch) {
  369. return {
  370. x: touch.pageX - position.left,
  371. y: touch.pageY - position.top
  372. };
  373. });
  374. },
  375. /**
  376. * Animation loop
  377. * does not support simultaneous animations
  378. * @param duration
  379. * @param framefn
  380. * @param timefn
  381. * @param callback
  382. */
  383. animate: function(duration, framefn, timefn, callback) {
  384. var startTime = new Date().getTime(),
  385. renderFrame = (function() {
  386. if (!this.inAnimation) {
  387. return;
  388. }
  389. var frameTime = new Date().getTime() - startTime,
  390. progress = frameTime / duration;
  391. if (frameTime >= duration) {
  392. framefn(1);
  393. if (callback) {
  394. callback();
  395. }
  396. this.update();
  397. this.stopAnimation();
  398. this.update();
  399. } else {
  400. if (timefn) {
  401. progress = timefn(progress);
  402. }
  403. framefn(progress);
  404. this.update();
  405. requestAnimationFrame(renderFrame);
  406. }
  407. }).bind(this);
  408. this.inAnimation = true;
  409. requestAnimationFrame(renderFrame);
  410. },
  411. /**
  412. * Stops the animation
  413. */
  414. stopAnimation: function() {
  415. this.inAnimation = false;
  416. },
  417. /**
  418. * Swing timing function for animations
  419. * @param p
  420. * @return {Number}
  421. */
  422. swing: function(p) {
  423. return -Math.cos(p * Math.PI) / 2 + 0.5;
  424. },
  425. getContainerX: function() {
  426. if (this.el[0]) {
  427. return this.el[0].offsetWidth;
  428. } else {
  429. return 0;
  430. }
  431. },
  432. getContainerY: function() {
  433. return this.el[0].offsetHeight;
  434. },
  435. setContainerY: function(y) {
  436. y = y.toFixed(2);
  437. return this.container.height(y);
  438. },
  439. /**
  440. * Creates the expected html structure
  441. */
  442. setupMarkup: function() {
  443. this.container = $('<div class="pinch-zoom-container"></div>');
  444. this.el.before(this.container);
  445. this.container.append(this.el);
  446. this.container.css({
  447. 'position': 'relative',
  448. // 'width':'auto',
  449. // 'height':'auto'
  450. });
  451. // Zepto doesn't recognize `webkitTransform..` style
  452. this.el.css({
  453. '-webkit-transform-origin': '0% 0%',
  454. '-moz-transform-origin': '0% 0%',
  455. '-ms-transform-origin': '0% 0%',
  456. '-o-transform-origin': '0% 0%',
  457. 'transform-origin': '0% 0%',
  458. 'position': 'relative'
  459. });
  460. },
  461. end: function() {
  462. this.hasInteraction = false;
  463. this.sanitize();
  464. this.update();
  465. },
  466. /**
  467. * Binds all required event listeners
  468. */
  469. bindEvents: function() {
  470. detectGestures(this.container.eq(0), this, this.viewerContainer);
  471. // Zepto and jQuery both know about `on`
  472. $(window).on('resize', this.update.bind(this));
  473. $(this.el).find('img').on('load', this.update.bind(this));
  474. },
  475. /**
  476. * Updates the css values according to the current zoom factor and offset
  477. */
  478. update: function() {
  479. if (this.updatePlaned) {
  480. return;
  481. }
  482. this.updatePlaned = true;
  483. setTimeout((function() {
  484. this.updatePlaned = false;
  485. this.updateAspectRatio();
  486. var zoomFactor = this.getInitialZoomFactor() * this.zoomFactor,
  487. offsetX = (-this.offset.x / zoomFactor).toFixed(3),
  488. offsetY = (-this.offset.y / zoomFactor).toFixed(3);
  489. this.lastclientY = offsetY;
  490. var transform3d = 'scale3d(' + zoomFactor + ', ' + zoomFactor + ',1) ' +
  491. 'translate3d(' + offsetX + 'px,' + offsetY + 'px,0px)',
  492. transform2d = 'scale(' + zoomFactor + ', ' + zoomFactor + ') ' +
  493. 'translate(' + offsetX + 'px,' + offsetY + 'px)',
  494. removeClone = (function() {
  495. if (this.clone) {
  496. this.clone.remove();
  497. delete this.clone;
  498. }
  499. }).bind(this);
  500. // Scale 3d and translate3d are faster (at least on ios)
  501. // but they also reduce the quality.
  502. // PinchZoom uses the 3d transformations during interactions
  503. // after interactions it falls back to 2d transformations
  504. if (!this.options.use2d || this.hasInteraction || this.inAnimation) {
  505. this.is3d = true;
  506. // removeClone();
  507. this.el.css({
  508. '-webkit-transform': transform3d,
  509. '-o-transform': transform2d,
  510. '-ms-transform': transform2d,
  511. '-moz-transform': transform2d,
  512. 'transform': transform3d
  513. });
  514. } else {
  515. // When changing from 3d to 2d transform webkit has some glitches.
  516. // To avoid this, a copy of the 3d transformed element is displayed in the
  517. // foreground while the element is converted from 3d to 2d transform
  518. if (this.is3d) {
  519. // this.clone = this.el.clone();
  520. // this.clone.css('pointer-events', 'none');
  521. // this.clone.appendTo(this.container);
  522. // setTimeout(removeClone, 200);
  523. }
  524. this.el.css({
  525. '-webkit-transform': transform2d,
  526. '-o-transform': transform2d,
  527. '-ms-transform': transform2d,
  528. '-moz-transform': transform2d,
  529. 'transform': transform2d
  530. });
  531. this.is3d = false;
  532. }
  533. this.done && this.done.call(this, zoomFactor)
  534. }).bind(this), 0);
  535. },
  536. /**
  537. * Enables event handling for gestures
  538. */
  539. enable: function() {
  540. this.enabled = true;
  541. },
  542. /**
  543. * Disables event handling for gestures
  544. */
  545. disable: function() {
  546. this.enabled = false;
  547. },
  548. //销毁还原
  549. destroy: function() {
  550. var dom = this.el.clone();
  551. var p = this.container.parent();
  552. this.container.remove();
  553. dom.removeAttr('style');
  554. p.append(dom);
  555. }
  556. };
  557. var detectGestures = function(el, target, viewerContainer) {
  558. var interaction = null,
  559. fingers = 0,
  560. lastTouchStart = null,
  561. startTouches = null,
  562. lastTouchY = null,
  563. clientY = null,
  564. lastclientY = 0,
  565. lastTop = 0,
  566. setInteraction = function(newInteraction, event) {
  567. if (interaction !== newInteraction) {
  568. if (interaction && !newInteraction) {
  569. switch (interaction) {
  570. case "zoom":
  571. target.handleZoomEnd(event);
  572. break;
  573. case 'drag':
  574. target.handleDragEnd(event);
  575. break;
  576. }
  577. }
  578. switch (newInteraction) {
  579. case 'zoom':
  580. target.handleZoomStart(event);
  581. break;
  582. case 'drag':
  583. target.handleDragStart(event);
  584. break;
  585. }
  586. }
  587. interaction = newInteraction;
  588. },
  589. updateInteraction = function(event) {
  590. if (fingers === 2) {
  591. setInteraction('zoom');
  592. } else if (fingers === 1 && target.canDrag()) {
  593. setInteraction('drag', event);
  594. } else {
  595. setInteraction(null, event);
  596. }
  597. },
  598. targetTouches = function(touches) {
  599. return Array.prototype.slice.call(touches).map(function(touch) {
  600. return {
  601. x: touch.pageX,
  602. y: touch.pageY
  603. };
  604. });
  605. },
  606. getDistance = function(a, b) {
  607. var x, y;
  608. x = a.x - b.x;
  609. y = a.y - b.y;
  610. return Math.sqrt(x * x + y * y);
  611. },
  612. calculateScale = function(startTouches, endTouches) {
  613. var startDistance = getDistance(startTouches[0], startTouches[1]),
  614. endDistance = getDistance(endTouches[0], endTouches[1]);
  615. return endDistance / startDistance;
  616. },
  617. cancelEvent = function(event) {
  618. event.stopPropagation();
  619. event.preventDefault();
  620. },
  621. detectDoubleTap = function(event) {
  622. var time = (new Date()).getTime();
  623. var pageY = event.changedTouches[0].pageY;
  624. var top = parentNode.scrollTop || 0;
  625. if (fingers > 1) {
  626. lastTouchStart = null;
  627. lastTouchY = null;
  628. cancelEvent(event);
  629. }
  630. if (time - lastTouchStart < 300 && Math.abs(pageY - lastTouchY) < 10 && Math.abs(lastTop - top) < 10) {
  631. cancelEvent(event);
  632. target.handleDoubleTap(event);
  633. switch (interaction) {
  634. case "zoom":
  635. target.handleZoomEnd(event);
  636. break;
  637. case 'drag':
  638. target.handleDragEnd(event);
  639. break;
  640. }
  641. }
  642. if (fingers === 1) {
  643. lastTouchStart = time;
  644. lastTouchY = pageY;
  645. lastTop = top;
  646. }
  647. },
  648. firstMove = true;
  649. if (viewerContainer) {
  650. var parentNode = viewerContainer[0];
  651. }
  652. if (parentNode) {
  653. parentNode.addEventListener('touchstart', function(event) {
  654. if (target.enabled) {
  655. firstMove = true;
  656. fingers = event.touches.length;
  657. detectDoubleTap(event);
  658. clientY = event.changedTouches[0].clientY;
  659. if (fingers > 1) {
  660. cancelEvent(event);
  661. }
  662. }
  663. });
  664. parentNode.addEventListener('touchmove', function(event) {
  665. if (target.enabled) {
  666. lastclientY = event.changedTouches[0].clientY;
  667. if (firstMove) {
  668. updateInteraction(event);
  669. if (interaction) {
  670. // cancelEvent(event);
  671. }
  672. startTouches = targetTouches(event.touches);
  673. } else {
  674. switch (interaction) {
  675. case 'zoom':
  676. target.handleZoom(event, calculateScale(startTouches, targetTouches(event.touches)));
  677. break;
  678. case 'drag':
  679. target.handleDrag(event);
  680. break;
  681. }
  682. if (interaction) {
  683. // cancelEvent(event);
  684. target.update(lastclientY);
  685. }
  686. }
  687. if (fingers > 1) {
  688. cancelEvent(event);
  689. }
  690. firstMove = false;
  691. }
  692. });
  693. parentNode.addEventListener('touchend', function(event) {
  694. if (target.enabled) {
  695. fingers = event.touches.length;
  696. if (fingers > 1) {
  697. cancelEvent(event);
  698. }
  699. updateInteraction(event);
  700. }
  701. });
  702. }
  703. };
  704. return PinchZoom;
  705. };
  706. var PinchZoom = definePinchZoom($);
  707. var Pdfh5 = function(dom, options) {
  708. this.container = $(dom);
  709. this.currentNum = 1; //当前页数从1开始
  710. this.thePDF = null;
  711. this.pdfRender = null;
  712. this.totalNum = null;
  713. this.pdfLoaded = false;
  714. this.pages = null;
  715. this.initTime = 0;
  716. this.startTime = 0;
  717. this.endTime = 0;
  718. this.renderTime = 0;
  719. this.timer = null;
  720. this.loadWidth = 1;
  721. this.docWidth = document.documentElement.clientWidth;
  722. this.eventType = {};
  723. this.init(options);
  724. };
  725. Pdfh5.prototype = {
  726. init: function(options) {
  727. var self = this;
  728. if (self.pdfLoaded) {
  729. return;
  730. }
  731. this.initTime = new Date().getTime();
  732. setTimeout(function() {
  733. self.eventType["start"] && self.eventType["start"].call(self, self.initTime);
  734. self.start && self.start(self.initTime)
  735. }, 0)
  736. options = options ? options : {};
  737. options.pdfurl = options.pdfurl ? options.pdfurl : null;
  738. options.data = options.data ? options.data : null;
  739. if (options.scrollEnable == 'undefined' || options.scrollEnable == undefined) {
  740. options.scrollEnable = true
  741. }
  742. if (options.scrollEnable == 'true' || options.scrollEnable == true) {
  743. options.scrollEnable = true
  744. }
  745. if (options.scrollEnable == 'null' || options.scrollEnable == null) {
  746. options.scrollEnable = true
  747. }
  748. if (options.scrollEnable == 'false') {
  749. options.scrollEnable = false
  750. }
  751. var html = '<div class="loadingBar">' +
  752. '<div class="progress">' +
  753. ' <div class="glimmer">' +
  754. '</div>' +
  755. ' </div>' +
  756. '</div>' +
  757. '<div class="pageNum">' +
  758. '<div class="pageNum-bg"></div>' +
  759. ' <div class="pageNum-num">' +
  760. ' <span class="pageNow">1</span>/' +
  761. '<span class="pageTotal">1</span>' +
  762. '</div>' +
  763. ' </div>' +
  764. '<div class="backTop">' +
  765. '</div>' +
  766. '<div class="loadEffect"></div>';
  767. if (!this.container.find('.pageNum')[0]) {
  768. this.container.append(html);
  769. }
  770. var viewer = document.createElement("div");
  771. viewer.className = 'pdfViewer';
  772. var viewerContainer = document.createElement("div");
  773. viewerContainer.className = 'viewerContainer';
  774. viewerContainer.appendChild(viewer);
  775. this.container.append(viewerContainer);
  776. this.viewer = $(viewer);
  777. this.viewerContainer = $(viewerContainer);
  778. this.pageNum = this.container.find('.pageNum');
  779. this.pageNow = this.pageNum.find('.pageNow');
  780. this.pageTotal = this.pageNum.find('.pageTotal');
  781. this.loadingBar = this.container.find('.loadingBar');
  782. this.progress = this.loadingBar.find('.progress');
  783. this.backTop = this.container.find('.backTop');
  784. this.loading = this.container.find('.loadEffect');
  785. var height = document.documentElement.clientHeight * (1 / 3);
  786. if (!options.scrollEnable) {
  787. this.viewerContainer.css({
  788. "overflow": "hidden"
  789. })
  790. } else {
  791. this.viewerContainer.css({
  792. "overflow": "auto"
  793. })
  794. }
  795. viewerContainer.addEventListener('scroll', function() {
  796. var scrollTop = viewerContainer.scrollTop;
  797. if (scrollTop >= 150) {
  798. if (self.backTop) {
  799. self.backTop.show();
  800. }
  801. } else {
  802. if (self.backTop) {
  803. self.backTop.fadeOut(200);
  804. }
  805. }
  806. if (self.viewerContainer) {
  807. self.pages = self.viewerContainer.find('.page');
  808. }
  809. clearTimeout(self.timer);
  810. if (self.pageNum) {
  811. self.pageNum.show();
  812. }
  813. if (self.pages) {
  814. self.pages.each(function(index, obj) {
  815. var top = obj.getBoundingClientRect().top;
  816. var bottom = obj.getBoundingClientRect().bottom;
  817. if (top <= height && bottom > height) {
  818. if (self.pageNum) {
  819. self.pageNow.text(index + 1)
  820. }
  821. }
  822. })
  823. }
  824. self.timer = setTimeout(function() {
  825. if (self.pageNum) {
  826. self.pageNum.fadeOut(200);
  827. }
  828. }, 1500)
  829. self.eventType["scroll"] && self.eventType["scroll"].call(self, scrollTop);
  830. self.scroll && self.scroll(scrollTop);
  831. })
  832. this.backTop.on('click tap', function() {
  833. var mart = self.viewer.css('transform');
  834. var arr = mart.replace(/[a-z\(\)\s]/g, '').split(',');
  835. var s1 = arr[0];
  836. var s2 = arr[3];
  837. var x = arr[4] / 2;
  838. var left = self.viewer[0].getBoundingClientRect().left;
  839. if (left <= -self.docWidth * 2) {
  840. x = -self.docWidth / 2
  841. }
  842. self.viewer.css({
  843. transform: 'scale(' + s1 + ', ' + s2 + ') translate(' + x + 'px, 0px)'
  844. })
  845. if (self.PinchZoom) {
  846. self.PinchZoom.offset.y = 0;
  847. self.PinchZoom.lastclientY = 0;
  848. }
  849. self.viewerContainer.animate({
  850. scrollTop: 0
  851. }, 300)
  852. self.eventType["backTop"] && self.eventType["backTop"].call(self);
  853. })
  854. //获取url带的参数地址
  855. function GetQueryString(name) {
  856. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
  857. var r = window.location.search.substr(1).match(reg);
  858. if (r != null) return decodeURIComponent(r[2]);
  859. return "";
  860. }
  861. var pdfurl = GetQueryString("file");
  862. if (pdfurl) {
  863. getDoc(pdfurl)
  864. } else if (options.pdfurl) {
  865. getDoc(options.pdfurl)
  866. } else {
  867. setTimeout(function() {
  868. var time = new Date().getTime();
  869. self.endTime = time - self.initTime;
  870. self.eventType["complete"] && self.eventType["complete"].call(self, "error", "文件路径错误", self.endTime);
  871. self.eventType["error"] && self.eventType["error"].call(self, "error", "文件路径错误", self.endTime);
  872. self.complete && self.complete("error", "文件路径错误", self.endTime)
  873. self.error && self.error("error", "文件路径错误", self.endTime)
  874. }, 0)
  875. }
  876. function getDoc(array) {
  877. if (self.pdfLoaded) {
  878. return;
  879. }
  880. pdfjsLib.getDocument(array).then(function(pdf) {
  881. if (self.pdfLoaded) {
  882. return;
  883. }
  884. self.thePDF = pdf;
  885. self.totalNum = pdf.numPages;
  886. self.thePDF.getPage(1).then(handlePages);
  887. self.pageTotal.text(self.totalNum)
  888. var time = new Date().getTime();
  889. self.startTime = time - self.initTime;
  890. self.eventType["renderStart"] && self.eventType["renderStart"].call(self, self.startTime);
  891. self.renderStart && self.renderStart(self.startTime)
  892. }).catch(function(err) {
  893. var time = new Date().getTime();
  894. self.endTime = time - self.initTime;
  895. self.eventType["complete"] && self.eventType["complete"].call(self, "error", err.responseText, self.endTime);
  896. self.eventType["error"] && self.eventType["error"].call(self, "error", err.responseText, self.endTime);
  897. self.complete && self.complete("error", err.responseText, self.endTime)
  898. self.error && self.error("error", err.responseText, self.endTime)
  899. })
  900. }
  901. function handlePages(page) {
  902. if (self.pdfLoaded) {
  903. return;
  904. }
  905. if (!options || !options.scale) {
  906. if (self.totalNum === 1) {
  907. options.scale = 1.8
  908. } else {
  909. options.scale = 2.5
  910. // options.scale = 1.8
  911. }
  912. }
  913. if (options && options.defalutScale) {
  914. if (self.totalNum === 1) {
  915. options.scale = options.defalutScale
  916. // options.scale = 1.8
  917. } else {
  918. options.scale = 2.5
  919. // options.scale = 1.8
  920. }
  921. }
  922. var viewport = page.getViewport(options.scale);
  923. var canvas = document.createElement("canvas");
  924. var winRatio = ($(window).width() / viewport.width) * 1;
  925. var obj = {
  926. 'Cheight': viewport.height * winRatio,
  927. 'width': viewport.width,
  928. 'height': viewport.height,
  929. 'canvas': canvas,
  930. 'index': self.currentNum
  931. }
  932. var context = canvas.getContext('2d');
  933. canvas.height = viewport.height;
  934. canvas.width = viewport.width;
  935. //在canvas上绘制
  936. self.pdfRender = page.render({
  937. canvasContext: context,
  938. viewport: viewport
  939. });
  940. obj.src = obj.canvas.toDataURL("image/jpeg");
  941. self.pdfRender.promise.then(function() {
  942. self.render(obj);
  943. }).then(function() {
  944. //开始下一页到绘制
  945. self.currentNum++;
  946. if (!self.pdfLoaded && self.thePDF && self.currentNum <= self.totalNum) {
  947. self.thePDF.getPage(self.currentNum).then(handlePages);
  948. } else {
  949. self.pdfLoaded = true;
  950. if (self.viewerContainer) {
  951. self.pages = self.viewerContainer.find('.page');
  952. }
  953. self.currentNum = self.totalNum;
  954. var time = new Date().getTime();
  955. self.endTime = time - self.initTime;
  956. if (self.progress) {
  957. self.progress.css({
  958. width: "100%"
  959. })
  960. }
  961. if (self.loadingBar) {
  962. self.loadingBar.fadeOut(200);
  963. }
  964. self.renderEnd && self.renderEnd(self.endTime)
  965. self.eventType["complete"] && self.eventType["complete"].call(self, "success", "PDF解析完毕", self.endTime);
  966. self.eventType["success"] && self.eventType["success"].call(self, "success", "PDF解析完毕", self.endTime);
  967. self.complete && self.complete("success", "PDF解析完毕", self.endTime)
  968. self.success && self.success("success", "PDF解析完毕", self.endTime)
  969. self.PinchZoom = new PinchZoom(self.viewer, {}, self.viewerContainer);
  970. self.PinchZoom.done = function(scale) {
  971. if (scale == 1) {
  972. if (self.viewerContainer) {
  973. self.viewerContainer.css({
  974. '-webkit-overflow-scrolling': 'touch'
  975. })
  976. }
  977. } else {
  978. if (self.viewerContainer) {
  979. self.viewerContainer.css({
  980. '-webkit-overflow-scrolling': 'auto'
  981. })
  982. }
  983. }
  984. self.zoomChange && self.zoomChange(scale)
  985. }
  986. }
  987. }).catch(function(err) {
  988. console.log(err)
  989. })
  990. }
  991. },
  992. render: function(obj) {
  993. if (this.pdfLoaded) {
  994. return;
  995. }
  996. var img = new Image();
  997. var time = new Date().getTime();
  998. var time2 = 0;
  999. if (this.renderTime == 0) {
  1000. time2 = time - this.startTime
  1001. } else {
  1002. time2 = time - this.renderTime
  1003. }
  1004. obj.src = obj.canvas.toDataURL("image/jpeg");
  1005. img.src = obj.src;
  1006. var page = document.createElement("div");
  1007. page.className = "page page" + obj.index;
  1008. page.setAttribute('data-index', obj.index);
  1009. $(page).css({
  1010. 'max-width': obj.width
  1011. })
  1012. page.appendChild(img);
  1013. if (this.viewer) {
  1014. this.viewer.append(page);
  1015. }
  1016. if (this.currentNum == 1) {
  1017. this.loadWidth = 100 / this.totalNum;
  1018. if (this.loadingBa) {
  1019. this.loadingBar.show();
  1020. }
  1021. if (this.loading) {
  1022. this.loading.fadeOut(200);
  1023. }
  1024. }
  1025. if (this.progress) {
  1026. this.progress.css({
  1027. width: this.loadWidth * this.currentNum + "%"
  1028. })
  1029. }
  1030. this.eventType["renderPages"] && this.eventType["renderPages"].call(this, page, time - this.initTime, time2);
  1031. this.renderPages && this.renderPages(page, time - this.initTime, time2)
  1032. this.renderTime = time;
  1033. },
  1034. show: function(callback) {
  1035. this.container.show();
  1036. this.eventType["show"] && this.eventType["show"].call(this);
  1037. callback && callback.call(this)
  1038. },
  1039. hide: function(callback) {
  1040. this.container.hide()
  1041. this.eventType["hide"] && this.eventType["show"].call(this);
  1042. callback && callback.call(this)
  1043. },
  1044. on: function(type, callback) {
  1045. this.eventType[type] = callback
  1046. },
  1047. scrollEnable: function(flag) {
  1048. if (!flag) {
  1049. this.viewerContainer.css({
  1050. "overflow": "hidden"
  1051. })
  1052. } else {
  1053. this.viewerContainer.css({
  1054. "overflow": "auto"
  1055. })
  1056. }
  1057. this.eventType["scrollEnable"] && this.eventType["scrollEnable"].call(this);
  1058. },
  1059. reset: function(callback) {
  1060. if (this.PinchZoom) {
  1061. this.PinchZoom.offset.y = 0;
  1062. this.PinchZoom.offset.x = 0;
  1063. this.PinchZoom.lastclientY = 0;
  1064. this.PinchZoom.zoomFactor = 1;
  1065. this.PinchZoom.update();
  1066. }
  1067. if (this.viewerContainer) {
  1068. this.viewerContainer.scrollTop(0);
  1069. }
  1070. this.eventType["reset"] && this.eventType["reset"].call(this);
  1071. callback && callback.call(this)
  1072. },
  1073. destroy: function(callback) {
  1074. var self = this;
  1075. this.reset();
  1076. if (this.thePDF) {
  1077. this.thePDF.destroy();
  1078. this.thePDF = null;
  1079. }
  1080. if (this.pdfRender) {
  1081. this.pdfRender.cancel();
  1082. this.pdfRender = null;
  1083. }
  1084. if (this.viewerContainer) {
  1085. this.viewerContainer.remove();
  1086. this.viewerContainer = null;
  1087. }
  1088. if (this.container) {
  1089. this.container.html('');
  1090. this.container = null;
  1091. }
  1092. this.backTop.off('tap');
  1093. this.backTop.off('click');
  1094. this.pdfLoaded = true;
  1095. this.currentNum = 1;
  1096. this.totalNum = null;
  1097. this.pages = null;
  1098. this.initTime = 0;
  1099. this.startTime = 0;
  1100. this.endTime = 0;
  1101. this.renderTime = 0;
  1102. this.viewer = null;
  1103. this.pageNum = null;
  1104. this.pageNow = null;
  1105. this.pageTotal = null;
  1106. this.loadingBar = null;
  1107. this.progress = null;
  1108. this.loading = null;
  1109. this.timer = null;
  1110. this.loadWidth = 1;
  1111. this.show = null;
  1112. this.hide = null;
  1113. callback && callback.call(this)
  1114. this.eventType["destroy"] && this.eventType["destroy"].call(this)
  1115. }
  1116. }
  1117. if (typeof define !== 'undefined' && define.amd) {
  1118. define(['jquery'], function($) {
  1119. return Pdfh5
  1120. });
  1121. } else {
  1122. window.Pdfh5 = Pdfh5
  1123. }
  1124. }).call(this);