├── _config.yml ├── .npmignore ├── .nyc_output ├── 45043f6e-beff-4e12-a0a7-ae5bac643cf7.json └── 7c1f508a-290f-4949-a555-383450ba7cb0.json ├── .coveralls.yml ├── src ├── styles │ ├── vars.less │ ├── index.less │ ├── mixins.less │ ├── global.less │ └── transition.less ├── index.js ├── fab-cantainer.vue ├── util.js ├── fab-item.vue └── fab.vue ├── .npmrc.template ├── docs ├── homepage │ ├── .vuepress │ │ ├── enhanceApp.js │ │ ├── config.js │ │ └── styles │ │ │ └── index.styl │ ├── homepage │ │ ├── assets │ │ │ ├── img │ │ │ │ └── search.83621669.svg │ │ │ └── js │ │ │ │ ├── 3.7f6d7170.js │ │ │ │ ├── 4.18c5d346.js │ │ │ │ ├── 6.45fa5a04.js │ │ │ │ ├── 14.5bdff9bc.js │ │ │ │ ├── 5.bb7e3cf6.js │ │ │ │ ├── 13.9cf746d3.js │ │ │ │ ├── 12.728611e1.js │ │ │ │ ├── 8.3c00d3d5.js │ │ │ │ ├── 11.85c7fed5.js │ │ │ │ └── 10.bae2c45d.js │ │ ├── 404.html │ │ ├── unfoldDirection.html │ │ ├── scroll.html │ │ ├── scrollAble.html │ │ ├── animate.html │ │ ├── iconfont.html │ │ ├── customitem.html │ │ └── basic.html │ ├── scrollAble.md │ ├── unfoldDirection.md │ ├── scroll.md │ ├── customitem.md │ ├── animate.md │ ├── iconfont.md │ ├── README.md │ └── basic.md └── index.html ├── $Recycle.Bin └── S-1-5-21-474691220-4098946160-4214325479-1001 │ └── desktop.ini ├── .travis.yml ├── .babelrc ├── demo ├── main.js ├── webpack.prod.config.js ├── webpack.dev.config.js ├── index.html └── App.vue ├── .gitignore ├── bin └── publish.js ├── test └── unit │ ├── index.js │ ├── specs │ ├── fab-item.spec.js │ ├── index.spec.js │ ├── fab-container.spec.js │ ├── util.spec.js │ └── fab.spec.js │ ├── webpack.test.config.js │ └── karma.conf.js ├── .eslintrc.js ├── webpack.config.js ├── LICENSE ├── package.json ├── readme.md └── readme.en.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .babelrc 3 | demo -------------------------------------------------------------------------------- /.nyc_output/45043f6e-beff-4e12-a0a7-ae5bac643cf7.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.nyc_output/7c1f508a-290f-4949-a555-383450ba7cb0.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: PYZjkMKDU2nZmTXPkj7NYIYDUPRdf1iMH 2 | -------------------------------------------------------------------------------- /src/styles/vars.less: -------------------------------------------------------------------------------- 1 | @big: 48px; 2 | @normal: 40px; 3 | @small: 32px; 4 | -------------------------------------------------------------------------------- /src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import './global.less'; 2 | @import './mixins.less'; 3 | @import './vars.less'; 4 | @import './transition.less'; -------------------------------------------------------------------------------- /.npmrc.template: -------------------------------------------------------------------------------- 1 | always-auth=true 2 | registry=https://packagecloud.io/capotej/left-pad-example/npm/ 3 | //packagecloud.io/capotej/left-pad-example/npm/:_authToken=${API_TOKEN} -------------------------------------------------------------------------------- /docs/homepage/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | import VueFloatActionButton from 'vue-float-action-button' 2 | 3 | export default ({ 4 | Vue 5 | }) => { 6 | Vue.use(VueFloatActionButton) 7 | } -------------------------------------------------------------------------------- /$Recycle.Bin/S-1-5-21-474691220-4098946160-4214325479-1001/desktop.ini: -------------------------------------------------------------------------------- 1 | [.ShellClassInfo] 2 | CLSID={645FF040-5081-101B-9F08-00AA002F954E} 3 | LocalizedResourceName=@%SystemRoot%\system32\shell32.dll,-8964 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | branches: 4 | only: 5 | - master 6 | 7 | node_js: 8 | - v11.0.0 9 | 10 | before_deploy: 11 | - npm install 12 | 13 | script: npm run test 14 | 15 | 16 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env", "stage-2"], 3 | "plugins": ["transform-runtime", "transform-function-bind"], 4 | "comments": false, 5 | "env": { 6 | "test": { 7 | "plugins": ["istanbul"] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /demo/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.vue' 2 | import Fab from '../dist/vue-fab' 3 | // import Fab from '../src/index' 4 | import Vue from 'vue' 5 | 6 | Vue.use(Fab) 7 | 8 | new Vue({ 9 | render: h => h(App) 10 | }).$mount('#app') 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | test/unit/coverage/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | .vscode/ 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/img/search.83621669.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bin/publish.js: -------------------------------------------------------------------------------- 1 | var exec = require('child_process').execSync 2 | 3 | var str = exec(`scp -r ./docs/ root@${process.env.host}:/data/wwwroot/vue-floation-action-button`) 4 | 5 | console.log(str.toString("utf8").trim()); 6 | 7 | console.log('上传成功') 8 | 9 | var str = exec(`scp -r ./docs/homepage/homepage root@${process.env.host}:/data/wwwroot/vue-floating-action-button/`) 10 | 11 | console.log(str.toString("utf8").trim()); 12 | 13 | console.log('上传成功') 14 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | // require all test files (files that ends with .spec.js) 2 | const testsContext = require.context('./specs', true, /\.spec$/) 3 | testsContext.keys().forEach(testsContext) 4 | 5 | // require all src files except main.js for coverage. 6 | // you can also change this to match only the subset of files that 7 | // you want coverage for. 8 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 9 | srcContext.keys().forEach(srcContext) 10 | -------------------------------------------------------------------------------- /src/styles/mixins.less: -------------------------------------------------------------------------------- 1 | @import './vars.less'; 2 | 3 | .fab-size (@size: @normal) { 4 | height: @size; 5 | width: @size; 6 | } 7 | 8 | .flex-center { 9 | display: flex; 10 | justify-content: center; 11 | align-items: center 12 | } 13 | 14 | .fab-shadow { 15 | box-shadow: 0px 2px 8px #666; 16 | } 17 | 18 | .transition (@delay: .2s, @timingFunc: linear) { 19 | transition: all @delay @timingFunc; 20 | } 21 | 22 | .absolute { 23 | position: absolute; 24 | left: 0; 25 | top: 0; 26 | } -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/3.7f6d7170.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[3],{329:function(t,e,n){},357:function(t,e,n){"use strict";n(329)},372:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,i=e.slots;return t("span",{class:["badge",n.type],style:{verticalAlign:n.vertical}},n.text||i().default)}},r=(n(357),n(42)),p=Object(r.a)(i,void 0,void 0,!1,null,"d34ec732",null);e.default=p.exports}}]); -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/4.18c5d346.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[4],{331:function(t,e,c){},359:function(t,e,c){"use strict";c(331)},369:function(t,e,c){"use strict";c.r(e);var i={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}}},n=(c(359),c(42)),s=Object(n.a)(i,(function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"f7a71570",null);e.default=s.exports}}]); -------------------------------------------------------------------------------- /src/styles/global.less: -------------------------------------------------------------------------------- 1 | @import './vars.less'; 2 | @import './mixins.less'; 3 | 4 | .fab-size-normal { 5 | .fab-size(@normal); 6 | } 7 | 8 | .fab-size-big { 9 | .fab-size(@big); 10 | } 11 | 12 | .fab-size-small { 13 | .fab-size(@small); 14 | } 15 | 16 | .vue-fab-material-icons { 17 | .flex-center(); 18 | .transition(.2s, ease); 19 | position: absolute; 20 | height: 100%; 21 | width: 100%; 22 | left: 0; 23 | top: 0; 24 | font-size: 1em; 25 | } 26 | 27 | .vue-fab-icons { 28 | .vue-fab-material-icons(); 29 | * { 30 | color: white; 31 | } 32 | } -------------------------------------------------------------------------------- /test/unit/specs/fab-item.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { mount, createLocalVue } from 'vue-test-utils' 3 | import index from '../../../src/index.js' 4 | import Fab from '../../../src/fab.vue' 5 | import FabItem from '../../../src/fab-item.vue' 6 | 7 | describe('fab-item.vue', () => { 8 | const localVue = createLocalVue() 9 | localVue.use(index) 10 | let wrapper = null 11 | 12 | beforeEach(() => { 13 | // 初始化wrapper 14 | wrapper = mount(Fab, { 15 | localVue, 16 | slots: { 17 | default: [FabItem] 18 | } 19 | }) 20 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 999999; 21 | }) 22 | 23 | afterEach(() => { 24 | // 废弃wrapper 25 | wrapper = null 26 | }) 27 | }) -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/6.45fa5a04.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[6],{361:function(t,e,s){"use strict";s.r(e);var n=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],o={methods:{getMsg:function(){return n[Math.floor(Math.random()*n.length)]}}},i=s(42),h=Object(i.a)(o,(function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"theme-container"},[e("div",{staticClass:"theme-default-content"},[e("h1",[this._v("404")]),this._v(" "),e("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),e("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=h.exports}}]); -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://eslint.org/docs/user-guide/configuring 同Vue标准eslint 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | sourceType: 'module' 8 | }, 9 | env: { 10 | browser: true, 11 | }, 12 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 13 | extends: 'standard', 14 | // required to lint *.vue files 15 | plugins: [ 16 | 'html' 17 | ], 18 | // add your custom rules here 19 | rules: { 20 | // allow async-await 21 | 'generator-star-spacing': 'off', 22 | // allow debugger during development 23 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 24 | 'template-curly-spacing': 'off', 25 | 'indent': 'off', 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/unit/webpack.test.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | loaders: [{ 4 | test: /\.vue$/, 5 | loader: "vue-loader" 6 | }, { 7 | test: /\.js$/, 8 | loader: [ 9 | 'babel-loader' 10 | ], 11 | exclude: /node_modules/ 12 | }, { 13 | test: /\.less$/, 14 | use: [{ 15 | loader: 'style-loader' // creates style nodes from JS strings 16 | }, { 17 | loader: 'css-loader' // translates CSS into CommonJS 18 | }, { 19 | loader: 'less-loader' // compiles Less to CSS 20 | }] 21 | }] 22 | }, 23 | resolve: { 24 | extensions: ['.js', '.vue'], 25 | alias: { 26 | 'vue$': 'vue/dist/vue.common.js' 27 | } 28 | }, 29 | devtool: 'eval-source-map' 30 | }; -------------------------------------------------------------------------------- /docs/homepage/scrollAble.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # 关闭滚动自动隐藏 6 | 7 | ## VueFab 8 | | Option | Type | Params | Description | 9 | | ------ | -------- | ------ | ---------- | 10 | | scrollAutoHide | Boolean | true / false | 默认关闭 | 11 | 对于不想开启滚动自动隐藏的开发者,提供了关闭的选项 12 | 13 | ``` html 14 | 15 | 16 | ``` 17 | 18 | 我
19 | 是
20 | 占
21 | 位
22 | 符
23 | 我
24 | 是
25 | 占
26 | 位
27 | 符
28 | 我
29 | 是
30 | 占
31 | 位
32 | 符
33 | 我
34 | 是
35 | 占
36 | 位
37 | 符
38 | 我
39 | 是
40 | 占
41 | 位
42 | 符
43 | 我
44 | 是
45 | 占
46 | 位
47 | 符
-------------------------------------------------------------------------------- /test/unit/specs/index.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 测试src/index.js 3 | */ 4 | 5 | import Vue from 'vue' 6 | import index from '../../../src/index.js' 7 | import { testSafariBrower } from '../../../src/util' 8 | 9 | describe('index.js', () => { 10 | it(`测试testSafariBrower函数在安卓的判断`, () => { 11 | const isSafariBrower = testSafariBrower('Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Mobile Safari/537.36') 12 | expect(isSafariBrower).toBe(false) 13 | }) 14 | it(`测试testSafariBrower函数在ios的判断`, () => { 15 | const isSafariBrower = testSafariBrower('User-Agent:Mozilla/5.0(iPhone;U;CPUiPhoneOS4_3_3likeMacOSX;en-us)AppleWebKit/533.17.9(KHTML,likeGecko)Version/5.0.2Mobile/8J2Safari/6533.18.5') 16 | expect(isSafariBrower).toBe(true) 17 | }) 18 | }) -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 3 | 4 | 5 | module.exports = { 6 | entry: { 7 | main: path.resolve(__dirname + '/src/index.js') 8 | }, 9 | output: { 10 | path: path.resolve(__dirname + '/dist'), 11 | filename: 'vue-fab.js', 12 | libraryTarget: 'umd' 13 | }, 14 | module: { 15 | loaders: [{ 16 | test: /\.vue$/, 17 | loader: "vue-loader" 18 | }, { 19 | test: /\.js$/, 20 | loader: 'babel-loader', 21 | exclude: /node_modules/, 22 | include: path.resolve(__dirname + '/src') 23 | }, { 24 | test: /\.css$/, 25 | loader: 'style!css!autoprefixer' 26 | }, { 27 | test: /\.less$/, 28 | loader: 'style!less' 29 | }] 30 | }, 31 | plugins: [ 32 | new UglifyJsPlugin({ 33 | compress: { 34 | drop_console: true, 35 | }, 36 | uglifyOptions: { 37 | warnings: false 38 | } 39 | }) 40 | ] 41 | } -------------------------------------------------------------------------------- /demo/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | module.exports = { 6 | entry: { 7 | main: path.resolve(__dirname + '/main.js') 8 | }, 9 | output: { 10 | path: path.resolve(__dirname, '../docs/'), 11 | filename: 'vue-fab.js' 12 | }, 13 | module: { 14 | loaders: [{ 15 | test: /\.vue$/, 16 | loader: "vue-loader" 17 | }, { 18 | test: /\.js$/, 19 | loader: 'babel-loader', 20 | exclude: /node_modules/ 21 | }, { 22 | test: /\.css$/, 23 | loader: 'style!css!autoprefixer' 24 | }, { 25 | test: /\.less$/, 26 | loader: 'style!less' 27 | }] 28 | }, 29 | devServer: { 30 | inline: true 31 | }, 32 | plugins: [ 33 | new UglifyJsPlugin({ 34 | uglifyOptions: { 35 | warnings: false 36 | } 37 | }), 38 | new HtmlWebpackPlugin({ 39 | template: path.join(__dirname, './index.html') 40 | }) 41 | ] 42 | } -------------------------------------------------------------------------------- /docs/homepage/unfoldDirection.md: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | # 配置展开方向 24 | 25 | VueFab提供多样的动画配置 26 | 27 | 通过修改unfoldDirection参数 来改变向上或者向下展示 28 | 29 | 37 | 38 | | Option | Type | Default | Params | Description | 39 | | ------ | -------- | ------ | ---------- | ------ | 40 | | unfoldDirection | String | 'up' | 'down' / 'up' | 向上展开或者向下展开 | 41 | -------------------------------------------------------------------------------- /docs/homepage/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/', 3 | title: 'Hello VueFab', 4 | description: '好用的FloatingActionButton', 5 | head: [ 6 | ['link', { 7 | async: 'async', 8 | rel: 'stylesheet', 9 | href: 'https://fonts.googleapis.com/icon?family=Material+Icons' 10 | }], 11 | ['link', { 12 | async: 'async', 13 | rel: 'stylesheet', 14 | href: 'http://at.alicdn.com/t/font_808119_6y2mme98u07.css' 15 | }] 16 | ], 17 | plugins: [ 18 | ['@vuepress/google-analytics', { 19 | ga: 'UA-145663310-4' 20 | }] 21 | ], 22 | dest: 'docs/homepage/homepage', 23 | themeConfig: { 24 | repo: 'a62527776a/vue-floating-action-button', 25 | repoLabel: 'Github', 26 | sidebarDepth: 2, 27 | sidebar: [ 28 | ['/', '介绍'], 29 | ['/basic', '基本使用'], 30 | ['/scroll', '滚动配置'], 31 | ['/animate', '动画配置'], 32 | ['/unfoldDirection', '配置展开方向'], 33 | ['/scrollAble', '关闭滚动自动隐藏'], 34 | ['/iconfont', '使用阿里巴巴iconfont'], 35 | ['/customitem', '通过slot自定义图片'] 36 | ] 37 | } 38 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import FAB from './fab.vue' 2 | import FABItem from './fab-item.vue' 3 | import FABCantainer from './fab-cantainer.vue' 4 | import { testSafariBrower, handleSafariBodyClickNotWorkEvent, listenClick } from './util' 5 | 6 | function install (Vue, options) { 7 | if (options && options.iconType) { 8 | FAB.props.iconType.default = options.iconType 9 | } 10 | Vue.component(FAB.name, FAB) 11 | Vue.component(FABItem.name, FABItem) 12 | Vue.component(FABCantainer.name, FABCantainer) 13 | Vue.directive('click-outside', { 14 | bind: (el, binding, vnode) => { 15 | el.__clickOutside__ = listenClick 16 | // 处理safari浏览器body对象无法响应click事件 17 | handleSafariBodyClickNotWorkEvent(listenClick, testSafariBrower(), { 18 | el, binding 19 | }) 20 | }, 21 | unbind: (el, binding) => { 22 | if (testSafariBrower()) { 23 | document.removeEventListener('click', el.__clickOutside__) 24 | } else { 25 | document.querySelector('html').removeEventListener('click', el.__clickOutside__) 26 | } 27 | } 28 | }) 29 | } 30 | 31 | module.exports = install 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present, iamkun 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. -------------------------------------------------------------------------------- /demo/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | 5 | module.exports = { 6 | entry: { 7 | main: path.resolve(__dirname + '/main.js') 8 | }, 9 | output: { 10 | path: path.resolve(__dirname + '/dist'), 11 | filename: 'vue-fab.js' 12 | }, 13 | module: { 14 | loaders: [{ 15 | test: /\.vue$/, 16 | loader: "vue-loader" 17 | }, { 18 | test: /\.js$/, 19 | loader: [ 20 | 'babel-loader' 21 | ], 22 | exclude: /node_modules/ 23 | }, { 24 | test: /\.css$/, 25 | loader: 'style!css!autoprefixer' 26 | }, { 27 | test: /\.less$/, 28 | loader: 'style!less' 29 | }, { 30 | test: /\.vue$/, 31 | enforce: 'pre', // 在babel-loader对源码进行编译前进行lint的检查 32 | include: /src/ // src文件夹下的文件需要被lint 33 | }] 34 | }, 35 | devServer: { 36 | inline: true 37 | }, 38 | devtool: 'eval-source-map', 39 | plugins: [ 40 | new UglifyJsPlugin({ 41 | uglifyOptions: { 42 | warnings: false 43 | } 44 | }), 45 | new HtmlWebpackPlugin({ 46 | template: path.join(__dirname, './index.html') 47 | }) 48 | ] 49 | } -------------------------------------------------------------------------------- /docs/homepage/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | .button { 2 | margin-top: 5px; 3 | margin-bottom: 5px; 4 | color: #fff; 5 | background-color: #3eaf7c; 6 | border-color: #3eaf7c; 7 | display: inline-block; 8 | padding: 6px 12px; 9 | margin-bottom: 0; 10 | font-size: 14px; 11 | font-weight: 400; 12 | line-height: 1.42857143; 13 | text-align: center; 14 | white-space: nowrap; 15 | vertical-align: middle; 16 | -ms-touch-action: manipulation; 17 | touch-action: manipulation; 18 | cursor: pointer; 19 | -webkit-user-select: none; 20 | -moz-user-select: none; 21 | -ms-user-select: none; 22 | user-select: none; 23 | background-image: none; 24 | border: 1px solid transparent; 25 | border-radius: 4px; 26 | } 27 | 28 | .form-input { 29 | display: block; 30 | width: 100%; 31 | height: 34px; 32 | padding: 6px 12px; 33 | font-size: 14px; 34 | line-height: 1.42857143; 35 | color: #555; 36 | background-color: #fff; 37 | background-image: none; 38 | border: 1px solid #ccc; 39 | border-radius: 4px; 40 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075); 41 | box-shadow: inset 0 1px 1px rgba(0,0,0,.075); 42 | -webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s; 43 | -o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; 44 | transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s; 45 | } -------------------------------------------------------------------------------- /docs/homepage/scroll.md: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 18 | 19 | # 滚动配置 20 | 21 | 滚动相关的配置 22 | 23 | ## 控制自动隐藏的方向 24 | 25 | 默认是从下滚动上面隐藏 26 | 改成down为从上滚动到下面隐藏 27 | 28 | 31 | 32 | 当前值 {{autoHideDirection}} 33 | 34 | | Option | Type | Default | Params | Description | 35 | | ------ | -------- | ------ | ---------- | ------ | 36 | | autoHideDirection | String | 'up' | 'up' / 'down' | 默认关闭 | 37 | 38 | 39 | ## 自动隐藏的阈值 40 | 41 | 滚动超过这个值才会触发自动隐藏 42 | 43 | 46 | 47 | 48 | 我
49 | 是
50 | 占
51 | 位
52 | 符
53 | 我
54 | 是
55 | 占
56 | 位
57 | 符
58 | 我
59 | 是
60 | 占
61 | 位
62 | 符
63 | 我
64 | 是
65 | 占
66 | 位
67 | 符
68 | 我
69 | 是
70 | 占
71 | 位
72 | 符
73 | 我
74 | 是
75 | 占
76 | 位
77 | 符
-------------------------------------------------------------------------------- /src/fab-cantainer.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 45 | 46 | 63 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-fab 9 | 10 | 17 | 18 | 24 | 25 | 26 | 27 |
28 | 29 | 42 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/14.5bdff9bc.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[14],{367:function(t,e,n){"use strict";n.r(e);var o={data:function(){return{unfoldDirectionType:"down",fabAutoHideAnimateModel:"default",fabItemAnimate:"default"}}},i=n(42),a=Object(i.a)(o,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("ClientOnly",[n("vue-fab",{staticStyle:{top:"170px"},attrs:{unfoldDirection:t.unfoldDirectionType,fabAutoHideAnimateModel:t.fabAutoHideAnimateModel,fabItemAnimate:t.fabItemAnimate}},t._l(4,(function(t){return n("fab-item",{attrs:{idx:t-1}})})),1)],1),t._v(" "),n("h1",{attrs:{id:"配置展开方向"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#配置展开方向"}},[t._v("#")]),t._v(" 配置展开方向")]),t._v(" "),n("p",[t._v("VueFab提供多样的动画配置")]),t._v(" "),n("p",[t._v("通过修改unfoldDirection参数 来改变向上或者向下展示")]),t._v(" "),[n("button",{staticClass:"button",on:{click:function(e){"down"===t.unfoldDirectionType?t.unfoldDirectionType="up":t.unfoldDirectionType="down"}}},[t._v("\n 点我切换\n")]),t._v(" "),n("p",[t._v("当前模式 "+t._s(t.unfoldDirectionType))])],t._v(" "),n("table",[n("thead",[n("tr",[n("th",[t._v("Option")]),t._v(" "),n("th",[t._v("Type")]),t._v(" "),n("th",[t._v("Default")]),t._v(" "),n("th",[t._v("Params")]),t._v(" "),n("th",[t._v("Description")])])]),t._v(" "),n("tbody",[n("tr",[n("td",[t._v("unfoldDirection")]),t._v(" "),n("td",[t._v("String")]),t._v(" "),n("td",[t._v("'up'")]),t._v(" "),n("td",[t._v("'down' / 'up'")]),t._v(" "),n("td",[t._v("向上展开或者向下展开")])])])])],2)}),[],!1,null,null,null);e.default=a.exports}}]); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | vue-fab 9 | 10 | 17 | 18 | 24 | 25 | 26 | 27 |
28 | 29 | 42 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/5.bb7e3cf6.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{330:function(e,t,o){},358:function(e,t,o){"use strict";o(330)},368:function(e,t,o){"use strict";o.r(t);o(23),o(94),o(65),o(96);var a={name:"CodeGroup",data:function(){return{codeTabs:[],activeCodeTabIndex:-1}},watch:{activeCodeTabIndex:function(e){this.codeTabs.forEach((function(e){e.elm.classList.remove("theme-code-block__active")})),this.codeTabs[e].elm.classList.add("theme-code-block__active")}},mounted:function(){var e=this;this.codeTabs=(this.$slots.default||[]).filter((function(e){return Boolean(e.componentOptions)})).map((function(t,o){return""===t.componentOptions.propsData.active&&(e.activeCodeTabIndex=o),{title:t.componentOptions.propsData.title,elm:t.elm}})),-1===this.activeCodeTabIndex&&this.codeTabs.length>0&&(this.activeCodeTabIndex=0)},methods:{changeCodeTab:function(e){this.activeCodeTabIndex=e}}},n=(o(358),o(42)),c=Object(n.a)(a,(function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("div",{staticClass:"theme-code-group"},[o("div",{staticClass:"theme-code-group__nav"},[o("ul",{staticClass:"theme-code-group__ul"},e._l(e.codeTabs,(function(t,a){return o("li",{key:t.title,staticClass:"theme-code-group__li"},[o("button",{staticClass:"theme-code-group__nav-tab",class:{"theme-code-group__nav-tab-active":a===e.activeCodeTabIndex},on:{click:function(t){return e.changeCodeTab(a)}}},[e._v("\n "+e._s(t.title)+"\n ")])])})),0)]),e._v(" "),e._t("default"),e._v(" "),e.codeTabs.length<1?o("pre",{staticClass:"pre-blank"},[e._v("// Make sure to add code blocks to your code group")]):e._e()],2)}),[],!1,null,"d6462338",null);t.default=c.exports}}]); -------------------------------------------------------------------------------- /docs/homepage/customitem.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 25 | 26 | # 使用slot自定义图片 27 | 28 | VueItem提供slot已供配置图片自定义图标 29 | 需要自己配置好slot元素的宽高以及border-radius!! 30 | 31 | 32 | ``` vue 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | ``` -------------------------------------------------------------------------------- /docs/homepage/animate.md: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 19 | 20 | # 动画配置 21 | 22 | VueFab提供多样的动画配置 23 | 24 | ## 滚动自动隐藏动画 25 | 26 | 34 | 35 | | Option | Type | Default | Params | Description | 36 | | ------ | -------- | ------ | ---------- | ------ | 37 | | fabAutoHideAnimateModel | String | 'default' | 'default' / 'alive' | fab滚动触发自动隐藏动画 分为 'default' ( 缩小隐藏 ) 以及 'alive' (向下滚动隐藏) | 38 | 39 | ## FabItem展开动画 40 | 41 | 46 | 47 | 当前模式 {{fabItemAnimate}} 48 | 49 | | Option | Type | Default | Params | Description | 50 | | ------ | -------- | ------ | ---------- | ------ | 51 | | fabItemAnimate | String | 'default' | 'default' / 'alive' | 打开关闭子菜单时过渡动画 分为 'default' (各自过渡) 'alive' (分裂过渡) | 52 | 53 | 54 | 我
55 | 是
56 | 占
57 | 位
58 | 符
59 | 我
60 | 是
61 | 占
62 | 位
63 | 符
64 | 我
65 | 是
66 | 占
67 | 位
68 | 符
69 | 我
70 | 是
71 | 占
72 | 位
73 | 符
74 | 我
75 | 是
76 | 占
77 | 位
78 | 符
79 | 我
80 | 是
81 | 占
82 | 位
83 | 符
-------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | export let handleClass = (type = 'MaterialDesign', iconClass = '') => { 2 | return type === 'MaterialDesign' ? 'material-icons' : 'iconfont icon-' + iconClass 3 | } 4 | 5 | /** 6 | * @method testSafariBrower 处理safari浏览器body无法响应click事件 7 | * @return { Boolean } true / false 8 | */ 9 | export const testSafariBrower = (userAgent = null) => { 10 | let UA = null 11 | userAgent ? UA = userAgent : UA = window.navigator.userAgent 12 | if (/iPad/i.test(UA) || /iPhone/i.test(UA) || /Mac/i.test(UA)) { 13 | return true 14 | } else { 15 | return false 16 | } 17 | } 18 | 19 | export class Timeout { 20 | timer = null 21 | handleTimeout () { 22 | return new Promise(resolve => { 23 | this.timer = setTimeout(() => { 24 | resolve(true) 25 | }, 300) 26 | }) 27 | } 28 | handleClearTimeout () { 29 | clearTimeout(this.timer) 30 | this.timer = null 31 | } 32 | } 33 | 34 | export const handleSafariBodyClickNotWorkEvent = (callBack, isSafari, obj) => { 35 | if (isSafari) { 36 | let setClassName = 'setCursor' 37 | let html = document.querySelector('html') 38 | const currentHTMLClass = html.getAttribute('class') 39 | if (currentHTMLClass) { 40 | if (currentHTMLClass.indexOf('setCursor') > -1) return 41 | setClassName = currentHTMLClass + ' setCursor' 42 | } 43 | html.setAttribute('class', setClassName) 44 | html.addEventListener('click', (e) => callBack(obj, e)) 45 | } else { 46 | window.addEventListener('click', (e) => callBack(obj, e)) 47 | } 48 | } 49 | 50 | export const listenClick = (obj, e) => { 51 | if (obj.el.contains(e.target) || e.target.dataset.outside) { 52 | return false 53 | } 54 | if (obj.binding) { 55 | if (obj.binding.value) { 56 | if (typeof obj.binding.value == 'function') { 57 | return obj.binding.value() 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docs/homepage/homepage/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

404

That's a Four-Oh-Four.
17 | Take me home. 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/styles/transition.less: -------------------------------------------------------------------------------- 1 | @import './mixins.less'; 2 | 3 | // #根据不同的动画模式 分类 4 | 5 | // ## default 动画模式 6 | 7 | // 1. 主Fab 8 | 9 | .fab-default-leave-to { 10 | transform: scale(0); 11 | } 12 | 13 | .fab-default-enter { 14 | transform: scale(0) rotate(-45deg); 15 | } 16 | 17 | // 2. Fab子菜单 18 | 19 | .fab-item-default-enter { 20 | opacity: 0; 21 | transform: translate3D(0, 5px, 0) scale(.0); 22 | } 23 | 24 | .fab-item-default-leave-to { 25 | opacity: 0; 26 | } 27 | 28 | // 3. Fab被激活的Icon动画 29 | 30 | .fab-active-icon-leave { 31 | transform: rotate(0deg) !important; 32 | } 33 | 34 | .fab-active-icon-leave-to { 35 | transform: rotate(45deg) !important; 36 | opacity: 0 !important; 37 | } 38 | 39 | .fab-active-icon-enter { 40 | opacity: 0; 41 | transform: rotate(0deg) !important; 42 | } 43 | 44 | .fab-active-icon-enter-to { 45 | transform: rotate(45deg) !important; 46 | opacity: 1; 47 | } 48 | 49 | // 4. Fab 未被激活的Icon动画 50 | 51 | .fab-icon-enter { 52 | transform: rotate(45deg); 53 | opacity: 0; 54 | } 55 | 56 | .fab-icon-enter-to { 57 | transform: rotate(0deg) 58 | } 59 | 60 | .fab-icon-leave { 61 | transform: rotate(45deg); 62 | opacity: 1; 63 | } 64 | 65 | .fab-icon-leave-to { 66 | transform: rotate(0deg) !important; 67 | opacity: 0; 68 | } 69 | 70 | .fab-icon-enter, .fab-icon-leave-to { 71 | opacity: 0; 72 | } 73 | 74 | .fab-icon-to, .fab-icon-leave { 75 | opacity: 1; 76 | } 77 | 78 | 79 | // alive 动画模式 80 | 81 | // 1. 主Fab 82 | 83 | .fab-alive-leave-to { 84 | transform: translateY(60px) !important; 85 | opacity: 0; 86 | } 87 | 88 | .fab-alive-leave-active, .fab-alive-enter-active { 89 | .transition(.3s); 90 | } 91 | 92 | .fab-alive-enter { 93 | transform: translateY(60px) !important; 94 | opacity: 0; 95 | } 96 | 97 | // 2. Fab子菜单 98 | 99 | .fab-item-alive-leave-to { 100 | transform: translateY(0px) !important; 101 | opacity: 0; 102 | } 103 | 104 | .fab-item-alive-enter-active { 105 | .transition(.6s); 106 | opacity: 0; 107 | } 108 | 109 | .fab-item-alive-enter { 110 | transform: translateY(0px) !important; 111 | opacity: 0; 112 | } -------------------------------------------------------------------------------- /docs/homepage/iconfont.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 55 | 56 | # 支持iconfont 57 | 58 | 对于使用阿里巴巴iconfont作为图标库的项目,我们提供参数以配置支持 59 | 60 | 默认使用MaterialDesign的图标库 61 | 62 | ##### Vue-Fab在底层已抹平两者使用区别 引入后仅需使用icon属性传入即可 63 | 64 | 65 | ## options 66 | 67 | | Option | Type | Params | Description | 68 | | ------ | -------- | ------ | ---------- | 69 | | iconType | String |'MaterialDesign'/'iconfont' | 根据您的使用习惯或开发依赖来决定使用哪种图标 | 70 | 71 | 74 | 75 | 当前图标库来源: {{currentSource}} 76 | 77 | 78 | ``` js 79 | Vue.use(VueFab, /* { 80 | ---------------------- 81 | // opitons 可选iconfont图标或MaterialIcons 82 | iconType: 'MaterialDesign' 83 | // iconType: 'iconfont' 84 | } */) 85 | ``` 86 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Mar 05 2018 20:28:11 GMT+0800 (中国标准时间) 3 | var webpackConfig = require('./webpack.test.config.js') 4 | 5 | module.exports = function(config) { 6 | config.set({ 7 | 8 | // base path that will be used to resolve all patterns (eg. files, exclude) 9 | basePath: '', 10 | 11 | 12 | // frameworks to use 13 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 14 | frameworks: ['jasmine'], 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | './index.js' 19 | ], 20 | 21 | 22 | // list of files / patterns to exclude 23 | exclude: [ 24 | '**/*.swp' 25 | ], 26 | 27 | 28 | // preprocess matching files before serving them to the browser 29 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 30 | preprocessors: { 31 | './index.js': ['webpack', 'sourcemap'] 32 | }, 33 | 34 | webpack: webpackConfig, 35 | 36 | // test results reporter to use 37 | // possible values: 'dots', 'progress' 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['spec', 'coverage', 'coveralls'], 40 | // web server port 41 | port: 9876, 42 | 43 | 44 | // enable / disable colors in the output (reporters and logs) 45 | colors: true, 46 | 47 | 48 | // level of logging 49 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 50 | logLevel: config.LOG_INFO, 51 | 52 | 53 | // enable / disable watching file and executing tests whenever any file changes 54 | autoWatch: true, 55 | 56 | 57 | // start these browsers 58 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 59 | browsers: ['ChromeHeadless'], 60 | 61 | // Continuous Integration mode 62 | // if true, Karma captures browsers, runs the tests and exits 63 | singleRun: true, 64 | 65 | // Concurrency level 66 | // how many browser should be started simultaneous 67 | concurrency: Infinity, 68 | 69 | coverageReporter: { 70 | type: 'lcov', 71 | dir: 'coverage/' 72 | } 73 | }) 74 | } 75 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/13.9cf746d3.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[13],{371:function(t,n,a){"use strict";a.r(n);var r=a(42),s=Object(r.a)({},(function(){var t=this,n=t.$createElement,a=t._self._c||n;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("ClientOnly",[a("vue-fab",{attrs:{scrollAutoHide:!1}})],1),t._v("\n# 关闭滚动自动隐藏\n"),a("h2",{attrs:{id:"vuefab"}},[a("a",{staticClass:"header-anchor",attrs:{href:"#vuefab"}},[t._v("#")]),t._v(" VueFab")]),t._v(" "),a("table",[a("thead",[a("tr",[a("th",[t._v("Option")]),t._v(" "),a("th",[t._v("Type")]),t._v(" "),a("th",[t._v("Params")]),t._v(" "),a("th",[t._v("Description")])])]),t._v(" "),a("tbody",[a("tr",[a("td",[t._v("scrollAutoHide")]),t._v(" "),a("td",[t._v("Boolean")]),t._v(" "),a("td",[t._v("true / false")]),t._v(" "),a("td",[t._v("默认关闭")])])])]),t._v(" "),a("p",[t._v("对于不想开启滚动自动隐藏的开发者,提供了关闭的选项")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("vue-fab")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":scrollAutoHide")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("false"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n")])])]),a("p",[t._v("我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br"),t._v("\n我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br"),t._v("\n我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br"),t._v("\n我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br"),t._v("\n我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br"),t._v("\n我"),a("br"),t._v("\n是"),a("br"),t._v("\n占"),a("br"),t._v("\n位"),a("br"),t._v("\n符"),a("br")])],1)}),[],!1,null,null,null);n.default=s.exports}}]); -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/12.728611e1.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[12],{365:function(t,r,n){"use strict";n.r(r);var v={data:function(){return{autoHideDirection:"up",autoHideThreshold:10}}},e=n(42),_=Object(e.a)(v,(function(){var t=this,r=t.$createElement,n=t._self._c||r;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("ClientOnly",[n("vue-fab",{attrs:{autoHideDirection:t.autoHideDirection,autoHideThreshold:t.autoHideThreshold}})],1),t._v(" "),n("h1",{attrs:{id:"滚动配置"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#滚动配置"}},[t._v("#")]),t._v(" 滚动配置")]),t._v(" "),n("p",[t._v("滚动相关的配置")]),t._v(" "),n("h2",{attrs:{id:"控制自动隐藏的方向"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#控制自动隐藏的方向"}},[t._v("#")]),t._v(" 控制自动隐藏的方向")]),t._v(" "),n("p",[t._v("默认是从下滚动上面隐藏\n改成down为从上滚动到下面隐藏")]),t._v(" "),[n("button",{staticClass:"button",on:{click:function(r){"up"===t.autoHideDirection?t.autoHideDirection="down":t.autoHideDirection="up"}}},[t._v("点我修改滚动自动隐藏的方向")])],t._v(" "),n("p",[t._v("当前值 "+t._s(t.autoHideDirection))]),t._v(" "),n("table",[n("thead",[n("tr",[n("th",[t._v("Option")]),t._v(" "),n("th",[t._v("Type")]),t._v(" "),n("th",[t._v("Default")]),t._v(" "),n("th",[t._v("Params")]),t._v(" "),n("th",[t._v("Description")])])]),t._v(" "),n("tbody",[n("tr",[n("td",[t._v("autoHideDirection")]),t._v(" "),n("td",[t._v("String")]),t._v(" "),n("td",[t._v("'up'")]),t._v(" "),n("td",[t._v("'up' / 'down'")]),t._v(" "),n("td",[t._v("默认关闭")])])])]),t._v(" "),n("h2",{attrs:{id:"自动隐藏的阈值"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#自动隐藏的阈值"}},[t._v("#")]),t._v(" 自动隐藏的阈值")]),t._v(" "),n("p",[t._v("滚动超过这个值才会触发自动隐藏")]),t._v(" "),[n("input",{directives:[{name:"model",rawName:"v-model",value:t.autoHideThreshold,expression:"autoHideThreshold"}],staticClass:"form-input",attrs:{type:"number"},domProps:{value:t.autoHideThreshold},on:{input:function(r){r.target.composing||(t.autoHideThreshold=r.target.value)}}})],t._v(" "),n("p",[t._v("我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br"),t._v("\n我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br"),t._v("\n我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br"),t._v("\n我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br"),t._v("\n我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br"),t._v("\n我"),n("br"),t._v("\n是"),n("br"),t._v("\n占"),n("br"),t._v("\n位"),n("br"),t._v("\n符"),n("br")])],2)}),[],!1,null,null,null);r.default=_.exports}}]); -------------------------------------------------------------------------------- /test/unit/specs/fab-container.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue } from 'vue-test-utils' 2 | import index from '../../../src/index.js' 3 | import FabCantainer from '../../../src/fab-cantainer.vue' 4 | import { Timeout } from '../../../src/util' 5 | 6 | describe('fab-cantainer.vue', () => { 7 | const localVue = createLocalVue() 8 | localVue.use(index) 9 | let wrapper = null 10 | 11 | beforeEach(() => { 12 | // 初始化wrapper 13 | wrapper = mount(FabCantainer, { 14 | localVue 15 | }) 16 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 999999; 17 | }) 18 | 19 | it('startAnimate', (done) => { 20 | let timeout = new Timeout() 21 | expect(wrapper.vm.touching).toBe(false) 22 | expect(wrapper.vm.animating).toBe(false) 23 | expect(wrapper.vm.mousedown).toBe(false) 24 | wrapper.vm.startAnimate() 25 | expect(wrapper.vm.touching).toBe(true) 26 | expect(wrapper.vm.animating).toBe(true) 27 | expect(wrapper.vm.mousedown).toBe(true) 28 | timeout.handleTimeout().then(() => { 29 | expect(wrapper.vm.animating).toBe(false) 30 | expect(wrapper.vm.mousedown).toBe(true) 31 | done() 32 | }) 33 | }) 34 | 35 | it('startAnimate 当mousedown为false时', (done) => { 36 | let timeout = new Timeout() 37 | expect(wrapper.vm.touching).toBe(false) 38 | expect(wrapper.vm.animating).toBe(false) 39 | expect(wrapper.vm.mousedown).toBe(false) 40 | wrapper.vm.startAnimate() 41 | expect(wrapper.vm.touching).toBe(true) 42 | expect(wrapper.vm.animating).toBe(true) 43 | expect(wrapper.vm.mousedown).toBe(true) 44 | wrapper.vm.mousedown = false 45 | timeout.handleTimeout().then(() => { 46 | expect(wrapper.vm.animating).toEqual(false) 47 | expect(wrapper.vm.mousedown).toEqual(false) 48 | done() 49 | }) 50 | }) 51 | 52 | it('endAnimate 当animating为true', () => { 53 | wrapper.vm.mousedown = true 54 | wrapper.vm.animating = true 55 | wrapper.vm.touching = true 56 | wrapper.vm.endAnimate() 57 | expect(wrapper.vm.mousedown).toEqual(false) 58 | expect(wrapper.vm.touching).toEqual(true) 59 | }) 60 | 61 | it('endAnimate 当animating为false', () => { 62 | wrapper.vm.mousedown = true 63 | wrapper.vm.animating = false 64 | wrapper.vm.touching = true 65 | wrapper.vm.timeout.handleTimeout() 66 | expect(typeof wrapper.vm.timeout.timer).toEqual('number') 67 | wrapper.vm.endAnimate() 68 | expect(wrapper.vm.mousedown).toEqual(false) 69 | expect(wrapper.vm.touching).toEqual(false) 70 | expect(wrapper.vm.timeout.timer).toEqual(null) 71 | }) 72 | 73 | afterEach(() => { 74 | // 废弃wrapper 75 | wrapper = null 76 | }) 77 | }) -------------------------------------------------------------------------------- /test/unit/specs/util.spec.js: -------------------------------------------------------------------------------- 1 | import { Timeout, handleSafariBodyClickNotWorkEvent, listenClick } from '../../../src/util' 2 | 3 | describe('util.js', () => { 4 | it('handleTimeout', (done) => { 5 | let timeout = new Timeout() 6 | timeout.handleTimeout().then((res) => { 7 | expect(res).toBe(true) 8 | done() 9 | }) 10 | }) 11 | it('handleClearTimeout', () => { 12 | let timeout1 = new Timeout() 13 | timeout1.handleTimeout() 14 | expect(typeof timeout1.timer).toBe('number') 15 | timeout1.handleClearTimeout() 16 | expect(timeout1.timer).toEqual(null) 17 | }) 18 | it('handleSafariBodyClickNotWorkEvent safari', () => { 19 | document.querySelector('html').className = '' 20 | expect(document.querySelector('html').getAttribute('class')).toEqual('') 21 | handleSafariBodyClickNotWorkEvent(() => {}, true) 22 | let _html = document.querySelector('html') 23 | expect(_html.getAttribute('class')).toEqual('setCursor') 24 | }) 25 | it('handleSafariBodyClickNotWorkEvent android', () => { 26 | document.querySelector('html').className = '' 27 | expect(document.querySelector('html').getAttribute('class')).toEqual('') 28 | handleSafariBodyClickNotWorkEvent(() => {}, false) 29 | let _html = document.querySelector('html') 30 | expect(_html.getAttribute('class')).toEqual('') 31 | }) 32 | 33 | it('listenClick, obj.el.contains(e.target)', () => { 34 | let dom = document.createElement('div') 35 | let childDom = document.createElement('div') 36 | let othorDom = document.createElement('div') 37 | dom.appendChild(childDom) 38 | expect(dom.contains(childDom)).toEqual(true) 39 | console.log(dom) 40 | let bindingValue = () => true 41 | let obj = { 42 | el: dom, 43 | binding: { 44 | value: bindingValue 45 | } 46 | } 47 | let _e = { 48 | target: childDom 49 | } 50 | expect(listenClick(obj, _e)).toEqual(false) 51 | _e.target = othorDom 52 | expect(listenClick(obj, _e)).toEqual(bindingValue()) 53 | }) 54 | 55 | it('listenClick, e.target.dataset.outside', () => { 56 | let dom = document.createElement('div') 57 | let othorDom = document.createElement('div') 58 | othorDom.setAttribute('data-outside', 'true') 59 | let childDom = document.createElement('div') 60 | expect(dom.contains(othorDom)).toEqual(false) 61 | let bindingValue = () => true 62 | let obj = { 63 | el: dom, 64 | binding: { 65 | value: bindingValue 66 | } 67 | } 68 | let _e = { 69 | target: othorDom 70 | } 71 | expect(listenClick(obj, _e)).toEqual(false) 72 | _e.target = childDom 73 | expect(listenClick(obj, _e)).toEqual(true) 74 | }) 75 | }) -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/8.3c00d3d5.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[8],{366:function(t,a,e){"use strict";e.r(a);var v={data:function(){return{fabAutoHideAnimateModel:"default",fabItemAnimate:"default"}}},_=e(42),n=Object(_.a)(v,(function(){var t=this,a=t.$createElement,e=t._self._c||a;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("ClientOnly",[e("vue-fab",{attrs:{fabAutoHideAnimateModel:t.fabAutoHideAnimateModel,fabItemAnimate:t.fabItemAnimate}},t._l(4,(function(t){return e("fab-item",{attrs:{idx:t-1}})})),1)],1),t._v(" "),e("h1",{attrs:{id:"动画配置"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#动画配置"}},[t._v("#")]),t._v(" 动画配置")]),t._v(" "),e("p",[t._v("VueFab提供多样的动画配置")]),t._v(" "),e("h2",{attrs:{id:"滚动自动隐藏动画"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#滚动自动隐藏动画"}},[t._v("#")]),t._v(" 滚动自动隐藏动画")]),t._v(" "),[e("button",{staticClass:"button",on:{click:function(a){"alive"===t.fabAutoHideAnimateModel?t.fabAutoHideAnimateModel="default":t.fabAutoHideAnimateModel="alive"}}},[t._v("\n 点我切换\n")]),t._v(" "),e("p",[t._v("当前模式 "+t._s(t.fabAutoHideAnimateModel))])],t._v(" "),e("table",[e("thead",[e("tr",[e("th",[t._v("Option")]),t._v(" "),e("th",[t._v("Type")]),t._v(" "),e("th",[t._v("Default")]),t._v(" "),e("th",[t._v("Params")]),t._v(" "),e("th",[t._v("Description")])])]),t._v(" "),e("tbody",[e("tr",[e("td",[t._v("fabAutoHideAnimateModel")]),t._v(" "),e("td",[t._v("String")]),t._v(" "),e("td",[t._v("'default'")]),t._v(" "),e("td",[t._v("'default' / 'alive'")]),t._v(" "),e("td",[t._v("fab滚动触发自动隐藏动画 分为 'default' ( 缩小隐藏 ) 以及 'alive' (向下滚动隐藏)")])])])]),t._v(" "),e("h2",{attrs:{id:"fabitem展开动画"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#fabitem展开动画"}},[t._v("#")]),t._v(" FabItem展开动画")]),t._v(" "),[e("button",{staticClass:"button",on:{click:function(a){"alive"===t.fabItemAnimate?t.fabItemAnimate="default":t.fabItemAnimate="alive"}}},[t._v("\n 点我切换\n")])],t._v(" "),e("p",[t._v("当前模式 "+t._s(t.fabItemAnimate))]),t._v(" "),e("table",[e("thead",[e("tr",[e("th",[t._v("Option")]),t._v(" "),e("th",[t._v("Type")]),t._v(" "),e("th",[t._v("Default")]),t._v(" "),e("th",[t._v("Params")]),t._v(" "),e("th",[t._v("Description")])])]),t._v(" "),e("tbody",[e("tr",[e("td",[t._v("fabItemAnimate")]),t._v(" "),e("td",[t._v("String")]),t._v(" "),e("td",[t._v("'default'")]),t._v(" "),e("td",[t._v("'default' / 'alive'")]),t._v(" "),e("td",[t._v("打开关闭子菜单时过渡动画 分为 'default' (各自过渡) 'alive' (分裂过渡)")])])])]),t._v(" "),e("p",[t._v("我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br"),t._v("\n我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br"),t._v("\n我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br"),t._v("\n我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br"),t._v("\n我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br"),t._v("\n我"),e("br"),t._v("\n是"),e("br"),t._v("\n占"),e("br"),t._v("\n位"),e("br"),t._v("\n符"),e("br")])],2)}),[],!1,null,null,null);a.default=n.exports}}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-float-action-button", 3 | "version": "0.7.9", 4 | "description": "vue, float action button", 5 | "main": "dist/vue-fab.js", 6 | "scripts": { 7 | "test": "cross-env BABEL_ENV=test karma start ./test/unit/karma.conf.js", 8 | "build": "webpack --config ./webpack.config.js", 9 | "build:demo": "webpack --config ./demo/webpack.prod.config.js", 10 | "dev": "webpack-dev-server --inline --progress --config demo/webpack.dev.config.js", 11 | "docs:dev": "vuepress dev docs/homepage", 12 | "docs:build": "vuepress build docs/homepage" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/a62527776a/vue-floating-action-button.git" 17 | }, 18 | "keywords": [ 19 | "vue", 20 | "floating action button" 21 | ], 22 | "publishConfig": { 23 | "access": "public" 24 | }, 25 | "author": "chenyu ", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/a62527776a/vue-floating-action-button/issues" 29 | }, 30 | "homepage": "https://github.com/a62527776a/vue-floating-action-button#readme", 31 | "devDependencies": { 32 | "@vuepress/plugin-google-analytics": "^1.0.0-rc.1", 33 | "babel": "^6.23.0", 34 | "babel-core": "^6.26.0", 35 | "babel-eslint": "^8.2.2", 36 | "babel-loader": "^7.1.3", 37 | "babel-plugin-istanbul": "4.1.6", 38 | "babel-plugin-transform-function-bind": "^6.22.0", 39 | "babel-plugin-transform-runtime": "^6.23.0", 40 | "babel-preset-env": "^1.6.1", 41 | "babel-preset-es2015": "^6.24.1", 42 | "babel-preset-stage-2": "^6.24.1", 43 | "coveralls": "^3.0.4", 44 | "cross-env": "^5.2.0", 45 | "css-loader": "^0.28.10", 46 | "eslint": "^4.18.1", 47 | "eslint-config-standard": "^11.0.0", 48 | "eslint-friendly-formatter": "^3.0.0", 49 | "eslint-loader": "^2.0.0", 50 | "eslint-plugin-html": "^4.0.2", 51 | "eslint-plugin-import": "^2.9.0", 52 | "eslint-plugin-node": "^6.0.1", 53 | "eslint-plugin-promise": "^3.6.0", 54 | "eslint-plugin-standard": "^3.0.1", 55 | "html-webpack-plugin": "^2.30.1", 56 | "istanbul": "^0.4.5", 57 | "jasmine-core": "2.5.2", 58 | "karma": "^4.1.0", 59 | "karma-babel-preprocessor": "^7.0.0", 60 | "karma-chrome-launcher": "^2.2.0", 61 | "karma-coverage": "^1.1.2", 62 | "karma-coveralls": "^2.1.0", 63 | "karma-jasmine": "^1.1.1", 64 | "karma-phantomjs-launcher": "^1.0.4", 65 | "karma-sourcemap-loader": "^0.3.7", 66 | "karma-spec-reporter": "^0.0.32", 67 | "karma-webpack": "^2.0.13", 68 | "less": "^2.7.3", 69 | "less-loader": "^4.0.5", 70 | "nyc": "13.3.0", 71 | "style-loader": "^0.20.2", 72 | "transform-runtime": "^0.0.0", 73 | "vue": "^2.5.13", 74 | "vue-float-action-button": "^0.6.7", 75 | "vue-loader": "^14.1.1", 76 | "vue-template-compiler": "^2.5.13", 77 | "vue-test-utils": "^1.0.0-beta.11", 78 | "vuepress": "^1.0.3", 79 | "webpack": "^3.10.0", 80 | "webpack-dev-server": "^2.11.1" 81 | }, 82 | "dependencies": {} 83 | } 84 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/11.85c7fed5.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[11],{370:function(n,t,o){"use strict";o.r(t);var e=o(42),a=Object(e.a)({},(function(){var n=this,t=n.$createElement,o=n._self._c||t;return o("ContentSlotsDistributor",{attrs:{"slot-key":n.$parent.slotKey}},[n.currentSource?o("ClientOnly",[o("vue-fab",{attrs:{"icon-type":n.currentSource,icon:n.mainIcon[n.currentSource],mainBtnColor:"#3eaf7c"}},n._l(n.menu[n.currentSource],(function(n,t){return o("fab-item",{attrs:{idx:t,color:n.color,title:n.title,icon:n.icon}})})),1)],1):n._e(),n._v(" "),o("script",[n._v("\nexport default {\n data () {\n return {\n currentSource: 'iconfont',\n mainIcon: {\n iconfont: 'icon-jia',\n MaterialDesign: 'add'\n },\n menu: {\n MaterialDesign: [\n {\n icon: 'done',\n title: 'good job!',\n color: '#ff9900'\n },\n {\n icon: 'toc',\n title: '',\n color: '#999'\n }\n ],\n iconfont: [\n {\n icon: 'icon-Rxing',\n title: 'iconfont.cn',\n color: '#483218'\n },\n {\n icon: 'icon-huanyihuan1',\n title: '',\n color: '#c8d'\n },\n {\n icon: 'icon-fangda1',\n title: '',\n color: '#3DB'\n }\n ]\n }\n }\n },\n methods: {\n handleSource () {\n this.currentSource === 'iconfont' ? this.currentSource = 'MaterialDesign' : this.currentSource = 'iconfont'\n }\n }\n}\n")]),n._v(" "),o("h1",{attrs:{id:"支持iconfont"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#支持iconfont"}},[n._v("#")]),n._v(" 支持iconfont")]),n._v(" "),o("p",[n._v("对于使用阿里巴巴iconfont作为图标库的项目,我们提供参数以配置支持")]),n._v(" "),o("p",[n._v("默认使用MaterialDesign的图标库")]),n._v(" "),o("h5",{attrs:{id:"vue-fab在底层已抹平两者使用区别-引入后仅需使用icon属性传入即可"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#vue-fab在底层已抹平两者使用区别-引入后仅需使用icon属性传入即可"}},[n._v("#")]),n._v(" Vue-Fab在底层已抹平两者使用区别 引入后仅需使用icon属性传入即可")]),n._v(" "),o("h2",{attrs:{id:"options"}},[o("a",{staticClass:"header-anchor",attrs:{href:"#options"}},[n._v("#")]),n._v(" options")]),n._v(" "),o("table",[o("thead",[o("tr",[o("th",[n._v("Option")]),n._v(" "),o("th",[n._v("Type")]),n._v(" "),o("th",[n._v("Params")]),n._v(" "),o("th",[n._v("Description")])])]),n._v(" "),o("tbody",[o("tr",[o("td",[n._v("iconType")]),n._v(" "),o("td",[n._v("String")]),n._v(" "),o("td",[n._v("'MaterialDesign'/'iconfont'")]),n._v(" "),o("td",[n._v("根据您的使用习惯或开发依赖来决定使用哪种图标")])])])]),n._v(" "),[o("button",{staticClass:"button",on:{click:n.handleSource}},[n._v("点击切换图标库来源")])],n._v(" "),o("p",[n._v("当前图标库来源: "+n._s(n.currentSource))]),n._v(" "),o("div",{staticClass:"language-js extra-class"},[o("pre",{pre:!0,attrs:{class:"language-js"}},[o("code",[n._v("Vue"),o("span",{pre:!0,attrs:{class:"token punctuation"}},[n._v(".")]),o("span",{pre:!0,attrs:{class:"token function"}},[n._v("use")]),o("span",{pre:!0,attrs:{class:"token punctuation"}},[n._v("(")]),n._v("VueFab"),o("span",{pre:!0,attrs:{class:"token punctuation"}},[n._v(",")]),n._v(" "),o("span",{pre:!0,attrs:{class:"token comment"}},[n._v("/* {\n ----------------------\n // opitons 可选iconfont图标或MaterialIcons\n iconType: 'MaterialDesign'\n // iconType: 'iconfont'\n} */")]),o("span",{pre:!0,attrs:{class:"token punctuation"}},[n._v(")")]),n._v("\n")])])])],2)}),[],!1,null,null,null);t.default=a.exports}}]); -------------------------------------------------------------------------------- /src/fab-item.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 136 | 137 | -------------------------------------------------------------------------------- /docs/homepage/README.md: -------------------------------------------------------------------------------- 1 | 2 | 4 | 11 | 12 | 13 | 14 | 41 | 42 | 43 | # 介绍 44 | 45 | vue-float-action-button是一个美观、动画流畅、自定义内容丰富的一个Vue FAB组件 46 | 47 |

48 | 49 | Monthly downloads 50 | 51 | 5.8 kB min+gzip 52 | License 54 | 55 | 56 | 57 | Coverage Status 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 |

66 | 67 | 68 | ## Features 69 | 70 | * **支持多种动画** 71 | * **支持滚动/手势自动显示/隐藏** 72 | * **支持Material Design ICON** 73 | * **支持阿里巴巴iconfont.cn** 74 | * **支持点击空白处自动隐藏** 75 | * **动画流畅复刻Material Design** 76 | * **0依赖 体积小 min + gzip打包后 仅5.8KB** 77 | 78 | *** 79 | 80 | ## Installation and use 81 | 82 | ``` sh 83 | $ yarn add vue-float-action-button 84 | $ npm install vue-float-action-button 85 | ``` 86 | 87 | ``` js 88 | import App from './App.vue' 89 | import VueFab from 'vue-float-action-button' 90 | import Vue from 'vue' 91 | 92 | Vue.use(VueFab, /* { 93 | ---------------------- 94 | // opitons 可选iconfont图标或MaterialIcons 95 | iconType: 'MaterialDesign' 96 | // iconType: 'iconfont' 97 | } */) 98 | 99 | new Vue({ 100 | render: h => h(App) 101 | }).$mount('#app') 102 | ``` 103 | 104 | 本组件支持两种图标 105 | 一种为 Material Design 的图标 需要在网页中引入 106 | ``` html 107 | // index.html 108 | 109 | 110 | // App.vue 111 | 112 | 113 | 114 | 115 | 116 | 117 | ``` 118 | 所有ICON皆可从 https://material.io/icons/ 中查找 119 | 120 | 如使用**阿里巴巴矢量图标库** https://www.iconfont.cn 则按提示引入 121 | ``` html 122 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | ``` 133 | 134 | #### Vue-Fab在底层已抹平两者使用区别 引入后仅需使用icon属性传入即可 135 | 136 | 137 | *** 138 | 139 | ## Examples 140 | 141 | 142 | ``` html 143 | 155 | 156 | 183 | 184 | ``` 185 | -------------------------------------------------------------------------------- /docs/homepage/homepage/unfoldDirection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 配置展开方向

VueFab提供多样的动画配置

通过修改unfoldDirection参数 来改变向上或者向下展示

当前模式 down

Option Type Default Params Description
unfoldDirection String 'up' 'down' / 'up' 向上展开或者向下展开
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /docs/homepage/basic.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 41 | 42 | # 基本使用 43 | 44 | vue-fab 提供一系列自定义属性 45 | ## VueFab 46 | 47 | ``` html 48 | 54 | 62 | 63 | 89 | ``` 90 | 91 | ### Props 92 | 93 | | Option | Type | Default | Params | Description | 94 | | ------ | ------ | -------- | ------ | ---------- | 95 | | icon | String | 'add' | / | 未激活的icon (如果用alibaba iconfont则必填) | 96 | | activeIcon | String | 'add' | / | 激活后的icon | 97 | | size | String | 'Normal' | 'big' / 'normal' / 'small' | 主Fab的尺寸 子菜单会随之变化 | 98 | |mainBtnColor|String|'#E64C3B'| / | 主按钮颜色| 99 | | z-index|Number|5 | / |fab的层级| 100 | 101 | ### icon 102 | 103 | 没有展开的vue-fab展示的图标 104 | 105 | 108 | 109 | ### activeIcon 110 | 111 | 展开之后的vue-fab展示的图标 112 | 113 | 116 | 117 | ### size 118 | 119 | 展开之后的vue-fab展示的图标 120 | 121 | 128 | 129 | ### mainBtnColor 130 | 131 | 主按钮的颜色 132 | 133 | 138 | 139 | ### z-index 140 | 141 | z-index 142 | 143 | 148 | 149 | ## FabItem 150 | 151 | ### Props 152 | 153 | | Option | Type | Default | Params | Description | 154 | | ------ | ------ | -------- | ------ | ---------- | 155 | | idx | Number | 0 | / | 下标 决定了位置以及clickItem事件返回的值(必须) | 156 | | title | String | '' | / | 菜单项标题 如果不填 将不显示title框 | 157 | | icon | String | 'add' | / | Submenu item icon Supports [Material Icon] (https://material.io/icons/) and [iconfont](https://www.iconfont.cn/) icon | 158 | | color | String | '#FFF' | / | 支持css颜色标准 默认为白色 不填写该值将自动拥有一个值为0px 2px 8px #666的阴影 | 159 | | titleColor| String | #666 | / | 子菜单标题字体颜色 | 160 | | titleBgColor | String | #FFF | / | 子菜单背景颜色 | 161 | 162 | 163 | ### idx 164 | 165 | 必填项 影响UI以及后续一些返回值 166 | 167 | 170 | 171 | ### title 172 | 173 | 作为该fabItem的标题 174 | 默认为空 175 | 176 | 179 | 180 | ### icon 181 | 182 | 作为该fabItem的图标 183 | 184 | 187 | 188 | ### color 189 | 子按钮的颜色 190 | 191 | 196 | 197 | 198 | ### titleColor 199 | 200 | 标题的颜色 201 | 202 | 207 | 208 | ### titleBgColor 209 | 210 | 标题栏的背景颜色 211 | 212 | 217 | 218 | ## FabItem Event 219 | 220 | ### clickItem 221 | 222 | 点击子菜单时将触发clickItem事件, 223 | 224 | | Name | type | Param | Description | 225 | | ---- | --- | ------- | ----------- | 226 | | clickItem | Object | { idx } | 当菜单项不为空且点击菜单项时,会返回该菜单项传入的idx值 | 227 | 228 | ``` html 229 | 230 | 235 | 236 | 245 | 246 | ``` 247 | 248 | -------------------------------------------------------------------------------- /docs/homepage/homepage/scroll.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 滚动配置

滚动相关的配置

# 控制自动隐藏的方向

默认是从下滚动上面隐藏 21 | 改成down为从上滚动到下面隐藏

当前值 up

Option Type Default Params Description
autoHideDirection String 'up' 'up' / 'down' 默认关闭

# 自动隐藏的阈值

滚动超过这个值才会触发自动隐藏


22 | 是
23 | 占
24 | 位
25 | 符
26 | 我
27 | 是
28 | 占
29 | 位
30 | 符
31 | 我
32 | 是
33 | 占
34 | 位
35 | 符
36 | 我
37 | 是
38 | 占
39 | 位
40 | 符
41 | 我
42 | 是
43 | 占
44 | 位
45 | 符
46 | 我
47 | 是
48 | 占
49 | 位
50 | 符

59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /docs/homepage/homepage/scrollAble.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
21 | # 关闭滚动自动隐藏 22 |

# VueFab

Option Type Params Description
scrollAutoHide Boolean true / false 默认关闭

对于不想开启滚动自动隐藏的开发者,提供了关闭的选项

<vue-fab :scrollAutoHide="false">
23 | </vue-fab>
24 | 


25 | 是
26 | 占
27 | 位
28 | 符
29 | 我
30 | 是
31 | 占
32 | 位
33 | 符
34 | 我
35 | 是
36 | 占
37 | 位
38 | 符
39 | 我
40 | 是
41 | 占
42 | 位
43 | 符
44 | 我
45 | 是
46 | 占
47 | 位
48 | 符
49 | 我
50 | 是
51 | 占
52 | 位
53 | 符

62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/homepage/homepage/animate.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 动画配置

VueFab提供多样的动画配置

# 滚动自动隐藏动画

当前模式 default

Option Type Default Params Description
fabAutoHideAnimateModel String 'default' 'default' / 'alive' fab滚动触发自动隐藏动画 分为 'default' ( 缩小隐藏 ) 以及 'alive' (向下滚动隐藏)

# FabItem展开动画

当前模式 default

Option Type Default Params Description
fabItemAnimate String 'default' 'default' / 'alive' 打开关闭子菜单时过渡动画 分为 'default' (各自过渡) 'alive' (分裂过渡)


25 | 是
26 | 占
27 | 位
28 | 符
29 | 我
30 | 是
31 | 占
32 | 位
33 | 符
34 | 我
35 | 是
36 | 占
37 | 位
38 | 符
39 | 我
40 | 是
41 | 占
42 | 位
43 | 符
44 | 我
45 | 是
46 | 占
47 | 位
48 | 符
49 | 我
50 | 是
51 | 占
52 | 位
53 | 符

62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /docs/homepage/homepage/iconfont.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 支持iconfont

对于使用阿里巴巴iconfont作为图标库的项目,我们提供参数以配置支持

默认使用MaterialDesign的图标库

# Vue-Fab在底层已抹平两者使用区别 引入后仅需使用icon属性传入即可

# options

Option Type Params Description
iconType String 'MaterialDesign'/'iconfont' 根据您的使用习惯或开发依赖来决定使用哪种图标

当前图标库来源:

Vue.use(VueFab, /* {
69 |   ----------------------
70 |   // opitons 可选iconfont图标或MaterialIcons
71 |   iconType: 'MaterialDesign'
72 |   // iconType: 'iconfont'
73 | } */)
74 | 
83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Vue Floating Action Button 2 | 3 | &&这是一个Vue2.0的仓库&& 4 | 5 | ##### ❤❤❤❤ a beautiful Floating Action Button ❤❤❤❤ 6 | ##### 支持iconfont以及material icons 7 |

8 | 9 | Monthly downloads 10 | 11 | 5.8 kB min+gzip 12 | License 14 | 15 | Coverage Status 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |

24 | 25 | **[Demo](https://vue-fab.github.io/)** 26 | 27 | **[homepage and document](https://a62527776a.github.io/)** 28 | 29 | **[English Doc](https://github.com/a62527776a/vue-floating-action-button/blob/master/readme.en.md)** 30 | 31 | **[demo代码](https://github.com/a62527776a/vue-floating-action-button/blob/master/demo/App.vue)** 32 | 33 | ![8.gif](http://upload-images.jianshu.io/upload_images/5738345-8348ec8f54f0d160.gif?imageMogr2/auto-orient/strip) 34 | 35 | ![7.gif](http://upload-images.jianshu.io/upload_images/5738345-a13b5b7b511f8484.gif?imageMogr2/auto-orient/strip) 36 | 37 | 38 | *** 39 | 40 | ## Features 41 | 42 | * **支持多种动画** 43 | * **支持滚动/手势自动显示/隐藏** 44 | * **支持Material Design ICON** 45 | * **支持阿里巴巴iconfont.cn** 46 | * **支持点击空白处自动隐藏** 47 | * **动画流畅复刻Material Design** 48 | * **0依赖 体积小 min + gzip打包后 仅5.8KB** 49 | 50 | *** 51 | 52 | ## Installation and use 53 | 54 | ``` 55 | $ yarn add vue-float-action-button 56 | $ npm install vue-float-action-button 57 | ``` 58 | 59 | ``` 60 | import App from './App.vue' 61 | import VueFab from 'vue-float-action-button' 62 | import Vue from 'vue' 63 | 64 | Vue.use(VueFab, /* { 65 | ---------------------- 66 | // opitons 可选iconfont图标或MaterialIcons 67 | iconType: 'MaterialDesign' 68 | // iconType: 'iconfont' 69 | } */) 70 | 71 | new Vue({ 72 | render: h => h(App) 73 | }).$mount('#app') 74 | ``` 75 | 76 | 本组件支持两种图标 77 | 一种为 Material Design 的图标 需要在网页中引入 78 | ``` 79 | // index.html 80 | 81 | 82 | // App.vue 83 | 84 | 85 | 86 | 87 | 88 | 89 | ``` 90 | 所有ICON皆可从 https://material.io/icons/ 中查找 91 | 92 | 如使用**阿里巴巴矢量图标库** https://www.iconfont.cn 则按提示引入 93 | ``` 94 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | ``` 105 | 106 | #### Vue-Fab在底层已抹平两者使用区别 引入后仅需使用icon属性传入即可 107 | 108 | 109 | *** 110 | 111 | ## Examples 112 | 113 | 114 | ``` 115 | 127 | 128 | 155 | 156 | ``` 157 | 158 | *** 159 | 160 | # API 161 | 162 | ## options 163 | 164 | | Option | Type | Params | Description | 165 | | ------ | -------- | ------ | ---------- | 166 | | iconType | String |'MaterialDesign'/'iconfont' | 根据您的使用习惯或开发依赖来决定使用哪种图标 | 167 | 168 | ``` 169 | Vue.use(VueFab, /* { 170 | ---------------------- 171 | // opitons 可选iconfont图标或MaterialIcons 172 | iconType: 'MaterialDesign' 173 | // iconType: 'iconfont' 174 | } */) 175 | ``` 176 | 177 | **vue-fab API** 178 | 179 | ## Props 180 | 181 | | Option | Type | Default | Params | Description | 182 | | ------ | ------ | -------- | ------ | ---------- | 183 | | iconType | String | 'MaterialDesign' | 'MaterialDesign'/'iconfont' | 根据您的使用习惯或开发依赖来决定使用哪种图标 | 184 | | autoHideDirection | String | 'up' | 'up' / 'down' | 滚动自动隐藏的方向控制,默认值up为向下展示向上隐藏down值为向上展示向下隐藏 | 185 | | unfoldDirection | String | 'up' | 'up' / 'down' | 展开方向,向上或者向下 | 186 | | icon | String | 'add' | / | 未激活的icon | 187 | | activeIcon | String | 'add' | / | 激活后的icon | 188 | | size | String | 'Normal' | 'big' / 'normal' / 'small' | 主Fab的尺寸 子菜单会随之变化 | 189 | |mainBtnColor|String|'#E64C3B'| / | 主按钮颜色| 190 | |hidden| Boolean | true | true / false | 是否隐藏Fab | 191 | |fabAnimateBezier | String | linear | 'ease' / 'linear' / 'ease-in' / 'ease-out' / 'ease-in-out' / '.18,.89,.91,.17' | 主按钮显示消失(hidden)的贝塞尔曲线 如填入贝塞尔曲线 直接填入'n,n,n,n'或'liner' | 192 | | z-index|Number|5 | / |fab的层级| 193 | | shadow | Boolean | true | true / false | 主button的阴影| 194 | | clickAutoClose| Boolean | true | true / false | 点击子菜单项后是否关闭菜单 | 195 | | scrollAutoHide | Boolean | true | true / false | 滚动是否触发自动隐藏 (PC端和Mobile端实现方式有所不同 分别根据scroll事件和touchmove事件实现) | 196 | | autoHideThreshold | Number | 10 | / | 滚动触发自动隐藏的阈值 单位px | 197 | | fabAutoHideAnimateModel | String | 'alive' | 'default' / 'alive' | fab滚动触发自动隐藏动画 分为 'default' ( 缩小隐藏 ) 以及 'alive' (向下滚动隐藏) | 198 | | fabItemAnimate | String | 'default' | 'default' / 'alive' | 打开关闭子菜单时过渡动画 分为 'default' (各自过渡) 'alive' (分裂过渡) | 199 | | fabAliveAnimateBezier | String' | '.16,1.01,.61,1.2' | 'ease' / 'linear' / 'ease-in' / 'ease-out' / 'ease-in-out' / '.18,.89,.91,.17' | 子菜单列表在alive动画模式下的贝塞尔曲线 注:仅fabMenuAnimate为alive时生效 | 200 | | globalOptions | Object | {spacing: 40, delay: 0.05} | / | 每个fab-item的动画延迟和间距 | 201 | * **delay**: 菜单项淡入淡出的延迟差 为上一个菜单项加该延迟的延迟(单位 s) (fabItemAnimate为alive情况下该值会自动 除以 3) 202 | * **spacing** 每个菜单项的间距 单位 px 203 | 204 | ## Methods 205 | 206 | | Name | Param | Type | Default | Description | 207 | | ---- | -------- | ------- | ------- | ----------- | 208 | | onOffFab | True / False | Boolean | True | 显示或者隐藏Fab | 209 | 210 | **fab-item API** 211 | 212 | ## Props 213 | 214 | | Option | Type | Default | Params | Description | 215 | | ------ | ------ | -------- | ------ | ---------- | 216 | | idx | Number | 0 | / | 下标 决定了位置以及clickItem事件返回的值(必须) | 217 | | title | String | '' | / | 菜单项标题 如果不填 将不显示title框 | 218 | | icon | String | 'add' | / | Submenu item icon Supports [Material Icon] (https://material.io/icons/) and [iconfont](https://www.iconfont.cn/) icon | 219 | | color | String | '#FFF' | / | 支持css颜色标准 默认为白色 不填写该值将自动拥有一个值为0px 2px 8px #666的阴影 | 220 | | titleColor| String | #666 | / | 子菜单标题字体颜色 | 221 | | titleBgColor | String | #FFF | / | 子菜单背景颜色 | 222 | 223 | ## Event 224 | 225 | | Name | Param | Description | 226 | | ---- | ------- | ----------- | 227 | | clickItem | {idx} | 当菜单项不为空且点击菜单项时,会返回该菜单项传入的idx值 | 228 | 229 | ## 230 | 231 | 232 | ## 注意 233 | 234 | * 由于safari浏览器的事件传递机制 window、document、body 不响应click事件** 235 | 如若出现**点击空白区域无法自动关闭子菜单的情况** 请检查html高度是否100% 236 | 如果没有100% 请单独给html标签设置css属性 min-height: 100% 237 | 项目对safari浏览器做了单独处理 在safari浏览器中将点击事件挂载在了HTML标签上,除iOS系统外的浏览器挂载在window上 238 | 239 | *** 240 | ## LICENSE 241 | MIT 242 | -------------------------------------------------------------------------------- /readme.en.md: -------------------------------------------------------------------------------- 1 | # Vue Floating Action Button 2 | 3 | ### support material icons 4 | 5 | ##### ❤❤❤❤ a beautiful Floating Action Button ❤❤❤❤ 6 |

7 | 8 | Monthly downloads 9 | 10 | 5.8 kB min+gzip 11 | License 13 | 14 | Coverage Status 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |

23 | 24 | ![8.gif](http://upload-images.jianshu.io/upload_images/5738345-8348ec8f54f0d160.gif?imageMogr2/auto-orient/strip) 25 | 26 | ![7.gif](http://upload-images.jianshu.io/upload_images/5738345-a13b5b7b511f8484.gif?imageMogr2/auto-orient/strip) 27 | 28 | **[Demo](https://vue-fab.github.io/)** 29 | 30 | **[homepage and document](https://a62527776a.github.io/)** 31 | 32 | **[demo代码](https://github.com/a62527776a/vue-floating-action-button/blob/master/demo/App.vue)** 33 | 34 | 35 | *** 36 | 37 | ## Features 38 | 39 | * **Support multiple animations** 40 | * **Support scrolling/gestures to show/hide automatically** 41 | * **Support Material Design ICON** 42 | * **Support Alibaba iconfont.cn** 43 | * **Support click on the blank space to automatically hide** 44 | * **Smooth animation reproduction Material Design** 45 | * **0 dependency Small size min + gzip packaged only 5.8KB** 46 | 47 | 48 | *** 49 | 50 | ## Installation and use 51 | 52 | ``` 53 | $ yarn add vue-float-action-button 54 | $ npm install vue-float-action-button 55 | ``` 56 | 57 | ``` 58 | import App from './App.vue' 59 | import VueFab from 'vue-float-action-button' 60 | import Vue from 'vue' 61 | 62 | Vue.use(VueFab, /* { 63 | ---------------------- 64 | // opitons Optional Alibaba iconfont icon or MaterialIcons 65 | iconType: 'MaterialDesign' 66 | // iconType: 'iconfont' 67 | } */) 68 | 69 | new Vue({ 70 | render: h => h(App) 71 | }).$mount('#app') 72 | ``` 73 | 74 | This component supports two icons 75 | A material design icon needs to be introduced in the web page 76 | ``` 77 | // index.html 78 | 79 | 80 | // App.vue 81 | 82 | 83 | 84 | 85 | 86 | 87 | ``` 88 | 89 | All ICONs can be found from https://material.io/icons/ 90 | 91 | If you use **Alibaba Vector Icon Library** https://www.iconfont.cn, follow the prompts to import 92 | ``` 93 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | ``` 104 | 105 | #### Vue-Fab has smoothed out the difference between the two in the bottom layer. After introduction, only use the icon attribute to pass in. 106 | 107 | *** 108 | 109 | ## Examples 110 | 111 | 112 | ``` 113 | 125 | 126 | 153 | 154 | ``` 155 | 156 | *** 157 | 158 | 159 | # API 160 | 161 | ## options 162 | 163 | | Option | Type | Params | Description | 164 | | ------ | -------- | ------ | ---------- | 165 | | iconType | String |'MaterialDesign'/'iconfont' | Decide which icon to use based on your usage habits or development dependencies | 166 | 167 | ``` 168 | Vue.use(VueFab, /* { 169 | ---------------------- 170 | // opitons Optional iconfont icon or MaterialIcons 171 | iconType: 'MaterialDesign' 172 | // iconType: 'iconfont' 173 | } */) 174 | ``` 175 | 176 | **vue-fab API** 177 | 178 | ## Props 179 | 180 | | Option | Type | Default | Params | Description | 181 | | ------ | ------ | -------- | ------ | ---------- | 182 | | iconType | String | 'MaterialDesign' | 'MaterialDesign'/'iconfont' | Decide which icon to use based on your usage habits or development dependencies | 183 | | autoHideDirection | String | 'up' | 'up' / 'down' | Rolling auto-hiding direction control, default value up to show up to hide down value to show up to hide down value | 184 | | unfoldDirection | String | 'up' | 'up' / 'down' | Expanding direction, up or down | 185 | | icon | String | 'add' | / | Inactive icon | 186 | | activeIcon | String | 'add' | / | Activated icon | 187 | | size | String | 'Normal' | 'big' / 'normal' / 'small' | The size of the main Fab will change with the submenu. | 188 | |mainBtnColor|String|'#E64C3B'| / | Main button color| 189 | |hidden| Boolean | true | true / false | Whether to hide Fab | 190 | |fabAnimateBezier | String | linear | 'ease' / 'linear' / 'ease-in' / 'ease-out' / 'ease-in-out' / '.18,.89,.91,.17' | The main button shows the hidden Bezier curve. Fill in the Bezier curve and fill in 'n, n, n, n' or 'liner' directly. | 191 | | z-index|Number|5 | / |Fab level| 192 | | shadow | Boolean | true | true / false | Main button shadow| 193 | | clickAutoClose| Boolean | true | true / false | Whether to close the menu after clicking the submenu item | 194 | | scrollAutoHide | Boolean | true | true / false | Whether scrolling triggers auto-hiding (the PC side and the mobile side are implemented differently according to the scroll event and the touchmove event respectively) | 195 | | autoHideThreshold | Number | 10 | / | Scroll triggered auto-hidden threshold unit px | 196 | | fabAutoHideAnimateModel | String | 'alive' | 'default' / 'alive' | Fab scrolling triggers auto-hide animations into 'default' (reduce hidden) and 'alive' (scroll down) | 197 | | fabItemAnimate | String | 'default' | 'default' / 'alive' | Transition animation when opening the close submenu is divided into 'default' (respectively) 'alive' (split transition) | 198 | | fabAliveAnimateBezier | String' | '.16,1.01,.61,1.2' | 'ease' / 'linear' / 'ease-in' / 'ease-out' / 'ease-in-out' / '.18,.89,.91,.17' | Sub-menu list Bezier curve in alive animation mode Note: Only when fabMenuAnimate is alive | 199 | | globalOptions | Object | {spacing: 40, delay: 0.05} | / | Animation delay and spacing for each fab-item | 200 | * **delay**: The delay difference between the fade in and out of the menu item is the delay (in s) of the delay for the previous menu item (the value is automatically divided by 3 when the fabItemAnimate is alive) 201 | * **spacing** Spacing of each menu item Unit px 202 | 203 | ## Methods 204 | 205 | | Name | Param | Type | Default | Description | 206 | | ---- | -------- | ------- | ------- | ----------- | 207 | | onOffFab | True / False | Boolean | True | Show or hide Fab | 208 | 209 | **fab-item API** 210 | 211 | ## Props 212 | 213 | | Option | Type | Default | Params | Description | 214 | | ------ | ------ | -------- | ------ | ---------- | 215 | | idx | Number | 0 | / | The subscript determines the position and the value returned by the clickItem event (required) | 216 | | title | String | '' | / | Menu item title If not filled, the title box will not be displayed. | 217 | | icon | String | 'add' | / | Submenu item icon Supports [Material Icon] (https://material.io/icons/) and [iconfont](https://www.iconfont.cn/) icon | 218 | | color | String | '#FFF' | / | Support for css color standard Default to white Do not fill in this value will automatically have a shadow with a value of 0px 2px 8px #666 | 219 | | titleColor| String | #666 | / | Submenu title font color | 220 | | titleBgColor | String | #FFF | / | Submenu background color | 221 | 222 | ## Event 223 | 224 | | Name | Param | Description | 225 | | ---- | ------- | ----------- | 226 | | clickItem | {idx} | When the menu item is not empty and the menu item is clicked, the idx value passed in the menu item is returned. | 227 | 228 | ## 229 | 230 | 231 | ## Note 232 | 233 | Due to safari's event delivery mechanism window, document, body does not respond to click eventsIf clicks on a blank area and the submenu cannot be automatically closed ** Please check if the html height is 100% 234 |    If there is no 100%, please set the css attribute to the html tag separately min-height: 100% 235 |    The project handles the safari browser separately. In the safari browser, the click event is mounted on the HTML tag, and the browser other than the iOS system is mounted on the window. 236 | 237 | *** 238 | ## LICENSE 239 | MIT 240 | -------------------------------------------------------------------------------- /src/fab.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 307 | 308 | 347 | -------------------------------------------------------------------------------- /test/unit/specs/fab.spec.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 测试 fab.vue 3 | */ 4 | 5 | import Vue from 'vue' 6 | import { mount, createLocalVue } from 'vue-test-utils' 7 | import index from '../../../src/index.js' 8 | import Fab from '../../../src/fab.vue' 9 | import FabItem from '../../../src/fab-item.vue' 10 | 11 | describe('fab.vue', () => { 12 | const localVue = createLocalVue() 13 | localVue.use(index) 14 | let wrapper = null 15 | 16 | beforeEach(() => { 17 | // 初始化wrapper 18 | wrapper = mount(Fab, { 19 | localVue, 20 | slots: { 21 | default: [FabItem, FabItem, FabItem] 22 | } 23 | }) 24 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 999999; 25 | }) 26 | 27 | afterEach(() => { 28 | // 废弃wrapper 29 | wrapper = null 30 | }) 31 | 32 | it('测试default slot是否有效', () => { 33 | expect(wrapper.contains(FabItem)).toBe(true) 34 | }) 35 | 36 | it('测试props', () => { 37 | // icon 38 | const expectIcon = 'https' 39 | expect(wrapper.props().icon).toBe('add') 40 | wrapper.setProps({ 41 | icon: expectIcon 42 | }) 43 | expect(wrapper.find('.material-icons').text()).toBe(expectIcon) 44 | }) 45 | 46 | it('测试onOffFab函数', () => { 47 | wrapper.vm.onOffFab(false) 48 | expect(wrapper.vm.hidden).toBe(false) 49 | wrapper.vm.onOffFab(true) 50 | expect(wrapper.vm.hidden).toBe(true) 51 | }) 52 | 53 | it('openMenu 函数在有子item的情况', () => { 54 | expect(wrapper.vm.$children.length).toBe(4) 55 | expect(wrapper.vm.active).toBe(false) 56 | wrapper.vm.openMenu() 57 | expect(wrapper.vm.active).toBe(true) 58 | }) 59 | 60 | it('checkDirection 判断方向', () => { 61 | wrapper.vm.scrollTop = 40 62 | expect(wrapper.vm.checkDirection(80)).toBe('up') 63 | expect(wrapper.vm.checkDirection(20)).toBe('down') 64 | }) 65 | 66 | it('recordScrollTopByChangeDirection', () => { 67 | let tobeScrollTop = 80 68 | wrapper.vm.scrollTop = 40 69 | wrapper.vm.scrollDirection = 'down' 70 | let _direction = wrapper.vm.checkDirection(tobeScrollTop) 71 | expect(wrapper.vm.checkDirection(tobeScrollTop)).toBe(_direction) 72 | wrapper.vm.recordScrollTopByChangeDirection(tobeScrollTop) 73 | expect(wrapper.vm.scrollTop).toBe(tobeScrollTop) 74 | expect(wrapper.vm.changeDirectionScrollTop).toBe(tobeScrollTop) 75 | expect(wrapper.vm.scrollDirection).toBe(_direction) 76 | }) 77 | 78 | it('listenTouchStart', () => { 79 | let mockEvent = { 80 | touches: [ 81 | { 82 | clientY: 8 83 | } 84 | ] 85 | } 86 | wrapper.vm.listenTouchStart(mockEvent) 87 | expect(wrapper.vm.touchEventInfo.startY).toBe(8) 88 | }) 89 | 90 | it('listenTouchMove', () => { 91 | let mockTouchStartY = 4 92 | let mockTouchMoveY = 8 93 | let mockTouchstartEvent = { 94 | touches: [ 95 | { 96 | clientY: mockTouchStartY 97 | } 98 | ] 99 | } 100 | let mockTouchMoveEvent = { 101 | touches: [ 102 | { 103 | clientY: mockTouchMoveY 104 | } 105 | ] 106 | } 107 | wrapper.vm.listenTouchStart(mockTouchstartEvent) 108 | wrapper.vm.listenTouchMove(mockTouchMoveEvent) 109 | expect(wrapper.vm.touchEventInfo.startY).toBe(mockTouchStartY) 110 | expect(wrapper.vm.touchEventInfo.offsetY).toBe(mockTouchMoveY - mockTouchStartY) 111 | mockTouchStartY = 4 112 | mockTouchMoveY = 88 113 | mockTouchstartEvent.touches[0].clientY = mockTouchStartY 114 | mockTouchMoveEvent.touches[0].clientY = mockTouchMoveY 115 | wrapper.vm.listenTouchStart(mockTouchstartEvent) 116 | wrapper.vm.listenTouchMove(mockTouchMoveEvent) 117 | expect(wrapper.vm.hidden).toBe(false) 118 | expect(wrapper.vm.touchEventInfo.offsetY).toBe(0) 119 | }) 120 | 121 | it('overflowThreshold', () => { 122 | expect(wrapper.vm.autoHideThreshold).toBe(50) 123 | wrapper.vm.touchEventInfo.offsetY = -60 124 | expect(wrapper.vm.overflowThreshold).toBe(true) 125 | wrapper.vm.touchEventInfo.offsetY = -40 126 | expect(wrapper.vm.overflowThreshold).toBe(false) 127 | wrapper.vm.touchEventInfo.offsetY = 40 128 | expect(wrapper.vm.overflowThreshold).toBe(false) 129 | wrapper.vm.touchEventInfo.offsetY = 60 130 | expect(wrapper.vm.overflowThreshold).toBe(true) 131 | }) 132 | 133 | it('watch hidden', () => { 134 | wrapper.vm.hidden = true 135 | wrapper.vm.active = true 136 | wrapper.vm.hidden = false 137 | expect(wrapper.vm.active).toBe(true) 138 | }) 139 | 140 | it('测试主按钮点击事件', () => { 141 | // 有子菜单的情况下 142 | wrapper.find('.fab').trigger('click') 143 | expect(wrapper.vm.active).toBe(true) 144 | wrapper.find('.fab').trigger('click') 145 | expect(wrapper.vm.active).toBe(false) 146 | const localWrapper = mount(Fab) 147 | // 无子菜单的情况下 148 | expect(localWrapper.vm.$children.length).toBe(1) 149 | localWrapper.find('.fab').trigger('click') 150 | // 不会打开菜单 151 | expect(localWrapper.vm.active).toBe(false) 152 | }) 153 | 154 | // 打开子菜单后隐藏整个菜单 预期为自动关闭自菜单 155 | it('当且仅当hidden属性为false active属性为true时 active属性是否会变成false', (done) => { 156 | wrapper.find('.fab').trigger('click') 157 | // 当主菜单显示在屏幕上时 打开子菜单后 158 | expect(wrapper.vm.active).toBe(true) 159 | expect(wrapper.vm.hidden).toBe(true) 160 | // // 关闭主菜单 161 | wrapper.vm.onOffFab(false) 162 | // // 自动关闭子菜单 163 | expect(wrapper.vm.hidden).toBe(false) 164 | wrapper.vm.$nextTick(() => { 165 | expect(wrapper.vm.active).toBe(false) 166 | done() 167 | }) 168 | }) 169 | 170 | // 当子菜单打开之后 预期点击除子菜单以外的地方全部关闭 171 | it('测试click-outside事件', () => { 172 | // 先将子菜单打开 173 | wrapper.find('.fab').trigger('click') 174 | // 点击body关闭子菜单 175 | document.body.click() 176 | expect(wrapper.vm.active).toBe(false) 177 | wrapper.find('.fab').trigger('click') 178 | let buttonEl = document.createElement('button') 179 | document.body.appendChild(buttonEl) 180 | // 点击其他元素预期关闭子菜单 181 | buttonEl.click() 182 | expect(wrapper.vm.active).toBe(false) 183 | wrapper.find('.fab').trigger('click') 184 | // 再次点击主菜单按钮预期关闭子菜单 185 | wrapper.find('.fab').trigger('click') 186 | expect(wrapper.vm.active).toBe(false) 187 | }) 188 | 189 | it('clickoutside', () => { 190 | wrapper.vm.active = true 191 | wrapper.vm.clickoutside() 192 | expect(wrapper.vm.active).toBe(false) 193 | }) 194 | 195 | it('scrollDirectionUpAndHidden', () => { 196 | wrapper.vm.scrollDirection = 'up' 197 | wrapper.vm.hidden = true 198 | expect(wrapper.vm.scrollDirectionUpAndHidden).toBe(true) 199 | }) 200 | 201 | it('scrollDirectionDownAndShow', () => { 202 | wrapper.vm.scrollDirection = 'down' 203 | wrapper.vm.hidden = false 204 | expect(wrapper.vm.scrollDirectionDownAndShow).toBe(true) 205 | }) 206 | 207 | it('scrollDirectionUpAndShow', () => { 208 | wrapper.vm.scrollDirection = 'up' 209 | wrapper.vm.hidden = false 210 | expect(wrapper.vm.scrollDirectionUpAndShow).toBe(true) 211 | }) 212 | 213 | it('notChangeHideStatus', () => { 214 | let vm = wrapper.vm 215 | vm.autoHideDirection = 'up' 216 | wrapper.vm.scrollDirection = 'up' 217 | wrapper.vm.hidden = true 218 | expect(vm.notChangeHideStatus).toBe(true) 219 | wrapper.vm.scrollDirection = 'down' 220 | wrapper.vm.hidden = false 221 | expect(vm.notChangeHideStatus).toBe(true) 222 | vm.autoHideDirection = 'down' 223 | wrapper.vm.scrollDirection = 'up' 224 | wrapper.vm.hidden = false 225 | expect(vm.notChangeHideStatus).toBe(true) 226 | wrapper.vm.scrollDirection = 'down' 227 | wrapper.vm.hidden = true 228 | expect(vm.notChangeHideStatus).toBe(true) 229 | }) 230 | 231 | it('scrollDirectionDownAndHidden', () => { 232 | wrapper.vm.scrollDirection = 'down' 233 | wrapper.vm.hidden = true 234 | expect(wrapper.vm.scrollDirectionDownAndHidden).toBe(true) 235 | }) 236 | 237 | // it('测试滚动自动隐藏事件', () => { 238 | 239 | // }) 240 | 241 | it('computedOffsetOver', () => { 242 | wrapper.vm.autoHideThreshold = 60 243 | expect(wrapper.vm.computedOffsetOver(80)).toBe(false) 244 | expect(wrapper.vm.computedOffsetOver(40)).toBe(true) 245 | }) 246 | 247 | it('computedShowHideByOffset', () => { 248 | wrapper.vm.scrollDirection = 60 249 | wrapper.vm.autoHideDirection = 60 250 | expect(wrapper.vm.computedShowHideByOffset()).toBe(true) 251 | wrapper.vm.autoHideDirection = 50 252 | expect(wrapper.vm.computedShowHideByOffset()).toBe(false) 253 | }) 254 | 255 | it('computedTransitionName', () => { 256 | wrapper.setProps({activeIcon: 'add'}) 257 | wrapper.setProps({icon: 'add'}) 258 | expect(wrapper.vm.computedTransitionName).toEqual('fab-icon') 259 | wrapper.setProps({icon: 'remove'}) 260 | wrapper.vm.active = true 261 | expect(wrapper.vm.computedTransitionName).toEqual('fab-active-icon') 262 | wrapper.vm.active = false 263 | expect(wrapper.vm.computedTransitionName).toEqual('fab-icon') 264 | }) 265 | 266 | it('iconType 切换 展示 界面样式', () => { 267 | wrapper.setProps({iconType: 'MaterialDesign'}) 268 | expect(wrapper.contains('.material-icons')).toEqual(true) 269 | wrapper.setProps({iconType: 'iconfont'}) 270 | expect(wrapper.contains('.vue-fab-iconfont-icons')).toEqual(true) 271 | }) 272 | 273 | it('hidden 样式展示', () => { 274 | wrapper.setData({ hidden : true }) 275 | expect(wrapper.contains('.fab')).toEqual(true) 276 | wrapper.setData({ hidden : false }) 277 | expect(wrapper.contains('.fab')).toEqual(false) 278 | }) 279 | 280 | it('size 样式展示', () => { 281 | let sizeopt = ['normal', 'big', 'small'] 282 | sizeopt.map(item => { 283 | wrapper.setProps({size : item}) 284 | expect(wrapper.contains('.fab-size-' + item)).toEqual(true) 285 | }) 286 | }) 287 | 288 | it('active 影响样式', () => { 289 | wrapper.setData({ active : true }) 290 | expect(wrapper.contains('.fab-active')).toEqual(true) 291 | wrapper.setData({ active : false }) 292 | expect(wrapper.contains('.fab-active')).toEqual(false) 293 | }) 294 | 295 | // it('测试子菜单点击后自动关闭', (done) => { 296 | // wrapper.find('.fab').trigger('click') 297 | // wrapper.find('.fab-item').trigger('click') 298 | // setTimeout( _ => { 299 | // expect(wrapper.vm.active).toBe(false) 300 | // done() 301 | // }, 600) 302 | // // 测试props clickAutoClose 属性是否生效 303 | // // false 预期为点击后不关闭子菜单 304 | // wrapper.setProps({ 305 | // clickAutoClose: false 306 | // }) 307 | // expect(wrapper.vm.clickAutoClose).toBe(false) 308 | // // 打开子菜单 309 | // wrapper.find('.fab').trigger('click') 310 | // // 点击子菜单 311 | // wrapper.find('.fab-item').trigger('click') 312 | // // 预期不自动关闭子菜单 313 | // setTimeout( _ => { 314 | // expect(wrapper.vm.active).toBe(true) 315 | // done() 316 | // }, 600) 317 | // }) 318 | 319 | // it('测试子菜单props idx属性对top、transitionDelay属性的影响', (done) => { 320 | // 给每个子菜单复制idx属性 321 | // for (let i = 1; i < 4; i++) { 322 | // wrapper.vm.$children[i].idx = i - 1 323 | // } 324 | // // 打开子菜单 325 | // wrapper.find('.fab').trigger('click') 326 | // let wrappers = wrapper.findAll('.fab-item') 327 | // setTimeout(_ => { 328 | // for (let i = 0; i < wrappers.length; i++) { 329 | // // -40 - this.idx * this.$parent.globalOptions.spacing + 'px' 330 | // let topReg = new RegExp(`${-40 - i * wrapper.vm.globalOptions.spacing}px`) 331 | // // this.idx * this.$parent.globalOptions.delay + 's' 332 | // let delayReg = new RegExp(`${i * wrapper.vm.globalOptions.delay}s`) 333 | // expect(wrappers.wrappers[i].attributes().style).toMatch(topReg) 334 | // expect(wrappers.wrappers[i].attributes().style).toMatch(delayReg) 335 | // } 336 | // done() 337 | // }, 600) 338 | // }) 339 | 340 | }) -------------------------------------------------------------------------------- /docs/homepage/homepage/customitem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 使用slot自定义图片

VueItem提供slot已供配置图片自定义图标
21 | 需要自己配置好slot元素的宽高以及border-radius!!

<vue-fab icon="add" size="normal" >
22 |   <fab-item @clickItem="clickItem" :idx="0" icon="edit">
23 |   </fab-item>
24 |   <fab-item @clickItem="clickItem" :idx="1" icon="edit">
25 |     <img src="http://ued.rr.tv/hqdefault (20).jpg" style="width: 100%;height: 100%;border-radius: 50%" />
26 |   </fab-item>
27 |   <fab-item @clickItem="clickItem" :idx="2" icon="edit">
28 |     <img src="http://ued.rr.tv/hqdefault (4).jpg"  style="width: 100%;height: 100%;border-radius: 50%" />
29 |   </fab-item>
30 |   <fab-item @clickItem="clickItem" :idx="3" title="自定义图标,得自己配置好border-radius以及width,height" icon="edit">
31 |     <img src="http://ued.rr.tv/0.96058651790303660.png"  style="width: 100%;height: 100%;border-radius: 50%" />
32 |   </fab-item>
33 | </vue-fab>
34 | 
35 | 
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /docs/homepage/homepage/assets/js/10.bae2c45d.js: -------------------------------------------------------------------------------- 1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[10],{364:function(t,a,s){"use strict";s.r(a);var n={data:function(){return{}}},e=s(42),r=Object(e.a)(n,(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("ClientOnly",[s("vue-fab",{attrs:{icon:"add",size:"normal"}},[s("fab-item",{attrs:{idx:0,icon:"edit"},on:{clickItem:t.clickItem}}),t._v(" "),s("fab-item",{attrs:{idx:1,icon:"edit"},on:{clickItem:t.clickItem}},[s("img",{staticStyle:{width:"100%",height:"100%","border-radius":"50%"},attrs:{src:"http://ued.rr.tv/hqdefault (20).jpg"}})]),t._v(" "),s("fab-item",{attrs:{idx:2,icon:"edit"},on:{clickItem:t.clickItem}},[s("img",{staticStyle:{width:"100%",height:"100%","border-radius":"50%"},attrs:{src:"http://ued.rr.tv/hqdefault (4).jpg"}})]),t._v(" "),s("fab-item",{attrs:{idx:3,title:"自定义图标,得自己配置好border-radius以及width,height",icon:"edit"},on:{clickItem:t.clickItem}},[s("img",{staticStyle:{width:"100%",height:"100%","border-radius":"50%"},attrs:{src:"http://ued.rr.tv/0.96058651790303660.png"}})])],1)],1),t._v(" "),s("h1",{attrs:{id:"使用slot自定义图片"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#使用slot自定义图片"}},[t._v("#")]),t._v(" 使用slot自定义图片")]),t._v(" "),s("p",[t._v("VueItem提供slot已供配置图片自定义图标"),s("br"),t._v("\n需要自己配置好slot元素的宽高以及border-radius!!")]),t._v(" "),s("div",{staticClass:"language-vue extra-class"},[s("pre",{pre:!0,attrs:{class:"language-vue"}},[s("code",[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("vue-fab")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("icon")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("add"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("size")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("normal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("fab-item")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("@clickItem")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("clickItem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":idx")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("icon")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("edit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("fab-item")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("@clickItem")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("clickItem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":idx")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("1"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("icon")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("edit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://ued.rr.tv/hqdefault (20).jpg"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token style-attr"}},[s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("style")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),s("span",{pre:!0,attrs:{class:"token style language-css"}},[s("span",{pre:!0,attrs:{class:"token property"}},[t._v("width")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("height")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("border-radius")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 50%")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("fab-item")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("@clickItem")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("clickItem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":idx")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("2"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("icon")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("edit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://ued.rr.tv/hqdefault (4).jpg"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token style-attr"}},[s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("style")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),s("span",{pre:!0,attrs:{class:"token style language-css"}},[s("span",{pre:!0,attrs:{class:"token property"}},[t._v("width")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("height")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("border-radius")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 50%")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("fab-item")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("@clickItem")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("clickItem"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v(":idx")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("title")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("自定义图标,得自己配置好border-radius以及width,height"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("icon")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("edit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("img")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("src")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("http://ued.rr.tv/0.96058651790303660.png"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token style-attr"}},[s("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("style")]),s("span",{pre:!0,attrs:{class:"token attr-value"}},[s("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),s("span",{pre:!0,attrs:{class:"token style language-css"}},[s("span",{pre:!0,attrs:{class:"token property"}},[t._v("width")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("height")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 100%"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),s("span",{pre:!0,attrs:{class:"token property"}},[t._v("border-radius")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" 50%")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])])]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("/>")])]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token tag"}},[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n")])])])],1)}),[],!1,null,null,null);a.default=r.exports}}]); -------------------------------------------------------------------------------- /docs/homepage/homepage/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello VueFab 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

# 基本使用

vue-fab 提供一系列自定义属性

# VueFab

<vue-fab 
21 |   :mainBtnColor="mainBtnColor" 
22 |   :zIndex="zIndex" 
23 |   :icon="icon" 
24 |   :size="size" 
25 |   :activeIcon="activeIcon">
26 |   <fab-item 
27 |     @clickItem="clickItem"
28 |     :idx="fabItem.idx" 
29 |     :title="fabItem.title" 
30 |     :icon="fabItem.icon" 
31 |     :color="fabItem.color" 
32 |     :titleColor="fabItem.titleColor" 
33 |     :titleBgColor="fabItem.titleBgColor" />
34 | </vue-fab>
35 | <script>
36 | export default {
37 |   data () {
38 |     return {
39 |       icon: 'add',
40 |       activeIcon: 'add',
41 |       size: 'normal',
42 |       mainBtnColor: '#E64C3B',
43 |       zIndex: 5,
44 |       fabItem: {
45 |         idx: 1,
46 |         title: '这是一个演示的标题',
47 |         icon: 'toc',
48 |         color: '#C7D23B',
49 |         titleColor: '#666',
50 |         titleBgColor: '#FFF'
51 |       }
52 |     }
53 |   },
54 |   methods: {
55 |     clickItem (item) {
56 |       window.alert(item.idx)
57 |     }
58 |   }
59 | }
60 | </script>
61 | 

# Props

Option Type Default Params Description
icon String 'add' / 未激活的icon (如果用alibaba iconfont则必填)
activeIcon String 'add' / 激活后的icon
size String 'Normal' 'big' / 'normal' / 'small' 主Fab的尺寸 子菜单会随之变化
mainBtnColor String '#E64C3B' / 主按钮颜色
z-index Number 5 / fab的层级

# icon

没有展开的vue-fab展示的图标

# activeIcon

展开之后的vue-fab展示的图标

# size

展开之后的vue-fab展示的图标

# mainBtnColor

主按钮的颜色

# z-index

z-index

# FabItem

# Props

Option Type Default Params Description
idx Number 0 / 下标 决定了位置以及clickItem事件返回的值(必须)
title String '' / 菜单项标题 如果不填 将不显示title框
icon String 'add' / Submenu item icon Supports [Material Icon] (https://material.io/icons/) and iconfont (opens new window) icon
color String '#FFF' / 支持css颜色标准 默认为白色 不填写该值将自动拥有一个值为0px 2px 8px #666的阴影
titleColor String #666 / 子菜单标题字体颜色
titleBgColor String #FFF / 子菜单背景颜色

# idx

必填项 影响UI以及后续一些返回值

# title

作为该fabItem的标题 62 | 默认为空

# icon

作为该fabItem的图标

# color

子按钮的颜色

# titleColor

标题的颜色

# titleBgColor

标题栏的背景颜色

# FabItem Event

# clickItem

点击子菜单时将触发clickItem事件,

Name type Param Description
clickItem Object { idx } 当菜单项不为空且点击菜单项时,会返回该菜单项传入的idx值

63 | <template>
64 |   <vue-fab>
65 |     <fab-item @clickItem="clickItem" />
66 |   </vue-fab>
67 | </template>
68 | 
69 | <script>
70 | export default {
71 |   methods: {
72 |     clickItem (item) {
73 |       window.alert(item.idx)
74 |     }
75 |   }
76 | }
77 | </script>
78 | 
79 | 
88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /demo/App.vue: -------------------------------------------------------------------------------- 1 |