pdfh5.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  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.init(options);
  723. };
  724. Pdfh5.prototype = {
  725. init: function (options) {
  726. var self = this;
  727. if (self.pdfLoaded) {
  728. return;
  729. }
  730. this.initTime = new Date().getTime();
  731. setTimeout(function () {
  732. self.start && self.start(self.initTime)
  733. }, 0)
  734. options = options ? options : {};
  735. options.pdfurl = options.pdfurl ? options.pdfurl : null;
  736. options.data = options.data ? options.data : null;
  737. var html = '<div class="loadingBar">' +
  738. '<div class="progress">' +
  739. ' <div class="glimmer">' +
  740. '</div>' +
  741. ' </div>' +
  742. '</div>' +
  743. '<div class="pageNum">' +
  744. '<div class="pageNum-bg"></div>' +
  745. ' <div class="pageNum-num">' +
  746. ' <span class="pageNow">1</span>/' +
  747. '<span class="pageTotal">1</span>' +
  748. '</div>' +
  749. ' </div>' +
  750. '<div class="backTop">' +
  751. '</div>' +
  752. '<div class="loadEffect"></div>'
  753. ;
  754. if (!this.container.find('.pageNum')[0]) {
  755. this.container.append(html);
  756. }
  757. var viewer = document.createElement("div");
  758. viewer.className = 'pdfViewer';
  759. var viewerContainer = document.createElement("div");
  760. viewerContainer.className = 'viewerContainer';
  761. viewerContainer.appendChild(viewer);
  762. this.container.append(viewerContainer);
  763. this.viewer = $(viewer);
  764. this.viewerContainer = $(viewerContainer);
  765. this.pageNum = this.container.find('.pageNum');
  766. this.pageNow = this.pageNum.find('.pageNow');
  767. this.pageTotal = this.pageNum.find('.pageTotal');
  768. this.loadingBar = this.container.find('.loadingBar');
  769. this.progress = this.loadingBar.find('.progress');
  770. this.backTop = this.container.find('.backTop');
  771. this.loading = this.container.find('.loadEffect');
  772. var height = document.documentElement.clientHeight * (1 / 3);
  773. viewerContainer.addEventListener('scroll', function () {
  774. var scrollTop = viewerContainer.scrollTop;
  775. if (scrollTop >= 150) {
  776. if(self.backTop){
  777. self.backTop.show();
  778. }
  779. } else {
  780. if(self.backTop){
  781. self.backTop.fadeOut(200);
  782. }
  783. }
  784. if(self.viewerContainer){
  785. self.pages = self.viewerContainer.find('.page');
  786. }
  787. clearTimeout(self.timer);
  788. if(self.pageNum){
  789. self.pageNum.show();
  790. }
  791. if(self.pages){
  792. self.pages.each(function (index, obj) {
  793. var top = obj.getBoundingClientRect().top;
  794. var bottom = obj.getBoundingClientRect().bottom;
  795. if (top <= height && bottom > height) {
  796. if(self.pageNum){
  797. self.pageNow.text(index + 1)
  798. }
  799. }
  800. })
  801. }
  802. self.timer = setTimeout(function () {
  803. if(self.pageNum){
  804. self.pageNum.fadeOut(200);
  805. }
  806. }, 1500)
  807. self.scroll && self.scroll(scrollTop);
  808. })
  809. this.backTop.on('click tap', function () {
  810. var mart = self.viewer.css('transform');
  811. var arr = mart.replace(/[a-z\(\)\s]/g, '').split(',');
  812. var s1 = arr[0];
  813. var s2 = arr[3];
  814. var x = arr[4] / 2;
  815. var left = self.viewer[0].getBoundingClientRect().left;
  816. if (left <= -self.docWidth * 2) {
  817. x = -self.docWidth / 2
  818. }
  819. self.viewer.css({
  820. transform: 'scale(' + s1 + ', ' + s2 + ') translate(' + x + 'px, 0px)'
  821. })
  822. if (self.PinchZoom) {
  823. self.PinchZoom.offset.y = 0;
  824. self.PinchZoom.lastclientY = 0;
  825. }
  826. self.viewerContainer.animate({
  827. scrollTop: 0
  828. }, 300)
  829. })
  830. //获取url带的参数地址
  831. function GetQueryString(name) {
  832. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
  833. var r = window.location.search.substr(1).match(reg);
  834. if (r != null) return decodeURIComponent(r[2]);
  835. return "";
  836. }
  837. var pdfurl = GetQueryString("file");
  838. if (pdfurl) {
  839. getDoc(pdfurl)
  840. } else if(options.pdfurl){
  841. getDoc(options.pdfurl)
  842. } else {
  843. setTimeout(function () {
  844. var time = new Date().getTime();
  845. self.endTime = time - self.initTime;
  846. self.complete && self.complete("error", "文件路径不能为空", self.endTime)
  847. self.error && self.error("error", "文件路径不能为空", self.endTime)
  848. }, 0)
  849. }
  850. function getDoc(array) {
  851. if (self.pdfLoaded) {
  852. return;
  853. }
  854. pdfjsLib.getDocument(array).then(function (pdf) {
  855. if (self.pdfLoaded) {
  856. return;
  857. }
  858. self.thePDF = pdf;
  859. self.totalNum = pdf.numPages;
  860. self.thePDF.getPage(1).then(handlePages);
  861. self.pageTotal.text(self.totalNum)
  862. var time = new Date().getTime();
  863. self.startTime = time - self.initTime;
  864. self.renderStart && self.renderStart(self.startTime)
  865. }).catch(function (err) {
  866. var time = new Date().getTime();
  867. self.endTime = time - self.initTime;
  868. self.complete && self.complete("error", err.responseText, self.endTime)
  869. self.error && self.error("error", err.responseText, self.endTime)
  870. })
  871. }
  872. function handlePages(page) {
  873. if (self.pdfLoaded) {
  874. return;
  875. }
  876. if (!options || !options.scale) {
  877. if (self.totalNum === 1) {
  878. options.scale = 1.8
  879. } else {
  880. options.scale = 2.5
  881. // options.scale = 1.8
  882. }
  883. }
  884. if (options && options.defalutScale) {
  885. if (self.totalNum === 1) {
  886. options.scale = options.defalutScale
  887. // options.scale = 1.8
  888. } else {
  889. options.scale = 2.5
  890. // options.scale = 1.8
  891. }
  892. }
  893. var viewport = page.getViewport(options.scale);
  894. var canvas = document.createElement("canvas");
  895. var winRatio = ($(window).width() / viewport.width) * 1;
  896. var obj = {
  897. 'Cheight': viewport.height * winRatio,
  898. 'width': viewport.width,
  899. 'height': viewport.height,
  900. 'canvas': canvas,
  901. 'index': self.currentNum
  902. }
  903. var context = canvas.getContext('2d');
  904. canvas.height = viewport.height;
  905. canvas.width = viewport.width;
  906. //在canvas上绘制
  907. self.pdfRender = page.render({
  908. canvasContext: context,
  909. viewport: viewport
  910. });
  911. obj.src = obj.canvas.toDataURL("image/jpeg");
  912. self.pdfRender.promise.then(function () {
  913. self.render(obj);
  914. }).then(function () {
  915. //开始下一页到绘制
  916. self.currentNum++;
  917. if (!self.pdfLoaded && self.thePDF && self.currentNum <= self.totalNum) {
  918. self.thePDF.getPage(self.currentNum).then(handlePages);
  919. } else {
  920. self.pdfLoaded = true;
  921. if (self.viewerContainer) {
  922. self.pages = self.viewerContainer.find('.page');
  923. }
  924. self.currentNum = self.totalNum;
  925. var time = new Date().getTime();
  926. self.endTime = time - self.initTime;
  927. if (self.progress) {
  928. self.progress.css({
  929. width: "100%"
  930. })
  931. }
  932. if (self.loadingBar) {
  933. self.loadingBar.fadeOut(200);
  934. }
  935. self.renderEnd && self.renderEnd(self.endTime)
  936. self.complete && self.complete("success", "PDF解析完毕", self.endTime)
  937. self.success && self.success("success", "PDF解析完毕", self.endTime)
  938. self.PinchZoom = new PinchZoom(self.viewer, {}, self.viewerContainer);
  939. self.PinchZoom.done = function (scale) {
  940. if (scale == 1) {
  941. if (self.viewerContainer) {
  942. self.viewerContainer.css({
  943. '-webkit-overflow-scrolling': 'touch'
  944. })
  945. }
  946. } else {
  947. if (self.viewerContainer) {
  948. self.viewerContainer.css({
  949. '-webkit-overflow-scrolling': 'auto'
  950. })
  951. }
  952. }
  953. self.zoomChange && self.zoomChange(scale)
  954. }
  955. }
  956. }).catch(function(err){
  957. console.log(err)
  958. })
  959. }
  960. },
  961. render: function (obj) {
  962. if (this.pdfLoaded) {
  963. return;
  964. }
  965. var img = new Image();
  966. var time = new Date().getTime();
  967. var time2 = 0;
  968. if (this.renderTime == 0) {
  969. time2 = time - this.startTime
  970. } else {
  971. time2 = time - this.renderTime
  972. }
  973. obj.src = obj.canvas.toDataURL("image/jpeg");
  974. img.src = obj.src;
  975. var page = document.createElement("div");
  976. page.className = "page page" + obj.index;
  977. page.setAttribute('data-index', obj.index);
  978. $(page).css({
  979. 'max-width': obj.width
  980. })
  981. page.appendChild(img);
  982. if (this.viewer) {
  983. this.viewer.append(page);
  984. }
  985. if (this.currentNum == 1) {
  986. this.loadWidth = 100 / this.totalNum;
  987. if (this.loadingBa) {
  988. this.loadingBar.show();
  989. }
  990. if (this.loading) {
  991. this.loading.fadeOut(200);
  992. }
  993. }
  994. if (this.progress) {
  995. this.progress.css({
  996. width: this.loadWidth * this.currentNum + "%"
  997. })
  998. }
  999. this.renderPages && this.renderPages(page, time - this.initTime, time2)
  1000. this.renderTime = time;
  1001. },
  1002. show: function (callback) {
  1003. this.container.show();
  1004. callback && callback.call(this)
  1005. },
  1006. hide: function (callback) {
  1007. this.container.hide()
  1008. callback && callback.call(this)
  1009. },
  1010. reset: function (callback) {
  1011. if (this.PinchZoom) {
  1012. this.PinchZoom.offset.y = 0;
  1013. this.PinchZoom.offset.x = 0;
  1014. this.PinchZoom.lastclientY = 0;
  1015. this.PinchZoom.zoomFactor = 1;
  1016. this.PinchZoom.update();
  1017. }
  1018. if (this.viewerContainer) {
  1019. this.viewerContainer.scrollTop(0);
  1020. }
  1021. callback && callback.call(this)
  1022. },
  1023. destroy: function (callback) {
  1024. var self = this;
  1025. this.reset();
  1026. if(this.thePDF){
  1027. this.thePDF.destroy();
  1028. this.thePDF = null;
  1029. }
  1030. if(this.pdfRender){
  1031. this.pdfRender.cancel();
  1032. this.pdfRender = null;
  1033. }
  1034. if(this.viewerContainer){
  1035. this.viewerContainer.remove();
  1036. this.viewerContainer = null;
  1037. }
  1038. if(this.container){
  1039. this.container.html('');
  1040. this.container = null;
  1041. }
  1042. this.backTop.off('tap');
  1043. this.backTop.off('click');
  1044. this.pdfLoaded = true;
  1045. this.currentNum = 1;
  1046. this.totalNum = null;
  1047. this.pages = null;
  1048. this.initTime = 0;
  1049. this.startTime = 0;
  1050. this.endTime = 0;
  1051. this.renderTime = 0;
  1052. this.viewer = null;
  1053. this.pageNum = null;
  1054. this.pageNow = null;
  1055. this.pageTotal = null;
  1056. this.loadingBar = null;
  1057. this.progress = null;
  1058. this.loading = null;
  1059. this.timer = null;
  1060. this.loadWidth = 1;
  1061. this.show = null;
  1062. this.hide = null;
  1063. callback && callback.call(this)
  1064. }
  1065. }
  1066. if (typeof define !== 'undefined' && define.amd) {
  1067. define(['jquery'], function ($) {
  1068. return Pdfh5
  1069. });
  1070. } else {
  1071. window.Pdfh5 = Pdfh5
  1072. }
  1073. }).call(this);