UNPKG

isu-elements

Version:

Polymer components for building web apps.

198 lines (179 loc) 5.4 kB
import '@polymer/polymer/polymer-legacy.js' import { html } from '@polymer/polymer/lib/utils/html-tag.js' import { IronFitBehavior } from '@polymer/iron-fit-behavior/iron-fit-behavior.js' import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class' import { PolymerElement } from '@polymer/polymer' class IsuIronFit extends mixinBehaviors([IronFitBehavior], PolymerElement) { static get template () { return html` <style> :host { overflow: hidden; display: inline-block; background-color: white; width: max-content; } </style> <slot></slot> ` } static get properties () { return { scrollDomList: { // 全部滚动条dom type: Array, value () { return [] } }, _raf: { type: Element }, hidden: { type: Boolean, value: false, reflectToAttribute: true }, intersectionObserver: { // 监听是否可见 type: IntersectionObserver }, isVisible: { // 当前是否可见 type: Boolean, value: false } } } /** * 1. 查找全部父级滚动条 * 2. 绑定 Y 与 X 滚动事件(触发更新位置) * @param {Element} e dom */ scrollAddEvent (e) { let parent = e.parentNode || e.getRootNode().host if (parent && parent.toString() === '[object ShadowRoot]') parent = e.getRootNode().host // isu Dialog 组件兼容 const isIsuDialog = parent && parent.tagName === 'ISU-DIALOG' if (isIsuDialog) parent = parent.root.querySelector('.scrollable-container') if (parent) { if (this.ifOverflow(parent)) { // 需要预绑定。如果在_hidden那里监听。事件会有延迟。 this.addEvent(parent) this.push('scrollDomList', parent) } // isu Dialog 组件兼容 isIsuDialog ? this.scrollAddEvent(e.getRootNode().host) : this.scrollAddEvent(parent) } } /** * 绑定触发事件 * @param {Element} e dom * */ addEvent (e) { e.addEventListener('scroll', e => { this.debounce('__scrollOrResizeRefit', this.scrollOrResizeRefit.bind(this), 10) }) e.addEventListener('resize', e => { this.debounce('__scrollOrResizeRefit', this.scrollOrResizeRefit.bind(this), 10) }) } /** * 判断是否有滚动条 * 不用e.scrollHeight > e.clientHeight 是因为我节点设了overflow=auto 内容没超会不显示滚动条。但是我要绑定最近的一级的 * @param {Element} e dom * @return {boolean} 该节点是否有滚动条 * */ ifOverflow (e) { if (e === document) return true const data = window.getComputedStyle(e, null).overflow return data.includes('scroll') || data.includes('auto') } /** * 更新位置 * */ scrollOrResizeRefit () { if (!this.hidden && this.isVisible) { const self = this // 取消动画 this._raf && window.cancelAnimationFrame(this._raf) this._raf = window.requestAnimationFrame(() => { this._raf = null self.fit() }) } } /** * 设置可见 * @param {boolean} isVisibility 是否可见 * */ visibility (isVisibility) { this.style.visibility = isVisibility ? 'visible' : 'hidden' } connectedCallback () { const self = this self.sizingTarget = self.children[0] this.addEvent(window) } static get observers () { return ['_positionTargetChanged(positionTarget)', '_hidden(hidden)'] } /** * 绑定节点发生变化 * @param {Element} e 附着的元素的dom * */ _positionTargetChanged (e) { if (!e) return this._minWidth(e) this.destroy() this.scrollAddEvent(this) this.intersectionObserver = new IntersectionObserver(([entry]) => { if ((entry.isIntersecting || entry.intersectionRatio > 0.0) && !this.isVisible) { this.set('isVisible', true) this.visibility(true) this.scrollOrResizeRefit() } else if (!(entry.isIntersecting || entry.intersectionRatio > 0.0) && this.isVisible) { this.set('isVisible', false) this.visibility(false) } }, { rootMargin: '0px 0px', root: null, threshold: [0, 0.1] }) } /** * 设置最小宽度 * @param {Element} e dom * */ _minWidth (e) { if (!e) return this.style.minWidth = `${e.offsetWidth}px` } /** * 被用hidden隐藏 * 取消监听 * */ _hidden (hidden) { if (hidden === true && this.intersectionObserver) { this.intersectionObserver.unobserve(this.positionTarget) } else if (this.intersectionObserver) { this.intersectionObserver.observe(this.positionTarget) this.fixPosition() } } /** * 销毁全部绑定的事件 * */ destroy () { this.scrollDomList.forEach(function (item) { item.removeEventListener('scroll', e => { this.debounce('__scrollOrResizeRefit', this.scrollOrResizeRefit.bind(this), 10) }, true) item.removeEventListener('resize', e => { this.debounce('__scrollOrResizeRefit', this.scrollOrResizeRefit.bind(this), 10) }, true) }) this.set('scrollDomList', []) if (this.intersectionObserver) { this.intersectionObserver = '' } } /** * 修正位置 * */ fixPosition () { this.refit() this._minWidth(this.positionTarget) } } window.customElements.define('isu-iron-fit', IsuIronFit)