index.uts 4.1 KB
Newer Older
DCloud-yyl's avatar
DCloud-yyl 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 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 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
import {
  resolveComponentInstance,
  extend,
  isFunction,
  getCurrentPageVm,
  getPageIdByVm,
  addIntersectionObserver,
  removeIntersectionObserver,
} from '@dcloudio/uni-runtime'
import { CreateIntersectionObserver, CreateIntersectionObserverOptions, ObserveCallback, IntersectionObserver } from '../interface.uts'

export { CreateIntersectionObserver, CreateIntersectionObserverOptions, ObserveCallback, IntersectionObserver }

interface AddIntersectionObserverArgs {
  reqId: number
  component: ComponentPublicInstance
  options: ServiceIntersectionObserverOptions
  callback: ObserveCallback
}

interface RemoveIntersectionObserverArgs {
  reqId: number
  component: ComponentPublicInstance
}

interface RequestComponentObserverOptions {
  selector?: string
  rootMargin?: string
  relativeToSelector?: string
}

interface ServiceIntersectionObserverOptions extends CreateIntersectionObserverOptions, RequestComponentObserverOptions { }

interface Margins {
  /** 节点布局区域的下边界 */
  bottom?: number,
  /** 节点布局区域的左边界 */
  left?: number,
  /** 节点布局区域的右边界 */
  right?: number,
  /** 节点布局区域的上边界 */
  top?: number
}

const defaultOptions = {
  thresholds: [0],
  initialRatio: 0,
  observeAll: false,
} as CreateIntersectionObserverOptions

let reqComponentObserverId = 1

function normalizeRootMargin(margins: Margins | null = {}) {
  if (!margins) margins = {}
  const top = Number(margins.top) || 0
  const right = Number(margins.right) || 0
  const bottom = Number(margins.bottom) || 0
  const left = Number(margins.left) || 0
  return `${top}px ${right}px ${bottom}px ${left}px`
}
class ServiceIntersectionObserver {
  private _reqId?: number
  private _pageId: number
  private _component: ComponentPublicInstance
  private _options: ServiceIntersectionObserverOptions
  constructor(
    component: ComponentPublicInstance,
    options?: CreateIntersectionObserverOptions
  ) {
    this._pageId = getPageIdByVm(component)!
    this._component = component
    if (options) {
      if (typeof options.thresholds === 'undefined') options.thresholds = defaultOptions.thresholds
      if (typeof options.initialRatio === 'undefined') options.initialRatio = defaultOptions.initialRatio
      if (typeof options.observeAll === 'undefined') options.observeAll = defaultOptions.observeAll
    }
    this._options = (options ?? defaultOptions) as ServiceIntersectionObserverOptions
  }

  relativeTo(selector: string, margins?: Margins) {
    this._options.relativeToSelector = selector
    this._options.rootMargin = normalizeRootMargin(margins)
    return this
  }

  relativeToViewport(margins?: Margins) {
    this._options.relativeToSelector = undefined
    this._options.rootMargin = normalizeRootMargin(margins)
    return this
  }

  observe(
    selector: string,
    callback: ObserveCallback
  ) {
    if (!isFunction(callback)) {
      return
    }
    this._options.selector = selector
    this._reqId = reqComponentObserverId++
    addIntersectionObserver(
      {
        reqId: this._reqId,
        component: this._component,
        options: this._options,
        callback,
      } as AddIntersectionObserverArgs,
      this._pageId
    )
  }

  disconnect() {
    this._reqId &&
      removeIntersectionObserver(
        { reqId: this._reqId, component: this._component } as RemoveIntersectionObserverArgs,
        this._pageId
      )
  }
}
export const createIntersectionObserver = defineSyncApi<IntersectionObserver>(
  'createIntersectionObserver',
  (context: ComponentPublicInstance | null, options?: CreateIntersectionObserverOptions) => {
    let _options: ComponentPublicInstance | CreateIntersectionObserverOptions | null = options
    context = resolveComponentInstance(context)
    if (context && !getPageIdByVm(context)) {
      _options = context
      context = null
    }
    if (context) {
      return new ServiceIntersectionObserver(context as ComponentPublicInstance, _options as CreateIntersectionObserverOptions)
    }
    return new ServiceIntersectionObserver(getCurrentPageVm()!, _options as CreateIntersectionObserverOptions)
  }
) as CreateIntersectionObserver