parser.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. <!--
  2. parser 主模块组件
  3. github:https://github.com/jin-yufeng/Parser
  4. docs:https://jin-yufeng.github.io/Parser
  5. 插件市场:https://ext.dcloud.net.cn/plugin?id=805
  6. author:JinYufeng
  7. update:2020/03/26
  8. -->
  9. <template>
  10. <view style="display:inherit;">
  11. <slot v-if="!nodes.length" />
  12. <view class="top" :style="showAm+(selectable?';user-select:text;-webkit-user-select:text':'')" :animation="scaleAm"
  13. @tap="_tap" @touchstart="_touchstart" @touchmove="_touchmove">
  14. <!--#ifdef H5-->
  15. <div :id="'rtf'+uid"></div>
  16. <!--#endif-->
  17. <!--#ifndef H5-->
  18. <trees :nodes="nodes" :lazy-load="lazyLoad" :loadVideo="loadVideo" />
  19. <!--#endif-->
  20. </view>
  21. <image v-for="(item, index) in imgs" v-bind:key="index" :id="index" :src="item" hidden @load="_load" />
  22. </view>
  23. </template>
  24. <script>
  25. // #ifndef H5
  26. import trees from './libs/trees';
  27. var cache = {},
  28. // #ifdef MP-WEIXIN || MP-TOUTIAO
  29. fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null,
  30. // #endif
  31. Parser = require('./libs/MpHtmlParser.js');
  32. var document; // document 补丁包 https://jin-yufeng.github.io/Parser/#/instructions?id=document
  33. // 计算 cache 的 key
  34. function hash(str) {
  35. for (var i = str.length, val = 5381; i--;)
  36. val += (val << 5) + str.charCodeAt(i);
  37. return val;
  38. }
  39. // #endif
  40. // #ifdef H5
  41. var rpx = uni.getSystemInfoSync().screenWidth / 750,
  42. cfg = require('./libs/config.js');
  43. // #endif
  44. export default {
  45. name: 'parser',
  46. data() {
  47. return {
  48. // #ifdef APP-PLUS
  49. loadVideo: false,
  50. // #endif
  51. // #ifdef H5
  52. uid: this._uid,
  53. // #endif
  54. scaleAm: '',
  55. showAm: '',
  56. nodes: [],
  57. imgs: []
  58. }
  59. },
  60. // #ifndef H5
  61. components: {
  62. trees
  63. },
  64. // #endif
  65. props: {
  66. 'html': null,
  67. // #ifndef MP-ALIPAY
  68. 'autopause': {
  69. type: Boolean,
  70. default: true
  71. },
  72. // #endif
  73. 'autosetTitle': {
  74. type: Boolean,
  75. default: true
  76. },
  77. 'compress': Number,
  78. 'domain': String,
  79. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  80. 'gestureZoom': Boolean,
  81. // #endif
  82. // #ifdef MP-WEIXIN || MP-QQ || H5 || APP-PLUS
  83. 'lazyLoad': Boolean,
  84. // #endif
  85. 'selectable': Boolean,
  86. 'tagStyle': Object,
  87. 'showWithAnimation': Boolean,
  88. 'useAnchor': Boolean,
  89. 'useCache': Boolean,
  90. 'xml': Boolean
  91. },
  92. watch: {
  93. html(html) {
  94. this.setContent(html);
  95. }
  96. },
  97. mounted() {
  98. // #ifdef APP-NVUE
  99. console.error('本组件暂不支持 NVUE');
  100. // #endif
  101. // 图片数组
  102. this.imgList = [];
  103. this.imgList.each = function(f) {
  104. for (var i = 0, len = this.length; i < len; i++)
  105. this.setItem(i, f(this[i], i, this));
  106. }
  107. this.imgList.setItem = function(i, src) {
  108. if (!i || !src) return;
  109. // #ifndef MP-ALIPAY || APP-PLUS
  110. // 去重
  111. if (src.indexOf('http') == 0 && this.includes(src)) {
  112. var newSrc = '';
  113. for (var j = 0, c; c = src[j]; j++) {
  114. if (c == '/' && src[j - 1] != '/' && src[j + 1] != '/') break;
  115. newSrc += Math.random() > 0.5 ? c.toUpperCase() : c;
  116. }
  117. newSrc += src.substr(j);
  118. return this[i] = newSrc;
  119. }
  120. // #endif
  121. this[i] = src;
  122. // 暂存 data src
  123. if (src.includes('data:image')) {
  124. var filePath, info = src.match(/data:image\/(\S+?);(\S+?),(.+)/);
  125. if (!info) return;
  126. // #ifdef MP-WEIXIN || MP-TOUTIAO
  127. filePath = `${wx.env.USER_DATA_PATH}/${Date.now()}.${info[1]}`;
  128. fs && fs.writeFile({
  129. filePath,
  130. data: info[3],
  131. encoding: info[2],
  132. success: () => this[i] = filePath
  133. })
  134. // #endif
  135. // #ifdef APP-PLUS
  136. filePath = `_doc/parser_tmp/${Date.now()}.${info[1]}`;
  137. var bitmap = new plus.nativeObj.Bitmap();
  138. bitmap.loadBase64Data(src, () => {
  139. bitmap.save(filePath, {}, () => {
  140. bitmap.clear()
  141. this[i] = filePath;
  142. })
  143. })
  144. // #endif
  145. }
  146. }
  147. if (!this.nodes.length) this.setContent(this.html);
  148. },
  149. beforeDestroy() {
  150. // #ifdef H5
  151. if (this._observer) this._observer.disconnect();
  152. // #endif
  153. this.imgList.each(src => {
  154. // #ifdef APP-PLUS
  155. if (src && src.includes('_doc')) {
  156. plus.io.resolveLocalFileSystemURL(src, entry => {
  157. entry.remove();
  158. });
  159. }
  160. // #endif
  161. // #ifdef MP-WEIXIN || MP-TOUTIAO
  162. if (src && src.includes(uni.env.USER_DATA_PATH))
  163. fs && fs.unlink({
  164. filePath: src
  165. })
  166. // #endif
  167. })
  168. clearInterval(this._timer);
  169. },
  170. methods: {
  171. // #ifdef H5
  172. _Dom2Str(nodes) {
  173. var str = '';
  174. for (var node of nodes) {
  175. if (node.type == 'text')
  176. str += node.text;
  177. else {
  178. str += ('<' + node.name);
  179. for (var attr in node.attrs || {})
  180. str += (' ' + attr + '="' + node.attrs[attr] + '"');
  181. if (!node.children || !node.children.length) str += '>';
  182. else str += ('>' + this._Dom2Str(node.children) + '</' + node.name + '>');
  183. }
  184. }
  185. return str;
  186. },
  187. // #endif
  188. setContent(html, append) {
  189. // #ifdef H5
  190. if (!html) {
  191. if (this.rtf && !append) this.rtf.parentNode.removeChild(this.rtf);
  192. return;
  193. }
  194. if (typeof html != 'string') html = this._Dom2Str(html.nodes || html);
  195. // 处理 rpx
  196. if (html.includes('rpx'))
  197. html = html.replace(/[0-9.]*rpx/g, $ => parseFloat($) * rpx + 'px');
  198. var div = document.createElement('div');
  199. if (!append) {
  200. // 处理 tag-style 和 userAgentStyles
  201. let style = '<style>@keyframes show{0%{opacity:0}100%{opacity:1}}';
  202. for (var item in cfg.userAgentStyles)
  203. style += `${item}{${cfg.userAgentStyles[item]}}`;
  204. for (item in this.tagStyle)
  205. style += `${item}{${this.tagStyle[item]}}`;
  206. style += '</style>';
  207. html = style + html;
  208. if (this.rtf) this.rtf.parentNode.removeChild(this.rtf);
  209. this.rtf = div;
  210. } else {
  211. if (!this.rtf) this.rtf = div;
  212. else this.rtf.appendChild(div);
  213. }
  214. div.innerHTML = html;
  215. for (var styles = this.rtf.getElementsByTagName('style'), i = 0, style; style = styles[i++];) {
  216. style.innerHTML = style.innerHTML.replace(/\s*body/g, '#rtf' + this._uid);
  217. style.setAttribute('scoped', 'true');
  218. }
  219. // 懒加载
  220. if (!this._observer && this.lazyLoad && IntersectionObserver) {
  221. this._observer = new IntersectionObserver(changes => {
  222. for (let item, i = 0; item = changes[i++];) {
  223. if (item.isIntersecting) {
  224. item.target.src = item.target.getAttribute('data-src');
  225. item.target.removeAttribute('data-src');
  226. this._observer.unobserve(item.target);
  227. }
  228. }
  229. }, {
  230. rootMargin: '900px 0px 900px 0px'
  231. })
  232. }
  233. var _ts = this;
  234. // 获取标题
  235. var title = this.rtf.getElementsByTagName('title');
  236. if (title.length && this.autosetTitle)
  237. uni.setNavigationBarTitle({
  238. title: title[0].innerText
  239. })
  240. // 图片处理
  241. this.imgList.length = 0;
  242. var imgs = this.rtf.getElementsByTagName('img');
  243. for (let i = 0, j = 0, img; img = imgs[i]; i++) {
  244. img.style.maxWidth = '100%';
  245. var src = img.getAttribute('src');
  246. if (this.domain && src) {
  247. if (src[0] == '/') {
  248. if (src[1] == '/')
  249. img.src = (this.domain.includes('://') ? this.domain.split('://')[0] : '') + ':' + src;
  250. else img.src = this.domain + src;
  251. } else if (!src.includes('://')) img.src = this.domain + '/' + src;
  252. }
  253. if (!img.hasAttribute('ignore') && img.parentElement.nodeName != 'A') {
  254. img.i = j++;
  255. _ts.imgList.push(img.src || img.getAttribute('data-src'));
  256. img.onclick = function() {
  257. var preview = true;
  258. this.ignore = () => preview = false;
  259. _ts.$emit('imgtap', this);
  260. if (preview) {
  261. uni.previewImage({
  262. current: this.i,
  263. urls: _ts.imgList
  264. });
  265. }
  266. }
  267. }
  268. img.onerror = function() {
  269. _ts.$emit('error', {
  270. source: 'img',
  271. target: this
  272. });
  273. }
  274. if (_ts.lazyLoad && this._observer && img.src && img.i != 0) {
  275. img.setAttribute('data-src', img.src);
  276. img.removeAttribute('src');
  277. this._observer.observe(img);
  278. }
  279. }
  280. // 链接处理
  281. var links = this.rtf.getElementsByTagName('a');
  282. for (var link of links) {
  283. link.onclick = function() {
  284. var jump = true,
  285. href = this.getAttribute('href');
  286. _ts.$emit('linkpress', {
  287. href,
  288. ignore: () => jump = false
  289. });
  290. if (jump && href) {
  291. if (href[0] == '#') {
  292. if (_ts.useAnchor) {
  293. _ts.navigateTo({
  294. id: href.substr(1)
  295. })
  296. }
  297. } else if (href.indexOf('http') == 0 || href.indexOf('//') == 0)
  298. return true;
  299. else {
  300. uni.navigateTo({
  301. url: href
  302. })
  303. }
  304. }
  305. return false;
  306. }
  307. }
  308. // 视频处理
  309. var videos = this.rtf.getElementsByTagName('video');
  310. _ts.videoContexts = videos;
  311. for (let video, i = 0; video = videos[i++];) {
  312. video.style.maxWidth = '100%';
  313. video.onerror = function() {
  314. _ts.$emit('error', {
  315. source: 'video',
  316. target: this
  317. });
  318. }
  319. video.onplay = function() {
  320. if (_ts.autopause)
  321. for (let item, i = 0; item = _ts.videoContexts[i++];)
  322. if (item != this) item.pause();
  323. }
  324. }
  325. // 音频处理
  326. var audios = this.rtf.getElementsByTagName('audios');
  327. for (var audio of audios)
  328. audio.onerror = function() {
  329. _ts.$emit('error', {
  330. source: 'audio',
  331. target: this
  332. });
  333. }
  334. this.document = this.rtf;
  335. if (!append) document.getElementById('rtf' + this._uid).appendChild(this.rtf);
  336. this.$nextTick(() => {
  337. this.nodes = [1];
  338. this.$emit('load');
  339. })
  340. setTimeout(() => this.showAm = '', 500);
  341. // #endif
  342. // #ifndef H5
  343. var nodes;
  344. if (!html)
  345. return this.nodes = [];
  346. else if (typeof html == 'string') {
  347. let parser = new Parser(html, this);
  348. // 缓存读取
  349. if (this.useCache) {
  350. var hashVal = hash(html);
  351. if (cache[hashVal])
  352. nodes = cache[hashVal];
  353. else {
  354. nodes = parser.parse();
  355. cache[hashVal] = nodes;
  356. }
  357. } else nodes = parser.parse();
  358. this.$emit('parse', nodes);
  359. } else if (Object.prototype.toString.call(html) == '[object Array]') {
  360. // 非本插件产生的 array 需要进行一些转换
  361. if (html.length && html[0].PoweredBy != 'Parser') {
  362. let parser = new Parser(html, this);
  363. (function f(ns) {
  364. for (var i = 0, n; n = ns[i]; i++) {
  365. if (n.type == 'text') continue;
  366. n.attrs = n.attrs || {};
  367. for (var item in n.attrs)
  368. if (typeof n.attrs[item] != 'string') n.attrs[item] = n.attrs[item].toString();
  369. parser.matchAttr(n, parser);
  370. if (n.children && n.children.length) {
  371. parser.STACK.push(n);
  372. f(n.children);
  373. parser.popNode(parser.STACK.pop());
  374. } else n.children = void 0;
  375. }
  376. })(html);
  377. }
  378. nodes = html;
  379. } else if (typeof html == 'object' && html.nodes) {
  380. nodes = html.nodes;
  381. console.warn('错误的 html 类型:object 类型已废弃');
  382. } else
  383. return console.warn('错误的 html 类型:' + typeof html);
  384. // #ifdef APP-PLUS
  385. this.loadVideo = false;
  386. // #endif
  387. if (document) this.document = new document(this.nodes, 'nodes', this);
  388. if (append) this.nodes = this.nodes.concat(nodes);
  389. else this.nodes = nodes;
  390. if (nodes.length && nodes[0].title && this.autosetTitle)
  391. uni.setNavigationBarTitle({
  392. title: nodes[0].title
  393. })
  394. this.$nextTick(() => {
  395. this.imgList.length = 0;
  396. this.videoContexts = [];
  397. // #ifdef MP-TOUTIAO
  398. setTimeout(() => {
  399. // #endif
  400. var f = (cs) => {
  401. for (let i = 0, c; c = cs[i++];) {
  402. if (c.$options.name == 'trees') {
  403. for (var j = c.nodes.length, item; item = c.nodes[--j];) {
  404. if (item.c) continue;
  405. if (item.name == 'img') {
  406. this.imgList.setItem(item.attrs.i, item.attrs.src);
  407. // #ifndef MP-ALIPAY
  408. if (!c.observer && !c.imgLoad && item.attrs.i != '0') {
  409. if (this.lazyLoad && uni.createIntersectionObserver) {
  410. c.observer = uni.createIntersectionObserver(c);
  411. c.observer.relativeToViewport({
  412. top: 900,
  413. bottom: 900
  414. }).observe('._img', () => {
  415. c.imgLoad = true;
  416. c.observer.disconnect();
  417. })
  418. } else
  419. c.imgLoad = true;
  420. }
  421. // #endif
  422. }
  423. // #ifndef MP-ALIPAY
  424. else if (item.name == 'video') {
  425. var ctx = uni.createVideoContext(item.attrs.id, c);
  426. ctx.id = item.attrs.id;
  427. this.videoContexts.push(ctx);
  428. }
  429. // #endif
  430. // #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
  431. if (item.attrs && item.attrs.id) {
  432. this.anchors = this.anchors || [];
  433. this.anchors.push({
  434. id: item.attrs.id,
  435. node: c
  436. })
  437. }
  438. // #endif
  439. }
  440. }
  441. if (c.$children.length)
  442. f(c.$children)
  443. }
  444. }
  445. f(this.$children);
  446. // #ifdef MP-TOUTIAO
  447. }, 200)
  448. this.$emit('load');
  449. // #endif
  450. // #ifdef APP-PLUS
  451. setTimeout(() => {
  452. this.loadVideo = true;
  453. }, 3000);
  454. // #endif
  455. })
  456. // #endif
  457. var height;
  458. clearInterval(this._timer);
  459. this._timer = setInterval(() => {
  460. // #ifdef H5
  461. var res = [this.rtf.getBoundingClientRect()];
  462. // #endif
  463. // #ifndef APP-PLUS || H5
  464. this.createSelectorQuery()
  465. // #endif
  466. // #ifdef APP-PLUS
  467. uni.createSelectorQuery().in(this)
  468. // #endif
  469. // #ifndef H5
  470. .select('.top').boundingClientRect().exec(res => {
  471. // #endif
  472. this.width = res[0].width;
  473. if (res[0].height == height) {
  474. this.$emit('ready', res[0])
  475. clearInterval(this._timer);
  476. }
  477. height = res[0].height;
  478. // #ifndef H5
  479. });
  480. // #endif
  481. }, 350)
  482. if (this.showWithAnimation && !append) this.showAm = 'animation:show .5s';
  483. },
  484. getText(ns = this.html || this.nodes) {
  485. // #ifdef H5
  486. return this.rtf.innerText;
  487. // #endif
  488. // #ifndef H5
  489. var txt = '';
  490. for (var i = 0, n; n = ns[i++];) {
  491. if (n.type == 'text') txt += n.txt.replace(/&nbsp;/g, '\u00A0').replace(/&lt;/g, '<').replace(/&gt;/g, '>')
  492. .replace(/&amp;/g, '&');
  493. else if (n.type == 'br') txt += '\n';
  494. else {
  495. // 块级标签前后加换行
  496. var block = n.name == 'p' || n.name == 'div' || n.name == 'tr' || n.name == 'li' || (n.name[0] == 'h' && n.name[1] >
  497. '0' && n.name[1] < '7');
  498. if (block && txt && txt[txt.length - 1] != '\n') txt += '\n';
  499. if (n.children) txt += this.getText(n.children);
  500. if (block && txt[txt.length - 1] != '\n') txt += '\n';
  501. else if (n.name == 'td' || n.name == 'th') txt += '\t';
  502. }
  503. }
  504. return txt;
  505. // #endif
  506. },
  507. navigateTo(obj) {
  508. if (!this.useAnchor)
  509. return obj.fail && obj.fail({
  510. errMsg: 'Anchor is disabled'
  511. })
  512. // #ifdef H5
  513. if (!obj.id) {
  514. window.scrollTo(0, this.rtf.offsetTop);
  515. return obj.success && obj.success({
  516. errMsg: 'pageScrollTo:ok'
  517. });
  518. }
  519. var target = document.getElementById(obj.id);
  520. if (!target) return obj.fail && obj.fail({
  521. errMsg: 'Label not found'
  522. });
  523. obj.scrollTop = this.rtf.offsetTop + target.offsetTop;
  524. uni.pageScrollTo(obj);
  525. // #endif
  526. // #ifndef H5
  527. var Scroll = (selector, component) => {
  528. uni.createSelectorQuery().in(component ? component : this).select(selector).boundingClientRect().selectViewport()
  529. .scrollOffset()
  530. .exec(res => {
  531. if (!res || !res[0])
  532. return obj.fail && obj.fail({
  533. errMsg: 'Label not found'
  534. });
  535. obj.scrollTop = res[1].scrollTop + res[0].top;
  536. uni.pageScrollTo(obj);
  537. })
  538. }
  539. if (!obj.id) Scroll('.top');
  540. else {
  541. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  542. Scroll('.top >>> #' + obj.id + ', .top >>> .' + obj.id);
  543. // #endif
  544. // #ifdef MP-BAIDU || MP-ALIPAY || APP-PLUS
  545. for (var anchor of this.anchors)
  546. if (anchor.id == obj.id)
  547. Scroll('#' + obj.id + ', .' + obj.id, anchor.node);
  548. // #endif
  549. }
  550. // #endif
  551. },
  552. getVideoContext(id) {
  553. if (!id) return this.videoContexts;
  554. else
  555. for (var i = this.videoContexts.length; i--;)
  556. if (this.videoContexts[i].id == id) return this.videoContexts[i];
  557. },
  558. // 预加载
  559. preLoad(html, num) {
  560. // #ifdef H5
  561. if (html.constructor == Array)
  562. html = this._Dom2Str(html);
  563. var contain = document.createElement('div');
  564. contain.innerHTML = html;
  565. var imgs = contain.querySelectorAll('img');
  566. for (var i = imgs.length - 1; i >= num; i--)
  567. imgs[i].removeAttribute('src');
  568. // #endif
  569. // #ifndef H5
  570. if (typeof html == 'string') {
  571. var id = hash(html);
  572. html = new Parser(html, this).parse();
  573. cache[id] = html;
  574. }
  575. var wait = [];
  576. (function f(ns) {
  577. for (var i = 0, n; n = ns[i++];) {
  578. if (n.name == 'img' && n.attrs.src && !wait.includes(n.attrs.src))
  579. wait.push(n.attrs.src);
  580. f(n.children || []);
  581. }
  582. })(html);
  583. if (num) wait = wait.slice(0, num);
  584. this._wait = (this._wait || []).concat(wait);
  585. if (!this.imgs) this.imgs = this._wait.splice(0, 15);
  586. else if (this.imgs.length < 15)
  587. this.imgs = this.imgs.concat(this._wait.splice(0, 15 - this.imgs.length));
  588. // #endif
  589. },
  590. _load(e) {
  591. // #ifndef H5
  592. if (this._wait.length)
  593. this.$set(this.imgs, e.target.id, this._wait.shift());
  594. // #endif
  595. },
  596. _tap(e) {
  597. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  598. if (this.gestureZoom && e.timeStamp - this._lastT < 300) {
  599. var initY = e.touches[0].pageY - e.currentTarget.offsetTop;
  600. if (this._zoom) {
  601. this._scaleAm.translateX(0).scale(1).step();
  602. uni.pageScrollTo({
  603. scrollTop: (initY + this._initY) / 2 - e.touches[0].clientY,
  604. duration: 400
  605. })
  606. } else {
  607. var initX = e.touches[0].pageX - e.currentTarget.offsetLeft;
  608. this._initY = initY;
  609. this._scaleAm = uni.createAnimation({
  610. transformOrigin: `${initX}px ${this._initY}px 0`,
  611. timingFunction: 'ease-in-out'
  612. });
  613. // #ifdef MP-TOUTIAO
  614. this._scaleAm.opacity(1);
  615. // #endif
  616. this._scaleAm.scale(2).step();
  617. this._tMax = initX / 2;
  618. this._tMin = (initX - this.width) / 2;
  619. this._tX = 0;
  620. }
  621. this._zoom = !this._zoom;
  622. this.scaleAm = this._scaleAm.export();
  623. }
  624. this._lastT = e.timeStamp;
  625. // #endif
  626. },
  627. _touchstart(e) {
  628. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  629. if (e.touches.length == 1)
  630. this._initX = this._lastX = e.touches[0].pageX;
  631. // #endif
  632. },
  633. _touchmove(e) {
  634. // #ifndef MP-BAIDU || MP-ALIPAY || APP-PLUS
  635. var diff = e.touches[0].pageX - this._lastX;
  636. if (this._zoom && e.touches.length == 1 && Math.abs(diff) > 20) {
  637. this._lastX = e.touches[0].pageX;
  638. if ((this._tX <= this._tMin && diff < 0) || (this._tX >= this._tMax && diff > 0))
  639. return;
  640. this._tX += (diff * Math.abs(this._lastX - this._initX) * 0.05);
  641. if (this._tX < this._tMin) this._tX = this._tMin;
  642. if (this._tX > this._tMax) this._tX = this._tMax;
  643. this._scaleAm.translateX(this._tX).step();
  644. this.scaleAm = this._scaleAm.export();
  645. }
  646. // #endif
  647. }
  648. }
  649. }
  650. </script>
  651. <style>
  652. @keyframes show {
  653. 0% {
  654. opacity: 0
  655. }
  656. 100% {
  657. opacity: 1;
  658. }
  659. }
  660. /* #ifdef MP-WEIXIN */
  661. :host {
  662. display: block;
  663. overflow: scroll;
  664. -webkit-overflow-scrolling: touch;
  665. }
  666. .top {
  667. display: inherit;
  668. }
  669. /* #endif */
  670. </style>