|
@@ -0,0 +1,174 @@
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+ import { addResizeListener, removeResizeListener, toObject } from './util'
|
|
|
|
|
+ import scrollbarWidth from './scrollbar-width'
|
|
|
|
|
+ import Bar from './bar.vue'
|
|
|
|
|
+ require('./assets/css/index.scss')
|
|
|
|
|
+
|
|
|
|
|
+ export default {
|
|
|
|
|
+ name: 'ui-scrollbar',
|
|
|
|
|
+ components: { Bar },
|
|
|
|
|
+ props: {
|
|
|
|
|
+ native: Boolean,
|
|
|
|
|
+ childOverWidth: {
|
|
|
|
|
+ type: Boolean,
|
|
|
|
|
+ default: true
|
|
|
|
|
+ },
|
|
|
|
|
+ wrapStyle: {},
|
|
|
|
|
+ wrapClass: {},
|
|
|
|
|
+ viewClass: {},
|
|
|
|
|
+ viewStyle: {},
|
|
|
|
|
+ noresize: Boolean, // 如果 container 尺寸不会发生变化,最好设置它可以优化性能
|
|
|
|
|
+ tag: {
|
|
|
|
|
+ type: String,
|
|
|
|
|
+ default: 'div'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ data () {
|
|
|
|
|
+ return {
|
|
|
|
|
+ sizeWidth: '0',
|
|
|
|
|
+ sizeHeight: '0',
|
|
|
|
|
+ moveX: 0,
|
|
|
|
|
+ moveY: 0,
|
|
|
|
|
+ scrollbarMiddle: 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ computed: {
|
|
|
|
|
+ wrap () {
|
|
|
|
|
+ return this.$refs.wrap
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ mounted () {
|
|
|
|
|
+ if (this.native) return
|
|
|
|
|
+ this.$nextTick(this.update)
|
|
|
|
|
+ !this.noresize && addResizeListener(this.$refs.resize, this.update)
|
|
|
|
|
+ !this.noresize && addResizeListener(this.$refs.wrap, this.update)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ beforeDestroy () {
|
|
|
|
|
+ if (this.native) return
|
|
|
|
|
+ !this.noresize && removeResizeListener(this.$refs.resize, this.update)
|
|
|
|
|
+ !this.noresize && removeResizeListener(this.$refs.wrap, this.update)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ handleScroll () {
|
|
|
|
|
+ const wrap = this.wrap
|
|
|
|
|
+
|
|
|
|
|
+ this.moveY = (wrap.scrollTop * 100) / wrap.clientHeight
|
|
|
|
|
+ this.moveX = (wrap.scrollLeft * 100) / wrap.clientWidth
|
|
|
|
|
+ if (wrap.scrollTop + wrap.clientHeight === wrap.scrollHeight) {
|
|
|
|
|
+ this.$emit('scrollbottom')
|
|
|
|
|
+ }
|
|
|
|
|
+ if (wrap.scrollTop === 0) {
|
|
|
|
|
+ this.$emit('scrolltop')
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$emit('scrollMove', {
|
|
|
|
|
+ moveX: wrap.scrollLeft,
|
|
|
|
|
+ moveY: wrap.scrollTop,
|
|
|
|
|
+ clientX: wrap.clientWidth,
|
|
|
|
|
+ clientY: wrap.clientHeight,
|
|
|
|
|
+ scrollHeight: wrap.scrollHeight,
|
|
|
|
|
+ scrollWidth: wrap.scrollWidth
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ update () {
|
|
|
|
|
+ let heightPercentage = null
|
|
|
|
|
+ let widthPercentage = null
|
|
|
|
|
+ const wrap = this.wrap
|
|
|
|
|
+ if (!wrap) return
|
|
|
|
|
+
|
|
|
|
|
+ heightPercentage = (wrap.clientHeight * 100) / wrap.scrollHeight
|
|
|
|
|
+ widthPercentage = (wrap.clientWidth * 100) / wrap.scrollWidth
|
|
|
|
|
+
|
|
|
|
|
+ this.sizeHeight = heightPercentage < 100 ? heightPercentage + '%' : ''
|
|
|
|
|
+ this.sizeWidth = widthPercentage < 100 ? widthPercentage + '%' : ''
|
|
|
|
|
+ },
|
|
|
|
|
+ continueScroll (rowNum) {
|
|
|
|
|
+ const wrap = this.wrap
|
|
|
|
|
+ if (this.scrollbarMiddle === 0) {
|
|
|
|
|
+ this.scrollbarMiddle = wrap.scrollTop / 2 + wrap.clientHeight / 2 - wrap.clientHeight / rowNum
|
|
|
|
|
+ }
|
|
|
|
|
+ wrap.scrollTop = this.scrollbarMiddle
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ render (h) {
|
|
|
|
|
+ const gutter = scrollbarWidth()
|
|
|
|
|
+ let style = this.wrapStyle
|
|
|
|
|
+
|
|
|
|
|
+ if (gutter) {
|
|
|
|
|
+ const gutterWith = `-${gutter}px`
|
|
|
|
|
+ let gutterStyle = ''
|
|
|
|
|
+
|
|
|
|
|
+ let overFlowXStr = ''
|
|
|
|
|
+ let overflowYStr = ''
|
|
|
|
|
+ if (style && style.length > 0) {
|
|
|
|
|
+ style.forEach((styleItem) => {
|
|
|
|
|
+ if (styleItem['overflow-x'] || styleItem.overflowX) {
|
|
|
|
|
+ overFlowXStr = styleItem['overflow-x'] || styleItem.overflowX
|
|
|
|
|
+ }
|
|
|
|
|
+ if (styleItem['overflow-y'] || styleItem.overflowY) {
|
|
|
|
|
+ overflowYStr = styleItem['overflow-y'] || styleItem.overflowY
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (overFlowXStr === 'hidden') {
|
|
|
|
|
+ gutterStyle = `margin-right: ${gutterWith};`
|
|
|
|
|
+ } else {
|
|
|
|
|
+ gutterStyle = `margin-bottom: ${gutterWith}; margin-right: ${gutterWith};`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (Array.isArray(this.wrapStyle)) {
|
|
|
|
|
+ style = toObject(this.wrapStyle)
|
|
|
|
|
+ if (overFlowXStr !== 'hidden') {
|
|
|
|
|
+ style.marginBottom = gutterWith
|
|
|
|
|
+ }
|
|
|
|
|
+ if (overflowYStr !== 'hidden') {
|
|
|
|
|
+ style.marginRight = gutterWith
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (typeof this.wrapStyle === 'string') {
|
|
|
|
|
+ style += gutterStyle
|
|
|
|
|
+ } else {
|
|
|
|
|
+ style = gutterStyle
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const view = h(
|
|
|
|
|
+ this.tag,
|
|
|
|
|
+ {
|
|
|
|
|
+ class: ['ui-scrollbar__view', this.viewClass],
|
|
|
|
|
+ style: this.viewStyle,
|
|
|
|
|
+ ref: 'resize'
|
|
|
|
|
+ },
|
|
|
|
|
+ this.$slots.default
|
|
|
|
|
+ )
|
|
|
|
|
+ const wrap = (
|
|
|
|
|
+ <div
|
|
|
|
|
+ ref="wrap"
|
|
|
|
|
+ style={style}
|
|
|
|
|
+ onScroll={this.handleScroll}
|
|
|
|
|
+ class={[this.wrapClass, 'ui-scrollbar__wrap', gutter ? '' : 'ui-scrollbar__wrap--hidden-default']}
|
|
|
|
|
+ >{[view]}</div>
|
|
|
|
|
+ )
|
|
|
|
|
+ let nodes
|
|
|
|
|
+
|
|
|
|
|
+ if (!this.native) {
|
|
|
|
|
+ nodes = [
|
|
|
|
|
+ wrap,
|
|
|
|
|
+ <Bar move={this.moveX} size={this.sizeWidth}></Bar>,
|
|
|
|
|
+ <Bar vertical move={this.moveY} size={this.sizeHeight}></Bar>
|
|
|
|
|
+ ]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ nodes = [
|
|
|
|
|
+ <div ref="wrap" class={[this.wrapClass, 'ui-scrollbar__wrap']} style={style}>{[view]}</div>
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ return h('div', { class: ['ui-scrollbar', { 'child-over-width': !this.childOverWidth }] }, nodes)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+</script>
|