Merge pull request #22 from pdsuwwz/feature/vue3-support
🌈 feat: upgrade to Vue 3
This commit is contained in:
118
README.md
118
README.md
@@ -2,34 +2,49 @@
|
|||||||
|
|
||||||
 
|
 
|
||||||
|
|
||||||
📌 将页面元素固定在可视范围内。
|
📌 基于 Webpack 5 构建的 Vue 3.x 固钉组件,用于将页面元素固定在可视范围内。
|
||||||
|
|
||||||
**Live demo:** https://pdsuwwz.github.io/hoc-element-affix
|
**Live demo:** https://pdsuwwz.github.io/hoc-element-affix
|
||||||
|
|
||||||
|
|
||||||
|
## Version
|
||||||
|
|
||||||
|
* Vue 3.x 版本 | [Vue 2.x 版本](https://github.com/pdsuwwz/hoc-element-affix/tree/vue2.0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Environment Support
|
## Environment Support
|
||||||
|
|
||||||
* Vue 2.6.0+
|
* Vue 3.2.x
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install @hoc-element/affix
|
npm install @hoc-element/affix
|
||||||
|
|
||||||
|
# or
|
||||||
|
|
||||||
|
pnpm add @hoc-element/affix
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
import HocElementAffix from '@hoc-element/affix'
|
import HocElementAffix from '@hoc-element/affix'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
Vue.use(HocElementAffix)
|
createApp(App)
|
||||||
|
.use(HocElementAffix)
|
||||||
|
.mount('#app')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Feature
|
## Feature
|
||||||
|
|
||||||
- [x] 自定义顶部偏移量
|
- [x] 支持 Vue 3.x
|
||||||
- [x] 固定状态改变时触发的回调
|
- [x] 支持自定义顶部 `offsetTop` 偏移量
|
||||||
- [x] 插槽式的固定状态反馈
|
- [x] 支持固定状态改变触发回调
|
||||||
|
- [x] 支持 `Slot` 插槽式的固定状态反馈
|
||||||
|
|
||||||
## Using
|
## Using
|
||||||
|
|
||||||
@@ -56,92 +71,5 @@ Vue.use(HocElementAffix)
|
|||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
下面是比较全的例子,几乎囊括了 API 的所有用法,源码戳这: [Code](https://github.com/pdsuwwz/hoc-element-affix/tree/master/example/src/views/ExampleAffix.vue )
|
下面是比较全的例子,几乎囊括了 API 的所有用法,源码戳这: [Code](https://github.com/pdsuwwz/hoc-element-affix/tree/main/example/src/views/ExampleAffix.vue )
|
||||||
|
|
||||||
```html
|
|
||||||
<template>
|
|
||||||
<div class="box-container">
|
|
||||||
<div class="content">
|
|
||||||
<div class="long-list">
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in 50"
|
|
||||||
:key="index"
|
|
||||||
class="long"
|
|
||||||
>
|
|
||||||
<template
|
|
||||||
v-if="index === 2"
|
|
||||||
>
|
|
||||||
<hoc-el-affix>
|
|
||||||
<template v-slot="{ affixed }">
|
|
||||||
<div class="box">
|
|
||||||
<span style="font-size: 25px">{{ affixed ? '🍎' : '🍏' }}</span>
|
|
||||||
吸顶【插槽版】
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</hoc-el-affix>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-else-if="index === 9"
|
|
||||||
>
|
|
||||||
<hoc-el-affix
|
|
||||||
:offset-top="120"
|
|
||||||
@change="handleAffixed120"
|
|
||||||
>
|
|
||||||
<div class="box">
|
|
||||||
<span style="font-size: 25px">{{ isAffixed120 ? '🌝' : '🌚' }}</span>
|
|
||||||
距离顶部120px时固定【回调版】
|
|
||||||
</div>
|
|
||||||
</hoc-el-affix>
|
|
||||||
</template>
|
|
||||||
<template
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
{{ index === 49 ? '到底了' : `占位符${index + 1}` }}
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'ExampleAffix',
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isAffixed120: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleAffixed120 (affixed) {
|
|
||||||
this.isAffixed120 = affixed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.box-container {
|
|
||||||
.content {
|
|
||||||
position: relative;
|
|
||||||
width: 300px;
|
|
||||||
margin: 0 auto;
|
|
||||||
text-align: center;
|
|
||||||
.box {
|
|
||||||
width: 300px;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
border: 1px solid #000;
|
|
||||||
}
|
|
||||||
.long-list {
|
|
||||||
.long {
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const babelConfig = {
|
|||||||
{
|
{
|
||||||
targets: {
|
targets: {
|
||||||
chrome: '58',
|
chrome: '58',
|
||||||
ie: '6'
|
ie: '11'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -22,7 +22,8 @@ const babelConfig = {
|
|||||||
],
|
],
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
'@babel/plugin-syntax-dynamic-import',
|
||||||
'@babel/plugin-syntax-import-meta',
|
'@babel/plugin-syntax-import-meta',
|
||||||
['@babel/plugin-proposal-class-properties', { loose: true }],
|
['@babel/plugin-proposal-class-properties', { loose: false }],
|
||||||
|
['@babel/plugin-proposal-private-methods', { loose: false }],
|
||||||
'@babel/plugin-proposal-json-strings',
|
'@babel/plugin-proposal-json-strings',
|
||||||
[
|
[
|
||||||
'@babel/plugin-proposal-decorators',
|
'@babel/plugin-proposal-decorators',
|
||||||
|
|||||||
7
build/utils.js
Normal file
7
build/utils.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const path = require('path')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
resolve (dir = '') {
|
||||||
|
return path.join(process.cwd(), dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
const webpack = require('webpack')
|
const webpack = require('webpack')
|
||||||
const { VueLoaderPlugin } = require('vue-loader')
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
|
|
||||||
const notifier = require('node-notifier')
|
|
||||||
|
|
||||||
function resolve (dir) {
|
|
||||||
return path.join(process.cwd(), dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const { resolve } = require('./utils')
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
entry: './src/main.js',
|
entry: './src/main.js',
|
||||||
@@ -17,21 +12,9 @@ module.exports = {
|
|||||||
filename: 'hoc-el-affix.js',
|
filename: 'hoc-el-affix.js',
|
||||||
libraryTarget: 'umd'
|
libraryTarget: 'umd'
|
||||||
},
|
},
|
||||||
|
stats: 'minimal',
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
enforce: 'pre',
|
|
||||||
test: /\.(vue|js)(\?.*)?$/,
|
|
||||||
loader: 'eslint-loader',
|
|
||||||
include: resolve('src'),
|
|
||||||
options: {
|
|
||||||
fix: true,
|
|
||||||
failOnError: true,
|
|
||||||
useEslintrc: true,
|
|
||||||
configFile: resolve('.eslintrc.js'),
|
|
||||||
formatter: require('eslint-friendly-formatter')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
use: [
|
use: [
|
||||||
@@ -61,7 +44,7 @@ module.exports = {
|
|||||||
loader: 'vue-loader'
|
loader: 'vue-loader'
|
||||||
},
|
},
|
||||||
exclude: /node_modules/,
|
exclude: /node_modules/,
|
||||||
include: resolve('src')
|
include: resolve('./src')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
@@ -69,13 +52,42 @@ module.exports = {
|
|||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
extends: resolve('babel.config.js')
|
extends: resolve('./babel.config.js')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(png|jpg|gif|svg)$/,
|
test: /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
limit: 10000,
|
||||||
|
esModule: false,
|
||||||
|
name: '[name].[hash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
limit: 10000,
|
||||||
|
name: '[name].[hash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
limit: 10000,
|
||||||
|
name: '[name].[hash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|webp|woff2?|eot|ttf|otf|mp4|webm|ogg|mp3|wav|flac|aac)(\?\S*)?$/,
|
||||||
loader: 'file-loader',
|
loader: 'file-loader',
|
||||||
|
include: /node_modules/,
|
||||||
options: {
|
options: {
|
||||||
name: '[name].[ext]?[hash]'
|
name: '[name].[ext]?[hash]'
|
||||||
}
|
}
|
||||||
@@ -84,8 +96,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
vue$: 'vue/dist/vue.esm.js',
|
vue$: 'vue/dist/vue.esm-bundler.js',
|
||||||
'@': resolve('src')
|
'@': resolve('./src')
|
||||||
},
|
},
|
||||||
extensions: ['*', '.js', '.vue', '.json']
|
extensions: ['*', '.js', '.vue', '.json']
|
||||||
},
|
},
|
||||||
@@ -100,24 +112,17 @@ module.exports = {
|
|||||||
hints: false
|
hints: false
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
|
externals: {
|
||||||
|
vue: {
|
||||||
|
root: 'Vue',
|
||||||
|
commonjs: 'vue',
|
||||||
|
commonjs2: 'vue'
|
||||||
|
}
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
new webpack.LoaderOptionsPlugin({
|
new webpack.LoaderOptionsPlugin({
|
||||||
minimize: true
|
minimize: true
|
||||||
}),
|
}),
|
||||||
new FriendlyErrorsWebpackPlugin({
|
|
||||||
clearConsole: false,
|
|
||||||
onErrors: (severity, errors) => {
|
|
||||||
if (severity !== 'error') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const error = errors[0]
|
|
||||||
notifier.notify({
|
|
||||||
title: 'Webpack error',
|
|
||||||
message: `${severity}: ${error.name}`,
|
|
||||||
subtitle: error.file || ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,43 @@
|
|||||||
const path = require('path');
|
const webpack = require('webpack')
|
||||||
const webpack = require('webpack');
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
|
const ESLintPlugin = require('eslint-webpack-plugin')
|
||||||
|
|
||||||
const VueLoaderPlugin = require('vue-loader/lib/plugin');
|
const { resolve } = require('./utils')
|
||||||
|
|
||||||
function resolve (dir) {
|
|
||||||
return path.join(process.cwd(), dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type { webpack.Configuration }
|
||||||
|
*/
|
||||||
const webpackConfig = {
|
const webpackConfig = {
|
||||||
mode: process.env.NODE_ENV,
|
mode: process.env.NODE_ENV,
|
||||||
|
target: 'web',
|
||||||
entry: './example/main.js',
|
entry: './example/main.js',
|
||||||
output: {
|
output: {
|
||||||
path: resolve('./example/dist'),
|
path: resolve('./example/dist'),
|
||||||
filename: '[name].[hash:7].js',
|
filename: '[name].[fullhash:7].js'
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
vue$: 'vue/dist/vue.esm.js',
|
root: resolve(),
|
||||||
'source': resolve('./src'),
|
vue$: 'vue/dist/vue.esm-bundler.js',
|
||||||
'@': resolve('./example/src')
|
'@': resolve('./src'),
|
||||||
|
example: resolve('./example/src')
|
||||||
|
// Non-essential, turn it on when using npm link
|
||||||
|
// vue: resolve('./node_modules/vue')
|
||||||
},
|
},
|
||||||
extensions: ['*', '.js', '.vue', '.json']
|
extensions: ['*', '.js', '.vue', '.json']
|
||||||
|
// Non-essential, turn it on when using npm link
|
||||||
|
// symlinks: false
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
publicPath: '/',
|
port: 8086,
|
||||||
port: 8085,
|
historyApiFallback: true,
|
||||||
quiet: true,
|
|
||||||
hot: true,
|
|
||||||
open: true,
|
|
||||||
openPage: 'affix-example'
|
|
||||||
},
|
},
|
||||||
performance: {
|
performance: {
|
||||||
hints: false
|
hints: false
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
enforce: 'pre',
|
|
||||||
test: /\.(vue|jsx?)$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
loader: 'eslint-loader'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
test: /\.(jsx?|babel|es6)$/,
|
test: /\.(jsx?|babel|es6)$/,
|
||||||
include: process.cwd(),
|
include: process.cwd(),
|
||||||
@@ -64,27 +59,68 @@ const webpackConfig = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(scss|css)$/,
|
test: /\.css$/,
|
||||||
use: [
|
use: [
|
||||||
'vue-style-loader',
|
'vue-style-loader',
|
||||||
'css-loader',
|
'css-loader'
|
||||||
'sass-loader'
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(svg|otf|ttf|woff2?|eot|gif|png|jpe?g)(\?\S*)?$/,
|
test: /\.s(c|a)ss$/,
|
||||||
|
use: [
|
||||||
|
'vue-style-loader',
|
||||||
|
'css-loader',
|
||||||
|
{
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: {
|
||||||
|
// Prefer `dart-sass`
|
||||||
|
implementation: require('sass')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/,
|
||||||
loader: 'url-loader',
|
loader: 'url-loader',
|
||||||
query: {
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
limit: 10000,
|
limit: 10000,
|
||||||
name: '[name].[hash:7].[ext]'
|
esModule: false,
|
||||||
|
name: '[name].[fullhash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
limit: 10000,
|
||||||
|
name: '[name].[fullhash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
|
||||||
|
loader: 'url-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
options: {
|
||||||
|
limit: 10000,
|
||||||
|
name: '[name].[fullhash:7].[ext]'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpe?g|gif|svg|webp|woff2?|eot|ttf|otf|mp4|webm|ogg|mp3|wav|flac|aac)(\?\S*)?$/,
|
||||||
|
loader: 'file-loader',
|
||||||
|
include: /node_modules/,
|
||||||
|
options: {
|
||||||
|
name: '[name].[ext]?[fullhash]'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new webpack.HotModuleReplacementPlugin(),
|
new ESLintPlugin(),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
template: './example/index.html',
|
template: './example/index.html'
|
||||||
}),
|
}),
|
||||||
new VueLoaderPlugin(),
|
new VueLoaderPlugin(),
|
||||||
new webpack.LoaderOptionsPlugin({
|
new webpack.LoaderOptionsPlugin({
|
||||||
@@ -94,25 +130,15 @@ const webpackConfig = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new FriendlyErrorsWebpackPlugin({
|
new webpack.DefinePlugin({
|
||||||
clearConsole: false,
|
__VUE_OPTIONS_API__: false,
|
||||||
onErrors: (severity, errors) => {
|
__VUE_PROD_DEVTOOLS__: false,
|
||||||
if (severity !== 'error') {
|
}),
|
||||||
return
|
|
||||||
}
|
|
||||||
const error = errors[0]
|
|
||||||
notifier.notify({
|
|
||||||
title: 'Webpack error',
|
|
||||||
message: `${severity}: ${error.name}`,
|
|
||||||
subtitle: error.file || ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
optimization: {
|
optimization: {
|
||||||
minimizer: []
|
minimizer: []
|
||||||
},
|
},
|
||||||
devtool: '#eval-source-map'
|
devtool: 'eval-source-map'
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = webpackConfig;
|
module.exports = webpackConfig
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<router-view v-slot="{ Component }">
|
||||||
<router-view />
|
<component :is="Component" />
|
||||||
</div>
|
</router-view>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { defineComponent } from 'vue'
|
||||||
name: 'App'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style lang="scss">
|
|
||||||
|
|
||||||
</style>
|
export default defineComponent({
|
||||||
|
name: 'App'
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
import router from './router.js'
|
import router from './router.js'
|
||||||
|
|
||||||
import HocElementAffix from 'source/main.js'
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
|
||||||
Vue.use(HocElementAffix)
|
import HocElementAffix from 'root/lib/hoc-el-affix' // Switch to bundle lib
|
||||||
|
// import HocElementAffix from '@/main.js'
|
||||||
|
|
||||||
new Vue({
|
import 'example/styles/variables.scss'
|
||||||
router,
|
|
||||||
render: h => h(App)
|
createApp(App)
|
||||||
}).$mount('#app')
|
.use(router)
|
||||||
|
.use(HocElementAffix)
|
||||||
|
.mount('#app')
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
import Vue from 'vue'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import VueRouter from 'vue-router'
|
|
||||||
|
|
||||||
const Layout = () => import('@/components/Layout')
|
|
||||||
const importModule = (filePath) => {
|
const importModule = (filePath) => {
|
||||||
return () => import(`@/${filePath}`)
|
return () => import(`example/${filePath}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const routes = [{
|
const routes = [{
|
||||||
path: '/',
|
path: '/',
|
||||||
component: Layout,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'affix-example',
|
|
||||||
component: importModule('views/ExampleAffix')
|
component: importModule('views/ExampleAffix')
|
||||||
}
|
|
||||||
]
|
|
||||||
}]
|
}]
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
export default createRouter({
|
||||||
export default new VueRouter({
|
|
||||||
routes,
|
routes,
|
||||||
mode: 'history'
|
history: createWebHistory()
|
||||||
})
|
})
|
||||||
|
|||||||
23
example/src/assets/logo.svg
Normal file
23
example/src/assets/logo.svg
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
version="1.1"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:svgjs="http://svgjs.com/svgjs"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
style="enable-background:new 0 0 32 32"
|
||||||
|
xml:space="preserve"
|
||||||
|
><g><path
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="m12 1c-6.354 0-11.5 5.05-11.5 11.279 0 4.984 3.295 9.211 7.863 10.701.575.106.786-.243.786-.542 0-.268-.01-.978-.014-1.917-3.199.681-3.874-1.513-3.874-1.513-.523-1.302-1.279-1.65-1.279-1.65-1.042-.699.08-.685.08-.685 1.155.079 1.761 1.162 1.761 1.162 1.025 1.725 2.692 1.227 3.349.938.104-.729.4-1.227.728-1.509-2.554-.282-5.238-1.252-5.238-5.574 0-1.231.446-2.237 1.184-3.027-.129-.284-.517-1.431.101-2.985 0 0 .963-.303 3.162 1.156 1.775-.484 3.832-.523 5.75 0 2.185-1.459 3.148-1.156 3.148-1.156.618 1.554.23 2.7.115 2.985.733.79 1.179 1.795 1.179 3.027 0 4.333-2.688 5.287-5.247 5.564 1.035.87.762 2.494.762 5.175 0 .296.201.649.791.536 4.601-1.479 7.893-5.709 7.893-10.686 0-6.229-5.149-11.279-11.5-11.279z"
|
||||||
|
fill="#000000"
|
||||||
|
data-original="#b3b3b3"
|
||||||
|
/><path
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="m.184 10.462c-.779 4.906 1.401 10.823 8.123 13.006.12.022.231.032.335.032.782 0 1.32-.582 1.32-1.3-.097-.523.383-2.642-.92-2.357-2.519.536-2.821-.871-3.205-1.607 1.086 1.394 2.718 1.359 3.949.819.683-.3.326-1.064.65-1.343.496-.426.244-1.243-.407-1.314-2.314-.255-4.457-1.001-4.457-4.702 0-2.168 1.505-2.362 1.09-3.269-.015-.033-.333-.754-.045-1.849 1.419.262 2.072 1.28 2.753 1.097 1.687-.46 3.544-.46 5.23 0 .704.189 1.207-.801 2.738-1.103.441 1.654-.473 2.058.103 2.677.632.68.953 1.503.953 2.447 0 5.564-4.717 3.957-5.101 5.22-.088.288.005.599.235.792.61.513.53 1.83.465 2.889-.067 1.098-.125 2.045.482 2.579.214.19.595.393 1.284.253 6.634-2.131 8.83-8.022 8.063-12.917-2.096-13.368-21.526-13.352-23.638-.05zm8.27 10.978.004.505c-.523-.181-1.015-.39-1.475-.623.425.109.913.156 1.471.118zm.37-3.7c-.005.026-.01.053-.015.08-.853.252-1.509.001-1.957-.752 0-.001 0-.001-.001-.002.68.364 1.381.56 1.973.674zm3.176-15.74c11.833 0 14.502 16.267 3.469 19.941-.038-.297-.003-.857.021-1.252.058-.951.126-2.059-.213-2.985 5.088-1.059 5.513-6.646 3.554-9.135.243-.952.145-3.189-.729-3.463-.206-.065-1.305-.304-3.437 1.037-1.741-.416-3.62-.417-5.361 0-1.064-.667-3.462-1.752-3.922-.6-.534 1.342-.407 2.427-.248 3.03-1.739 2.204-1.218 5.894.534 7.626-.993-.475-2.361-.637-2.656.314-.323 1.037.912.911 1.679 2.804.073.236.208.513.415.788-6.811-5.565-3.525-18.105 6.894-18.105z"
|
||||||
|
fill="#ffffff"
|
||||||
|
data-original="#000000"
|
||||||
|
/></g></svg>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1,28 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<router-view v-slot="{ Component }">
|
||||||
<router-view />
|
<component :is="Component" />
|
||||||
</div>
|
</router-view>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: 'Layout'
|
name: 'Layout'
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
14
example/src/styles/variables.scss
Normal file
14
example/src/styles/variables.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #FCFAF2;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="box-container">
|
<div class="box-container">
|
||||||
|
<div class="banner">
|
||||||
|
<div class="repo">
|
||||||
|
<p>📌 @hoc-element/affix</p><a
|
||||||
|
href="https://github.com/pdsuwwz/hoc-element-affix"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="~example/assets/logo.svg"
|
||||||
|
alt="Logo"
|
||||||
|
>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="long-list">
|
<div class="long-list">
|
||||||
<div
|
<div
|
||||||
@@ -11,9 +24,9 @@
|
|||||||
v-if="index === 2"
|
v-if="index === 2"
|
||||||
>
|
>
|
||||||
<hoc-el-affix>
|
<hoc-el-affix>
|
||||||
<template v-slot="{ affixed }">
|
<template #default="{ affixed }">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<span style="font-size: 25px">{{ affixed ? '🍎' : '🍏' }}</span>
|
<span style="font-size: 25px">{{ affixed ? '🥳' : '😀' }}</span>
|
||||||
吸顶【插槽版】
|
吸顶【插槽版】
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -27,15 +40,15 @@
|
|||||||
@change="handleAffixed120"
|
@change="handleAffixed120"
|
||||||
>
|
>
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<span style="font-size: 25px">{{ isAffixed120 ? '🌝' : '🌚' }}</span>
|
<span style="font-size: 25px">{{ isAffixed120 ? '🍎' : '🍏' }}</span>
|
||||||
距离顶部120px时固定【回调版】
|
距离顶部 120px 时固定【回调版】
|
||||||
</div>
|
</div>
|
||||||
</hoc-el-affix>
|
</hoc-el-affix>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template
|
||||||
v-else
|
v-else
|
||||||
>
|
>
|
||||||
{{ index === 49 ? '到底了' : `占位符${index + 1}` }}
|
{{ index === 49 ? '🎊 到底了' : `占位符${index + 1}` }}
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,23 +57,51 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: 'ExampleAffix',
|
name: 'ExampleAffix',
|
||||||
data () {
|
setup () {
|
||||||
|
const isAffixed120 = ref(false)
|
||||||
|
|
||||||
|
const handleAffixed120 = (affixed) => {
|
||||||
|
isAffixed120.value = affixed
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAffixed120: false
|
isAffixed120,
|
||||||
}
|
handleAffixed120
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleAffixed120 (affixed) {
|
|
||||||
this.isAffixed120 = affixed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.box-container {
|
.box-container {
|
||||||
|
.banner {
|
||||||
|
padding: 30px 0;
|
||||||
|
background-color: #1C1C1C;
|
||||||
|
.repo {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 700;
|
||||||
|
a {
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (min-width: 400px) {
|
||||||
|
.banner {
|
||||||
|
.repo {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 300px;
|
width: 300px;
|
||||||
@@ -70,7 +111,7 @@ export default {
|
|||||||
width: 300px;
|
width: 300px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
border: 1px solid #000;
|
border: 1px solid #FCFAF2;
|
||||||
}
|
}
|
||||||
.long-list {
|
.long-list {
|
||||||
.long {
|
.long {
|
||||||
|
|||||||
12
jsconfig.json
Normal file
12
jsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"module": "commonjs",
|
||||||
|
"paths": {
|
||||||
|
"root/*": ["./*"],
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
"example/*": ["./example/src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
98
package.json
98
package.json
@@ -6,8 +6,8 @@
|
|||||||
"author": "Wisdom <pdsu.wwz@foxmail.com>",
|
"author": "Wisdom <pdsu.wwz@foxmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js --progress --colors --hide-modules",
|
"build": "cross-env NODE_ENV=production webpack --progress --config build/webpack.config.js",
|
||||||
"dev:example": "cross-env NODE_ENV=development webpack-dev-server --display-error-details --progress --colors --history-api-fallback --config build/webpack.example.js"
|
"dev:example": "cross-env NODE_ENV=development webpack serve --progress --config build/webpack.example.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -16,54 +16,57 @@
|
|||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^2.6.12"
|
"vue": ">=3.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.11.6",
|
"@babel/core": "^7.16.5",
|
||||||
"@babel/eslint-parser": "^7.11.5",
|
"@babel/eslint-parser": "^7.16.5",
|
||||||
"@babel/eslint-plugin": "^7.11.5",
|
"@babel/eslint-plugin": "^7.16.5",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
"@babel/plugin-proposal-class-properties": "^7.16.5",
|
||||||
"@babel/plugin-proposal-decorators": "^7.1.2",
|
"@babel/plugin-proposal-decorators": "^7.16.5",
|
||||||
"@babel/plugin-proposal-export-default-from": "^7.0.0",
|
"@babel/plugin-proposal-export-default-from": "^7.16.5",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
|
"@babel/plugin-proposal-export-namespace-from": "^7.16.5",
|
||||||
"@babel/plugin-proposal-function-sent": "^7.0.0",
|
"@babel/plugin-proposal-function-sent": "^7.16.5",
|
||||||
"@babel/plugin-proposal-json-strings": "^7.0.0",
|
"@babel/plugin-proposal-json-strings": "^7.16.5",
|
||||||
"@babel/plugin-proposal-numeric-separator": "^7.0.0",
|
"@babel/plugin-proposal-numeric-separator": "^7.16.5",
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.0.0",
|
"@babel/plugin-proposal-private-methods": "^7.16.5",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
"@babel/plugin-proposal-throw-expressions": "^7.16.5",
|
||||||
"@babel/plugin-syntax-import-meta": "^7.0.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.1.0",
|
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||||
"@babel/plugin-transform-runtime": "^7.0.0",
|
"@babel/plugin-transform-modules-commonjs": "^7.16.5",
|
||||||
"@babel/preset-env": "^7.0.0",
|
"@babel/plugin-transform-runtime": "^7.16.5",
|
||||||
"@babel/runtime": "^7.1.2",
|
"@babel/preset-env": "^7.16.5",
|
||||||
"@babel/runtime-corejs2": "^7.1.2",
|
"@babel/runtime": "^7.16.5",
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@babel/runtime-corejs2": "^7.16.5",
|
||||||
"babel-loader": "8.0.0",
|
"@vue/compiler-sfc": "^3.2.26",
|
||||||
"cross-env": "^5.0.5",
|
"@vue/eslint-config-standard": "^6.1.0",
|
||||||
"css-loader": "^0.28.7",
|
"babel-loader": "^8.2.3",
|
||||||
"eslint": "^7.10.0",
|
"core-js": "^3.20.1",
|
||||||
"eslint-config-standard": "^14.1.0",
|
"cross-env": "^7.0.3",
|
||||||
|
"css-loader": "^6.5.1",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-config-standard": "^16.0.3",
|
||||||
"eslint-friendly-formatter": "^4.0.1",
|
"eslint-friendly-formatter": "^4.0.1",
|
||||||
"eslint-loader": "^3.0.3",
|
"eslint-plugin-import": "^2.25.3",
|
||||||
"eslint-plugin-import": "^2.19.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-node": "^11.0.0",
|
"eslint-plugin-promise": "^5.2.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-vue": "^7.20.0",
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"eslint-plugin-vue": "^6.1.2",
|
"file-loader": "^6.2.0",
|
||||||
"file-loader": "^1.1.4",
|
"html-webpack-plugin": "^5.5.0",
|
||||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
"sass": "^1.45.1",
|
||||||
"html-webpack-plugin": "^4.5.0",
|
"sass-loader": "^8.0.2",
|
||||||
"node-notifier": "^8.0.1",
|
"style-loader": "^3.3.1",
|
||||||
"node-sass": "^4.5.3",
|
"url-loader": "^4.1.1",
|
||||||
"sass-loader": "^6.0.6",
|
"vue": "^3.2.26",
|
||||||
"vue-loader": "^15.8.3",
|
"vue-loader": "^16.8.3",
|
||||||
"vue-router": "^3.4.5",
|
"vue-router": "^4.0.12",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-style-loader": "^4.1.3",
|
||||||
"webpack": "^4.41.5",
|
"webpack": "^5.65.0",
|
||||||
"webpack-cli": "^3.3.10",
|
"webpack-cli": "^4.9.1",
|
||||||
"webpack-dev-server": "^3.11.0"
|
"webpack-dev-server": "^4.7.2"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"> 1%",
|
"> 1%",
|
||||||
@@ -72,7 +75,6 @@
|
|||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"vue",
|
"vue",
|
||||||
"element-ui",
|
|
||||||
"hoc",
|
"hoc",
|
||||||
"affix",
|
"affix",
|
||||||
"固钉",
|
"固钉",
|
||||||
|
|||||||
5548
pnpm-lock.yaml
generated
Normal file
5548
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
:style="stylePlaceHolder"
|
:style="stylePlaceHolder"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
ref="scroll-affix"
|
ref="refScrollAffix"
|
||||||
:style="affixStyle"
|
:style="affixStyle"
|
||||||
class="scroll-affix-container"
|
class="scroll-affix-container"
|
||||||
>
|
>
|
||||||
@@ -19,7 +19,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
import { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
name: 'Affix',
|
name: 'Affix',
|
||||||
props: {
|
props: {
|
||||||
offsetTop: {
|
offsetTop: {
|
||||||
@@ -27,105 +29,110 @@ export default {
|
|||||||
default: 0
|
default: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
emits: ['change'],
|
||||||
return {
|
setup (props, { emit }) {
|
||||||
affixStyle: {
|
// ref 实例
|
||||||
|
const refScrollAffix = ref({})
|
||||||
|
|
||||||
|
const affixStyle = ref({
|
||||||
position: 'initial',
|
position: 'initial',
|
||||||
top: 'initial'
|
top: 'initial'
|
||||||
},
|
|
||||||
|
|
||||||
// 占位区域样式
|
|
||||||
stylePlaceHolder: {},
|
|
||||||
|
|
||||||
// 占位区域
|
|
||||||
showPlaceHolder: false,
|
|
||||||
|
|
||||||
// 实例
|
|
||||||
instance: '',
|
|
||||||
|
|
||||||
// 用于记录实例的初始状态下的位置
|
|
||||||
defaultInstancePosition: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
getAffixed () {
|
|
||||||
return this.affixStyle.position === 'fixed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.instance = this.$refs['scroll-affix']
|
|
||||||
this.createAffix()
|
|
||||||
})
|
})
|
||||||
},
|
// 占位区域样式
|
||||||
beforeDestroy () {
|
const stylePlaceHolder = ref({})
|
||||||
document.removeEventListener('scroll', this.scrollListener)
|
// 占位区域
|
||||||
},
|
const showPlaceHolder = ref(false)
|
||||||
methods: {
|
// 用于记录实例的初始状态下的位置
|
||||||
getInstanceRect () {
|
const defaultInstancePosition = ref('')
|
||||||
return this.instance.getBoundingClientRect()
|
|
||||||
},
|
|
||||||
|
|
||||||
getWindowScrollTop () {
|
const getAffixed = computed(() => affixStyle.value.position === 'fixed')
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
createAffix()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('scroll', scrollListener)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getInstanceRect = () => {
|
||||||
|
return refScrollAffix.value.getBoundingClientRect()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getWindowScrollTop = () => {
|
||||||
return window.pageYOffset ||
|
return window.pageYOffset ||
|
||||||
document.documentElement.scrollTop ||
|
document.documentElement.scrollTop ||
|
||||||
document.body.scrollTop
|
document.body.scrollTop
|
||||||
},
|
|
||||||
|
|
||||||
createAffix () {
|
|
||||||
this.defaultInstancePosition = this.getInstanceRect().top
|
|
||||||
this.beforeListener()
|
|
||||||
document.addEventListener('scroll', this.scrollListener)
|
|
||||||
},
|
|
||||||
|
|
||||||
setFixedForInstance () {
|
|
||||||
this.affixStyle = {
|
|
||||||
position: 'fixed',
|
|
||||||
top: `${this.offsetTop}px`
|
|
||||||
}
|
}
|
||||||
this.$emit('change', true)
|
|
||||||
},
|
const createAffix = () => {
|
||||||
|
defaultInstancePosition.value = getInstanceRect().top
|
||||||
|
beforeListener()
|
||||||
|
document.addEventListener('scroll', scrollListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
const setFixedForInstance = () => {
|
||||||
|
affixStyle.value = {
|
||||||
|
position: 'fixed',
|
||||||
|
top: `${props.offsetTop}px`
|
||||||
|
}
|
||||||
|
emit('change', true)
|
||||||
|
}
|
||||||
|
|
||||||
// 用于设置实例在固定后的空白占位
|
// 用于设置实例在固定后的空白占位
|
||||||
setPlaceHolder () {
|
const setPlaceHolder = () => {
|
||||||
this.showPlaceHolder = true
|
showPlaceHolder.value = true
|
||||||
|
|
||||||
const instanceRect = this.getInstanceRect()
|
const instanceRect = getInstanceRect()
|
||||||
this.stylePlaceHolder = {
|
stylePlaceHolder.value = {
|
||||||
width: `${instanceRect.width}px`,
|
width: `${instanceRect.width}px`,
|
||||||
height: `${instanceRect.height}px`
|
height: `${instanceRect.height}px`
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
beforeListener () {
|
|
||||||
|
const beforeListener = () => {
|
||||||
// 若下一次进入页面发现滚动条所处位置已经超过了实例,则立即固定
|
// 若下一次进入页面发现滚动条所处位置已经超过了实例,则立即固定
|
||||||
if (this.defaultInstancePosition < this.offsetTop) {
|
if (defaultInstancePosition.value < props.offsetTop) {
|
||||||
this.setFixedForInstance()
|
setFixedForInstance()
|
||||||
this.setPlaceHolder()
|
setPlaceHolder()
|
||||||
}
|
}
|
||||||
|
|
||||||
this.defaultInstancePosition = this.getWindowScrollTop() + this.defaultInstancePosition
|
defaultInstancePosition.value = getWindowScrollTop() + defaultInstancePosition.value
|
||||||
},
|
}
|
||||||
scrollListener () {
|
|
||||||
const offsetTop = this.getInstanceRect().top
|
const scrollListener = () => {
|
||||||
|
const offsetTop = getInstanceRect().top
|
||||||
// 当实例距离顶部的距离刚好接近(0px+设置的 top 距离)时,则立即固定
|
// 当实例距离顶部的距离刚好接近(0px+设置的 top 距离)时,则立即固定
|
||||||
if (offsetTop < this.offsetTop) {
|
if (offsetTop < props.offsetTop) {
|
||||||
this.setFixedForInstance()
|
setFixedForInstance()
|
||||||
this.setPlaceHolder()
|
setPlaceHolder()
|
||||||
}
|
}
|
||||||
|
|
||||||
const windowScrollTop = this.getWindowScrollTop()
|
const windowScrollTop = getWindowScrollTop()
|
||||||
const isArrivalDefault = (this.defaultInstancePosition - this.offsetTop) >= windowScrollTop
|
const isArrivalDefault = (defaultInstancePosition.value - props.offsetTop) >= windowScrollTop
|
||||||
|
|
||||||
// 当实例的初始位置减去设置的 top 距离刚好接近滚动条滚过的距离时(即一直在向上滚动)&& 实例已经在固定状态,则取消固定
|
// 当实例的初始位置减去设置的 top 距离刚好接近滚动条滚过的距离时(即一直在向上滚动)&& 实例已经在固定状态,则取消固定
|
||||||
if (isArrivalDefault && this.affixStyle.position === 'fixed') {
|
if (isArrivalDefault && affixStyle.value.position === 'fixed') {
|
||||||
this.affixStyle = {}
|
affixStyle.value = {}
|
||||||
this.showPlaceHolder = false
|
showPlaceHolder.value = false
|
||||||
this.stylePlaceHolder = {}
|
stylePlaceHolder.value = {}
|
||||||
this.$emit('change', false)
|
emit('change', false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
refScrollAffix,
|
||||||
|
affixStyle,
|
||||||
|
stylePlaceHolder,
|
||||||
|
showPlaceHolder,
|
||||||
|
defaultInstancePosition,
|
||||||
|
|
||||||
|
getAffixed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import HocElAffix from './components/ScrollAffix'
|
import HocElAffix from '@/components/ScrollAffix'
|
||||||
|
|
||||||
const install = function (Vue, opts = {}) {
|
const install = function (app) {
|
||||||
Vue.component('HocElAffix', HocElAffix)
|
app.component('HocElAffix', HocElAffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof window !== 'undefined' && window.Vue) {
|
if (typeof window !== 'undefined' && window.Vue) {
|
||||||
|
|||||||
Reference in New Issue
Block a user