<template>
  <div ref="scrollRef" class="app-scroll-view">
    <div ref="afterHeaderRef" class="app-scroll-view__header after">
      <slot name="afterHeader"></slot>
    </div>
    <div ref="beforeHeaderRef" class="app-scroll-view__header before">
      <slot name="beforeHeader"></slot>
    </div>
    <slot></slot>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted, onActivated, onDeactivated, watch } from 'vue'

const props = defineProps({
  // 滚动元素名称(用于不同组件缓存滚动条位置)
  scrollName: {
    type: String,
    default: 'default',
  },
  // 滚动条位置
  scrollTop: {
    type: Number,
    default: 0,
  },
})

const emit = defineEmits(['update:scrollTop', 'scroll'])
const scrollRef = ref<HTMLDivElement>()
const beforeHeaderRef = ref<HTMLDivElement>() // 前标题栏
const afterHeaderRef = ref<HTMLDivElement>() // 后标题栏
const scrollMap = new Map<string, number>()

// 设置透明样式
const setOpacity = (style: CSSStyleDeclaration, value: number) => {
  if (value > 1) {
    style.setProperty('opacity', '1')
  } else if (value < 0) {
    style.setProperty('opacity', '0')
    style.setProperty('z-index', '-1')
  } else {
    style.setProperty('opacity', value.toString())
    style.setProperty('z-index', '1')
  }
}

// 监听滚动条位置
const onScroll = (e: Event) => {
  const el = e.target as HTMLDivElement
  const beforeHeaderEl = beforeHeaderRef.value
  const afterHeaderEl = afterHeaderRef.value
  scrollMap.set(props.scrollName, el.scrollTop)

  // 滚动隐藏标题栏
  if (beforeHeaderEl) {
    const { clientHeight, style } = beforeHeaderEl
    const alpha = (clientHeight * 2 - el.scrollTop) / clientHeight
    setOpacity(style, alpha)
  }
  // 滚动显示标题栏
  if (afterHeaderEl) {
    const { clientHeight, style } = afterHeaderEl
    const alpha = (el.scrollTop - clientHeight) / clientHeight
    setOpacity(style, alpha)
  }

  emit('update:scrollTop', el.scrollTop)
  emit('scroll', el)
}

// 设置滚动条位置
const setScrollTop = (callback?: (el: HTMLDivElement) => void) => {
  const el = scrollRef.value
  if (el) {
    el.style.setProperty('opacity', '0')
    el.scrollTop = scrollMap.get(props.scrollName) ?? 0

    window.setTimeout(() => {
      el.style.setProperty('opacity', '1')
      callback && callback(el)
    }, 0)
  }
}

onMounted(() => {
  window.setTimeout(() => {
    const scrollEl = scrollRef.value
    const beforeHeaderEl = beforeHeaderRef.value

    if (scrollEl && beforeHeaderEl) {
      scrollEl.style.setProperty('padding-top', beforeHeaderEl.clientHeight + 'px')
    }
  }, 0)
})

onActivated(() => {
  setScrollTop((el) => {
    el?.addEventListener('scroll', onScroll) // 监听滚动条事件
  })
})

onDeactivated(() => {
  const el = scrollRef.value
  el?.removeEventListener('scroll', onScroll) // 移除滚动条事件
})

watch(() => props.scrollName, () => setScrollTop())
</script>

<style lang="less" scoped>
@import './index.less';
</style>