├── .eslintignore ├── .prettierrc.js ├── src ├── core │ ├── third-party │ │ ├── scroller │ │ │ ├── README.MD │ │ │ ├── render.js │ │ │ ├── requestAnimationFrame.js │ │ │ ├── listener.js │ │ │ └── animate.js │ │ ├── resize-detector │ │ │ ├── README.MD │ │ │ └── index.js │ │ ├── easingPattern │ │ │ ├── README.MD │ │ │ └── index.js │ │ └── README.MD │ ├── mixins │ │ ├── index.js │ │ ├── mix-panel.js │ │ ├── config.js │ │ ├── api.js │ │ └── core.js │ └── components │ │ └── panel.js ├── mode │ ├── mix │ │ ├── mixins │ │ │ ├── index.js │ │ │ ├── update-mix.js │ │ │ └── api.js │ │ ├── index.js │ │ ├── mix-panel.js │ │ ├── config.js │ │ └── core.js │ ├── native │ │ ├── mixins │ │ │ ├── index.js │ │ │ ├── scrollAnimate.js │ │ │ ├── update-native.js │ │ │ └── api.js │ │ ├── index.js │ │ ├── config.js │ │ ├── native-panel.js │ │ └── core.js │ └── slide │ │ ├── mixins │ │ ├── index.js │ │ └── api.js │ │ ├── index.js │ │ ├── config.js │ │ ├── slide-panel.js │ │ └── core.js ├── shared │ ├── log.js │ ├── index.js │ ├── constants.js │ ├── scroll-map.js │ ├── zoomManager.js │ ├── utils.js │ ├── touchManager.js │ ├── base-config.js │ └── runtime.js ├── entry-slide-mode.js ├── entry-native-mode.js └── entry-mix-mode.js ├── .gitignore ├── test └── unit │ ├── index.js │ ├── specs │ ├── util.spec.js │ ├── mode.spec.js │ ├── event.spec.js │ ├── class-hooks.spec.js │ ├── slot.spec.js │ ├── scroll-panel.spec.js │ ├── bar.spec.js │ └── assert-warn-info.spec.js │ ├── karma.conf.js │ └── util.js ├── dist ├── vuescroll.css ├── vuescroll-native.d.ts └── vuescroll-slide.d.ts ├── types ├── index.d.ts ├── vue.d.ts ├── vuescroll.d.ts ├── Config.d.ts ├── vuescroll-native.d.ts └── vuescroll-slide.d.ts ├── jsconfig.json ├── .babelrc ├── scripts ├── alias.js ├── debug-build.js ├── build.js └── config.js ├── .eslintrc.js ├── LICENSE ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── COMMIT_CONVENTION.md ├── .circleci └── config.yml ├── examples └── base-scroll.html ├── package.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | coverage 3 | node_modules 4 | examples -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'none' 4 | }; 5 | -------------------------------------------------------------------------------- /src/core/third-party/scroller/README.MD: -------------------------------------------------------------------------------- 1 | # Scroller 2 | 3 | [Github Address](https://github.com/pbakaus/scroller) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | coverage 4 | package-lock.json 5 | debug.log 6 | yarn-error.log 7 | .idea 8 | .npmrc -------------------------------------------------------------------------------- /src/mode/mix/mixins/index.js: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import update from './update-mix'; 3 | 4 | export default [api, ...update]; 5 | -------------------------------------------------------------------------------- /src/core/third-party/resize-detector/README.MD: -------------------------------------------------------------------------------- 1 | # resize-detector 2 | 3 | [Github Address](https://github.com/wnr/element-resize-detector) 4 | -------------------------------------------------------------------------------- /src/mode/native/mixins/index.js: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import update from './update-native'; 3 | 4 | export default [api, update]; 5 | -------------------------------------------------------------------------------- /src/mode/slide/mixins/index.js: -------------------------------------------------------------------------------- 1 | import api from './api'; 2 | import update from './update-slide'; 3 | 4 | export default [api, update]; 5 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // require all test files 2 | const testsContext = require.context('./', true, /\.spec$/); 3 | testsContext.keys().forEach(testsContext); 4 | -------------------------------------------------------------------------------- /dist/vuescroll.css: -------------------------------------------------------------------------------- 1 | /* 2 | Since 4.10.0, you don't need to import css flie any more, 3 | this flie is only used for backward compatible. Maybe deleted 4 | in the future. 5 | */ 6 | -------------------------------------------------------------------------------- /src/mode/mix/mixins/update-mix.js: -------------------------------------------------------------------------------- 1 | import slideMix from 'mode/slide/mixins/update-slide'; 2 | import nativeMix from 'mode/native/mixins/update-native'; 3 | 4 | export default [slideMix, nativeMix]; 5 | -------------------------------------------------------------------------------- /src/shared/log.js: -------------------------------------------------------------------------------- 1 | export const log = { 2 | error: (msg) => { 3 | console.error(`[vuescroll] ${msg}`); 4 | }, 5 | warn: (msg) => { 6 | console.warn(`[vuescroll] ${msg}`); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import './vue'; 2 | 3 | import Config from './Config'; 4 | 5 | import { vuescroll } from './vuescroll'; 6 | 7 | export default vuescroll; 8 | 9 | export { Config }; 10 | -------------------------------------------------------------------------------- /src/mode/native/index.js: -------------------------------------------------------------------------------- 1 | import { createPanel } from './native-panel'; 2 | import core from './core'; 3 | import { configs } from './config'; 4 | 5 | export { core, createPanel as render, configs as extraConfigs }; 6 | -------------------------------------------------------------------------------- /src/core/third-party/easingPattern/README.MD: -------------------------------------------------------------------------------- 1 | # Smooth scroll easingPattern 2 | 3 | [Github Address](https://github.com/cferdinandi/smooth-scroll/blob/a10c2f39e0a20611a74296c9ee8fb1eb9abf90e2/src/js/smooth-scroll.js#L177) 4 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "emitDecoratorMetadata": true, 4 | "experimentalDecorators": true, 5 | "baseUrl": ".", 6 | "paths": { 7 | "src/*": ["./src/*"] 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/mode/native/config.js: -------------------------------------------------------------------------------- 1 | export const configs = [ 2 | { 3 | vuescroll: { 4 | wheelScrollDuration: 0, 5 | wheelDirectionReverse: false, 6 | checkShiftKey: true, 7 | deltaPercent: 1 8 | } 9 | } 10 | ]; 11 | -------------------------------------------------------------------------------- /types/vue.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Augment the typings of Vue.js 3 | */ 4 | 5 | import Vue from 'vue'; 6 | import Config from './Config'; 7 | 8 | declare module 'vue/types/vue' { 9 | interface Vue { 10 | $vuescrollConfig: Config; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "modules": false }], 4 | ], 5 | "plugins": ["@vue/babel-plugin-jsx"] 6 | // "env": { 7 | // "build": { 8 | // "plugins": ["external-helpers"] 9 | // } 10 | // } 11 | } 12 | -------------------------------------------------------------------------------- /src/entry-slide-mode.js: -------------------------------------------------------------------------------- 1 | import _install from 'src/core'; 2 | import { core, render, extraConfigs, extraValidators } from './mode/slide'; 3 | 4 | const Vuescroll = { 5 | ..._install(core, render, extraConfigs, extraValidators) 6 | }; 7 | 8 | export default Vuescroll; 9 | -------------------------------------------------------------------------------- /src/mode/mix/index.js: -------------------------------------------------------------------------------- 1 | import render from './mix-panel'; 2 | import core from './core'; 3 | import { configs, configValidators } from './config'; 4 | 5 | export { 6 | core, 7 | render, 8 | configs as extraConfigs, 9 | configValidators as extraValidators 10 | }; 11 | -------------------------------------------------------------------------------- /src/shared/index.js: -------------------------------------------------------------------------------- 1 | export * from './base-config'; 2 | export * from './constants'; 3 | export * from './log'; 4 | export * from './runtime'; 5 | export * from './scroll-map'; 6 | export * from './touchManager'; 7 | export * from './zoomManager'; 8 | export * from './utils'; 9 | -------------------------------------------------------------------------------- /src/mode/slide/index.js: -------------------------------------------------------------------------------- 1 | import { createPanel } from './slide-panel'; 2 | import core from './core'; 3 | import { configs, configValidator } from './config'; 4 | 5 | export { 6 | core, 7 | createPanel as render, 8 | configs as extraConfigs, 9 | configValidator as extraValidators 10 | }; 11 | -------------------------------------------------------------------------------- /src/core/third-party/README.MD: -------------------------------------------------------------------------------- 1 | #### The following is the third party library used by Vuescroll and its corresponding module. 2 | 3 | | Libarary Name | Module | 4 | | --------------- | ------------- | 5 | | Scroller | core, animate | 6 | | Smooth-scroll | easingPattern | 7 | | resize-detector | injectObject | 8 | -------------------------------------------------------------------------------- /src/entry-native-mode.js: -------------------------------------------------------------------------------- 1 | import { scrollTo } from 'src/mode/native/mixins/api'; 2 | import _install from 'src/core'; 3 | 4 | import { core, render, extraConfigs } from './mode/native'; 5 | 6 | const Vuescroll = { 7 | scrollTo, 8 | ..._install(core, render, extraConfigs) 9 | }; 10 | 11 | export default Vuescroll; 12 | -------------------------------------------------------------------------------- /src/entry-mix-mode.js: -------------------------------------------------------------------------------- 1 | import { scrollTo } from 'src/mode/native/mixins/api'; 2 | import _install from 'src/core'; 3 | import { core, render, extraConfigs, extraValidators } from './mode/mix'; 4 | 5 | const Vuescroll = { 6 | scrollTo, 7 | ..._install(core, render, extraConfigs, extraValidators) 8 | }; 9 | 10 | export default Vuescroll; 11 | -------------------------------------------------------------------------------- /src/shared/constants.js: -------------------------------------------------------------------------------- 1 | // all modes 2 | export const modes = ['slide', 'native']; 3 | // some small changes. 4 | export const smallChangeArray = [ 5 | 'mergedOptions.vuescroll.pullRefresh.tips', 6 | 'mergedOptions.vuescroll.pushLoad.tips', 7 | 'mergedOptions.vuescroll.scroller.disable', 8 | 'mergedOptions.rail', 9 | 'mergedOptions.bar' 10 | ]; 11 | // refresh/load dom ref/key... 12 | export const __REFRESH_DOM_NAME = 'refreshDom'; 13 | export const __LOAD_DOM_NAME = 'loadDom'; 14 | -------------------------------------------------------------------------------- /scripts/alias.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | 4 | const resolve = (p) => path.resolve(__dirname, '../', p); 5 | const alias = { 6 | src: resolve('src'), 7 | test: resolve('./test'), 8 | vue$: 'vue/dist/vue.cjs.js' 9 | }; 10 | 11 | const extend = (alias) => { 12 | const dirs = fs.readdirSync(alias.src); 13 | dirs.forEach((dir) => { 14 | alias[dir] = `${alias.src}/${dir}`; 15 | }); 16 | }; 17 | extend(alias); 18 | 19 | module.exports = alias; 20 | -------------------------------------------------------------------------------- /src/core/mixins/index.js: -------------------------------------------------------------------------------- 1 | import render from './mix-panel'; 2 | import core from './core'; 3 | import { configs, configValidators } from './config'; 4 | 5 | import { _install } from 'mode/shared/util'; 6 | 7 | const component = _install(core, render, configs, configValidators); 8 | 9 | export default function install(Vue, opts = {}) { 10 | Vue.component(opts.name || component.name, component); 11 | Vue.config.globalProperties.$vuescrollConfig = opts.ops || {}; 12 | } 13 | 14 | export { component }; 15 | -------------------------------------------------------------------------------- /src/mode/mix/mix-panel.js: -------------------------------------------------------------------------------- 1 | // begin importing 2 | import { createPanel as createNativePanel } from 'mode/native/native-panel'; 3 | import { createPanel as createSlidePanel } from 'mode/slide/slide-panel'; 4 | /** 5 | * create a scrollPanel 6 | * 7 | * @param {any} size 8 | * @param {any} vm 9 | * @returns 10 | */ 11 | export default function createPanel(vm) { 12 | if (vm.mode == 'native') { 13 | return createNativePanel(vm); 14 | } else if (vm.mode == 'slide') { 15 | return createSlidePanel(vm); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/core/mixins/mix-panel.js: -------------------------------------------------------------------------------- 1 | // begin importing 2 | import { createPanel as createNativePanel } from 'mode/native/native-panel'; 3 | import { createPanel as createSlidePanel } from 'mode/slide/slide-panel'; 4 | /** 5 | * create a scrollPanel 6 | * 7 | * @param {any} size 8 | * @param {any} vm 9 | * @returns 10 | */ 11 | export default function createPanel(vm) { 12 | if (vm.mode == 'native') { 13 | return createNativePanel(vm); 14 | } else if (vm.mode == 'slide') { 15 | return createSlidePanel(vm); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: 'eslint:recommended', 4 | parser: 'babel-eslint', 5 | rules: { 6 | semi: ['error', 'always'], 7 | 'no-undef': 0, 8 | 'no-unused-vars': [ 9 | 'error', 10 | { 11 | argsIgnorePattern: '^h$', 12 | varsIgnorePattern: '^h$' 13 | } 14 | ], 15 | quotes: ['error', 'single'], 16 | excludedFiles: 'dist/*.js'.anchor, 17 | 'no-console': [0], 18 | indent: 0 19 | }, 20 | parserOptions: { 21 | ecmaVersion: 6, 22 | sourceType: 'module', 23 | ecmaFeatures: { 24 | jsx: true, 25 | experimentalObjectRestSpread: true 26 | } 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /scripts/debug-build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const aliases = require('./alias'); 3 | const path = require('path'); 4 | const { build, blue } = require('./build'); 5 | let builds = require('./config').getAllBuilds(); 6 | 7 | const filePath = `${aliases.src}`; 8 | let timeout; 9 | 10 | print('Start building in debug mode....\n'); 11 | 12 | fs.watch(path.resolve('./', filePath), { recursive: true }, function( 13 | event, 14 | filename 15 | ) { 16 | print('Detected ' + event); 17 | if (filename) { 18 | print('Filename provided: ' + filename); 19 | } else { 20 | print('Filename not provided'); 21 | } 22 | 23 | clearTimeout(timeout); 24 | timeout = setTimeout(() => { 25 | build(builds); 26 | timeout = null; 27 | }, 500); 28 | }); 29 | 30 | function print(str) { 31 | console.log(blue(str)); 32 | } 33 | -------------------------------------------------------------------------------- /src/shared/scroll-map.js: -------------------------------------------------------------------------------- 1 | export const scrollMap = { 2 | vertical: { 3 | size: 'height', 4 | opsSize: 'width', 5 | posName: 'top', 6 | opposName: 'bottom', 7 | sidePosName: 'right', 8 | page: 'pageY', 9 | scroll: 'scrollTop', 10 | scrollSize: 'scrollHeight', 11 | offset: 'offsetHeight', 12 | client: 'clientY', 13 | axis: 'Y', 14 | scrollButton: { 15 | start: 'top', 16 | end: 'bottom' 17 | } 18 | }, 19 | horizontal: { 20 | size: 'width', 21 | opsSize: 'height', 22 | posName: 'left', 23 | opposName: 'right', 24 | sidePosName: 'bottom', 25 | page: 'pageX', 26 | scroll: 'scrollLeft', 27 | scrollSize: 'scrollWidth', 28 | offset: 'offsetWidth', 29 | client: 'clientX', 30 | axis: 'X', 31 | scrollButton: { 32 | start: 'left', 33 | end: 'right' 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/mode/mix/config.js: -------------------------------------------------------------------------------- 1 | import { log, modes } from 'shared'; 2 | import { 3 | configs as slideConfig, 4 | configValidator as slideValidator 5 | } from 'mode/slide/config'; 6 | import { configs as nativeConfig } from 'mode/native/config'; 7 | 8 | const { error } = log; 9 | 10 | const config = { 11 | // vuescroll 12 | vuescroll: { 13 | mode: 'native' 14 | } 15 | }; 16 | /** 17 | * validate the options 18 | * @export 19 | * @param {any} ops 20 | */ 21 | function configValidator(ops) { 22 | let renderError = false; 23 | const { vuescroll } = ops; 24 | 25 | // validate modes 26 | if (!~modes.indexOf(vuescroll.mode)) { 27 | error( 28 | `Unknown mode: ${vuescroll.mode},the vuescroll's option "mode" should be one of the ${modes}` 29 | ); 30 | renderError = true; 31 | } 32 | 33 | return renderError; 34 | } 35 | 36 | export const configs = [config, ...slideConfig, ...nativeConfig]; 37 | export const configValidators = [configValidator, slideValidator]; 38 | -------------------------------------------------------------------------------- /src/mode/mix/mixins/api.js: -------------------------------------------------------------------------------- 1 | import nativeApi from 'mode/native/mixins/api'; 2 | import slideApi from 'mode/slide/mixins/api'; 3 | 4 | export default { 5 | // mix slide and nitive modes apis. 6 | mixins: [slideApi, nativeApi], 7 | methods: { 8 | // private api 9 | internalScrollTo(destX, destY, speed, easing) { 10 | if (this.mode == 'native') { 11 | this.nativeScrollTo(destX, destY, speed, easing); 12 | } 13 | // for non-native we use scroller's scorllTo 14 | else if (this.mode == 'slide') { 15 | this.slideScrollTo(destX, destY, speed, easing); 16 | } 17 | }, 18 | stop() { 19 | this.nativeStop(); 20 | }, 21 | pause() { 22 | this.nativePause(); 23 | }, 24 | continue() { 25 | this.nativeContinue(); 26 | }, 27 | getCurrentviewDom() { 28 | return this.mode == 'slide' 29 | ? this.getCurrentviewDomSlide() 30 | : this.getCurrentviewDomNative(); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/core/mixins/config.js: -------------------------------------------------------------------------------- 1 | import { modes } from 'shared/constants'; 2 | import { error } from 'shared/util'; 3 | import { 4 | config as slideConfig, 5 | configValidator as slideValidator 6 | } from 'mode/slide/config'; 7 | 8 | import { config as nativeConfig } from 'mode/native/config'; 9 | 10 | const config = { 11 | // vuescroll 12 | vuescroll: { 13 | mode: 'native' 14 | } 15 | }; 16 | /** 17 | * validate the options 18 | * @export 19 | * @param {any} ops 20 | */ 21 | function configValidator(ops) { 22 | let renderError = false; 23 | const { vuescroll } = ops; 24 | 25 | // validate modes 26 | if (!~modes.indexOf(vuescroll.mode)) { 27 | error( 28 | `Unknown mode: ${ 29 | vuescroll.mode 30 | },the vuescroll's option "mode" should be one of the ${modes}` 31 | ); 32 | renderError = true; 33 | } 34 | 35 | return renderError; 36 | } 37 | 38 | export const configs = [config, slideConfig, nativeConfig]; 39 | export const configValidators = [configValidator, slideValidator]; 40 | -------------------------------------------------------------------------------- /test/unit/specs/util.spec.js: -------------------------------------------------------------------------------- 1 | import { deepCopy, mergeObject, isIos } from 'src/shared'; 2 | 3 | describe('Util', () => { 4 | it('after deeping copy, b[1].a1 should be 2', () => { 5 | const a = [{ a1: 1 }, { a1: 2 }]; 6 | const b = deepCopy(a); 7 | expect(b[1].a1).toBe(2); 8 | a[1].a1 = 3; 9 | expect(b[1].a1).toBe(2); 10 | }); 11 | 12 | it('deep copy a dom', () => { 13 | const a = document.createElement('div'); 14 | const b = deepCopy(a, b); 15 | expect(b.nodeType).not.toBe(null); 16 | }); 17 | 18 | it('deep merge shallowly,force', () => { 19 | const foo = document.createElement('div'); 20 | const a = [[1], [2], foo]; 21 | const b = [undefined, 2, 'bar']; 22 | const c = mergeObject(a, b, true /* force */, true /* shallow */); 23 | 24 | expect(c[0][0]).toBe(1); 25 | expect(c[1][0]).toBe(2); 26 | expect(c[2].nodeType).not.toBe(null); 27 | }); 28 | 29 | it('isIos should return false', () => { 30 | const IOS = isIos(); 31 | 32 | expect(IOS).toBe(false); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yi(Yves) Wang 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. -------------------------------------------------------------------------------- /src/shared/zoomManager.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ZoomManager 3 | * Get the browser zoom ratio 4 | */ 5 | 6 | export class ZoomManager { 7 | constructor() { 8 | this.originPixelRatio = this.getRatio(); 9 | this.lastPixelRatio = this.originPixelRatio; 10 | window.addEventListener('resize', () => { 11 | this.lastPixelRatio = this.getRatio(); 12 | }); 13 | } 14 | getRatio() { 15 | let ratio = 0; 16 | const screen = window.screen; 17 | const ua = navigator.userAgent.toLowerCase(); 18 | 19 | if (window.devicePixelRatio !== undefined) { 20 | ratio = window.devicePixelRatio; 21 | } else if (~ua.indexOf('msie')) { 22 | if (screen.deviceXDPI && screen.logicalXDPI) { 23 | ratio = screen.deviceXDPI / screen.logicalXDPI; 24 | } 25 | } else if ( 26 | window.outerWidth !== undefined && 27 | window.innerWidth !== undefined 28 | ) { 29 | ratio = window.outerWidth / window.innerWidth; 30 | } 31 | 32 | if (ratio) { 33 | ratio = Math.round(ratio * 100); 34 | } 35 | 36 | return ratio; 37 | } 38 | getRatioBetweenPreAndCurrent() { 39 | return this.originPixelRatio / this.lastPixelRatio; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/unit/specs/mode.spec.js: -------------------------------------------------------------------------------- 1 | import { 2 | destroyVM, 3 | createVue, 4 | makeTemplate, 5 | startSchedule 6 | } from 'test/unit/util'; 7 | 8 | describe('mode', () => { 9 | let vm; 10 | 11 | afterEach(() => { 12 | destroyVM(vm); 13 | }); 14 | 15 | it('toggle mode', done => { 16 | vm = createVue( 17 | { 18 | template: makeTemplate( 19 | { 20 | w: 200, 21 | h: 200 22 | }, 23 | { 24 | w: 100, 25 | h: 100 26 | } 27 | ), 28 | data: { 29 | ops: { 30 | vuescroll: { 31 | mode: 'native' 32 | } 33 | } 34 | } 35 | }, 36 | true 37 | ); 38 | 39 | let content = vm.$el.querySelector('.__view'); 40 | let panel = vm.$el.querySelector('.__panel'); 41 | expect(content).not.toBe(null); 42 | expect(content.parentNode).toEqual(panel); 43 | vm.ops.vuescroll.mode = 'slide'; 44 | startSchedule().then(() => { 45 | let content = vm.$el.querySelector('.__view'); 46 | let panel = vm.$el.querySelector('.__panel'); 47 | expect(panel).not.toBe(null); 48 | expect(content).toBe(null); 49 | done(); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /src/core/third-party/scroller/render.js: -------------------------------------------------------------------------------- 1 | import { getPrefix } from 'shared'; 2 | 3 | /* DOM-based rendering (Uses 3D when available, falls back on margin when transform not available) */ 4 | export function render(content, global, suffix, type) { 5 | if (type == 'position') { 6 | return function (left, top) { 7 | content.style.left = -left + 'px'; 8 | content.style.top = -top + 'px'; 9 | }; 10 | } 11 | 12 | var vendorPrefix = getPrefix(global); 13 | 14 | var helperElem = document.createElement('div'); 15 | var undef; 16 | 17 | var perspectiveProperty = vendorPrefix + 'Perspective'; 18 | var transformProperty = 'transform'; //vendorPrefix + 'Transform'; 19 | 20 | if (helperElem.style[perspectiveProperty] !== undef) { 21 | return function (left, top, zoom) { 22 | content.style[transformProperty] = 23 | 'translate3d(' + 24 | -left + 25 | suffix + 26 | ',' + 27 | -top + 28 | suffix + 29 | ',0) scale(' + 30 | zoom + 31 | ')'; 32 | }; 33 | } else if (helperElem.style[transformProperty] !== undef) { 34 | return function (left, top, zoom) { 35 | content.style[transformProperty] = 36 | 'translate(' + 37 | -left + 38 | suffix + 39 | ',' + 40 | -top + 41 | suffix + 42 | ') scale(' + 43 | zoom + 44 | ')'; 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/core/components/panel.js: -------------------------------------------------------------------------------- 1 | // begin importing 2 | import { insertChildrenIntoSlot, getRealParent } from 'shared'; 3 | import { h } from 'vue'; 4 | 5 | export default { 6 | name: 'ScrollPanel', 7 | props: { ops: { type: Object, required: true } }, 8 | methods: { 9 | // trigger scrollPanel options initialScrollX, 10 | // initialScrollY 11 | updateInitialScroll() { 12 | let x = 0; 13 | let y = 0; 14 | const parent = getRealParent(this); 15 | 16 | if (this.ops.initialScrollX) { 17 | x = this.ops.initialScrollX; 18 | } 19 | if (this.ops.initialScrollY) { 20 | y = this.ops.initialScrollY; 21 | } 22 | if (x || y) { 23 | parent.scrollTo({ x, y }); 24 | } 25 | } 26 | }, 27 | mounted() { 28 | setTimeout(() => { 29 | if (!this._isDestroyed) { 30 | this.updateInitialScroll(); 31 | } 32 | }, 0); 33 | }, 34 | render() { 35 | // eslint-disable-line 36 | let data = { 37 | class: ['__panel'], 38 | style: { 39 | position: 'relative', 40 | boxSizing: 'border-box' 41 | } 42 | }; 43 | 44 | const parent = getRealParent(this); 45 | 46 | const _customPanel = parent.$slots['scroll-panel']; 47 | if (_customPanel) { 48 | return insertChildrenIntoSlot(_customPanel, this.$slots.default, data); 49 | } 50 | return
🔥 vuescroll demo - base scroll
39 |40 | 41 | 👉 Go to online demo 43 |
44 | 45 |46 | ⭐ Star it on GitHub! 49 |
50 |
28 | 5. When I agree, your code will merge into the `dev` branch!
29 |
30 | # 中文版本
31 |
32 | 从两方面贡献代码:
33 |
34 | ### 代码层面
35 |
36 | Vuescroll 是极其容易扩展的,你基本只需要做 2 步即可。
37 |
38 | 1. 在 [global-config.js](https://github.com/YvesCoding/blob/dev/src/shared/global-config.js) 文件中对应的模块处修改/增加对应的特性,比如,我想增加一个可以配置滚动面板颜色的特性,默认是红色,如下图:
39 |
58 | 5. 等我点击同意, 你的代码就会被 merge 到`dev`分支了!
59 |
--------------------------------------------------------------------------------
/src/mode/slide/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The slide mode config
3 | */
4 | import { log } from 'shared';
5 | const { error } = log;
6 | export const configs = [
7 | {
8 | // vuescroll
9 | vuescroll: {
10 | // position or transform
11 | renderMethod: 'transform',
12 | // pullRefresh or pushLoad is only for the slide mode...
13 | pullRefresh: {
14 | enable: false,
15 | tips: {
16 | deactive: 'Pull to Refresh',
17 | active: 'Release to Refresh',
18 | start: 'Refreshing...',
19 | beforeDeactive: 'Refresh Successfully!'
20 | }
21 | },
22 | pushLoad: {
23 | enable: false,
24 | tips: {
25 | deactive: 'Push to Load',
26 | active: 'Release to Load',
27 | start: 'Loading...',
28 | beforeDeactive: 'Load Successfully!'
29 | },
30 | auto: false,
31 | autoLoadDistance: 0
32 | },
33 | paging: false,
34 | zooming: true,
35 | snapping: {
36 | enable: false,
37 | width: 100,
38 | height: 100
39 | },
40 | /* some scroller options */
41 | scroller: {
42 | /** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */
43 | bouncing: {
44 | top: 100,
45 | bottom: 100,
46 | left: 100,
47 | right: 100
48 | },
49 | /** Minimum zoom level */
50 | minZoom: 0.5,
51 | /** Maximum zoom level */
52 | maxZoom: 3,
53 | /** Multiply or decrease scrolling speed **/
54 | speedMultiplier: 1,
55 | /** This configures the amount of change applied to deceleration when reaching boundaries **/
56 | penetrationDeceleration: 0.03,
57 | /** This configures the amount of change applied to acceleration when reaching boundaries **/
58 | penetrationAcceleration: 0.08,
59 | /** Whether call e.preventDefault event when sliding the content or not */
60 | preventDefault: false,
61 | /** Whether call preventDefault when (mouse/touch)move*/
62 | preventDefaultOnMove: true,
63 | disable: false
64 | }
65 | }
66 | }
67 | ];
68 | /**
69 | * validate the options
70 | * @export
71 | * @param {any} ops
72 | */
73 | export function configValidator(ops) {
74 | let renderError = false;
75 | const { vuescroll } = ops;
76 |
77 | // validate pushLoad, pullReresh, snapping
78 | if (
79 | vuescroll.paging == vuescroll.snapping.enable &&
80 | vuescroll.paging &&
81 | (vuescroll.pullRefresh || vuescroll.pushLoad)
82 | ) {
83 | error(
84 | 'paging, snapping, (pullRefresh with pushLoad) can only one of them to be true.'
85 | );
86 | }
87 |
88 | return renderError;
89 | }
90 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vuescroll",
3 | "version": "5.1.1",
4 | "author": {
5 | "name": "Yves Wang"
6 | },
7 | "description": "A powerful, customizable, multi-mode scrollbar plugin based on Vue.js",
8 | "scripts": {
9 | "build": "cross-env BABEL_ENV=build node scripts/build.js",
10 | "debug": "cross-env BABEL_ENV=build VS_ENV=DEBUG node scripts/debug-build.js",
11 | "test": "npm run test:cover",
12 | "lint": "eslint --fix src scripts test",
13 | "test:cover": "karma start test/unit/karma.conf.js",
14 | "debug-test": "karma start test/unit/karma.conf.js --no-single-run",
15 | "report-coverage": "codecov",
16 | "gen-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
17 | },
18 | "license": "MIT",
19 | "homepage": "https://github.com/YvesCoding/vuescroll#readme",
20 | "keywords": [
21 | "vuescroll",
22 | "scrollbar",
23 | "vuescrollbar",
24 | "vue"
25 | ],
26 | "bugs": {
27 | "url": "https://github.com/YvesCoding/vuescroll/issues"
28 | },
29 | "peerDependencies": {
30 | "vue": "^3.0.0"
31 | },
32 | "typings": "types/index.d.ts",
33 | "files": [
34 | "src",
35 | "dist/*.js",
36 | "dist/*.css",
37 | "types/*.d.ts"
38 | ],
39 | "deprecated": false,
40 | "jsdelivr": "./dist/vuescroll.js",
41 | "main": "./dist/vuescroll.js",
42 | "unpkg": "./dist/vuescroll.js",
43 | "module": "./dist/vuescroll-esm.js",
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/YvesCoding/vuescroll.git"
47 | },
48 | "devDependencies": {
49 | "@babel/core": "^7.12.16",
50 | "@babel/eslint-parser": "^7.12.16",
51 | "@babel/plugin-syntax-jsx": "^7.12.13",
52 | "@babel/plugin-transform-spread": "^7.12.13",
53 | "@babel/preset-env": "^7.12.16",
54 | "@rollup/plugin-alias": "^3.1.2",
55 | "@rollup/plugin-babel": "^5.2.3",
56 | "@rollup/plugin-commonjs": "^17.1.0",
57 | "@rollup/plugin-node-resolve": "^11.1.1",
58 | "@rollup/plugin-replace": "^2.3.4",
59 | "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1",
60 | "@vue/babel-plugin-jsx": "^1.1.1",
61 | "babel-eslint": "^10.1.0",
62 | "babel-loader": "^8.2.3",
63 | "babel-plugin-istanbul": "^4.1.5",
64 | "babel-preset-es2015-rollup": "^3.0.0",
65 | "codecov": "^3.0.0",
66 | "cross-env": "^5.2.0",
67 | "css-loader": "^0.28.11",
68 | "eslint": "^4.13.1",
69 | "husky": "^2.3.0",
70 | "istanbul": "^0.4.5",
71 | "jasmine-core": "^2.9.1",
72 | "karma": "^4.4.1",
73 | "karma-chrome-launcher": "^2.2.0",
74 | "karma-coverage": "^1.1.1",
75 | "karma-jasmine": "^1.1.1",
76 | "karma-phantomjs-launcher": "^1.0.4",
77 | "karma-sourcemap-loader": "^0.3.7",
78 | "karma-webpack": "^2.0.9",
79 | "rollup": "2.38.5",
80 | "rollup-plugin-uglify": "^6.0.4",
81 | "vue": "^3.0.0",
82 | "webpack": "^4.0.0"
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/test/unit/specs/class-hooks.spec.js:
--------------------------------------------------------------------------------
1 | import {
2 | destroyVM,
3 | createVue,
4 | makeTemplate,
5 | trigger,
6 | startSchedule
7 | } from 'test/unit/util';
8 |
9 | const hasClass = (el, name) => {
10 | return el.classList.contains(name);
11 | };
12 |
13 | /**
14 | * we won't test mode and zooming here,
15 | * instead, we test it in
16 | * *-mode.spec.js and api/index.spec.js
17 | */
18 | describe('vuescroll', () => {
19 | let vm;
20 |
21 | afterEach(() => {
22 | destroyVM(vm);
23 | });
24 |
25 | it('class hook: hasVBar, hasHBar', done => {
26 | vm = createVue(
27 | {
28 | template: makeTemplate(
29 | {
30 | w: 200,
31 | h: 200
32 | },
33 | {
34 | w: 100,
35 | h: 100
36 | }
37 | ),
38 | data: {
39 | ops: {
40 | vuescroll: {
41 | sizeStrategy: 'number'
42 | }
43 | }
44 | }
45 | },
46 | true
47 | );
48 | const vs = vm.$refs['vs'].$el;
49 | const vmel = vm.$el;
50 |
51 | startSchedule()
52 | .then(() => {
53 | expect(hasClass(vs, 'hasVBar')).toBe(true);
54 | expect(hasClass(vs, 'hasHBar')).toBe(true);
55 |
56 | vmel.style.width = '200px';
57 | vmel.style.height = '200px';
58 | vm.$refs['vs'].refresh();
59 | })
60 | .wait(100)
61 | .then(() => {
62 | expect(hasClass(vs, 'hasVBar')).toBe(false);
63 | expect(hasClass(vs, 'hasHBar')).toBe(false);
64 |
65 | done();
66 | });
67 | });
68 |
69 | it('class hook: vBarVisible, hBarVisible', done => {
70 | vm = createVue(
71 | {
72 | template: makeTemplate(
73 | {
74 | w: 200,
75 | h: 200
76 | },
77 | {
78 | w: 100,
79 | h: 100
80 | }
81 | ),
82 | data: {
83 | ops: {
84 | bar: {
85 | onlyShowBarOnScroll: false
86 | }
87 | }
88 | }
89 | },
90 | true
91 | );
92 | const vs = vm.$refs['vs'].$el;
93 |
94 | startSchedule(1000)
95 | .then(() => {
96 | expect(hasClass(vs, 'vBarVisible')).toBe(false);
97 | expect(hasClass(vs, 'hBarVisible')).toBe(false);
98 |
99 | trigger(vs, 'mouseenter');
100 | })
101 | .wait(1)
102 | .then(() => {
103 | expect(hasClass(vs, 'vBarVisible')).toBe(true);
104 | expect(hasClass(vs, 'hBarVisible')).toBe(true);
105 |
106 | trigger(vs, 'mouseleave');
107 | })
108 | .wait(1)
109 | .then(() => {
110 | expect(hasClass(vs, 'vBarVisible')).toBe(false);
111 | expect(hasClass(vs, 'hBarVisible')).toBe(false);
112 |
113 | done();
114 | });
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Tue Jan 30 2018 19:35:39 GMT+0800 (中国标准时间)
3 |
4 | var alias = require('../../scripts/alias');
5 | var webpack = {
6 | mode: 'development',
7 | resolve: {
8 | alias: alias
9 | },
10 | module: {
11 | rules: [
12 | {
13 | test: /\.js$/,
14 | loader: 'babel-loader',
15 | exclude: /node_modules/,
16 | options: {
17 | plugins: [
18 | [
19 | 'istanbul',
20 | {
21 | exclude: ['src/core/third-party/**/*.js', 'test/**/*.js']
22 | }
23 | ]
24 | ]
25 | }
26 | }
27 | ]
28 | },
29 | devtool: '#inline-source-map'
30 | };
31 | module.exports = function (config) {
32 | config.set({
33 | webpack: webpack,
34 | plugins: [
35 | 'karma-jasmine',
36 | 'jasmine-core',
37 | 'karma-webpack',
38 | 'karma-coverage',
39 | 'karma-sourcemap-loader',
40 | //'karma-phantomjs-launcher'
41 | 'karma-chrome-launcher'
42 | ],
43 | // base path that will be used to resolve all patterns (eg. files, exclude)
44 | basePath: '',
45 |
46 | // frameworks to use
47 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
48 | frameworks: ['jasmine'],
49 |
50 | // list of files / patterns to load in the browser
51 | files: ['./index.js'],
52 |
53 | // list of files / patterns to exclude
54 | exclude: [],
55 |
56 | // preprocess matching files before serving them to the browser
57 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
58 | preprocessors: {
59 | './index.js': ['webpack', 'sourcemap']
60 | },
61 |
62 | // test results reporter to use
63 | // possible values: 'dots', 'progress'
64 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
65 | reporters: ['progress', 'coverage'],
66 |
67 | coverageReporter: {
68 | reporters: [
69 | { type: 'lcov', dir: '../coverage', subdir: '../coverage' },
70 | { type: 'text-summary', dir: '../coverage', subdir: '../coverage' }
71 | ]
72 | },
73 |
74 | // enable / disable colors in the output (reporters and logs)
75 | colors: true,
76 |
77 | // level of logging
78 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
79 | logLevel: config.LOG_INFO,
80 |
81 | // enable / disable watching file and executing tests whenever any file changes
82 | autoWatch: false,
83 |
84 | // start these browsers
85 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
86 | browsers: ['Chrome'],
87 |
88 | // Continuous Integration mode
89 | // if true, Karma captures browsers, runs the tests and exits
90 | singleRun: true,
91 |
92 | // Concurrency level
93 | // how many browser should be started simultaneous
94 | concurrency: Infinity
95 | });
96 | };
97 |
--------------------------------------------------------------------------------
/src/mode/native/mixins/scrollAnimate.js:
--------------------------------------------------------------------------------
1 | import { requestAnimationFrame } from 'core/third-party/scroller/requestAnimationFrame';
2 |
3 | function noop() {
4 | return true;
5 | }
6 |
7 | /* istanbul ignore next */
8 | const now =
9 | Date.now ||
10 | function() {
11 | return new Date().getTime();
12 | };
13 |
14 | export default class ScrollControl {
15 | constructor() {
16 | this.init();
17 |
18 | this.isRunning = false;
19 | }
20 |
21 | pause() {
22 | /* istanbul ignore if */
23 | if (!this.isRunning) return;
24 |
25 | this.isPaused = true;
26 | }
27 |
28 | stop() {
29 | this.isStopped = true;
30 | }
31 |
32 | continue() {
33 | /* istanbul ignore if */
34 | if (!this.isPaused) return;
35 |
36 | this.isPaused = false;
37 | this.ts = now() - this.percent * this.spd;
38 | this.execScroll();
39 | }
40 |
41 | startScroll(
42 | st,
43 | ed,
44 | spd,
45 | stepCb = noop,
46 | completeCb = noop,
47 | vertifyCb = noop,
48 | easingMethod = noop
49 | ) {
50 | const df = ed - st;
51 | const dir = df > 0 ? -1 : 1;
52 | const nt = now();
53 |
54 | if (!this.isRunning) {
55 | this.init();
56 | }
57 |
58 | if (dir != this.dir || nt - this.ts > 200) {
59 | this.ts = nt;
60 |
61 | this.dir = dir;
62 | this.st = st;
63 | this.ed = ed;
64 | this.df = df;
65 | } /* istanbul ignore next */ else {
66 | this.df += df;
67 | }
68 |
69 | this.spd = spd;
70 |
71 | this.completeCb = completeCb;
72 | this.vertifyCb = vertifyCb;
73 | this.stepCb = stepCb;
74 | this.easingMethod = easingMethod;
75 |
76 | if (!this.isRunning) this.execScroll();
77 | }
78 |
79 | execScroll() {
80 | if (!this.df) return;
81 |
82 | let percent = this.percent || 0;
83 | this.percent = 0;
84 | this.isRunning = true;
85 |
86 | const loop = () => {
87 | /* istanbul ignore if */
88 | if (!this.isRunning || !this.vertifyCb(percent) || this.isStopped) {
89 | this.isRunning = false;
90 | return;
91 | }
92 |
93 | percent = (now() - this.ts) / this.spd;
94 |
95 | if (this.isPaused) {
96 | this.percent = percent;
97 | this.isRunning = false;
98 | return;
99 | }
100 |
101 | if (percent < 1) {
102 | const value = this.st + this.df * this.easingMethod(percent);
103 | this.stepCb(value);
104 | this.ref(loop);
105 | } else {
106 | // trigger complete
107 | this.stepCb(this.st + this.df);
108 | this.completeCb();
109 |
110 | this.isRunning = false;
111 | }
112 | };
113 |
114 | this.ref(loop);
115 | }
116 |
117 | init() {
118 | this.st = 0;
119 | this.ed = 0;
120 | this.df = 0;
121 | this.spd = 0;
122 | this.ts = 0;
123 | this.dir = 0;
124 | this.ref = requestAnimationFrame(window);
125 |
126 | this.isPaused = false;
127 | this.isStopped = false;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/mode/slide/mixins/api.js:
--------------------------------------------------------------------------------
1 | import { log, getNumericValue, getCurrentViewportDom } from 'shared';
2 | const { warn } = log;
3 |
4 | export default {
5 | emits: [
6 | 'refresh-activate',
7 | 'refresh-before-deactivate',
8 | 'refresh-before-deactivate-end',
9 | 'refresh-deactivate',
10 |
11 | 'load-activate',
12 | 'load-before-deactivate',
13 | 'load-before-deactivate-end',
14 | 'load-deactivate'
15 | ],
16 | methods: {
17 | slideScrollTo(x, y, speed, easing) {
18 | const { scrollLeft, scrollTop } = this.getPosition();
19 |
20 | x = getNumericValue(x || scrollLeft, this.scroller.__maxScrollLeft);
21 | y = getNumericValue(y || scrollTop, this.scroller.__maxScrollTop);
22 |
23 | this.scroller.scrollTo(x, y, speed > 0, undefined, false, speed, easing);
24 | },
25 | zoomBy(factor, animate, originLeft, originTop, callback) {
26 | if (!this.scroller) {
27 | warn('zoomBy and zoomTo are only for slide mode!');
28 | return;
29 | }
30 | this.scroller.zoomBy(factor, animate, originLeft, originTop, callback);
31 | },
32 | zoomTo(level, animate = false, originLeft, originTop, callback) {
33 | if (!this.scroller) {
34 | warn('zoomBy and zoomTo are only for slide mode!');
35 | return;
36 | }
37 | this.scroller.zoomTo(level, animate, originLeft, originTop, callback);
38 | },
39 | getCurrentPage() {
40 | if (!this.scroller || !this.mergedOptions.vuescroll.paging) {
41 | warn(
42 | 'getCurrentPage and goToPage are only for slide mode and paging is enble!'
43 | );
44 | return;
45 | }
46 | return this.scroller.getCurrentPage();
47 | },
48 | goToPage(dest, animate = false) {
49 | if (!this.scroller || !this.mergedOptions.vuescroll.paging) {
50 | warn(
51 | 'getCurrentPage and goToPage are only for slide mode and paging is enble!'
52 | );
53 | return;
54 | }
55 | this.scroller.goToPage(dest, animate);
56 | },
57 | triggerRefreshOrLoad(type) {
58 | if (!this.scroller) {
59 | warn('You can only use triggerRefreshOrLoad in slide mode!');
60 | return;
61 | }
62 |
63 | const isRefresh = this.mergedOptions.vuescroll.pullRefresh.enable;
64 | const isLoad = this.mergedOptions.vuescroll.pushLoad.enable;
65 |
66 | if (type == 'refresh' && !isRefresh) {
67 | warn('refresh must be enabled!');
68 | return;
69 | } else if (type == 'load' && !isLoad) {
70 | // eslint-disable-next-line
71 | warn("load must be enabled and content's height > container's height!");
72 | return;
73 | } else if (type !== 'refresh' && type !== 'load') {
74 | warn('param must be one of load and refresh!');
75 | return;
76 | }
77 |
78 | /* istanbul ignore if */
79 | if (this.vuescroll.state[`${type}Stage`] == 'start') {
80 | return;
81 | }
82 |
83 | this.scroller.triggerRefreshOrLoad(type);
84 | return true;
85 | },
86 | getCurrentviewDomSlide() {
87 | const parent = this.scrollPanelElm;
88 | const domFragment = getCurrentViewportDom(parent, this.$el);
89 | return domFragment;
90 | }
91 | }
92 | };
93 |
--------------------------------------------------------------------------------
/.github/COMMIT_CONVENTION.md:
--------------------------------------------------------------------------------
1 | ## Git Commit Message Convention
2 |
3 | > This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
4 |
5 | #### TL;DR:
6 |
7 | Messages must be matched by the following regex:
8 |
9 | ``` js
10 | /^(revert: )?(feat|fix|polish|docs|style|refactor|perf|test|workflow|ci|chore|types)(\(.+\))?: .{1,50}/
11 | ```
12 |
13 | #### Examples
14 |
15 | Appears under "Features" header, `compiler` subheader:
16 |
17 | ```
18 | feat(compiler): add 'comments' option
19 | ```
20 |
21 | Appears under "Bug Fixes" header, `v-model` subheader, with a link to issue #28:
22 |
23 | ```
24 | fix(v-model): handle events on blur
25 |
26 | close #28
27 | ```
28 |
29 | Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
30 |
31 | ```
32 | perf(core): improve vdom diffing by removing 'foo' option
33 |
34 | BREAKING CHANGE: The 'foo' option has been removed.
35 | ```
36 |
37 | The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
38 |
39 | ```
40 | revert: feat(compiler): add 'comments' option
41 |
42 | This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
43 | ```
44 |
45 | ### Full Message Format
46 |
47 | A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
48 |
49 | ```
50 |