<template>
  <div class="waterfull" ref="waterfull" :style="{ height: listHeight + 'px' }">
    <template v-for="(item, index) in data" :key="index">
      <div class="__list_item__" :style="listStyle[index]">
        <slot name="item" :item="item"></slot>
      </div>
    </template>
  </div>
</template>
<script setup>
import { watch } from 'vue';
import { debounce } from '@/utils/index';
import _ from 'lodash';
const { proxy } = getCurrentInstance();
const emit = defineEmits(['endFun']);
const props = defineProps({
  data: {
    type: Array,
    default: () => [],
  },
  column: {
    type: Number,
    default: () => 6,
  },
});
const { column, data } = toRefs(props);
const column_gap = 20;
const row_gap = 10;
const waterfull = ref(null);
//瀑布流排列样式
const listStyle = ref([]);
//瀑布流总体高度
const listHeight = ref(0);
//瀑布流排列高度计算相关数组
let heightList = new Array(column.value).fill(0);
//开始布局
const layout = async () => {
  await calcListStyle();
  await nextTick();
  setTimeout(() => {
    execWaterfull();
  }, 300);
};
//图片预加载 和初始化listStyle
const calcListStyle = async () => {
  //可能存在padding
  const listWidth = document.defaultView ? parseFloat(document.defaultView.getComputedStyle(waterfull.value).width) : waterfull.value.clientWidth;
  const itemWidth = 250;
  //图片预加载
  const imgsPromise = [];
  listStyle.value = data.value.map((item, index) => {
    if (listStyle.value[index]) {
      if (parseFloat(itemWidth + '') != parseFloat(listStyle.value[index].width)) {
        return {
          ...listStyle.value[index],
          width: itemWidth + 'px',
          loaded: false,
        };
      }
      return listStyle.value[index];
    } else {
      imgsPromise.push(
        new Promise((resolve, reject) => {
          const imgElement = new Image();
          imgElement.src = item.thumb;
          imgElement.onload = () => {
            resolve(true);
          };
          imgElement.onerror = () => {
            reject(false);
          };
        }),
      );
      return {
        width: itemWidth + 'px',
        top: '',
        left: '',
        loaded: false,
        opacity: 0,
      };
    }
  });
  await Promise.all(imgsPromise);
};
//执行瀑布流排序
const execWaterfull = () => {
  const listItemElements = Array.from(document.querySelectorAll('.__list_item__'));
  listItemElements.forEach((element, index) => {
    //已经执行过瀑布流排序的return
    if (listStyle?.value[index]?.loaded) {
      return;
    }
    const elementHeight = element.offsetHeight;

    const _index = heightList.findIndex(item => item == Math.min(...heightList));
    listStyle.value[index] = Object.assign(listStyle.value[index], {
      top: `${heightList[_index]}px`,
      left: `${_index * (parseFloat(listStyle.value[index].width) + column_gap)}px`,
      loaded: true,
      opacity: 1,
    });

    heightList[_index] += elementHeight + row_gap;
    listHeight.value = Math.max(...heightList);
  });
};

function setEmpty() {
  heightList = new Array(column.value).fill(0);
}

defineExpose({ setEmpty });

//当list数据发生变化时，重新布局
watch(() => _.cloneDeep(props.data), layout);
onMounted(() => {
  layout();
  //当窗口大小发生改变时，重新布局
  const resizeCallback = debounce(async () => {
    //listStyle heightList 重置
    heightList = new Array(column.value).fill(0);
    await calcListStyle();
    await nextTick();
    setTimeout(() => {
      execWaterfull();
    }, 300);
  }, 300);
  window.onresize = resizeCallback;
});
</script>
<style scoped lang="scss">
.waterfull {
  //   display: grid;
  //   gap: 20px;
  //   grid-template-columns: repeat(6, 250px);
  position: relative;
  .__list_item__ {
    position: absolute;
    transition: all 0.3s ease;
    animation: show 0.3s ease;
  }
}
@keyframes show {
  0% {
    transform: scale(0.7);
  }
  100% {
    transform: scale(1);
  }
}
</style>
