├── .babelrc ├── .gitignore ├── License ├── Notice ├── README.md ├── _config.yml ├── build └── index.js ├── dist └── vue-recyclerview.js ├── docs ├── index.html ├── preview-layers.gif ├── preview3.gif └── static │ ├── css │ ├── app.2bb0d9dc847da20230442720d2103dcb.css │ ├── app.2bb0d9dc847da20230442720d2103dcb.css.map │ ├── app.7d3ad7e88e99cf18d39465516da7579f.css │ └── app.7d3ad7e88e99cf18d39465516da7579f.css.map │ ├── images │ ├── avatar0.jpg │ ├── avatar1.jpg │ ├── avatar2.jpg │ ├── avatar3.jpg │ ├── change.svg │ ├── image0.jpg │ ├── image1.jpg │ ├── image10.jpg │ ├── image11.jpg │ ├── image12.jpg │ ├── image13.jpg │ ├── image14.jpg │ ├── image15.jpg │ ├── image16.jpg │ ├── image17.jpg │ ├── image18.jpg │ ├── image19.jpg │ ├── image2.jpg │ ├── image20.jpg │ ├── image21.jpg │ ├── image22.jpg │ ├── image23.jpg │ ├── image24.jpg │ ├── image25.jpg │ ├── image26.jpg │ ├── image27.jpg │ ├── image28.jpg │ ├── image29.jpg │ ├── image3.jpg │ ├── image30.jpg │ ├── image31.jpg │ ├── image32.jpg │ ├── image33.jpg │ ├── image34.jpg │ ├── image35.jpg │ ├── image36.jpg │ ├── image37.jpg │ ├── image38.jpg │ ├── image39.jpg │ ├── image4.jpg │ ├── image40.jpg │ ├── image41.jpg │ ├── image42.jpg │ ├── image43.jpg │ ├── image44.jpg │ ├── image45.jpg │ ├── image46.jpg │ ├── image47.jpg │ ├── image48.jpg │ ├── image49.jpg │ ├── image5.jpg │ ├── image50.jpg │ ├── image51.jpg │ ├── image52.jpg │ ├── image53.jpg │ ├── image54.jpg │ ├── image55.jpg │ ├── image56.jpg │ ├── image57.jpg │ ├── image58.jpg │ ├── image59.jpg │ ├── image6.jpg │ ├── image60.jpg │ ├── image61.jpg │ ├── image62.jpg │ ├── image63.jpg │ ├── image64.jpg │ ├── image65.jpg │ ├── image66.jpg │ ├── image67.jpg │ ├── image68.jpg │ ├── image69.jpg │ ├── image7.jpg │ ├── image70.jpg │ ├── image71.jpg │ ├── image72.jpg │ ├── image73.jpg │ ├── image74.jpg │ ├── image75.jpg │ ├── image76.jpg │ ├── image8.jpg │ ├── image9.jpg │ ├── loading-spin.svg │ ├── unknown.jpg │ └── wechat-more.svg │ └── js │ ├── app.2b25506cb0d292f91404.js │ ├── app.56c384006672e90daf9f.js │ ├── manifest.73190b5d0bff45a0b48b.js │ ├── manifest.796267cb3f01c4fdf4e1.js │ ├── recyclerview.js │ ├── stats.min.js │ ├── vendor.a9735cae838026c96895.js │ └── vendor.ac6550a3b63e7c994c54.js ├── examples ├── component │ ├── App.vue │ ├── Item.vue │ ├── Tombstone.vue │ ├── index.js │ └── mi-fetch.js └── simple │ ├── index.html │ └── mi-fetch.js ├── package.json └── src ├── content-source.js ├── index.js ├── infinite.js ├── polyfill.js ├── recyclerview.css ├── recyclerview.js └── util.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ], 5 | "plugins": [ 6 | "external-helpers" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | **/.DS_Store 4 | *.pyc 5 | serve 6 | *.pem 7 | .vscode 8 | npm-debug.log 9 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Awe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Notice: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2014 Google Inc. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-recyclerview 2 | 3 | [![npm](https://img.shields.io/npm/v/vue-recyclerview.svg)](https://www.npmjs.com/package/vue-recyclerview) 4 | 5 | Mastering Large Lists with the vue-recyclerview 6 | 7 | ## Feature 8 | 9 | - DOM recyleing 10 | - Multiple column 11 | - Waterflow 12 | 13 | ## Preview 14 | 15 | ![](https://hilongjw.github.io/vue-recyclerview/preview3.gif) 16 | 17 | ## Demo 18 | 19 | [https://hilongjw.github.io/vue-recyclerview/](https://hilongjw.github.io/vue-recyclerview/) 20 | 21 | ## Requirements 22 | 23 | Vue 2.0 + 24 | 25 | ## Installation 26 | 27 | ### Direct Download / CDN 28 | 29 | https://unpkg.com/vue-recyclerview/dist/vue-recyclerview 30 | 31 | [unpkg.com](https://unpkg.com) provides NPM-based CDN links. The above link will always point to the latest release on NPM. You can also use a specific version/tag via URLs like https://unpkg.com/vue-recyclerview/dist/vue-recyclerview.js 32 | 33 | Include vue-recyclerview after Vue and it will install itself automatically: 34 | 35 | ```html 36 | 37 | 38 | ``` 39 | 40 | ### NPM 41 | 42 | ```bash 43 | $ npm install vue-recyclerview 44 | ``` 45 | 46 | When used with a module system, you must explicitly install the `vue-recyclerview` via `Vue.use()`: 47 | 48 | ```javascript 49 | import Vue from 'vue' 50 | import VueRecyclerviewNew from 'vue-recyclerview' 51 | 52 | Vue.use(VueRecyclerviewNew) 53 | ``` 54 | 55 | You don't need to do this when using global script tags. 56 | 57 | ### Dev Build 58 | 59 | You will have to clone directly from GitHub and build `vue-recyclerview` yourself if 60 | you want to use the latest dev build. 61 | 62 | $ git clone git@github.com:hilongjw/vue-recyclerview.git node_modules/vue-recyclerview 63 | $ cd node_modules/vue-recyclerview 64 | $ npm install 65 | $ npm run build 66 | 67 | 68 | ## Getting Started 69 | 70 | > We will be using [ES2015](https://github.com/lukehoban/es6features) in the code samples in the guide. 71 | 72 | ### main.js 73 | 74 | ```javascript 75 | // If using a module system (e.g. via vue-cli), import Vue and RecyclerView and then call Vue.use(RecyclerView). 76 | // import Vue from 'vue' 77 | // import RecyclerView from 'vue-recyclerview' 78 | // import App from './App.vue' 79 | // Vue.use(RecyclerView) 80 | 81 | // Now the app has started! 82 | new Vue({ 83 | render: h => h(App) 84 | }).$mount('#app') 85 | ``` 86 | 87 | ### App.vue 88 | 89 | ```html 90 | 101 | 102 | 118 | ``` 119 | 120 | [Full example code](https://github.com/hilongjw/vue-recyclerview/blob/master/examples/component) 121 | 122 | ## Props Options 123 | 124 | |key|description|defualt|type/options| 125 | |:---|---|---|---| 126 | | `fetch`|Data fetching function ||| 127 | |`list`|List data of RecyclerView|[]| 128 | |`prerender`|Number of items to instantiate beyond current view in the opposite direction.|20|Number| 129 | |`remain`|Number of items to instantiate beyond current view in the opposite direction.|10|Number| 130 | |`column`|Specifies how many columns the listings should be displayed in|1|Number| 131 | |`item`|The Vue component of RecyclerView's item||Vue component| 132 | |`tombstone`|The Vue component of RecyclerView's tombstone||Vue component| 133 | |`loading`|The loading component behind the RecyclerView pull-to-refresh |built-in loading|Vue component| 134 | |`options`|advanced options|-|Object| 135 | 136 | 137 | - fetch:Function 138 | 139 | ``` 140 | function fetch (limit:Number, skip:Number) { 141 | return Promise.resolve({ 142 | list: list // Array, 143 | count: count // Number 144 | }) 145 | } 146 | 147 | ``` 148 | 149 | - list 150 | 151 | 152 | ```javascript 153 | [ 154 | // item 155 | { 156 | vm: vm, // 157 | data: { 158 | name: 'test' 159 | }, 160 | node: null, 161 | height: 100, 162 | width: 100, 163 | top: 0, 164 | }, 165 | // tombstone 166 | { 167 | vm: null 168 | data: null, 169 | node: null, 170 | height: 100, 171 | width: 100, 172 | top: 0, 173 | }] 174 | ``` 175 | 176 | - options 177 | 178 | ```vue 179 | 191 | ``` 192 | 193 | ```javascript 194 | data () { 195 | return { 196 | wechatOptions: { 197 | reuseVM: true, 198 | usePrefix: true, 199 | props: { 200 | color: { 201 | value: '' 202 | } 203 | } 204 | } 205 | } 206 | } 207 | ``` 208 | 209 | default: 210 | 211 | ```javascript 212 | const options = { 213 | preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|IMG)$/ }, 214 | distance: 50, 215 | animation_duration_ms: 200, 216 | tombstone_class: 'tombstone', 217 | invisible_class: 'invisible', 218 | prerender: 20, 219 | remain: 10, 220 | preventDefault: false, 221 | column: 1, 222 | waterflow: false, 223 | cacheVM: 0, 224 | reuseVM: false, 225 | usePrefix: false, 226 | props: {} 227 | } 228 | 229 | ``` 230 | 231 | ## Instance Method 232 | 233 | - scrollToIndex 234 | 235 | ```javascript 236 | this.$refs.RecyclerView.scrollToIndex(100) 237 | 238 | ``` 239 | 240 | ## License 241 | 242 | [MIT](https://github.com/hilongjw/vue-recyclerview/blob/master/License) 243 | 244 | the project inspired by [infinite-scroller](https://github.com/GoogleChrome/ui-element-samples/tree/gh-pages/infinite-scroller) 245 | 246 | 247 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /build/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var rollup = require('rollup') 4 | var babel = require('rollup-plugin-babel') 5 | var uglify = require('rollup-plugin-uglify') 6 | var postcss = require('rollup-plugin-postcss') 7 | var cssnano = require('cssnano') 8 | 9 | var version = process.env.VERSION || require('../package.json').version 10 | 11 | var banner = 12 | '/*!\n' + 13 | ' * Vue-RecyclerView.js v' + version + '\n' + 14 | ' * (c) ' + new Date().getFullYear() + ' Awe \n' + 15 | ' * Released under the MIT License.\n' + 16 | ' */\n' 17 | 18 | rollup.rollup({ 19 | entry: path.resolve(__dirname, '..', 'src/index.js'), 20 | plugins: [ 21 | postcss({ 22 | plugins: [cssnano()], 23 | extensions: ['.css'] 24 | }), 25 | babel(), 26 | uglify() 27 | ] 28 | }) 29 | .then(bundle => { 30 | return write(path.resolve(__dirname, '../dist/vue-recyclerview.js'), bundle.generate({ 31 | format: 'umd', 32 | moduleName: 'RecyclerView' 33 | }).code) 34 | }) 35 | .then(() => { 36 | console.log('vue-recyclerview.js v' + version + ' builded') 37 | }) 38 | .catch(console.log) 39 | 40 | function getSize (code) { 41 | return (code.length / 1024).toFixed(2) + 'kb' 42 | } 43 | 44 | function blue (str) { 45 | return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' 46 | } 47 | 48 | function write (dest, code) { 49 | return new Promise(function (resolve, reject) { 50 | code = banner + code 51 | fs.writeFile(dest, code, function (err) { 52 | if (err) return reject(err) 53 | console.log(blue(dest) + ' ' + getSize(code)) 54 | resolve() 55 | }) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /dist/vue-recyclerview.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Vue-RecyclerView.js v0.4.1 3 | * (c) 2018 Awe 4 | * Released under the MIT License. 5 | */ 6 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.RecyclerView=e()}(this,function(){"use strict";function t(t){if(!t)return u;if(d.test(t.type)){var e=t.touches[0];return{x:e.clientX,y:e.clientY}}return m.test(t.type)?{x:t.clientX,y:t.clientY}:u}function e(t,e){for(var i in e)if(e[i].test(t[i]))return!0;return!1}function i(t,e){if(null==t)throw new TypeError("Cannot convert undefined or null to object");for(var i=Object(t),s=1;s1&&void 0!==arguments[1]&&arguments[1],w(t));return t.component(e.name,e),e}var a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},l=(function(){function t(t){this.value=t}function e(e){function i(t,e){return new Promise(function(i,o){var h={key:t,arg:e,resolve:i,reject:o,next:null};r?r=r.next=h:(n=r=h,s(t,e))})}function s(i,n){try{var r=e[i](n),h=r.value;h instanceof t?Promise.resolve(h.value).then(function(t){s("next",t)},function(t){s("throw",t)}):o(r.done?"return":"normal",r.value)}catch(t){o("throw",t)}}function o(t,e){switch(t){case"return":n.resolve({value:e,done:!0});break;case"throw":n.reject(e);break;default:n.resolve({value:e,done:!1})}n=n.next,n?s(n.key,n.arg):r=null}var n,r;this._invoke=i,"function"!=typeof e.return&&(this.return=void 0)}"function"==typeof Symbol&&Symbol.asyncIterator&&(e.prototype[Symbol.asyncIterator]=function(){return this}),e.prototype.next=function(t){return this._invoke("next",t)},e.prototype.throw=function(t){return this._invoke("throw",t)},e.prototype.return=function(t){return this._invoke("return",t)}}(),function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}),c=function(){function t(t,e){for(var i=0;i0&&this.items_[i-1].height;)e+=this.items_[i-1].height,i--;s=Math.max(-i,Math.ceil(Math.min(e,0)/this.tombstoneSize_))}else{for(;e>0&&i=this.items_.length||!this.items_[i].height)&&(s=Math.floor(Math.max(e,0)/this.tombstoneSize_))}return i+=s,e-=s*this.tombstoneSize_,i=Math.min(i,this.MAX_COUNT-1),{index:Math.floor(i/this.column)*this.column,offset:e}},setStyle:function(t,e,i){o(t,e,i,this.options.usePrefix)},fill:function(t,e){this.firstAttachedItem_=Math.max(0,t),this.lastAttachedItem_=e,this.attachContent()},getTombstone:function(){var t=this.tombstones_.pop();return t?(t.classList.remove(this.INVISIBLE_CLASS),t.style.opacity=1,this.setStyle(t,"transform",""),this.setStyle(t,"transition",""),t):this.source_.createTombstone(this.baseNode.cloneNode(!0))},layoutInView:function(t){var e=this.posList.get(Math.floor(t/this.column),t%this.column);if(!e)return!0;var i=e-this.anchorScrollTop;return i>.5*-window.innerHeight&&ithis.firstAttachedItem_;)this.curPos-=this.items_[e-1].height||this.tombstoneSize_,e--;for(;ethis.MAX_COUNT&&(this.lastAttachedItem_=this.MAX_COUNT),s=this.firstAttachedItem_;si.cacheVM&&i.cacheVM>50&&this.recycle(10,t)},recycle:function(t,e){for(var i=void 0,s=Object.keys(this.data),o=s.length;t;)t--,i=s[Math.floor(Math.random()*o)],this.data[i]&&this.length--&&this.data[i].$destroy(),this.data[i]=null}},this.reuseVM={queue:[],generate:function(t,e){var i=n(s.reuseVM.queue,function(t){return!t.inuse});if(i)i.vm.data=t,i.inuse=!0,i.id=t.id;else{var o={props:{data:t}};s.options.props.data=t,s.options.props&&Object.keys(s.options.props).map(function(t){o.props[t]=s.options.props[t]});var r={el:e,data:o.props,render:function(t){return t(s.itemRender,o)}};i={id:t.id,inuse:!0,vm:new s.Vue(r)},s.reuseVM.queue.push(i)}return i.vm},free:function(t){n(this.queue,function(e){return e.id===t}).inuse=!1},destroy:function(t,e){for(var i=0,s=this.queue.length;i0||(this.pulling=!0,this.startPointer=t(i),o(this.$list,"transition","transform .2s",this.options.usePrefix),this.preventDefault&&!e(i.target,this._options.preventDefaultException)&&i.preventDefault())},_move:function(i){if(this.pulling){var s=t(i),o=s.y-this.startPointer.y;if(o<0)return void this._scrollTo(-o);this.preventDefault&&!e(i.target,this._options.preventDefaultException)&&i.preventDefault(),this.distance=Math.floor(.5*o),this.distance>this._options.distance&&(this.distance=this._options.distance),f(this._renderListStyle.bind(this))}},_end:function(t){var i=this;this.pulling&&(this.preventDefault&&!e(t.target,this._options.preventDefaultException)&&t.preventDefault(),this.pulling=!1,this.$list.style.transition="transform .3s",this.$nextTick(function(){i.$list.style.transform=""}),this.distance>=this._options.distance&&(this.distance=0,this._scroller.clear()))}}}};!function(t,e){void 0===e&&(e={});var i=e.insertAt;if(t&&"undefined"!=typeof document){var s=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===i&&s.firstChild?s.insertBefore(o,s.firstChild):s.appendChild(o),o.styleSheet?o.styleSheet.cssText=t:o.appendChild(document.createTextNode(t))}}(".recyclerview-container{position:relative}.recyclerview-loading{position:absolute;top:0;left:0;width:100%;text-align:center;padding:10px;font-size:14px;color:#9e9e9e}.recyclerview{background:#fff;margin:0;padding:0;overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch;width:100%;height:100%;position:absolute;box-sizing:border-box;contain:layout;will-change:transform}",{});var S={install:h};return"undefined"!=typeof window&&window.Vue&&window.Vue.use(h),S}); 7 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | vue-recyclerview
-------------------------------------------------------------------------------- /docs/preview-layers.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/preview-layers.gif -------------------------------------------------------------------------------- /docs/preview3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/preview3.gif -------------------------------------------------------------------------------- /docs/static/css/app.2bb0d9dc847da20230442720d2103dcb.css: -------------------------------------------------------------------------------- 1 | html{background:#eee}body,html{min-height:100%;margin:0;padding:0;overflow:hidden;font-family:Roboto,sans-serif;-webkit-tap-highlight-color:rgba(0,0,0,0)}.wechat .recyclerview,html .wechat-list{background:#efefef}.switch{position:fixed;top:50px;right:0;z-index:100;background:#03a9f4;color:#fff;font-size:14px;padding:10px}.nav-header{background:#373b3e;height:50px;line-height:50px;color:#fff;padding:0 20px}.nav-header-action{position:absolute;height:50px;width:50px;padding-right:10px;top:0;right:0;color:#fff;line-height:45px;text-align:right}.nav-header-action-icon{width:26px;margin-top:14px}.recyclerview-container{height:600px;height:calc(100vh - 50px)}.testlist{-ms-flex-wrap:wrap;flex-wrap:wrap}.chat-view-footer,.testlist{display:-ms-flexbox;display:flex}.chat-view-footer{position:fixed;bottom:0;width:100%;height:50px}.wechat{height:calc(100vh - 100px)}.chat-input{width:100%;height:50px;border:none;outline:none;padding:5px;line-height:14px}.chat-send-btn{-ms-flex-negative:0;flex-shrink:0;width:100px;height:50px;background:#8bc34a;border:none;color:#fff;font-size:14px}.chat-item{display:-ms-flexbox;display:flex;padding:10px 0;width:100%;contain:layout;will-change:transform}.avatar{border-radius:4px;margin-left:10px;margin-right:6px;height:30px;width:30px}.chat-item p{margin:0;word-wrap:break-word;font-size:13px}.chat-item.tombstone p{width:100%;height:.5em;background-color:#ccc;margin:.5em 0}.chat-item .bubble img{max-width:100%;height:auto}.bubble{padding:7px 10px;color:#333;background:#fff;border-radius:4px;position:relative;max-width:420px;min-width:80px;margin:0 5px}.bubble:before{content:"";border-style:solid;border-width:5px 5px 5px 0;border-color:transparent #fff transparent transparent;position:absolute;top:10.6px;left:-5px}.meta{font-size:.8rem;color:#999;margin-top:3px}.from-me{-ms-flex-pack:end;justify-content:flex-end}.from-me .avatar{-ms-flex-order:1;order:1;margin-left:6px;margin-right:10px}.from-me .bubble{background:#a2e563}.from-me .bubble:before{left:100%;border-width:5px;border-color:transparent transparent transparent #a2e563}.invisible,.state{display:none}@media screen and (max-width:640px){.bubble{max-width:220px}}.mi-item{padding:0 0 3px;position:absolute;background:#fff;width:100%;list-style:none}.mi-item:after{content:"";position:absolute;bottom:1.5px;left:150px;right:0;border-bottom:1px solid #e4e4e4}.version-item{display:-ms-flexbox;display:flex;display:-webkit-box;box-align:center;-webkit-box-align:center;width:100%}.version-item-img{width:180px;height:180px;position:relative}.version-item-img img{width:100%}.version-item .version-item-intro{box-flex:1;display:block;padding:0 15px 5px}.version-item .version-item-intro .version-item-name{font-size:14px;color:rgba(0,0,0,.87);margin-bottom:8px}.version-item .version-item-intro .version-item-brief{font-size:12px;color:rgba(0,0,0,.54);margin-bottom:8px;line-height:15px;overflow:hidden}.version-item-brief p{text-overflow:ellipsis;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;word-break:break-all}.version-item .version-item-intro .version-item-intro-price{font-size:15px;position:relative;margin-left:10px}.version-item .version-item-intro span{color:#ff6000}.mi-item.tombstone p{width:100%;height:.5em;background-color:#ccc;margin:.5em 0}.common-list .mi-item{position:relative;list-style:none}.action-filter{top:50px;height:100vh;background:rgba(0,0,0,.31);z-index:100}.action-filter,.action-modal{position:fixed;left:0;width:100%}.action-modal{display:-ms-flexbox;display:flex;bottom:0;min-height:200px;background:#fff;z-index:101;padding:20px;box-sizing:border-box}.action-item{width:25%;text-align:center;color:#6d6d6d}.action-item-icon{font-size:30px;margin-bottom:5px}.action-item-text{font-size:12px}.simple-item{position:relative;width:33.33%;min-height:150px;background:#000;border:1px solid #000;box-sizing:border-box;will-change:transform}.simple-item-id{position:absolute;top:5px;left:5px;color:#737373;background:#fff;padding:5px;border-radius:4px;min-width:20px;text-align:center}.simple-item-image{width:100%;min-height:150px;vertical-align:bottom}.simple-item-loading{position:absolute;top:50%;left:50%;margin:-16px 0 0 -16px;vertical-align:bottom}.recyclerview-container{position:relative}.recyclerview-loading{position:absolute;top:0;left:0;width:100%;text-align:center;padding:10px;font-size:14px;color:#9e9e9e}.recyclerview{background:#fff;margin:0;padding:0;overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch;width:100%;height:100%;position:absolute;box-sizing:border-box;contain:layout;will-change:transform} -------------------------------------------------------------------------------- /docs/static/css/app.2bb0d9dc847da20230442720d2103dcb.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./src/App.vue","webpack:///./src/components/ChatItem.vue","webpack:///./src/components/MiItem.vue","webpack:///./src/components/MiTombstone.vue","webpack:///./src/components/MiCommonList.vue","webpack:///./src/components/ActionModal.vue","webpack:///./src/components/TestItem.vue","webpack:///./src/components/TestTombstone.vue","webpack:///./src/recyclerview/recyclerview.css"],"names":[],"mappings":"AACA,KACE,eAAoB,CAEtB,UACE,gBACA,SACA,UACA,gBACA,8BACA,yCAA2C,CAE7C,wCAEE,kBAAoB,CAEtB,QACE,eACA,SACA,QACA,YACA,mBACA,WACA,eACA,YAAc,CAEhB,YACE,mBACA,YACA,iBACA,WACA,cAAgB,CAElB,mBACE,kBACA,YACA,WACA,mBACA,MACA,QACA,WACA,iBACA,gBAAkB,CAEpB,wBACE,WACA,eAAiB,CAEnB,wBACE,aACA,yBAA2B,CAE7B,UAGE,mBACI,cAAgB,CAEtB,4BALE,oBACA,YAAc,CAWf,kBANC,eACA,SACA,WAGA,WAAa,CAEf,QACE,0BAA4B,CAE9B,YACE,WACA,YACA,YACA,aACA,YACA,gBAAkB,CAEpB,eACE,oBACI,cACJ,YACA,YACA,mBACA,YACA,WACA,cAAgB,CCpFlB,WACE,oBACA,aACA,eACA,WACA,eACA,qBAAuB,CAEzB,QACE,kBACA,iBACA,iBACA,YACA,UAAY,CAEd,aACE,SACA,qBACA,cAAgB,CAElB,uBACE,WACA,YACA,sBACA,aAAgB,CAElB,uBACE,eACA,WAAa,CAEf,QACE,iBACA,WACA,gBACA,kBACA,kBACA,gBACA,eACA,YAAc,CAEhB,eACE,WACA,mBACA,2BACA,sDACA,kBACA,WACA,SAAW,CAMb,MACE,gBACA,WACA,cAAgB,CAElB,SACE,kBACI,wBAA0B,CAEhC,iBACE,iBACI,QACJ,gBACA,iBAAmB,CAErB,iBACE,kBAAoB,CAEtB,wBACE,UACA,iBACA,wDAA0D,CAK5D,kBACE,YAAc,CAEhB,oCACA,QACI,eAAiB,CACpB,CCrFD,SACE,gBACA,kBACA,gBACA,WACA,eAAiB,CAEnB,eACE,WACA,kBACA,aACA,WACA,QACA,+BAAiC,CAEnC,cACE,oBACA,aACA,oBACA,iBACA,yBACA,UAAY,CAEd,kBACI,YACA,aACA,iBAAmB,CAEvB,sBACI,UAAY,CAEhB,kCACE,WACA,cACA,kBAAoB,CAEtB,qDACE,eACA,sBACA,iBAAmB,CAErB,sDACE,eACA,sBACA,kBACA,iBACA,eAAiB,CAEnB,sBACI,uBACA,gBACA,oBACA,qBACA,oBAAsB,CAE1B,4DACE,eACA,kBACA,gBAAkB,CAEpB,uCACI,aAAe,CC7DnB,qBACE,WACA,YACA,sBACA,aAAgB,CCJlB,sBACE,kBACA,eAAiB,CCFnB,eAEE,SAEA,aAEA,2BACA,WAAa,CAEf,6BARE,eAEA,OAEA,UAAY,CAgBb,cAXC,oBACA,aAEA,SAGA,iBACA,gBACA,YACA,aACA,qBAAuB,CAEzB,aACE,UACA,kBACA,aAAe,CAEjB,kBACE,eACA,iBAAmB,CAErB,kBACE,cAAgB,CChClB,aACE,kBACA,aACA,iBACA,gBACA,sBACA,sBACA,qBAAuB,CAEzB,gBACE,kBACA,QACA,SACA,cACA,gBACA,YACA,kBACA,eACA,iBAAmB,CAErB,mBACE,WACA,iBACA,qBAAuB,CCvBzB,qBACI,kBACA,QACA,SACA,uBACA,qBAAuB,CCN3B,wBACE,iBAAmB,CAErB,sBACE,kBACA,MACA,OACA,WACA,kBACA,aACA,eACA,aAAe,CAEjB,cACE,gBACA,SACA,UACA,kBACA,kBACA,iCACA,WACA,YACA,kBACA,sBACA,eACA,qBAAuB","file":"static/css/app.2bb0d9dc847da20230442720d2103dcb.css","sourcesContent":["\nhtml {\n background: #eeeeee;\n}\nhtml, body {\n min-height: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n font-family: 'Roboto', sans-serif;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\nhtml .wechat-list,\n.wechat .recyclerview {\n background: #efefef;\n}\n.switch {\n position: fixed;\n top: 50px;\n right: 0;\n z-index: 100;\n background: #03A9F4;\n color: #fff;\n font-size: 14px;\n padding: 10px;\n}\n.nav-header {\n background: #373b3e;\n height: 50px;\n line-height: 50px;\n color: #fff;\n padding: 0 20px;\n}\n.nav-header-action {\n position: absolute;\n height: 50px;\n width: 50px;\n padding-right: 10px;\n top: 0;\n right: 0;\n color: #fff;\n line-height: 45px;\n text-align: right;\n}\n.nav-header-action-icon {\n width: 26px;\n margin-top: 14px;\n}\n.recyclerview-container {\n height: 600px;\n height: calc(100vh - 50px);\n}\n.testlist {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n}\n.chat-view-footer {\n position: fixed;\n bottom: 0;\n width: 100%;\n display: -ms-flexbox;\n display: flex;\n height: 50px;\n}\n.wechat {\n height: calc(100vh - 100px);\n}\n.chat-input {\n width: 100%;\n height: 50px;\n border: none;\n outline: none;\n padding: 5px;\n line-height: 14px;\n}\n.chat-send-btn {\n -ms-flex-negative: 0;\n flex-shrink: 0;\n width: 100px;\n height: 50px;\n background: #8BC34A;\n border: none;\n color: #fff;\n font-size: 14px;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/App.vue","\n.chat-item {\n display: -ms-flexbox;\n display: flex;\n padding: 10px 0;\n width: 100%;\n contain: layout;\n will-change: transform;\n}\n.avatar {\n border-radius: 4px;\n margin-left: 10px;\n margin-right: 6px;\n height: 30px;\n width: 30px;\n}\n.chat-item p {\n margin: 0;\n word-wrap: break-word;\n font-size: 13px;\n}\n.chat-item.tombstone p {\n width: 100%;\n height: 0.5em;\n background-color: #ccc;\n margin: 0.5em 0;\n}\n.chat-item .bubble img {\n max-width: 100%;\n height: auto;\n}\n.bubble {\n padding: 7px 10px;\n color: #333;\n background: #fff;\n border-radius: 4px;\n position: relative;\n max-width: 420px;\n min-width: 80px;\n margin: 0 5px;\n}\n.bubble::before {\n content: '';\n border-style: solid;\n border-width: 5px 5px 5px 0;\n border-color: transparent #fff transparent transparent;\n position: absolute;\n top: 10.6px;\n left: -5px;\n}\n\n/*.bubble img {\n width: 80%;\n}*/\n.meta {\n font-size: 0.8rem;\n color: #999;\n margin-top: 3px;\n}\n.from-me {\n -ms-flex-pack: end;\n justify-content: flex-end;\n}\n.from-me .avatar {\n -ms-flex-order: 1;\n order: 1;\n margin-left: 6px;\n margin-right: 10px;\n}\n.from-me .bubble {\n background: #a2e563;\n}\n.from-me .bubble::before {\n left: 100%;\n border-width: 5px 5px 5px 5px;\n border-color: transparent transparent transparent #a2e563;\n}\n.state {\n display: none;\n}\n.invisible {\n display: none;\n}\n@media screen and (max-width: 640px) {\n.bubble {\n max-width: 220px;\n}\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ChatItem.vue","\n.mi-item {\n padding: 0 0 3px;\n position: absolute;\n background: #fff;\n width: 100%;\n list-style: none;\n}\n.mi-item::after {\n content: \"\";\n position: absolute;\n bottom: 1.5px;\n left: 150px;\n right: 0;\n border-bottom: 1px solid #e4e4e4;\n}\n.version-item {\n display: -ms-flexbox;\n display: flex;\n display: -webkit-box;\n box-align: center;\n -webkit-box-align: center;\n width: 100%;\n}\n.version-item-img {\n width: 180px;\n height: 180px;\n position: relative;\n}\n.version-item-img img {\n width: 100%;\n}\n.version-item .version-item-intro {\n box-flex: 1;\n display: block;\n padding: 0 15px 5px;\n}\n.version-item .version-item-intro .version-item-name {\n font-size: 14px;\n color: rgba(0,0,0,.87);\n margin-bottom: 8px;\n}\n.version-item .version-item-intro .version-item-brief {\n font-size: 12px;\n color: rgba(0,0,0,.54);\n margin-bottom: 8px;\n line-height: 15px;\n overflow: hidden;\n}\n.version-item-brief p {\n text-overflow: ellipsis;\n overflow: hidden;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n word-break: break-all;\n}\n.version-item .version-item-intro .version-item-intro-price {\n font-size: 15px;\n position: relative;\n margin-left: 10px;\n}\n.version-item .version-item-intro span {\n color: #ff6000;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiItem.vue","\n.mi-item.tombstone p {\n width: 100%;\n height: 0.5em;\n background-color: #ccc;\n margin: 0.5em 0;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiTombstone.vue","\n.common-list .mi-item {\n position: relative;\n list-style: none;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiCommonList.vue","\n.action-filter {\n position: fixed;\n top: 50px;\n left: 0;\n height: 100vh;\n width: 100%;\n background: rgba(0, 0, 0, 0.31);\n z-index: 100;\n}\n.action-modal {\n display: -ms-flexbox;\n display: flex;\n position: fixed;\n bottom: 0;\n left: 0;\n width: 100%;\n min-height: 200px;\n background: #fff;\n z-index: 101;\n padding: 20px;\n box-sizing: border-box;\n}\n.action-item {\n width: 25%;\n text-align: center;\n color: #6d6d6d;\n}\n.action-item-icon {\n font-size: 30px;\n margin-bottom: 5px;\n}\n.action-item-text {\n font-size: 12px;\n}\n\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ActionModal.vue","\n.simple-item {\n position: relative;\n width: 33.33%;\n min-height: 150px;\n background: #000;\n border: 1px solid #000;\n box-sizing: border-box;\n will-change: transform;\n}\n.simple-item-id {\n position: absolute;\n top: 5px;\n left: 5px;\n color: #737373;\n background: #fff;\n padding: 5px;\n border-radius: 4px;\n min-width: 20px;\n text-align: center;\n}\n.simple-item-image {\n width: 100%;\n min-height: 150px;\n vertical-align: bottom;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/TestItem.vue","\n.simple-item-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n margin: -16px 0 0 -16px;\n vertical-align: bottom;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/TestTombstone.vue",".recyclerview-container {\n position: relative;\n}\n.recyclerview-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n text-align: center;\n padding: 10px;\n font-size: 14px;\n color: #9E9E9E;\n}\n.recyclerview {\n background: #fff;\n margin: 0;\n padding: 0;\n overflow-x: hidden;\n overflow-y: scroll;\n -webkit-overflow-scrolling: touch;\n width: 100%;\n height: 100%;\n position: absolute;\n box-sizing: border-box;\n contain: layout;\n will-change: transform;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/recyclerview/recyclerview.css"],"sourceRoot":""} -------------------------------------------------------------------------------- /docs/static/css/app.7d3ad7e88e99cf18d39465516da7579f.css: -------------------------------------------------------------------------------- 1 | html{background:#eee}body,html{min-height:100%;margin:0;padding:0;overflow:hidden;font-family:Roboto,sans-serif;-webkit-tap-highlight-color:rgba(0,0,0,0)}.wechat .recyclerview,html .wechat-list{background:#efefef}.switch{position:fixed;top:50px;right:0;z-index:100;background:#03a9f4;color:#fff;font-size:14px;padding:10px}.nav-header{background:#373b3e;height:50px;line-height:50px;color:#fff;padding:0 20px}.nav-header-action{position:absolute;height:50px;width:50px;padding-right:10px;top:0;right:0;color:#fff;line-height:45px;text-align:right}.nav-header-action-icon{width:26px;margin-top:14px}.recyclerview-container{height:calc(100vh - 50px)}.testlist{-ms-flex-wrap:wrap;flex-wrap:wrap}.chat-view-footer,.testlist{display:-webkit-box;display:-ms-flexbox;display:flex}.chat-view-footer{position:fixed;bottom:0;width:100%;height:50px}.wechat{height:calc(100vh - 100px)}.chat-input{width:100%;height:50px;border:none;outline:none;padding:5px;line-height:14px}.chat-send-btn{-ms-flex-negative:0;flex-shrink:0;width:100px;height:50px;background:#8bc34a;border:none;color:#fff;font-size:14px}.chat-item{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px 0;width:100%;contain:layout;will-change:transform}.avatar{border-radius:4px;margin-left:10px;margin-right:6px;height:30px;width:30px}.chat-item p{margin:0;word-wrap:break-word;font-size:13px}.chat-item.tombstone p{width:100%;height:.5em;background-color:#ccc;margin:.5em 0}.chat-item .bubble img{max-width:100%;height:auto}.bubble{padding:7px 10px;color:#333;background:#fff;border-radius:4px;position:relative;max-width:420px;min-width:80px;margin:0 5px}.bubble:before{content:"";border-style:solid;border-width:5px 5px 5px 0;border-color:transparent #fff transparent transparent;position:absolute;top:10.6px;left:-5px}.meta{font-size:.8rem;color:#999;margin-top:3px}.from-me{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.from-me .avatar{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1;margin-left:6px;margin-right:10px}.from-me .bubble{background:#a2e563}.from-me .bubble:before{left:100%;border-width:5px;border-color:transparent transparent transparent #a2e563}.invisible,.state{display:none}@media screen and (max-width:640px){.bubble{max-width:220px}}.mi-item{padding:0 0 3px;position:absolute;background:#fff;width:100%;list-style:none}.mi-item:after{content:"";position:absolute;bottom:1.5px;left:150px;right:0;border-bottom:1px solid #e4e4e4}.version-item{display:-ms-flexbox;display:flex;display:-webkit-box;box-align:center;-webkit-box-align:center;width:100%}.version-item-img{width:180px;height:180px;position:relative}.version-item-img img{width:100%}.version-item .version-item-intro{-webkit-box-flex:1;box-flex:1;display:block;padding:0 15px 5px}.version-item .version-item-intro .version-item-name{font-size:14px;color:rgba(0,0,0,.87);margin-bottom:8px}.version-item .version-item-intro .version-item-brief{font-size:12px;color:rgba(0,0,0,.54);margin-bottom:8px;line-height:15px;overflow:hidden}.version-item-brief p{text-overflow:ellipsis;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;word-break:break-all}.version-item .version-item-intro .version-item-intro-price{font-size:15px;position:relative;margin-left:10px}.version-item .version-item-intro span{color:#ff6000}.mi-item.tombstone p{width:100%;height:.5em;background-color:#ccc;margin:.5em 0}.common-list .mi-item{position:relative;list-style:none}.action-filter{top:50px;height:100vh;background:rgba(0,0,0,.31);z-index:100}.action-filter,.action-modal{position:fixed;left:0;width:100%}.action-modal{display:-webkit-box;display:-ms-flexbox;display:flex;bottom:0;min-height:200px;background:#fff;z-index:101;padding:20px;box-sizing:border-box}.action-item{width:25%;text-align:center;color:#6d6d6d}.action-item-icon{font-size:30px;margin-bottom:5px}.action-item-text{font-size:12px}.simple-item{position:relative;width:33.33%;min-height:150px;background:#000;border:1px solid #000;box-sizing:border-box;will-change:transform}.simple-item-id{position:absolute;top:5px;left:5px;color:#737373;background:#fff;padding:5px;border-radius:4px;min-width:20px;text-align:center}.simple-item-image{width:100%;min-height:150px;vertical-align:bottom}.simple-item-loading{position:absolute;top:50%;left:50%;margin:-16px 0 0 -16px;vertical-align:bottom}.recyclerview-container{position:relative}.recyclerview-loading{position:absolute;top:0;left:0;width:100%;text-align:center;padding:10px;font-size:14px;color:#9e9e9e}.recyclerview{background:#fff;margin:0;padding:0;overflow-x:hidden;overflow-y:scroll;-webkit-overflow-scrolling:touch;width:100%;height:100%;position:absolute;box-sizing:border-box;contain:layout;will-change:transform} -------------------------------------------------------------------------------- /docs/static/css/app.7d3ad7e88e99cf18d39465516da7579f.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./src/App.vue","webpack:///./src/components/ChatItem.vue","webpack:///./src/components/MiItem.vue","webpack:///./src/components/MiTombstone.vue","webpack:///./src/components/MiCommonList.vue","webpack:///./src/components/ActionModal.vue","webpack:///./src/components/TestItem.vue","webpack:///./src/components/TestTombstone.vue","webpack:///./src/recyclerview/recyclerview.css"],"names":[],"mappings":"AACA,KACE,eAAoB,CAEtB,UACE,gBACA,SACA,UACA,gBACA,8BACA,yCAA2C,CAE7C,wCAEE,kBAAoB,CAEtB,QACE,eACA,SACA,QACA,YACA,mBACA,WACA,eACA,YAAc,CAEhB,YACE,mBACA,YACA,iBACA,WACA,cAAgB,CAElB,mBACE,kBACA,YACA,WACA,mBACA,MACA,QACA,WACA,iBACA,gBAAkB,CAEpB,wBACE,WACA,eAAiB,CAEnB,wBACE,yBAA2B,CAE7B,UAIE,mBACI,cAAgB,CAEtB,4BANE,oBACA,oBACA,YAAc,CAYf,kBAPC,eACA,SACA,WAIA,WAAa,CAEf,QACE,0BAA4B,CAE9B,YACE,WACA,YACA,YACA,aACA,YACA,gBAAkB,CAEpB,eACE,oBACI,cACJ,YACA,YACA,mBACA,YACA,WACA,cAAgB,CCrFlB,WACE,oBACA,oBACA,aACA,eACA,WACA,eACA,qBAAuB,CAEzB,QACE,kBACA,iBACA,iBACA,YACA,UAAY,CAEd,aACE,SACA,qBACA,cAAgB,CAElB,uBACE,WACA,YACA,sBACA,aAAgB,CAElB,uBACE,eACA,WAAa,CAEf,QACE,iBACA,WACA,gBACA,kBACA,kBACA,gBACA,eACA,YAAc,CAEhB,eACE,WACA,mBACA,2BACA,sDACA,kBACA,WACA,SAAW,CAMb,MACE,gBACA,WACA,cAAgB,CAElB,SACE,qBACI,kBACI,wBAA0B,CAEpC,iBACE,4BACI,iBACI,QACR,gBACA,iBAAmB,CAErB,iBACE,kBAAoB,CAEtB,wBACE,UACA,iBACA,wDAA0D,CAK5D,kBACE,YAAc,CAEhB,oCACA,QACI,eAAiB,CACpB,CCxFD,SACE,gBACA,kBACA,gBACA,WACA,eAAiB,CAEnB,eACE,WACA,kBACA,aACA,WACA,QACA,+BAAiC,CAEnC,cACE,oBACA,aACA,oBACA,iBACA,yBACA,UAAY,CAEd,kBACI,YACA,aACA,iBAAmB,CAEvB,sBACI,UAAY,CAEhB,kCACE,mBACA,WACA,cACA,kBAAoB,CAEtB,qDACE,eACA,sBACA,iBAAmB,CAErB,sDACE,eACA,sBACA,kBACA,iBACA,eAAiB,CAEnB,sBACI,uBACA,gBACA,oBACA,qBACA,4BACA,oBAAsB,CAE1B,4DACE,eACA,kBACA,gBAAkB,CAEpB,uCACI,aAAe,CC/DnB,qBACE,WACA,YACA,sBACA,aAAgB,CCJlB,sBACE,kBACA,eAAiB,CCFnB,eAEE,SAEA,aAEA,2BACA,WAAa,CAEf,6BARE,eAEA,OAEA,UAAY,CAiBb,cAZC,oBACA,oBACA,aAEA,SAGA,iBACA,gBACA,YACA,aACA,qBAAuB,CAEzB,aACE,UACA,kBACA,aAAe,CAEjB,kBACE,eACA,iBAAmB,CAErB,kBACE,cAAgB,CCjClB,aACE,kBACA,aACA,iBACA,gBACA,sBACA,sBACA,qBAAuB,CAEzB,gBACE,kBACA,QACA,SACA,cACA,gBACA,YACA,kBACA,eACA,iBAAmB,CAErB,mBACE,WACA,iBACA,qBAAuB,CCvBzB,qBACI,kBACA,QACA,SACA,uBACA,qBAAuB,CCN3B,wBACE,iBAAmB,CAErB,sBACE,kBACA,MACA,OACA,WACA,kBACA,aACA,eACA,aAAe,CAEjB,cACE,gBACA,SACA,UACA,kBACA,kBACA,iCACA,WACA,YACA,kBACA,sBACA,eACA,qBAAuB","file":"static/css/app.7d3ad7e88e99cf18d39465516da7579f.css","sourcesContent":["\nhtml {\n background: #eeeeee;\n}\nhtml, body {\n min-height: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n font-family: 'Roboto', sans-serif;\n -webkit-tap-highlight-color: rgba(0,0,0,0);\n}\nhtml .wechat-list,\n.wechat .recyclerview {\n background: #efefef;\n}\n.switch {\n position: fixed;\n top: 50px;\n right: 0;\n z-index: 100;\n background: #03A9F4;\n color: #fff;\n font-size: 14px;\n padding: 10px;\n}\n.nav-header {\n background: #373b3e;\n height: 50px;\n line-height: 50px;\n color: #fff;\n padding: 0 20px;\n}\n.nav-header-action {\n position: absolute;\n height: 50px;\n width: 50px;\n padding-right: 10px;\n top: 0;\n right: 0;\n color: #fff;\n line-height: 45px;\n text-align: right;\n}\n.nav-header-action-icon {\n width: 26px;\n margin-top: 14px;\n}\n.recyclerview-container {\n height: calc(100vh - 50px);\n}\n.testlist {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n}\n.chat-view-footer {\n position: fixed;\n bottom: 0;\n width: 100%;\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n height: 50px;\n}\n.wechat {\n height: calc(100vh - 100px);\n}\n.chat-input {\n width: 100%;\n height: 50px;\n border: none;\n outline: none;\n padding: 5px;\n line-height: 14px;\n}\n.chat-send-btn {\n -ms-flex-negative: 0;\n flex-shrink: 0;\n width: 100px;\n height: 50px;\n background: #8BC34A;\n border: none;\n color: #fff;\n font-size: 14px;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/App.vue","\n.chat-item {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n padding: 10px 0;\n width: 100%;\n contain: layout;\n will-change: transform;\n}\n.avatar {\n border-radius: 4px;\n margin-left: 10px;\n margin-right: 6px;\n height: 30px;\n width: 30px;\n}\n.chat-item p {\n margin: 0;\n word-wrap: break-word;\n font-size: 13px;\n}\n.chat-item.tombstone p {\n width: 100%;\n height: 0.5em;\n background-color: #ccc;\n margin: 0.5em 0;\n}\n.chat-item .bubble img {\n max-width: 100%;\n height: auto;\n}\n.bubble {\n padding: 7px 10px;\n color: #333;\n background: #fff;\n border-radius: 4px;\n position: relative;\n max-width: 420px;\n min-width: 80px;\n margin: 0 5px;\n}\n.bubble::before {\n content: '';\n border-style: solid;\n border-width: 5px 5px 5px 0;\n border-color: transparent #fff transparent transparent;\n position: absolute;\n top: 10.6px;\n left: -5px;\n}\n\n/*.bubble img {\n width: 80%;\n}*/\n.meta {\n font-size: 0.8rem;\n color: #999;\n margin-top: 3px;\n}\n.from-me {\n -webkit-box-pack: end;\n -ms-flex-pack: end;\n justify-content: flex-end;\n}\n.from-me .avatar {\n -webkit-box-ordinal-group: 2;\n -ms-flex-order: 1;\n order: 1;\n margin-left: 6px;\n margin-right: 10px;\n}\n.from-me .bubble {\n background: #a2e563;\n}\n.from-me .bubble::before {\n left: 100%;\n border-width: 5px 5px 5px 5px;\n border-color: transparent transparent transparent #a2e563;\n}\n.state {\n display: none;\n}\n.invisible {\n display: none;\n}\n@media screen and (max-width: 640px) {\n.bubble {\n max-width: 220px;\n}\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ChatItem.vue","\n.mi-item {\n padding: 0 0 3px;\n position: absolute;\n background: #fff;\n width: 100%;\n list-style: none;\n}\n.mi-item::after {\n content: \"\";\n position: absolute;\n bottom: 1.5px;\n left: 150px;\n right: 0;\n border-bottom: 1px solid #e4e4e4;\n}\n.version-item {\n display: -ms-flexbox;\n display: flex;\n display: -webkit-box;\n box-align: center;\n -webkit-box-align: center;\n width: 100%;\n}\n.version-item-img {\n width: 180px;\n height: 180px;\n position: relative;\n}\n.version-item-img img {\n width: 100%;\n}\n.version-item .version-item-intro {\n -webkit-box-flex: 1;\n box-flex: 1;\n display: block;\n padding: 0 15px 5px;\n}\n.version-item .version-item-intro .version-item-name {\n font-size: 14px;\n color: rgba(0,0,0,.87);\n margin-bottom: 8px;\n}\n.version-item .version-item-intro .version-item-brief {\n font-size: 12px;\n color: rgba(0,0,0,.54);\n margin-bottom: 8px;\n line-height: 15px;\n overflow: hidden;\n}\n.version-item-brief p {\n text-overflow: ellipsis;\n overflow: hidden;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n word-break: break-all;\n}\n.version-item .version-item-intro .version-item-intro-price {\n font-size: 15px;\n position: relative;\n margin-left: 10px;\n}\n.version-item .version-item-intro span {\n color: #ff6000;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiItem.vue","\n.mi-item.tombstone p {\n width: 100%;\n height: 0.5em;\n background-color: #ccc;\n margin: 0.5em 0;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiTombstone.vue","\n.common-list .mi-item {\n position: relative;\n list-style: none;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/MiCommonList.vue","\n.action-filter {\n position: fixed;\n top: 50px;\n left: 0;\n height: 100vh;\n width: 100%;\n background: rgba(0, 0, 0, 0.31);\n z-index: 100;\n}\n.action-modal {\n display: -webkit-box;\n display: -ms-flexbox;\n display: flex;\n position: fixed;\n bottom: 0;\n left: 0;\n width: 100%;\n min-height: 200px;\n background: #fff;\n z-index: 101;\n padding: 20px;\n box-sizing: border-box;\n}\n.action-item {\n width: 25%;\n text-align: center;\n color: #6d6d6d;\n}\n.action-item-icon {\n font-size: 30px;\n margin-bottom: 5px;\n}\n.action-item-text {\n font-size: 12px;\n}\n\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/ActionModal.vue","\n.simple-item {\n position: relative;\n width: 33.33%;\n min-height: 150px;\n background: #000;\n border: 1px solid #000;\n box-sizing: border-box;\n will-change: transform;\n}\n.simple-item-id {\n position: absolute;\n top: 5px;\n left: 5px;\n color: #737373;\n background: #fff;\n padding: 5px;\n border-radius: 4px;\n min-width: 20px;\n text-align: center;\n}\n.simple-item-image {\n width: 100%;\n min-height: 150px;\n vertical-align: bottom;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/TestItem.vue","\n.simple-item-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n margin: -16px 0 0 -16px;\n vertical-align: bottom;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/components/TestTombstone.vue",".recyclerview-container {\n position: relative;\n}\n.recyclerview-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n text-align: center;\n padding: 10px;\n font-size: 14px;\n color: #9E9E9E;\n}\n.recyclerview {\n background: #fff;\n margin: 0;\n padding: 0;\n overflow-x: hidden;\n overflow-y: scroll;\n -webkit-overflow-scrolling: touch;\n width: 100%;\n height: 100%;\n position: absolute;\n box-sizing: border-box;\n contain: layout;\n will-change: transform;\n}\n\n\n\n// WEBPACK FOOTER //\n// ./src/recyclerview/recyclerview.css"],"sourceRoot":""} -------------------------------------------------------------------------------- /docs/static/images/avatar0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/avatar0.jpg -------------------------------------------------------------------------------- /docs/static/images/avatar1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/avatar1.jpg -------------------------------------------------------------------------------- /docs/static/images/avatar2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/avatar2.jpg -------------------------------------------------------------------------------- /docs/static/images/avatar3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/avatar3.jpg -------------------------------------------------------------------------------- /docs/static/images/change.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/images/image0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image0.jpg -------------------------------------------------------------------------------- /docs/static/images/image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image1.jpg -------------------------------------------------------------------------------- /docs/static/images/image10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image10.jpg -------------------------------------------------------------------------------- /docs/static/images/image11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image11.jpg -------------------------------------------------------------------------------- /docs/static/images/image12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image12.jpg -------------------------------------------------------------------------------- /docs/static/images/image13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image13.jpg -------------------------------------------------------------------------------- /docs/static/images/image14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image14.jpg -------------------------------------------------------------------------------- /docs/static/images/image15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image15.jpg -------------------------------------------------------------------------------- /docs/static/images/image16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image16.jpg -------------------------------------------------------------------------------- /docs/static/images/image17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image17.jpg -------------------------------------------------------------------------------- /docs/static/images/image18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image18.jpg -------------------------------------------------------------------------------- /docs/static/images/image19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image19.jpg -------------------------------------------------------------------------------- /docs/static/images/image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image2.jpg -------------------------------------------------------------------------------- /docs/static/images/image20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image20.jpg -------------------------------------------------------------------------------- /docs/static/images/image21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image21.jpg -------------------------------------------------------------------------------- /docs/static/images/image22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image22.jpg -------------------------------------------------------------------------------- /docs/static/images/image23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image23.jpg -------------------------------------------------------------------------------- /docs/static/images/image24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image24.jpg -------------------------------------------------------------------------------- /docs/static/images/image25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image25.jpg -------------------------------------------------------------------------------- /docs/static/images/image26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image26.jpg -------------------------------------------------------------------------------- /docs/static/images/image27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image27.jpg -------------------------------------------------------------------------------- /docs/static/images/image28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image28.jpg -------------------------------------------------------------------------------- /docs/static/images/image29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image29.jpg -------------------------------------------------------------------------------- /docs/static/images/image3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image3.jpg -------------------------------------------------------------------------------- /docs/static/images/image30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image30.jpg -------------------------------------------------------------------------------- /docs/static/images/image31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image31.jpg -------------------------------------------------------------------------------- /docs/static/images/image32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image32.jpg -------------------------------------------------------------------------------- /docs/static/images/image33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image33.jpg -------------------------------------------------------------------------------- /docs/static/images/image34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image34.jpg -------------------------------------------------------------------------------- /docs/static/images/image35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image35.jpg -------------------------------------------------------------------------------- /docs/static/images/image36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image36.jpg -------------------------------------------------------------------------------- /docs/static/images/image37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image37.jpg -------------------------------------------------------------------------------- /docs/static/images/image38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image38.jpg -------------------------------------------------------------------------------- /docs/static/images/image39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image39.jpg -------------------------------------------------------------------------------- /docs/static/images/image4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image4.jpg -------------------------------------------------------------------------------- /docs/static/images/image40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image40.jpg -------------------------------------------------------------------------------- /docs/static/images/image41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image41.jpg -------------------------------------------------------------------------------- /docs/static/images/image42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image42.jpg -------------------------------------------------------------------------------- /docs/static/images/image43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image43.jpg -------------------------------------------------------------------------------- /docs/static/images/image44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image44.jpg -------------------------------------------------------------------------------- /docs/static/images/image45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image45.jpg -------------------------------------------------------------------------------- /docs/static/images/image46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image46.jpg -------------------------------------------------------------------------------- /docs/static/images/image47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image47.jpg -------------------------------------------------------------------------------- /docs/static/images/image48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image48.jpg -------------------------------------------------------------------------------- /docs/static/images/image49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image49.jpg -------------------------------------------------------------------------------- /docs/static/images/image5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image5.jpg -------------------------------------------------------------------------------- /docs/static/images/image50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image50.jpg -------------------------------------------------------------------------------- /docs/static/images/image51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image51.jpg -------------------------------------------------------------------------------- /docs/static/images/image52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image52.jpg -------------------------------------------------------------------------------- /docs/static/images/image53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image53.jpg -------------------------------------------------------------------------------- /docs/static/images/image54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image54.jpg -------------------------------------------------------------------------------- /docs/static/images/image55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image55.jpg -------------------------------------------------------------------------------- /docs/static/images/image56.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image56.jpg -------------------------------------------------------------------------------- /docs/static/images/image57.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image57.jpg -------------------------------------------------------------------------------- /docs/static/images/image58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image58.jpg -------------------------------------------------------------------------------- /docs/static/images/image59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image59.jpg -------------------------------------------------------------------------------- /docs/static/images/image6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image6.jpg -------------------------------------------------------------------------------- /docs/static/images/image60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image60.jpg -------------------------------------------------------------------------------- /docs/static/images/image61.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image61.jpg -------------------------------------------------------------------------------- /docs/static/images/image62.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image62.jpg -------------------------------------------------------------------------------- /docs/static/images/image63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image63.jpg -------------------------------------------------------------------------------- /docs/static/images/image64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image64.jpg -------------------------------------------------------------------------------- /docs/static/images/image65.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image65.jpg -------------------------------------------------------------------------------- /docs/static/images/image66.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image66.jpg -------------------------------------------------------------------------------- /docs/static/images/image67.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image67.jpg -------------------------------------------------------------------------------- /docs/static/images/image68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image68.jpg -------------------------------------------------------------------------------- /docs/static/images/image69.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image69.jpg -------------------------------------------------------------------------------- /docs/static/images/image7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image7.jpg -------------------------------------------------------------------------------- /docs/static/images/image70.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image70.jpg -------------------------------------------------------------------------------- /docs/static/images/image71.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image71.jpg -------------------------------------------------------------------------------- /docs/static/images/image72.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image72.jpg -------------------------------------------------------------------------------- /docs/static/images/image73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image73.jpg -------------------------------------------------------------------------------- /docs/static/images/image74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image74.jpg -------------------------------------------------------------------------------- /docs/static/images/image75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image75.jpg -------------------------------------------------------------------------------- /docs/static/images/image76.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image76.jpg -------------------------------------------------------------------------------- /docs/static/images/image8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image8.jpg -------------------------------------------------------------------------------- /docs/static/images/image9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/image9.jpg -------------------------------------------------------------------------------- /docs/static/images/loading-spin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /docs/static/images/unknown.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hilongjw/vue-recyclerview/38a7632264803a9c18fbb2a22d1ca2034cd7a109/docs/static/images/unknown.jpg -------------------------------------------------------------------------------- /docs/static/images/wechat-more.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/js/manifest.73190b5d0bff45a0b48b.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,i){for(var u,a,f,s=0,l=[];s1&&void 0!==arguments[1]?arguments[1]:1,n=t.getBoundingClientRect();return n.top0&&n.left0}function a(t,e){for(var n=0,i=t.length;n0?i:n)(t)}},function(t,e,n){var i=n(9);t.exports=function(t,e){if(!i(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!i(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!i(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!i(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var i=n(0),o=n(1),r=n(12),s=n(27),u=n(4).f;t.exports=function(t){var e=o.Symbol||(o.Symbol=r?{}:i.Symbol||{});"_"==t.charAt(0)||t in e||u(e,t,{value:s.f(t)})}},function(t,e,n){e.f=n(7)},function(t,e,n){t.exports={default:n(52),__esModule:!0}},function(t,e){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,e,n){var i=n(9),o=n(0).document,r=i(o)&&i(o.createElement);t.exports=function(t){return r?o.createElement(t):{}}},function(t,e,n){t.exports=!n(2)&&!n(8)(function(){return 7!=Object.defineProperty(n(30)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){"use strict";var i=n(12),o=n(11),r=n(37),s=n(5),u=n(19),c=n(63),a=n(21),h=n(69),l=n(7)("iterator"),f=!([].keys&&"next"in[].keys()),d=function(){return this};t.exports=function(t,e,n,p,m,_,v){c(n,e,p);var y,g,b,S=function(t){if(!f&&t in O)return O[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},w=e+" Iterator",x="values"==m,I=!1,O=t.prototype,T=O[l]||O["@@iterator"]||m&&O[m],M=T||S(m),A=m?x?S("entries"):M:void 0,N="Array"==e?O.entries||T:T;if(N&&(b=h(N.call(new t)))!==Object.prototype&&b.next&&(a(b,w,!0),i||"function"==typeof b[l]||s(b,l,d)),x&&T&&"values"!==T.name&&(I=!0,M=function(){return T.call(this)}),i&&!v||!f&&!I&&O[l]||s(O,l,M),u[e]=M,u[w]=d,m)if(y={values:x?M:S("values"),keys:_?M:S("keys"),entries:A},v)for(g in y)g in O||r(O,g,y[g]);else o(o.P+o.F*(f||I),e,y);return y}},function(t,e,n){var i=n(10),o=n(66),r=n(18),s=n(22)("IE_PROTO"),u=function(){},c=function(){var t,e=n(30)("iframe"),i=r.length;for(e.style.display="none",n(60).appendChild(e),e.src="javascript:",t=e.contentWindow.document,t.open(),t.write(" 46 | -------------------------------------------------------------------------------- /examples/component/Item.vue: -------------------------------------------------------------------------------- 1 | 67 | 87 | 88 | 101 | -------------------------------------------------------------------------------- /examples/component/Tombstone.vue: -------------------------------------------------------------------------------- 1 | 9 | 29 | -------------------------------------------------------------------------------- /examples/component/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import RecyclerView from 'vue-recyclerview' 4 | 5 | Vue.config.productionTip = false 6 | 7 | Vue.use(RecyclerView) 8 | 9 | /* eslint-disable no-new */ 10 | new Vue({ 11 | el: '#app', 12 | render: h => h(App) 13 | }) 14 | -------------------------------------------------------------------------------- /examples/component/mi-fetch.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable*/ 2 | const baseData = [ 3 | { 4 | "id": "4802", 5 | "stand_img_id": "8862271", 6 | "name": "米家空气净化器Pro", 7 | "market_price_max": "1499.00", 8 | "market_price_min": "1499.00", 9 | "price_max": "1499.00", 10 | "price_min": "1499.00", 11 | "has_store": "1", 12 | "is_cos": false, 13 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/f11b3f5b4a1df7e9bdfce788b6506ebb.jpg", 14 | "action": { 15 | "log_code": "30000001110001001" 16 | }, 17 | "product_comment": "OLED 显示屏幕 / 激光颗粒物传感器 / 500m³/h颗粒物 CADR / 60㎡适用面积", 18 | "tag_img": "" 19 | }, 20 | { 21 | "id": "3398", 22 | "stand_img_id": "2718284", 23 | "name": "小米空气净化器2", 24 | "market_price_max": "699.00", 25 | "market_price_min": "699.00", 26 | "price_max": "699.00", 27 | "price_min": "699.00", 28 | "has_store": "1", 29 | "is_cos": false, 30 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7ac41c17d544d20044d7f6c3e6ce579a.jpg", 31 | "action": { 32 | "log_code": "30000001110002001" 33 | }, 34 | "product_comment": "全新空气增压系统,净化能力高达310m³/h / 高效360°桶形净化滤芯 / 低分贝,低功耗 / 手机智能控制,一目了然", 35 | "tag_img": "" 36 | }, 37 | { 38 | "id": "4803", 39 | "stand_img_id": "8862454", 40 | "name": "米家PM2.5检测仪", 41 | "market_price_max": "399.00", 42 | "market_price_min": "399.00", 43 | "price_max": "399.00", 44 | "price_min": "399.00", 45 | "has_store": "1", 46 | "is_cos": false, 47 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/563988090779f68e8f709a1ce3c58ea6.jpg", 48 | "action": { 49 | "log_code": "30000001110003001" 50 | }, 51 | "product_comment": "高精度激光传感器 / 一体黑 OLED 屏 / 智能联动 / 轻小便携", 52 | "tag_img": "" 53 | }, 54 | { 55 | "id": "4665", 56 | "stand_img_id": "8859941", 57 | "name": "空气净化器滤芯 除甲醛增强版", 58 | "market_price_max": "169.00", 59 | "market_price_min": "169.00", 60 | "price_max": "169.00", 61 | "price_min": "169.00", 62 | "has_store": "1", 63 | "is_cos": false, 64 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7442b315c1cc06f64700e39212dbf054.jpg", 65 | "action": { 66 | "log_code": "30000001110004001" 67 | }, 68 | "product_comment": "小米空气净化器、小米空气净化器 2、米家空气净化器 Pro 通用 / 阻挡致病细菌 / 有效去除PM2.5 / 有效去除甲醛", 69 | "tag_img": "" 70 | }, 71 | { 72 | "id": "4664", 73 | "stand_img_id": "8859983", 74 | "name": "空气净化器滤芯", 75 | "market_price_max": "149.00", 76 | "market_price_min": "149.00", 77 | "price_max": "149.00", 78 | "price_min": "149.00", 79 | "has_store": "1", 80 | "is_cos": false, 81 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/c1f9eecd1d0b411315f2e698e953a52d.jpg?bg=BCCBD7", 82 | "action": { 83 | "log_code": "30000001110005001" 84 | }, 85 | "product_comment": "小米空气净化器、小米空气净化器 2、米家空气净化器 Pro 通用 / 3层高效净化悬浮物 / 可吸入颗粒物、甲醛与异味", 86 | "tag_img": "" 87 | }, 88 | { 89 | "id": "5253", 90 | "stand_img_id": "8878121", 91 | "name": "米家空气净化器滤芯 抗菌版", 92 | "market_price_max": "159.00", 93 | "market_price_min": "159.00", 94 | "price_max": "159.00", 95 | "price_min": "159.00", 96 | "has_store": "1", 97 | "is_cos": false, 98 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/627e742506047f3fe2971d4d87e10924.jpg", 99 | "action": { 100 | "log_code": "30000001110006001" 101 | }, 102 | "product_comment": "抗菌率达99%以上 / 过滤PM2.5细颗粒物 / 活性炭有效吸附甲醛", 103 | "tag_img": "" 104 | }, 105 | { 106 | "id": "5439", 107 | "stand_img_id": "8883808", 108 | "name": "米家车载空气净化器滤芯", 109 | "market_price_max": "49.00", 110 | "market_price_min": "49.00", 111 | "price_max": "49.00", 112 | "price_min": "49.00", 113 | "has_store": "0", 114 | "is_cos": true, 115 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/192b741dd0b17b301bd7955bb5e7a7f5.jpg?bg=65A0B7", 116 | "action": { 117 | "log_code": "30000001110007001" 118 | }, 119 | "product_comment": "【4月6日0点 首卖】PET初效滤网 / H11高效过滤器 / 360°桶形设计", 120 | "tag_img": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 121 | }, 122 | { 123 | "id": "4117", 124 | "stand_img_id": "6700405", 125 | "name": "USB-C至HDMI多功能转接器", 126 | "market_price_max": "149.00", 127 | "market_price_min": "149.00", 128 | "price_max": "149.00", 129 | "price_min": "149.00", 130 | "has_store": "1", 131 | "is_cos": false, 132 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/c01573cec4d3363d7efd511cbf7cfbc1.jpg", 133 | "action": { 134 | "log_code": "30000001110001001" 135 | }, 136 | "product_comment": "一举多得的转接器,让你的笔记本更强大 / 4K高清输出 / USB 3.0高速传输 / PD2.0智能充电", 137 | "tag_img": "" 138 | }, 139 | { 140 | "id": "4679", 141 | "stand_img_id": "8863343", 142 | "name": "小米便携鼠标", 143 | "market_price_max": "99.00", 144 | "market_price_min": "99.00", 145 | "price_max": "99.00", 146 | "price_min": "99.00", 147 | "has_store": "1", 148 | "is_cos": false, 149 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/6451e09563f5fe44bfc912bc128fceeb.jpg", 150 | "action": { 151 | "log_code": "30000001110002001" 152 | }, 153 | "product_comment": "阳极氧化铝合金外壳+ABS材质 / 蓝牙或2.4G双模式 / 轻薄便携", 154 | "tag_img": "" 155 | }, 156 | { 157 | "id": "1719", 158 | "stand_img_id": "1040014", 159 | "name": "小米金属鼠标垫", 160 | "market_price_max": "59.00", 161 | "market_price_min": "49.00", 162 | "price_max": "59.00", 163 | "price_min": "49.00", 164 | "has_store": "1", 165 | "is_cos": false, 166 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8873564985f8151acdbef39e48ab316c.jpg?bg=C0C4C7", 167 | "action": { 168 | "log_code": "30000001110003001" 169 | }, 170 | "product_comment": "简约、纤薄、时尚 / 航空铝合金 / 专业之选", 171 | "tag_img": "" 172 | }, 173 | { 174 | "id": "4132", 175 | "stand_img_id": "7556964", 176 | "name": "小米笔记本内胆包12.5英寸", 177 | "market_price_max": "99.00", 178 | "market_price_min": "69.00", 179 | "price_max": "99.00", 180 | "price_min": "69.00", 181 | "has_store": "1", 182 | "is_cos": false, 183 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8cac1e6358258a06d8cd2ed4b4226f44.jpg", 184 | "action": { 185 | "log_code": "30000001110004001" 186 | }, 187 | "product_comment": "强力吸附,紧贴爱机每一角落 / 加密麂皮绒里布 / 适装MacBook Air 11寸笔记本及MacBook 12.5寸笔记本", 188 | "tag_img": "" 189 | }, 190 | { 191 | "id": "1173", 192 | "stand_img_id": "1039802", 193 | "name": "小米鼠标垫加大号", 194 | "market_price_max": "19.00", 195 | "market_price_min": "19.00", 196 | "price_max": "19.00", 197 | "price_min": "19.00", 198 | "has_store": "1", 199 | "is_cos": false, 200 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/eb425a1dd77653409470fb66b8f8f82a.jpg", 201 | "action": { 202 | "log_code": "30000001110005001" 203 | }, 204 | "product_comment": "大面积,超爽体验 / 材质柔软,不易变形 / 超强抓力,不易滑动 / 简约风格,放在哪里都美观大方", 205 | "tag_img": "" 206 | }, 207 | { 208 | "id": "4682", 209 | "stand_img_id": "8864594", 210 | "name": "小米笔记本贴纸 13.3英寸", 211 | "market_price_max": "29.90", 212 | "market_price_min": "29.90", 213 | "price_max": "29.90", 214 | "price_min": "29.90", 215 | "has_store": "1", 216 | "is_cos": false, 217 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7957abb8ffac6dc5e75b124a58895432.jpg", 218 | "action": { 219 | "log_code": "30000001110006001" 220 | }, 221 | "product_comment": "色彩细腻,图案精准 / 防水耐磨,容易操作 / 小米笔记本专用贴纸", 222 | "tag_img": "" 223 | }, 224 | { 225 | "id": "4256", 226 | "stand_img_id": "8868571", 227 | "name": "小米笔记本内胆包13.3英寸", 228 | "market_price_max": "99.00", 229 | "market_price_min": "69.00", 230 | "price_max": "99.00", 231 | "price_min": "69.00", 232 | "has_store": "1", 233 | "is_cos": false, 234 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8cac1e6358258a06d8cd2ed4b4226f44.jpg", 235 | "action": { 236 | "log_code": "30000001110007001" 237 | }, 238 | "product_comment": "强力吸附,紧贴爱机每一角落 / 加密麂皮绒里布 / 至简纤薄 轻松随行无压力", 239 | "tag_img": "" 240 | }, 241 | { 242 | "id": "4681", 243 | "stand_img_id": "8864630", 244 | "name": "小米笔记本贴纸 12.5英寸", 245 | "market_price_max": "29.90", 246 | "market_price_min": "29.90", 247 | "price_max": "29.90", 248 | "price_min": "29.90", 249 | "has_store": "1", 250 | "is_cos": false, 251 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/1b8a63495fff6861d5485f1d3a843483.jpg", 252 | "action": { 253 | "log_code": "30000001110008001" 254 | }, 255 | "product_comment": "色彩细腻,图案精准 / 防水耐磨,容易操作 / 小米笔记本专用贴纸", 256 | "tag_img": "" 257 | }, 258 | { 259 | "id": "5434", 260 | "stand_img_id": "8883766", 261 | "name": "小米无线鼠标", 262 | "market_price_max": "69.00", 263 | "market_price_min": "69.00", 264 | "price_max": "69.00", 265 | "price_min": "69.00", 266 | "has_store": "0", 267 | "is_cos": true, 268 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/747bdc5fe4ae9976ac05e8af1eb4c52e.jpg?bg=BAC6A4", 269 | "action": { 270 | "log_code": "30000001110009001" 271 | }, 272 | "product_comment": "2.4G高速传输 / 1200dpi 精准定位 / 一键“后退”* / 耐脏亲肤涂层 / 人体工学设计 ", 273 | "tag_img": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 274 | }, 275 | { 276 | "id": "5499", 277 | "stand_img_id": "8884114", 278 | "name": "悦米机械键盘", 279 | "market_price_max": "299.00", 280 | "market_price_min": "299.00", 281 | "price_max": "299.00", 282 | "price_min": "299.00", 283 | "has_store": "0", 284 | "is_cos": true, 285 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/dd4e70d66276839ed73d9a9b897d4859.jpg?bg=FFFFFF", 286 | "action": { 287 | "log_code": "30000001110010001" 288 | }, 289 | "product_comment": "87键 / 铝合金机身 / TTC 红轴 / 简约百搭", 290 | "tag_img": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 291 | }, 292 | { 293 | "id": "5287", 294 | "stand_img_id": "8880692", 295 | "name": "小米USB-C电源适配器(45W)", 296 | "market_price_max": "99.00", 297 | "market_price_min": "99.00", 298 | "price_max": "99.00", 299 | "price_min": "99.00", 300 | "has_store": "0", 301 | "is_cos": true, 302 | "img_url": "//cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/648060ed1a8ef00f5756679ef7ba6971.jpg", 303 | "action": { 304 | "log_code": "30000001110011001" 305 | }, 306 | "product_comment": "PD2.0 协议 / 精致小巧 / 可为 QC3.0 设备充电 / 附赠USB-C to USB-C数据线\n", 307 | "tag_img": "" 308 | } 309 | ] 310 | 311 | let id = 0 312 | 313 | function pickeOne () { 314 | return baseData[Math.floor(Math.random() * baseData.length)] 315 | } 316 | 317 | function getItem () { 318 | return new Promise(resolve => { 319 | var item = pickeOne() 320 | item.id = ++id 321 | var image = new Image() 322 | image.src = item.img_url 323 | image.addEventListener('load', () => { 324 | resolve(item) 325 | }) 326 | image.addEventListener('error', () => { 327 | item.img_url = '' 328 | resolve(item) 329 | }) 330 | }) 331 | } 332 | 333 | function query (limit, skip) { 334 | return new Promise((resolve, reject) => { 335 | setTimeout(() => { 336 | var items = [] 337 | for (var i = 0; i < limit; i++) { 338 | items[i] = getItem() 339 | } 340 | resolve(Promise.all(items)) 341 | }, 200) 342 | }) 343 | } 344 | 345 | export default function fetch (limit, skip) { 346 | limit = Math.max(30, limit) 347 | return query(limit, skip) 348 | .then(list => { 349 | return { 350 | list: list, 351 | count: 1000 352 | } 353 | }) 354 | } 355 | 356 | -------------------------------------------------------------------------------- /examples/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 87 | 88 | 89 |
90 | 91 | 92 | 93 | 160 | 161 | -------------------------------------------------------------------------------- /examples/simple/mi-fetch.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable*/ 2 | const baseData = [ 3 | { 4 | "id": "4802", 5 | "stand_img_id": "8862271", 6 | "name": "米家空气净化器Pro", 7 | "market_price_max": "1499.00", 8 | "market_price_min": "1499.00", 9 | "price_max": "1499.00", 10 | "price_min": "1499.00", 11 | "has_store": "1", 12 | "is_cos": false, 13 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/f11b3f5b4a1df7e9bdfce788b6506ebb.jpg", 14 | "action": { 15 | "log_code": "30000001110001001" 16 | }, 17 | "product_comment": "OLED 显示屏幕 / 激光颗粒物传感器 / 500m³/h颗粒物 CADR / 60㎡适用面积", 18 | "tag_img": "" 19 | }, 20 | { 21 | "id": "3398", 22 | "stand_img_id": "2718284", 23 | "name": "小米空气净化器2", 24 | "market_price_max": "699.00", 25 | "market_price_min": "699.00", 26 | "price_max": "699.00", 27 | "price_min": "699.00", 28 | "has_store": "1", 29 | "is_cos": false, 30 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7ac41c17d544d20044d7f6c3e6ce579a.jpg", 31 | "action": { 32 | "log_code": "30000001110002001" 33 | }, 34 | "product_comment": "全新空气增压系统,净化能力高达310m³/h / 高效360°桶形净化滤芯 / 低分贝,低功耗 / 手机智能控制,一目了然", 35 | "tag_img": "" 36 | }, 37 | { 38 | "id": "4803", 39 | "stand_img_id": "8862454", 40 | "name": "米家PM2.5检测仪", 41 | "market_price_max": "399.00", 42 | "market_price_min": "399.00", 43 | "price_max": "399.00", 44 | "price_min": "399.00", 45 | "has_store": "1", 46 | "is_cos": false, 47 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/563988090779f68e8f709a1ce3c58ea6.jpg", 48 | "action": { 49 | "log_code": "30000001110003001" 50 | }, 51 | "product_comment": "高精度激光传感器 / 一体黑 OLED 屏 / 智能联动 / 轻小便携", 52 | "tag_img": "" 53 | }, 54 | { 55 | "id": "4665", 56 | "stand_img_id": "8859941", 57 | "name": "空气净化器滤芯 除甲醛增强版", 58 | "market_price_max": "169.00", 59 | "market_price_min": "169.00", 60 | "price_max": "169.00", 61 | "price_min": "169.00", 62 | "has_store": "1", 63 | "is_cos": false, 64 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7442b315c1cc06f64700e39212dbf054.jpg", 65 | "action": { 66 | "log_code": "30000001110004001" 67 | }, 68 | "product_comment": "小米空气净化器、小米空气净化器 2、米家空气净化器 Pro 通用 / 阻挡致病细菌 / 有效去除PM2.5 / 有效去除甲醛", 69 | "tag_img": "" 70 | }, 71 | { 72 | "id": "4664", 73 | "stand_img_id": "8859983", 74 | "name": "空气净化器滤芯", 75 | "market_price_max": "149.00", 76 | "market_price_min": "149.00", 77 | "price_max": "149.00", 78 | "price_min": "149.00", 79 | "has_store": "1", 80 | "is_cos": false, 81 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/c1f9eecd1d0b411315f2e698e953a52d.jpg?bg=BCCBD7", 82 | "action": { 83 | "log_code": "30000001110005001" 84 | }, 85 | "product_comment": "小米空气净化器、小米空气净化器 2、米家空气净化器 Pro 通用 / 3层高效净化悬浮物 / 可吸入颗粒物、甲醛与异味", 86 | "tag_img": "" 87 | }, 88 | { 89 | "id": "5253", 90 | "stand_img_id": "8878121", 91 | "name": "米家空气净化器滤芯 抗菌版", 92 | "market_price_max": "159.00", 93 | "market_price_min": "159.00", 94 | "price_max": "159.00", 95 | "price_min": "159.00", 96 | "has_store": "1", 97 | "is_cos": false, 98 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/627e742506047f3fe2971d4d87e10924.jpg", 99 | "action": { 100 | "log_code": "30000001110006001" 101 | }, 102 | "product_comment": "抗菌率达99%以上 / 过滤PM2.5细颗粒物 / 活性炭有效吸附甲醛", 103 | "tag_img": "" 104 | }, 105 | { 106 | "id": "5439", 107 | "stand_img_id": "8883808", 108 | "name": "米家车载空气净化器滤芯", 109 | "market_price_max": "49.00", 110 | "market_price_min": "49.00", 111 | "price_max": "49.00", 112 | "price_min": "49.00", 113 | "has_store": "0", 114 | "is_cos": true, 115 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/192b741dd0b17b301bd7955bb5e7a7f5.jpg?bg=65A0B7", 116 | "action": { 117 | "log_code": "30000001110007001" 118 | }, 119 | "product_comment": "【4月6日0点 首卖】PET初效滤网 / H11高效过滤器 / 360°桶形设计", 120 | "tag_img": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 121 | }, 122 | { 123 | "id": "4117", 124 | "stand_img_id": "6700405", 125 | "name": "USB-C至HDMI多功能转接器", 126 | "market_price_max": "149.00", 127 | "market_price_min": "149.00", 128 | "price_max": "149.00", 129 | "price_min": "149.00", 130 | "has_store": "1", 131 | "is_cos": false, 132 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/c01573cec4d3363d7efd511cbf7cfbc1.jpg", 133 | "action": { 134 | "log_code": "30000001110001001" 135 | }, 136 | "product_comment": "一举多得的转接器,让你的笔记本更强大 / 4K高清输出 / USB 3.0高速传输 / PD2.0智能充电", 137 | "tag_img": "" 138 | }, 139 | { 140 | "id": "4679", 141 | "stand_img_id": "8863343", 142 | "name": "小米便携鼠标", 143 | "market_price_max": "99.00", 144 | "market_price_min": "99.00", 145 | "price_max": "99.00", 146 | "price_min": "99.00", 147 | "has_store": "1", 148 | "is_cos": false, 149 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/6451e09563f5fe44bfc912bc128fceeb.jpg", 150 | "action": { 151 | "log_code": "30000001110002001" 152 | }, 153 | "product_comment": "阳极氧化铝合金外壳+ABS材质 / 蓝牙或2.4G双模式 / 轻薄便携", 154 | "tag_img": "" 155 | }, 156 | { 157 | "id": "1719", 158 | "stand_img_id": "1040014", 159 | "name": "小米金属鼠标垫", 160 | "market_price_max": "59.00", 161 | "market_price_min": "49.00", 162 | "price_max": "59.00", 163 | "price_min": "49.00", 164 | "has_store": "1", 165 | "is_cos": false, 166 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8873564985f8151acdbef39e48ab316c.jpg?bg=C0C4C7", 167 | "action": { 168 | "log_code": "30000001110003001" 169 | }, 170 | "product_comment": "简约、纤薄、时尚 / 航空铝合金 / 专业之选", 171 | "tag_img": "" 172 | }, 173 | { 174 | "id": "4132", 175 | "stand_img_id": "7556964", 176 | "name": "小米笔记本内胆包12.5英寸", 177 | "market_price_max": "99.00", 178 | "market_price_min": "69.00", 179 | "price_max": "99.00", 180 | "price_min": "69.00", 181 | "has_store": "1", 182 | "is_cos": false, 183 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8cac1e6358258a06d8cd2ed4b4226f44.jpg", 184 | "action": { 185 | "log_code": "30000001110004001" 186 | }, 187 | "product_comment": "强力吸附,紧贴爱机每一角落 / 加密麂皮绒里布 / 适装MacBook Air 11寸笔记本及MacBook 12.5寸笔记本", 188 | "tag_img": "" 189 | }, 190 | { 191 | "id": "1173", 192 | "stand_img_id": "1039802", 193 | "name": "小米鼠标垫加大号", 194 | "market_price_max": "19.00", 195 | "market_price_min": "19.00", 196 | "price_max": "19.00", 197 | "price_min": "19.00", 198 | "has_store": "1", 199 | "is_cos": false, 200 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/eb425a1dd77653409470fb66b8f8f82a.jpg", 201 | "action": { 202 | "log_code": "30000001110005001" 203 | }, 204 | "product_comment": "大面积,超爽体验 / 材质柔软,不易变形 / 超强抓力,不易滑动 / 简约风格,放在哪里都美观大方", 205 | "tag_img": "" 206 | }, 207 | { 208 | "id": "4682", 209 | "stand_img_id": "8864594", 210 | "name": "小米笔记本贴纸 13.3英寸", 211 | "market_price_max": "29.90", 212 | "market_price_min": "29.90", 213 | "price_max": "29.90", 214 | "price_min": "29.90", 215 | "has_store": "1", 216 | "is_cos": false, 217 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/7957abb8ffac6dc5e75b124a58895432.jpg", 218 | "action": { 219 | "log_code": "30000001110006001" 220 | }, 221 | "product_comment": "色彩细腻,图案精准 / 防水耐磨,容易操作 / 小米笔记本专用贴纸", 222 | "tag_img": "" 223 | }, 224 | { 225 | "id": "4256", 226 | "stand_img_id": "8868571", 227 | "name": "小米笔记本内胆包13.3英寸", 228 | "market_price_max": "99.00", 229 | "market_price_min": "69.00", 230 | "price_max": "99.00", 231 | "price_min": "69.00", 232 | "has_store": "1", 233 | "is_cos": false, 234 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/8cac1e6358258a06d8cd2ed4b4226f44.jpg", 235 | "action": { 236 | "log_code": "30000001110007001" 237 | }, 238 | "product_comment": "强力吸附,紧贴爱机每一角落 / 加密麂皮绒里布 / 至简纤薄 轻松随行无压力", 239 | "tag_img": "" 240 | }, 241 | { 242 | "id": "4681", 243 | "stand_img_id": "8864630", 244 | "name": "小米笔记本贴纸 12.5英寸", 245 | "market_price_max": "29.90", 246 | "market_price_min": "29.90", 247 | "price_max": "29.90", 248 | "price_min": "29.90", 249 | "has_store": "1", 250 | "is_cos": false, 251 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/1b8a63495fff6861d5485f1d3a843483.jpg", 252 | "action": { 253 | "log_code": "30000001110008001" 254 | }, 255 | "product_comment": "色彩细腻,图案精准 / 防水耐磨,容易操作 / 小米笔记本专用贴纸", 256 | "tag_img": "" 257 | }, 258 | { 259 | "id": "5434", 260 | "stand_img_id": "8883766", 261 | "name": "小米无线鼠标", 262 | "market_price_max": "69.00", 263 | "market_price_min": "69.00", 264 | "price_max": "69.00", 265 | "price_min": "69.00", 266 | "has_store": "0", 267 | "is_cos": true, 268 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/747bdc5fe4ae9976ac05e8af1eb4c52e.jpg?bg=BAC6A4", 269 | "action": { 270 | "log_code": "30000001110009001" 271 | }, 272 | "product_comment": "2.4G高速传输 / 1200dpi 精准定位 / 一键“后退”* / 耐脏亲肤涂层 / 人体工学设计 ", 273 | "tag_img": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 274 | }, 275 | { 276 | "id": "5499", 277 | "stand_img_id": "8884114", 278 | "name": "悦米机械键盘", 279 | "market_price_max": "299.00", 280 | "market_price_min": "299.00", 281 | "price_max": "299.00", 282 | "price_min": "299.00", 283 | "has_store": "0", 284 | "is_cos": true, 285 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/dd4e70d66276839ed73d9a9b897d4859.jpg?bg=FFFFFF", 286 | "action": { 287 | "log_code": "30000001110010001" 288 | }, 289 | "product_comment": "87键 / 铝合金机身 / TTC 红轴 / 简约百搭", 290 | "tag_img": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/89854765948bdb494f85deeab661a53e.png?bg=95C85E" 291 | }, 292 | { 293 | "id": "5287", 294 | "stand_img_id": "8880692", 295 | "name": "小米USB-C电源适配器(45W)", 296 | "market_price_max": "99.00", 297 | "market_price_min": "99.00", 298 | "price_max": "99.00", 299 | "price_min": "99.00", 300 | "has_store": "0", 301 | "is_cos": true, 302 | "img_url": "https://cdn.cnbj0.fds.api.mi-img.com/b2c-mimall-media/648060ed1a8ef00f5756679ef7ba6971.jpg", 303 | "action": { 304 | "log_code": "30000001110011001" 305 | }, 306 | "product_comment": "PD2.0 协议 / 精致小巧 / 可为 QC3.0 设备充电 / 附赠USB-C to USB-C数据线\n", 307 | "tag_img": "" 308 | } 309 | ] 310 | 311 | let id = 0 312 | 313 | function pickeOne () { 314 | return baseData[Math.floor(Math.random() * baseData.length)] 315 | } 316 | 317 | function getItem () { 318 | return new Promise(resolve => { 319 | var item = pickeOne() 320 | item.id = ++id 321 | var image = new Image() 322 | image.src = item.img_url 323 | image.addEventListener('load', () => { 324 | resolve(item) 325 | }) 326 | image.addEventListener('error', () => { 327 | item.img_url = '' 328 | resolve(item) 329 | }) 330 | }) 331 | } 332 | 333 | function query (limit, skip) { 334 | return new Promise((resolve, reject) => { 335 | setTimeout(() => { 336 | var items = [] 337 | for (var i = 0; i < limit; i++) { 338 | items[i] = getItem() 339 | } 340 | resolve(Promise.all(items)) 341 | }, 200) 342 | }) 343 | } 344 | 345 | function fetch (limit, skip) { 346 | limit = Math.max(30, limit) 347 | return query(limit, skip) 348 | .then(list => { 349 | return { 350 | list: list, 351 | count: 1000 352 | } 353 | }) 354 | } 355 | 356 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-recyclerview", 3 | "version": "0.4.1", 4 | "description": "Mastering Large Lists with the vue-recyclerview", 5 | "main": "dist/vue-recyclerview.js", 6 | "directories": { 7 | "build": "node build", 8 | "example": "examples" 9 | }, 10 | "scripts": { 11 | "build": "node build" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/hilongjw/vue-recyclerview.git" 16 | }, 17 | "unpkg": "dist/vue-recyclerview.js", 18 | "files": [ 19 | "README.md", 20 | "src", 21 | "dist/*.js" 22 | ], 23 | "keywords": [ 24 | "vue-recyclerview", 25 | "vue", 26 | "recyclerview" 27 | ], 28 | "devDependencies": { 29 | "babel-cli": "^6.14.0", 30 | "babel-plugin-external-helpers": "^6.22.0", 31 | "babel-polyfill": "^6.13.0", 32 | "babel-preset-es2015": "^6.22.0", 33 | "babel-preset-es2015-rollup": "^1.2.0", 34 | "cssnano": "^3.10.0", 35 | "rollup": "^0.35.10", 36 | "rollup-plugin-babel": "^2.6.1", 37 | "rollup-plugin-postcss": "^0.5.3", 38 | "rollup-plugin-uglify": "^1.0.1" 39 | }, 40 | "author": "Awe ", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/hilongjw/vue-recyclerview/issues" 44 | }, 45 | "homepage": "https://github.com/hilongjw/vue-recyclerview#readme" 46 | } 47 | -------------------------------------------------------------------------------- /src/content-source.js: -------------------------------------------------------------------------------- 1 | import { find } from './util' 2 | 3 | export default class ContentSource { 4 | constructor (Vue, options) { 5 | this.itemRender = options.item 6 | this.TombstoneRender = options.tombstone 7 | this.fetch = options.fetch 8 | this.Vue = Vue 9 | this.options = options 10 | this.itemCache = { 11 | data: {}, 12 | length: 0, 13 | get (key) { 14 | return this.data[key] 15 | }, 16 | set (key, vm) { 17 | this.length++ 18 | this.data[key] = vm 19 | if (this.length > options.cacheVM && options.cacheVM > 50) { 20 | this.recycle(10, key) 21 | } 22 | }, 23 | recycle (count, except) { 24 | let key 25 | let keys = Object.keys(this.data) 26 | let len = keys.length 27 | while (count) { 28 | count-- 29 | key = keys[Math.floor(Math.random() * len)] 30 | this.data[key] && this.length-- && this.data[key].$destroy() 31 | this.data[key] = null 32 | } 33 | } 34 | } 35 | 36 | this.reuseVM = { 37 | queue: [ 38 | // { 39 | // inuse: false, 40 | // vm: vm 41 | // } 42 | ], 43 | generate: (data, el) => { 44 | let item = find(this.reuseVM.queue, item => !item.inuse) 45 | 46 | // this.reuseVM.queue.find(i => !i.inuse) 47 | 48 | if (!item) { 49 | const vmOptions = { 50 | props: { 51 | data: data 52 | } 53 | } 54 | this.options.props.data = data 55 | if (this.options.props) { 56 | Object.keys(this.options.props).map(key => { 57 | vmOptions.props[key] = this.options.props[key] 58 | }) 59 | } 60 | const vmConfig = { 61 | el: el, 62 | data: vmOptions.props, 63 | render: h => h(this.itemRender, vmOptions) 64 | } 65 | item = { 66 | id: data.id, 67 | inuse: true, 68 | vm: new this.Vue(vmConfig) 69 | } 70 | this.reuseVM.queue.push(item) 71 | } else { 72 | item.vm.data = data 73 | // item.vm.$forceUpdate() 74 | item.inuse = true 75 | item.id = data.id 76 | } 77 | 78 | return item.vm 79 | }, 80 | free (id) { 81 | let item = find(this.queue, i => i.id === id) 82 | item.inuse = false 83 | }, 84 | destroy (id, all) { 85 | for (let i = 0, len = this.queue.length; i < len; i++) { 86 | if (this.queue[i].id === id || all) { 87 | this.queue.vm && this.queue.vm.$destroy() 88 | this.queue.splice(i, 1) 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | createTombstone (el) { 96 | const vm = new this.Vue({ 97 | el: el, 98 | render: h => h(this.TombstoneRender) 99 | }) 100 | return vm.$el 101 | } 102 | 103 | free (data) { 104 | this.reuseVM.free(data.id) 105 | } 106 | 107 | render (data, el, item) { 108 | if (this.options.reuseVM) { 109 | const vm = this.reuseVM.generate(data, el) 110 | item.vm = vm 111 | return vm.$el 112 | } 113 | 114 | let vm 115 | const vmOptions = { 116 | props: { 117 | data: data 118 | } 119 | } 120 | this.options.props.data = data 121 | if (this.options.props) { 122 | Object.keys(this.options.props).map(key => { 123 | vmOptions.props[key] = this.options.props[key] 124 | }) 125 | } 126 | const vmConfig = { 127 | el: el, 128 | render: h => h(this.itemRender, vmOptions) 129 | } 130 | if (this.options.cacheVM) { 131 | vm = this.itemCache.get(data.id) 132 | if (vm) { 133 | item.vm = vm 134 | return vm.$el 135 | } 136 | vm = new this.Vue(vmConfig) 137 | this.itemCache.set(data.id, vm) 138 | item.vm = vm 139 | return vm.$el 140 | } 141 | vm = new this.Vue(vmConfig) 142 | item.vm = vm 143 | return vm.$el 144 | } 145 | 146 | destroy () { 147 | this.reuseVM.destroy(null, true) 148 | return this.reuseVM.queue 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './polyfill' 2 | import RecyclerView from './recyclerview' 3 | import './recyclerview.css' 4 | 5 | function install (Vue, options = {}) { 6 | const component = RecyclerView(Vue) 7 | Vue.component(component.name, component) 8 | return component 9 | } 10 | 11 | export default { 12 | install 13 | } 14 | 15 | if (typeof window !== 'undefined' && window.Vue) { 16 | window.Vue.use(install) 17 | } 18 | -------------------------------------------------------------------------------- /src/infinite.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable*/ 2 | /** 3 | * Copyright 2015 Google Inc. All rights reserved. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * Author surma https://github.com/surma 18 | * Modified by Awe @hilongjw 19 | */ 20 | import { inView, setStyle } from './util' 21 | 22 | const MAX_COUNT = Infinity 23 | 24 | /** 25 | * Construct an infinite scroller. 26 | * @param {Element} scroller The scrollable element to use as the infinite 27 | * scroll region. 28 | * @param {InfiniteScrollerSource} source A provider of the content to be 29 | * displayed in the infinite scroll region. 30 | */ 31 | export default function InfiniteScroller (scroller, source, options) { 32 | // Number of items to instantiate beyond current view in the opposite direction. 33 | this.RUNWAY_ITEMS = options.prerender 34 | // Number of items to instantiate beyond current view in the opposite direction. 35 | this.RUNWAY_ITEMS_OPPOSITE = options.remain 36 | // The number of pixels of additional length to allow scrolling to. 37 | // this.SCROLL_RUNWAY = options.SCROLL_RUNWAY || SCROLL_RUNWAY 38 | 39 | // The animation interval (in ms) for fading in content from tombstones. 40 | this.ANIMATION_DURATION_MS = options.animation_duration_ms 41 | this.TOMBSTONE_CLASS = options.tombstone_class 42 | this.INVISIBLE_CLASS = options.invisible_class 43 | this.MAX_COUNT = MAX_COUNT 44 | this.column = options.column || 1 45 | this.waterflow = options.waterflow 46 | 47 | this.anchorItem = { 48 | index: 0, 49 | offset: 0 50 | } 51 | this.timer = null 52 | this.firstAttachedItem_ = 0 53 | this.lastAttachedItem_ = 0 54 | this.anchorScrollTop = 0 55 | this.tombstoneSize_ = 0 56 | this.tombstoneWidth_ = 0 57 | this.tombstones_ = [] 58 | this.scroller_ = scroller 59 | this.source_ = source 60 | this.items_ = options.list || [] 61 | this.loadedItems_ = 0 62 | this.requestInProgress_ = false 63 | this.cacheVM = options.cacheVM 64 | this.options = options 65 | 66 | if (!this.source_.fetch) { 67 | this.setItems(options.list) 68 | } 69 | 70 | this.curPos = 0 71 | this.unusedNodes = [] 72 | this.baseNode = document.createElement('div') 73 | 74 | this.scroller_.addEventListener('scroll', this.onScroll_.bind(this)) 75 | window.addEventListener('resize', this.onResize_.bind(this)) 76 | window.addEventListener('orientationchange', this.onResize_.bind(this)) 77 | 78 | // Create an element to force the scroller to allow scrolling to a certain 79 | // point. 80 | // this.scrollRunway_ = document.createElement('div') 81 | 82 | // // Internet explorer seems to require some text in this div in order to 83 | // // ensure that it can be scrolled to. 84 | // this.scrollRunway_.textContent = ' ' 85 | // this.scrollRunwayEnd_ = 0 86 | // this.scrollRunway_.style.position = 'absolute' 87 | // this.scrollRunway_.style.height = '1px' 88 | // this.scrollRunway_.style.width = '1px' 89 | // this.scrollRunway_.style.transition = 'transform 0.2s' 90 | // this.scroller_.appendChild(this.scrollRunway_) 91 | this.initPosList() 92 | this.onResize_() 93 | } 94 | 95 | InfiniteScroller.prototype = { 96 | 97 | /** 98 | * Called when the browser window resizes to adapt to new scroller bounds and 99 | * layout sizes of items within the scroller. 100 | */ 101 | onResize_ () { 102 | // TODO: If we already have tombstones attached to the document, it would 103 | // probably be more efficient to use one of them rather than create a new 104 | // one to measure. 105 | var tombstone = this.source_.createTombstone(this.baseNode.cloneNode(true)) 106 | tombstone.style.position = 'absolute' 107 | this.scroller_.appendChild(tombstone) 108 | tombstone.classList.remove(this.INVISIBLE_CLASS) 109 | this.tombstoneSize_ = tombstone.offsetHeight / this.column 110 | this.tombstoneWidth_ = tombstone.offsetWidth 111 | this.scroller_.removeChild(tombstone) 112 | 113 | // Reset the cached size of items in the scroller as they may no longer be 114 | // correct after the item content undergoes layout. 115 | for (var i = 0; i < this.items_.length; i++) { 116 | this.items_[i].top = -1 117 | this.items_[i].height = this.items_[i].width = this.items_[i].cacheHeightCount = 0 118 | } 119 | this.onScroll_() 120 | }, 121 | 122 | /** 123 | * Called when the scroller scrolls. This determines the newly anchored item 124 | * and offset and then updates the visible elements, requesting more items 125 | * from the source if we've scrolled past the end of the currently available 126 | * content. 127 | */ 128 | onScroll_ () { 129 | const delta = this.scroller_.scrollTop - this.anchorScrollTop 130 | 131 | if (this.scroller_.scrollTop == 0) { 132 | this.anchorItem = { 133 | index: 0, 134 | offset: 0 135 | } 136 | } else { 137 | this.anchorItem = this.calculateAnchoredItem(this.anchorItem, delta) 138 | } 139 | 140 | this.anchorScrollTop = this.scroller_.scrollTop 141 | 142 | const lastScreenItem = this.calculateAnchoredItem(this.anchorItem, this.scroller_.offsetHeight) 143 | 144 | if (delta < 0) { 145 | this.fill(this.anchorItem.index - this.RUNWAY_ITEMS, lastScreenItem.index + this.RUNWAY_ITEMS_OPPOSITE) 146 | } else { 147 | this.fill(this.anchorItem.index - this.RUNWAY_ITEMS_OPPOSITE, lastScreenItem.index + this.RUNWAY_ITEMS) 148 | } 149 | }, 150 | 151 | /** 152 | * Calculates the item that should be anchored after scrolling by delta from 153 | * the initial anchored item. 154 | * @param {{index: number, offset: number}} initialAnchor The initial position 155 | * to scroll from before calculating the new anchor position. 156 | * @param {number} delta The offset from the initial item to scroll by. 157 | * @return {{index: number, offset: number}} Returns the new item and offset 158 | * scroll should be anchored to. 159 | */ 160 | calculateAnchoredItem (initialAnchor, delta) { 161 | if (delta === 0) return initialAnchor 162 | delta += initialAnchor.offset 163 | var i = initialAnchor.index 164 | var tombstones = 0 165 | if (delta < 0) { 166 | while (delta < 0 && i > 0 && this.items_[i - 1].height) { 167 | delta += this.items_[i - 1].height 168 | i-- 169 | } 170 | tombstones = Math.max(-i, Math.ceil(Math.min(delta, 0) / this.tombstoneSize_)) 171 | } else { 172 | while (delta > 0 && i < this.items_.length && this.items_[i].height && this.items_[i].height < delta) { 173 | delta -= this.items_[i].height 174 | i++ 175 | } 176 | if (i >= this.items_.length || !this.items_[i].height) 177 | tombstones = Math.floor(Math.max(delta, 0) / this.tombstoneSize_) 178 | } 179 | i += tombstones 180 | delta -= tombstones * this.tombstoneSize_ 181 | i = Math.min(i, this.MAX_COUNT - 1) 182 | 183 | return { 184 | index: Math.floor(i / this.column) * this.column, 185 | offset: delta 186 | } 187 | }, 188 | 189 | setStyle (el, key, val) { 190 | setStyle(el, key, val, this.options.usePrefix) 191 | }, 192 | 193 | /** 194 | * Sets the range of items which should be attached and attaches those items. 195 | * @param {number} start The first item which should be attached. 196 | * @param {number} end One past the last item which should be attached. 197 | */ 198 | fill (start, end) { 199 | this.firstAttachedItem_ = Math.max(0, start) 200 | this.lastAttachedItem_ = end 201 | this.attachContent() 202 | }, 203 | 204 | /** 205 | * Creates or returns an existing tombstone ready to be reused. 206 | * @return {Element} A tombstone element ready to be used. 207 | */ 208 | getTombstone () { 209 | const tombstone = this.tombstones_.pop() 210 | if (tombstone) { 211 | tombstone.classList.remove(this.INVISIBLE_CLASS) 212 | tombstone.style.opacity = 1 213 | this.setStyle(tombstone, 'transform', '') 214 | this.setStyle(tombstone, 'transition', '') 215 | return tombstone 216 | } 217 | return this.source_.createTombstone(this.baseNode.cloneNode(true)) 218 | }, 219 | 220 | layoutInView (i) { 221 | const top = this.posList.get(Math.floor(i / this.column), i % this.column) 222 | if (!top) return true 223 | const index = top - this.anchorScrollTop 224 | return (index > -window.innerHeight * .5 && index < window.innerHeight) 225 | }, 226 | 227 | getUnUsedNodes (clearAll) { 228 | if (this.waterflow) { 229 | for (let i = 0, len = this.items_.length; i < len; i++) { 230 | if (this.items_[i].node && (clearAll || !this.layoutInView(i))) { 231 | if (this.items_[i].vm) { 232 | this.clearItem(this.items_[i]) 233 | } else { 234 | this.clearTombstone(this.items_[i]) 235 | } 236 | this.items_[i].vm = null 237 | this.items_[i].node = null 238 | } 239 | } 240 | } else { 241 | for (let i = 0, len = this.items_.length; i < len; i++) { 242 | if (i === this.firstAttachedItem_) { 243 | i = this.lastAttachedItem_ - 1 244 | continue 245 | } 246 | if (this.items_[i].vm) { 247 | this.clearItem(this.items_[i]) 248 | } else { 249 | this.clearTombstone(this.items_[i]) 250 | } 251 | 252 | this.items_[i].vm = null 253 | this.items_[i].node = null 254 | } 255 | } 256 | }, 257 | 258 | clearItem (item) { 259 | if (this.options.reuseVM) { 260 | this.scroller_.removeChild(item.node) 261 | this.source_.free(item.data) 262 | } else { 263 | if (this.cacheVM && item.node) { 264 | return this.scroller_.removeChild(item.node) 265 | } 266 | item.vm.$destroy() 267 | if (item.node) { 268 | this.unusedNodes.push(item.node) 269 | } 270 | } 271 | }, 272 | 273 | clearTombstone (item) { 274 | if (item.node) { 275 | if (item.node.classList.contains(this.TOMBSTONE_CLASS)) { 276 | this.tombstones_.push(item.node) 277 | this.tombstones_[this.tombstones_.length - 1].classList.add(this.INVISIBLE_CLASS) 278 | } else { 279 | this.unusedNodes.push(item.node) 280 | } 281 | } 282 | }, 283 | 284 | clearUnUsedNodes () { 285 | while (this.unusedNodes.length) { 286 | this.scroller_.removeChild(this.unusedNodes.pop()) 287 | } 288 | }, 289 | 290 | getNodePosition () { 291 | // Fix scroll position in case we have realized the heights of elements 292 | // that we didn't used to know. 293 | // TODO: We should only need to do this when a height of an item becomes 294 | // known above. 295 | this.anchorScrollTop = 0 296 | for (let i = 0; i < this.anchorItem.index; i++) { 297 | this.anchorScrollTop += this.items_[i].height || this.tombstoneSize_ 298 | } 299 | this.anchorScrollTop += this.anchorItem.offset 300 | 301 | this.curPos = this.anchorScrollTop - this.anchorItem.offset 302 | let i = this.anchorItem.index 303 | while (i > this.firstAttachedItem_) { 304 | this.curPos -= this.items_[i - 1].height || this.tombstoneSize_ 305 | i-- 306 | } 307 | while (i < this.firstAttachedItem_) { 308 | this.curPos += this.items_[i].height || this.tombstoneSize_ 309 | i++ 310 | } 311 | }, 312 | 313 | initPosList () { 314 | let data = {} 315 | for (let i = 0, len = this.column; i < len; i++) { 316 | data[i] = this.curPos 317 | } 318 | 319 | this.posList = { 320 | data: { 321 | 0: data 322 | }, 323 | get (row, col) { 324 | if (!this.data[row]) { 325 | let data = {} 326 | for (let i = 0, len = this.column; i < len; i++) { 327 | data[i] = this.curPos 328 | } 329 | this.data[row] = data // Array.from({ length: this.column }).map(i => this.curPos) 330 | } 331 | if (col === undefined) return this.data[row] 332 | return this.data[row][col] 333 | }, 334 | set (row, col, val) { 335 | this.get(row)[col] = val 336 | } 337 | } 338 | }, 339 | 340 | tombstoneLayout (tombstoneAnimations) { 341 | let i 342 | let anim 343 | let x 344 | for (i in tombstoneAnimations) { 345 | anim = tombstoneAnimations[i] 346 | x = (i % this.column) * this.items_[i].width 347 | this.setStyle(this.items_[i].node, 'transform', 'translate3d(' + x + 'px,' + (this.anchorScrollTop + anim[1]) * this.column + 'px, 0) scale(' + (this.tombstoneWidth_ / this.items_[i].width) + ', ' + (this.tombstoneSize_ / this.items_[i].height) + ')') 348 | // Call offsetTop on the nodes to be animated to force them to apply current transforms. 349 | this.items_[i].node.offsetTop 350 | anim[0].offsetTop 351 | this.setStyle(this.items_[i].node, 'transition', 'transform ' + this.ANIMATION_DURATION_MS + 'ms') 352 | } 353 | }, 354 | 355 | itemLayout (tombstoneAnimations) { 356 | let i 357 | let anim 358 | let x = 0 359 | let y = 0 360 | let row = 0 361 | let curPosList 362 | 363 | let size = 0 364 | 365 | for (i = this.firstAttachedItem_; i < this.lastAttachedItem_; i++) { 366 | anim = tombstoneAnimations[i] 367 | if (this.waterflow) { 368 | row = Math.floor(i / this.column) 369 | } 370 | x = (i % this.column) * (this.items_[i].width || this.tombstoneWidth_) 371 | y = this.waterflow ? this.posList.get(row, i % this.column) : this.curPos 372 | if (anim) { 373 | this.setStyle(anim[0], 'transition', 'transform ' + this.ANIMATION_DURATION_MS + 'ms, opacity ' + this.ANIMATION_DURATION_MS + 'ms') 374 | this.setStyle(anim[0], 'transform', 'translate3d(' + x + 'px,' + y + 'px, 0) scale(' + (this.items_[i].width / this.tombstoneWidth_) + ', ' + (this.items_[i].height / this.tombstoneSize_) + ')') 375 | anim[0].style.opacity = 0 376 | } 377 | if (this.items_[i].node && this.curPos !== this.items_[i].top) { 378 | if (!anim) this.setStyle(this.items_[i].node, 'transition', '') 379 | this.setStyle(this.items_[i].node, 'transform', 'translate3d('+ x + 'px,' + y + 'px, 0)') 380 | } 381 | this.items_[i].top = y 382 | 383 | if ((i + 1) % this.column === 0) { 384 | this.curPos += (this.items_[i].height || this.tombstoneSize_) * this.column 385 | } 386 | if (this.waterflow) { 387 | this.posList.set(row + 1, i % this.column, y + (this.items_[i].height || this.tombstoneSize_) * this.column) 388 | } 389 | } 390 | }, 391 | 392 | setAnimatePosition (tombstoneAnimations) { 393 | this.tombstoneLayout(tombstoneAnimations) 394 | this.itemLayout(tombstoneAnimations) 395 | }, 396 | 397 | renderItems () { 398 | let tombstoneAnimations = {} 399 | let node 400 | let newNodes = [] 401 | let i 402 | 403 | const last = Math.floor((this.lastAttachedItem_ + this.RUNWAY_ITEMS) / this.column) * this.column 404 | 405 | if (last > this.MAX_COUNT) { 406 | this.lastAttachedItem_ = this.MAX_COUNT 407 | } 408 | // Create DOM nodes. 409 | for (i = this.firstAttachedItem_; i < this.lastAttachedItem_; i++) { 410 | while (this.items_.length <= i) { 411 | this.addItem_() 412 | } 413 | if (this.items_[i].node) { 414 | // if it's a tombstone but we have data, replace it. 415 | if (this.items_[i].node.classList.contains(this.TOMBSTONE_CLASS) && 416 | this.items_[i].data) { 417 | // TODO: Probably best to move items on top of tombstones and fade them in instead. 418 | if (this.ANIMATION_DURATION_MS) { 419 | this.items_[i].node.style.zIndex = 1; 420 | tombstoneAnimations[i] = [this.items_[i].node, this.items_[i].top - this.anchorScrollTop] 421 | } else { 422 | this.items_[i].node.classList.add(this.INVISIBLE_CLASS) 423 | this.tombstones_.push(this.items_[i].node) 424 | } 425 | this.items_[i].node = null 426 | } else { 427 | continue 428 | } 429 | } 430 | if (this.waterflow) { 431 | if (this.layoutInView(i)) { 432 | if (this.items_[i].data) { 433 | node = this.source_.render(this.items_[i].data, (this.unusedNodes.pop() || this.baseNode.cloneNode(true)), this.items_[i]) 434 | } else { 435 | node = this.getTombstone() 436 | } 437 | // Maybe don't do this if it's already attached? 438 | node.style.position = 'absolute' 439 | this.items_[i].top = -1 440 | // this.scroller_.appendChild(node) 441 | this.items_[i].node = node 442 | newNodes.push(node) 443 | } 444 | } else { 445 | if (this.items_[i].data) { 446 | node = this.source_.render(this.items_[i].data, (this.unusedNodes.pop() || this.baseNode.cloneNode(true)), this.items_[i]) 447 | } else { 448 | node = this.getTombstone() 449 | } 450 | // Maybe don't do this if it's already attached? 451 | node.style.position = 'absolute' 452 | this.items_[i].top = -1 453 | // this.scroller_.appendChild(node) 454 | this.items_[i].node = node 455 | newNodes.push(node) 456 | } 457 | } 458 | 459 | let len = newNodes.length 460 | for (i = 0; i < len; i++) { 461 | this.scroller_.appendChild(newNodes[i]) 462 | } 463 | return tombstoneAnimations 464 | }, 465 | 466 | cacheItemHeight (force) { 467 | let rect = {} 468 | for (let i = this.firstAttachedItem_; i < this.lastAttachedItem_; i++) { 469 | // cacheItemsHeight 470 | if (this.items_[i].data && this.items_[i].node && (force || !this.items_[i].height)) { 471 | this.items_[i].height = this.items_[i].node.offsetHeight / this.column 472 | this.items_[i].width = this.items_[i].node.offsetWidth 473 | this.items_[i].cacheHeightCount = 0 474 | } else if (this.items_[i].cacheHeightCount < 10) { 475 | // if height's cache is not match 476 | this.items_[i].cacheHeightCount++ 477 | if (this.items_[i].height && this.items_[i].node && this.items_[i].height !== this.items_[i].node.offsetHeight / this.column) { 478 | this.items_[i].height = this.items_[i].node.offsetHeight / this.column 479 | } 480 | } 481 | } 482 | }, 483 | 484 | /** 485 | * Attaches content to the scroller and updates the scroll position if 486 | * necessary. 487 | */ 488 | attachContent () { 489 | this.getUnUsedNodes() 490 | 491 | let tombstoneAnimations = this.renderItems() 492 | 493 | this.clearUnUsedNodes() 494 | 495 | this.cacheItemHeight() 496 | 497 | this.getNodePosition() 498 | 499 | this.setAnimatePosition(tombstoneAnimations) 500 | 501 | // this.setScrollRunway() 502 | 503 | if (this.ANIMATION_DURATION_MS) { 504 | // TODO: Should probably use transition end, but there are a lot of animations we could be listening to. 505 | setTimeout(() => { 506 | this.tombstoneAnimation(tombstoneAnimations) 507 | }, this.ANIMATION_DURATION_MS) 508 | } 509 | 510 | this.maybeRequestContent() 511 | }, 512 | 513 | setItems (list) { 514 | list = list || [] 515 | this.items_ = list 516 | this.MAX_COUNT = list.length 517 | }, 518 | 519 | scrollToIndex (index) { 520 | const commonItemCount = this.lastAttachedItem_ - this.firstAttachedItem_ 521 | this.fill(index - commonItemCount, index + 1) 522 | }, 523 | 524 | setScrollRunway () { 525 | this.scrollRunwayEnd_ = Math.max(this.scrollRunwayEnd_, this.curPos + this.SCROLL_RUNWAY) 526 | this.setStyle(this.scrollRunway_, 'transform', 'translate(0, ' + this.scrollRunwayEnd_ + 'px)') 527 | this.scroller_.scrollTop = this.anchorScrollTop 528 | }, 529 | 530 | tombstoneAnimation (tombstoneAnimations) { 531 | let anim 532 | for (var i in tombstoneAnimations) { 533 | anim = tombstoneAnimations[i] 534 | anim[0].classList.add(this.INVISIBLE_CLASS) 535 | this.tombstones_.push(anim[0]) 536 | } 537 | tombstoneAnimations = null 538 | }, 539 | 540 | /** 541 | * Requests additional content if we don't have enough currently. 542 | */ 543 | maybeRequestContent () { 544 | // Don't issue another request if one is already in progress as we don't 545 | // know where to start the next request yet. 546 | if (this.requestInProgress_) return 547 | var itemsNeeded = this.lastAttachedItem_ - this.loadedItems_; 548 | if (itemsNeeded <= 0) return 549 | this.requestInProgress_ = true 550 | if (!this.source_.fetch) return 551 | this.source_.fetch(itemsNeeded, this.loadedItems_).then(data => { 552 | this.MAX_COUNT = data.count 553 | this.addContent(data.list) 554 | }) 555 | }, 556 | 557 | /** 558 | * Adds an item to the items list. 559 | */ 560 | addItem_ () { 561 | this.items_.push({ 562 | vm: null, 563 | data: null, 564 | node: null, 565 | height: 0, 566 | width: 0, 567 | top: 0, 568 | }) 569 | }, 570 | 571 | /** 572 | * Adds the given array of items to the items list and then calls 573 | * attachContent to update the displayed content. 574 | * @param {Array} items The array of items to be added to the infinite 575 | * scroller list. 576 | */ 577 | addContent (items) { 578 | if (!items.length) return 579 | this.requestInProgress_ = false 580 | 581 | let index 582 | for (var i = 0; i < items.length; i++) { 583 | if (this.items_.length <= this.loadedItems_) { 584 | this.addItem_() 585 | } 586 | if (this.loadedItems_ <= this.MAX_COUNT) { 587 | index = this.loadedItems_++ 588 | this.items_[index].data = items[i] 589 | } 590 | } 591 | 592 | this.attachContent() 593 | }, 594 | 595 | clear () { 596 | this.loadedItems_ = 0 597 | this.requestInProgress_ = false 598 | 599 | this.firstAttachedItem_ = -1 600 | this.lastAttachedItem_ = -1 601 | 602 | this.getUnUsedNodes(true) 603 | this.clearUnUsedNodes() 604 | 605 | this.items_ = [] 606 | 607 | this.onResize_() 608 | }, 609 | 610 | destroy () { 611 | this.scroller_.removeEventListener('scroll', this.onScroll_) 612 | window.removeEventListener('resize', this.onResize_) 613 | window.removeEventListener('orientationchange', this.onResize_) 614 | this.clear() 615 | } 616 | } 617 | -------------------------------------------------------------------------------- /src/polyfill.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * Polyfill for Object.keys 4 | * 5 | * @see: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/keys 6 | */ 7 | if (!Object.keys) { 8 | Object.keys = (function () { 9 | var hasOwnProperty = Object.prototype.hasOwnProperty, 10 | hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), 11 | dontEnums = [ 12 | 'toString', 13 | 'toLocaleString', 14 | 'valueOf', 15 | 'hasOwnProperty', 16 | 'isPrototypeOf', 17 | 'propertyIsEnumerable', 18 | 'constructor' 19 | ], 20 | dontEnumsLength = dontEnums.length; 21 | 22 | return function (obj) { 23 | if (typeof obj !== 'object' && typeof obj !== 'function' || obj === null) throw new TypeError('Object.keys called on non-object'); 24 | 25 | var result = []; 26 | 27 | for (var prop in obj) { 28 | if (hasOwnProperty.call(obj, prop)) result.push(prop); 29 | } 30 | 31 | if (hasDontEnumBug) { 32 | for (var i=0; i < dontEnumsLength; i++) { 33 | if (hasOwnProperty.call(obj, dontEnums[i])) result.push(dontEnums[i]); 34 | } 35 | } 36 | return result; 37 | } 38 | })() 39 | }; -------------------------------------------------------------------------------- /src/recyclerview.css: -------------------------------------------------------------------------------- 1 | .recyclerview-container { 2 | position: relative; 3 | } 4 | .recyclerview-loading { 5 | position: absolute; 6 | top: 0; 7 | left: 0; 8 | width: 100%; 9 | text-align: center; 10 | padding: 10px; 11 | font-size: 14px; 12 | color: #9E9E9E; 13 | } 14 | .recyclerview { 15 | background: #fff; 16 | margin: 0; 17 | padding: 0; 18 | overflow-x: hidden; 19 | overflow-y: scroll; 20 | -webkit-overflow-scrolling: touch; 21 | width: 100%; 22 | height: 100%; 23 | position: absolute; 24 | box-sizing: border-box; 25 | contain: layout; 26 | will-change: transform; 27 | } 28 | -------------------------------------------------------------------------------- /src/recyclerview.js: -------------------------------------------------------------------------------- 1 | import InfiniteScroller from './infinite' 2 | import ContentSource from './content-source' 3 | import { 4 | getEventPosition, 5 | requestAnimationFrame, 6 | preventDefaultException, 7 | assign, 8 | setStyle 9 | } from './util' 10 | 11 | const Loading = { 12 | render (h) { 13 | return h('div', { 14 | attrs: { 15 | class: 'recyclerview-loading' 16 | } 17 | }, 'Loading...') 18 | } 19 | } 20 | 21 | const Tombstone = { 22 | render (h) { 23 | return h('div', { 24 | attrs: { 25 | class: 'recyclerview-item tombstone' 26 | }, 27 | style: { 28 | height: '100px', 29 | width: '100%' 30 | } 31 | }, '') 32 | } 33 | } 34 | 35 | const options = { 36 | preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|IMG)$/ }, 37 | distance: 50, 38 | animation_duration_ms: 200, 39 | tombstone_class: 'tombstone', 40 | invisible_class: 'invisible', 41 | prerender: 20, 42 | remain: 10, 43 | preventDefault: false, 44 | column: 1, 45 | waterflow: false, 46 | cacheVM: 0, 47 | reuseVM: false, 48 | usePrefix: false, 49 | props: {} 50 | } 51 | 52 | export default (Vue) => { 53 | return { 54 | name: 'RecyclerView', 55 | props: { 56 | fetch: Function, 57 | list: Array, 58 | item: Object, 59 | loading: Object, 60 | tombstone: { 61 | type: Object, 62 | default: () => Tombstone 63 | }, 64 | column: Number, 65 | prerender: Number, 66 | remain: Number, 67 | waterflow: Boolean, 68 | preventDefault: Boolean, 69 | options: Object, 70 | tag: { 71 | type: String, 72 | default: 'div' 73 | } 74 | }, 75 | render (h) { 76 | return h(this.tag, { 77 | attrs: { 78 | class: 'recyclerview-container' 79 | } 80 | }, [ 81 | h(this.loading || Loading), 82 | h(this.tag, { 83 | attrs: { 84 | class: 'recyclerview' 85 | }, 86 | on: { 87 | touchstart: this._start, 88 | touchmove: this._move, 89 | touchend: this._end, 90 | touchcancel: this._end, 91 | mousedown: this._start, 92 | mousemove: this._move, 93 | mouseup: this._end 94 | } 95 | })] 96 | ) 97 | }, 98 | data () { 99 | return { 100 | startPointer: { 101 | x: 0, 102 | y: 0 103 | }, 104 | $list: null, 105 | _options: {}, 106 | distance: 0, 107 | pulling: false, 108 | _contentSource: null, 109 | _scroller: null 110 | } 111 | }, 112 | mounted () { 113 | this.init() 114 | }, 115 | beforeDestroy () { 116 | this._scroller.destroy() 117 | this._scroller = null 118 | }, 119 | methods: { 120 | init () { 121 | this._options = assign({}, options, { 122 | prerender: this.prerender || options.prerender, 123 | remain: this.remain || options.remain, 124 | column: this.column || options.column, 125 | waterflow: this.waterflow || options.waterflow, 126 | fetch: this.fetch, 127 | list: this.list, 128 | item: this.item, 129 | loading: this.loading, 130 | tombstone: this.tombstone 131 | }, this.options) 132 | 133 | this._contentSource = new ContentSource(Vue, this._options) 134 | 135 | this.$list = this.$el.querySelector('.recyclerview') 136 | this._scroller = new InfiniteScroller( 137 | this.$list, 138 | this._contentSource, 139 | this._options 140 | ) 141 | this.$emit('inited') 142 | }, 143 | scrollToIndex (index) { 144 | if (this.waterflow) { 145 | for (let i = 0, len = this._scroller.items_.length; i < len; i++) { 146 | if (i === index) { 147 | this._scrollTo(this._scroller.items_[i].top - this._scroller.items_[i].height * this._options.column + this.$list.offsetWidth) 148 | } 149 | } 150 | return 151 | } 152 | index = Number(index) 153 | this._scroller.scrollToIndex(index) 154 | this.$nextTick(() => { 155 | this._scrollToBottom() 156 | }) 157 | }, 158 | _scrollTo (top) { 159 | top = top || 0 160 | this.$list.scrollTop = Number(top) 161 | }, 162 | _scrollToBottom () { 163 | this._scrollTo(this.$list.scrollHeight) 164 | }, 165 | _renderListStyle () { 166 | setStyle(this.$list, 'transform', 'translate3d(0, ' + this.distance + 'px, 0)', this.options.usePrefix) 167 | }, 168 | _start (e) { 169 | if (this.$list.scrollTop > 0) return 170 | this.pulling = true 171 | this.startPointer = getEventPosition(e) 172 | setStyle(this.$list, 'transition', 'transform .2s', this.options.usePrefix) 173 | if (this.preventDefault && !preventDefaultException(e.target, this._options.preventDefaultException)) { 174 | e.preventDefault() 175 | } 176 | }, 177 | _move (e) { 178 | if (!this.pulling) return 179 | const pointer = getEventPosition(e) 180 | const distance = pointer.y - this.startPointer.y 181 | 182 | if (distance < 0) { 183 | this._scrollTo(-distance) 184 | return 185 | } 186 | 187 | if (this.preventDefault && !preventDefaultException(e.target, this._options.preventDefaultException)) { 188 | e.preventDefault() 189 | } 190 | 191 | this.distance = Math.floor(distance * 0.5) 192 | if (this.distance > this._options.distance) { 193 | this.distance = this._options.distance 194 | } 195 | requestAnimationFrame(this._renderListStyle.bind(this)) 196 | }, 197 | _end (e) { 198 | if (!this.pulling) return 199 | if (this.preventDefault && !preventDefaultException(e.target, this._options.preventDefaultException)) { 200 | e.preventDefault() 201 | } 202 | this.pulling = false 203 | this.$list.style.transition = 'transform .3s' 204 | this.$nextTick(() => { 205 | this.$list.style.transform = '' 206 | }) 207 | if (this.distance >= this._options.distance) { 208 | this.distance = 0 209 | this._scroller.clear() 210 | } 211 | } 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | const defaultPosition = { 2 | x: 0, 3 | y: 0 4 | } 5 | 6 | const mouseEvent = /mouse(down|move|up)/ 7 | const touchEvent = /touch(start|move|end)/ 8 | 9 | export function getEventPosition (e) { 10 | if (!e) return defaultPosition 11 | if (touchEvent.test(e.type)) { 12 | let touch = e.touches[0] 13 | return { 14 | x: touch.clientX, 15 | y: touch.clientY 16 | } 17 | } else if (mouseEvent.test(e.type)) { 18 | return { 19 | x: e.clientX, 20 | y: e.clientY 21 | } 22 | } 23 | return defaultPosition 24 | } 25 | 26 | export const requestAnimationFrame = window.requestAnimationFrame || 27 | window.webkitRequestAnimationFrame || 28 | window.mozRequestAnimationFrame || 29 | window.oRequestAnimationFrame || 30 | window.msRequestAnimationFrame || 31 | function (callback) { 32 | window.setTimeout(callback, 1000 / 60) 33 | } 34 | 35 | export function preventDefaultException (el, exceptions) { 36 | for (let i in exceptions) { 37 | if (exceptions[i].test(el[i])) { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | 44 | export function assign (target, varArgs) { // .length of function is 2 45 | if (target == null) { // TypeError if undefined or null 46 | throw new TypeError('Cannot convert undefined or null to object') 47 | } 48 | 49 | var to = Object(target) 50 | 51 | for (var index = 1; index < arguments.length; index++) { 52 | var nextSource = arguments[index] 53 | 54 | if (nextSource) { // Skip over if undefined or null 55 | for (var nextKey in nextSource) { 56 | // Avoid bugs when hasOwnProperty is shadowed 57 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 58 | to[nextKey] = nextSource[nextKey] 59 | } 60 | } 61 | } 62 | } 63 | return to 64 | } 65 | 66 | const prefixs = ['ms', 'Moz', 'Webkit', 'O'] 67 | const prefixCache = {} 68 | function getPrefix (_key) { 69 | if (prefixCache[_key]) return prefixCache[_key] 70 | const key = _key[0].toUpperCase() + _key.slice(1, _key.length) 71 | prefixCache[_key] = prefixs.map(p => p + key) 72 | return prefixCache[_key] 73 | } 74 | 75 | export function setStyle (el, key, value, usePrefix) { 76 | el.style[key] = value 77 | if (!usePrefix) return 78 | const keys = getPrefix(key) 79 | keys.map(prefixedKey => { 80 | el.style[prefixedKey] = value 81 | }) 82 | } 83 | 84 | export function inView (el, preLoad = 1) { 85 | const rect = el.getBoundingClientRect() 86 | return rect.top < window.innerHeight * preLoad && 87 | rect.bottom > 0 && 88 | rect.left < window.innerWidth * preLoad && 89 | rect.right > 0 90 | } 91 | 92 | export function find (arr, handler) { 93 | for (let i = 0, len = arr.length; i < len; i++) { 94 | if (handler(arr[i], i)) { 95 | return arr[i] 96 | } 97 | } 98 | } 99 | --------------------------------------------------------------------------------