Skip to content

Commit

Permalink
为了类型检查,换成 script setup
Browse files Browse the repository at this point in the history
动态类型,静态检查,检查个 JB
  • Loading branch information
Dave-12138 committed Sep 5, 2024
1 parent e98d925 commit 778c87f
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 116 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-waterfall-mini",
"version": "1.1.2",
"version": "1.1.3",
"author": {
"name": "Dave_12138",
"email": "[email protected]",
Expand Down
6 changes: 3 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script setup lang="ts">
import { reactive, } from 'vue';
import { reactive } from 'vue';
import { Waterfall } from './main';
import BSInput from './components/BSInput.vue';
const c = Array.from<string>("0123456789ABCDEF")
const d = () => c[(c.length * Math.random()) | 0]
const _createItem = () => ({ id: crypto.randomUUID(), h: 3 + (Math.random() * 30) | 0, c: '#' + d() + d() + d() });
const _createItem = () => ({ id: crypto.randomUUID(), h: (3 + (Math.random() * 30) | 0) + 'rem', c: '#' + d() + d() + d() });
const createItem = () => {
const e = _createItem();
try {
Expand Down Expand Up @@ -91,7 +91,7 @@ const settings = reactive({
<div class=" container">
<Waterfall :list="list" #="{ item, index }" :="props" :style="{ ...settings.style }">
<div class="my-item" @click="list.splice(index, 1)" :style="{ backgroundColor: item.c }">
<div :style="{ height: item.h + 'rem' }">
<div :style="{ height: item.h }">
<div>{{ item[settings.key1] }}</div>
<div v-html="item[settings.key2]"></div>
</div>
Expand Down
209 changes: 97 additions & 112 deletions src/components/Waterfall.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<script lang="ts">
import { computed, defineComponent, provide, reactive, ref, watch } from 'vue';
import type { PropType, CSSProperties } from 'vue';
<script lang="ts" setup>
import { computed, provide, reactive, ref, watch } from 'vue';
import type { CSSProperties } from 'vue';
import { useDebounceFn, useResizeObserver } from "@vueuse/core";
type ColCount = number;
type Item = Record<string, any>;
/**
* @member x 第几列
* @member y 高度,单位是px
Expand All @@ -17,116 +18,100 @@ function toCss(obj: Record<string, any>, forceCustomName: boolean = false): CSSP
.map((k) => ({ [k.replace(prevReg, "--$1").replace(/\B([A-Z])/g, "-$1").toLowerCase()]: obj[k] }))
.reduce((o, p) => Object.assign(o, p), {})
}
type Item = Record<string, any>;
export default defineComponent({
props: {
list: {
type: Array as PropType<Item[]>,
default: () => []
},
breakPoint: {
type: Function as PropType<(wapperWidth: number) => ColCount>,
default: (w: number) => Math.floor(w / 200)
},
rowKey: {
type: Function as PropType<(element: Item) => PropertyKey>,
default: (e: { id: string }) => e.id
},
transition: {
type: Number,
default: 300
},
joinDuration: {
type: Number,
default: 300
},
animate: {
type: String,
default: "fade-in"
}
},
setup(props, { expose }) {
const wtfElement = ref<HTMLDivElement | null>(null);
const wapperWidth = ref<number>(0);
const borderOffset = reactive({ OffsetX: 0, OffsetY: 0, });
useResizeObserver(wtfElement, ([entry]) => {
const { width, top, left } = entry.contentRect;
borderOffset.OffsetX = left;
borderOffset.OffsetY = top;
wapperWidth.value = width;
})
// 有几列(桶)
const colCount = computed(() => Math.max(1, props.breakPoint(wapperWidth.value)));
// 列(桶)宽
const itemFlexWidth = computed(() => Math.floor(wapperWidth.value / colCount.value));
// 所有item高度表
function getItemHeights() {
return props.list.map((_, i) => wtfElement.value?.children[i]?.getBoundingClientRect().height ?? 1 << 28).map(n => Math.round(n));
}
// 位置表
const itemPosList = reactive<Record<string, ItemPos>>({});
// waterfall容器 高度
const wapperHeight = ref(0);
// waterfall容器style
const wtfCss = computed<CSSProperties>(() => ({
height: `${wapperHeight.value}px`,
...toCss({
ItemWidth: itemFlexWidth.value,
Transition: props.transition,
JoinDuration: props.joinDuration,
...borderOffset
}),
}))
const listed = reactive<Record<string, boolean>>({});
/**
* Get String Key 获取key,但是string
* @param item
*/
function gsk(item: Item): string {
return props.rowKey(item)?.toString() ?? props.list.indexOf(item).toString();
}
// 计算所有item位置
function render() {
if (!wapperWidth.value) {
return;
}
// 列(桶)高度
const bucketHeights = Array.from<number>({ length: colCount.value }).fill(0);
// itemPosList.splice(0);
const heights = getItemHeights();
heights.forEach((itemHeight, index) => {
// 当前最短的列(桶)
const minBuckIndex = bucketHeights.reduce((pv, v, i, arr) => arr[pv] > v ? i : pv, 0);
itemPosList[gsk(props.list[index])] = ({ x: minBuckIndex, y: bucketHeights[minBuckIndex] });
bucketHeights[minBuckIndex] += itemHeight;
});
wapperHeight.value = Math.max(...bucketHeights) + borderOffset.OffsetY;
setTimeout(() => {
props.list.forEach((e, i) => {
listed[gsk(e)] = true;
});
}, Math.max(20, props.transition));
}
// 防抖
const rerender = useDebounceFn(render, () => 100);
// 提供给子组件的重排接口
provide("imgLoaded", rerender);
watch([colCount, wapperWidth, () => props.list], () => {
rerender();
}, { deep: true, immediate: false });
function cssPos(item: Item): CSSProperties {
const { x, y } = itemPosList[gsk(item)] ?? { x: 0, y: 9000 };
return toCss({ x, y }, true);
}
function joined(item: Item): boolean {
return listed[gsk(item)] === true;
}
// 仅暴露重排函数
expose<{ rerender: () => void }>({ rerender });
return { wtfElement, wtfCss, cssPos, joined };
}
interface Props {
list: Item[];
breakPoint: (wapperWidth: number) => ColCount;
rowKey: (element: Item) => PropertyKey;
transition: number;
joinDuration: number;
animate: string;
}
const props = withDefaults(defineProps<Props>(), {
list: () => [],
breakPoint: (w: number) => Math.floor(w / 200),
rowKey: (e: Item | { id: PropertyKey }): PropertyKey => e.id,
transition: 300,
joinDuration: 300,
animate: "fade-in"
});
const wtfElement = ref<HTMLDivElement | null>(null);
const wapperWidth = ref<number>(0);
const borderOffset = reactive({ OffsetX: 0, OffsetY: 0, });
useResizeObserver(wtfElement, ([entry]) => {
const { width, top, left } = entry.contentRect;
borderOffset.OffsetX = left;
borderOffset.OffsetY = top;
wapperWidth.value = width;
})
// 有几列(桶)
const colCount = computed(() => Math.max(1, props.breakPoint(wapperWidth.value)));
// 列(桶)宽
const itemFlexWidth = computed(() => Math.floor(wapperWidth.value / colCount.value));
// 所有item高度表
function getItemHeights() {
return props.list.map((_, i) => wtfElement.value?.children[i]?.getBoundingClientRect().height ?? 1 << 28).map(n => Math.round(n));
}
// 位置表
const itemPosList = reactive<Record<string, ItemPos>>({});
// waterfall容器 高度
const wapperHeight = ref(0);
// waterfall容器style
const wtfCss = computed<CSSProperties>(() => ({
height: `${wapperHeight.value}px`,
...toCss({
ItemWidth: itemFlexWidth.value,
Transition: props.transition,
JoinDuration: props.joinDuration,
...borderOffset
}),
}))
const listed = reactive<Record<string, boolean>>({});
/**
* Get String Key 获取key,但是string
* @param item
*/
function gsk(item: Item): string {
return props.rowKey(item)?.toString() ?? props.list.indexOf(item).toString();
}
// 计算所有item位置
function render() {
if (!wapperWidth.value) {
return;
}
// 列(桶)高度
const bucketHeights = Array.from<number>({ length: colCount.value }).fill(0);
// itemPosList.splice(0);
const heights = getItemHeights();
heights.forEach((itemHeight, index) => {
// 当前最短的列(桶)
const minBuckIndex = bucketHeights.reduce((pv, v, i, arr) => arr[pv] > v ? i : pv, 0);
itemPosList[gsk(props.list[index])] = ({ x: minBuckIndex, y: bucketHeights[minBuckIndex] });
bucketHeights[minBuckIndex] += itemHeight;
});
wapperHeight.value = Math.max(...bucketHeights) + borderOffset.OffsetY;
setTimeout(() => {
props.list.forEach((e, i) => {
listed[gsk(e)] = true;
});
}, Math.max(20, props.transition));
}
// 防抖
const rerender = useDebounceFn(render, () => 100);
// 提供给子组件的重排接口
provide("imgLoaded", rerender);
watch([colCount, wapperWidth, () => props.list], () => {
rerender();
}, { deep: true, immediate: false });
function cssPos(item: Item): CSSProperties {
const { x, y } = itemPosList[gsk(item)] ?? { x: 0, y: 9000 };
return toCss({ x, y }, true);
}
function joined(item: Item): boolean {
return listed[gsk(item)] === true;
}
// 仅暴露重排函数
defineExpose<{ rerender: () => void }>({ rerender });
</script>
<template>
<div class="waterfall-list" :style="wtfCss" ref="wtfElement">
Expand Down

0 comments on commit 778c87f

Please sign in to comment.