style: 更新UI样式和字体设置
This commit is contained in:
@ -1,6 +1,11 @@
|
||||
<template>
|
||||
<div ref="waterfallWrapper" class="waterfall-list" :style="{ height: `${wrapperHeight}px` }">
|
||||
<div v-for="(item, index) in list" :key="getKey(item, index)" :style="{height:`${colWidth * item.height / item.width}px`}" class="waterfall-item">
|
||||
<div ref="waterfallWrapper" class="waterfall-list" :style="wrapperStyle">
|
||||
<div
|
||||
v-for="(item, index) in list"
|
||||
:key="getKey(item, index)"
|
||||
:style="getItemStyle(index)"
|
||||
class="waterfall-item"
|
||||
>
|
||||
<div class="waterfall-card h-full">
|
||||
<slot name="item" :item="item" :index="index" :url="getRenderURL(item)" />
|
||||
</div>
|
||||
@ -11,10 +16,10 @@
|
||||
<script setup lang="ts">
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import type { PropType } from "vue";
|
||||
import { provide, ref, watch } from "vue";
|
||||
import { computed, provide, useTemplateRef, watch } from "vue";
|
||||
import type { ViewCard } from "../types/waterfall";
|
||||
import { useCalculateCols, useLayout } from "../use";
|
||||
import Lazy from "../utils/Lazy";
|
||||
import { waterfallImageLoadedKey } from "../utils/keys";
|
||||
import { getValue } from "../utils/util";
|
||||
|
||||
const props = defineProps({
|
||||
@ -34,6 +39,14 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 200,
|
||||
},
|
||||
widthSelector: {
|
||||
type: String,
|
||||
default: "width",
|
||||
},
|
||||
heightSelector: {
|
||||
type: String,
|
||||
default: "height",
|
||||
},
|
||||
columns: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
@ -72,7 +85,7 @@ const props = defineProps({
|
||||
},
|
||||
loadProps: {
|
||||
type: Object,
|
||||
default: () => { },
|
||||
default: () => {},
|
||||
},
|
||||
crossOrigin: {
|
||||
type: Boolean,
|
||||
@ -84,62 +97,91 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const lazy = new Lazy(props.lazyload, props.loadProps, props.crossOrigin);
|
||||
provide("lazy", lazy);
|
||||
const waterfallWrapper = useTemplateRef<HTMLElement>("waterfallWrapper");
|
||||
|
||||
// 容器块信息
|
||||
const { waterfallWrapper, wrapperWidth, colWidth, cols, offsetX } =
|
||||
useCalculateCols(props);
|
||||
// 瀹瑰櫒鍧椾俊鎭?
|
||||
const { wrapperWidth, colWidth, cols, offsetX } = useCalculateCols(
|
||||
props,
|
||||
waterfallWrapper
|
||||
);
|
||||
|
||||
// 容器高度,块定位
|
||||
const getNumericValue = (item: ViewCard, selector: string): number | null => {
|
||||
const value = getValue(item, selector)[0];
|
||||
const resolved = Number(value);
|
||||
|
||||
if (!Number.isFinite(resolved) || resolved <= 0) return null;
|
||||
|
||||
return resolved;
|
||||
};
|
||||
|
||||
const getItemHeightByIndex = (index: number): number | null => {
|
||||
const item = props.list[index];
|
||||
if (!item || colWidth.value <= 0) return null;
|
||||
|
||||
const width = getNumericValue(item, props.widthSelector);
|
||||
const height = getNumericValue(item, props.heightSelector);
|
||||
|
||||
if (!width || !height) return null;
|
||||
|
||||
return (colWidth.value * height) / width;
|
||||
};
|
||||
|
||||
const itemHeights = computed(() =>
|
||||
props.list.map((_, index) => getItemHeightByIndex(index))
|
||||
);
|
||||
|
||||
// 瀹瑰櫒楂樺害锛屽潡瀹氫綅
|
||||
const { wrapperHeight, layoutHandle } = useLayout(
|
||||
props,
|
||||
colWidth,
|
||||
cols,
|
||||
offsetX,
|
||||
waterfallWrapper
|
||||
waterfallWrapper,
|
||||
(index, item) => getItemHeightByIndex(index) ?? item.offsetHeight
|
||||
);
|
||||
|
||||
// 1s内最多执行一次排版,减少性能开销
|
||||
const wrapperStyle = computed(() => ({
|
||||
height: `${wrapperHeight.value}px`,
|
||||
backgroundColor: props.backgroundColor,
|
||||
}));
|
||||
|
||||
const getItemStyle = (index: number) => {
|
||||
const height = itemHeights.value[index];
|
||||
if (!height) return undefined;
|
||||
|
||||
return {
|
||||
height: `${height}px`,
|
||||
};
|
||||
};
|
||||
|
||||
// 1s鍐呮渶澶氭墽琛屼竴娆℃帓鐗堬紝鍑忓皯鎬ц兘寮€閿€
|
||||
const renderer = useDebounceFn(() => {
|
||||
layoutHandle();
|
||||
// console.log("强制更新排版");
|
||||
}, props.delay);
|
||||
|
||||
// 列表发生变化直接触发排版
|
||||
watch(
|
||||
() => [wrapperWidth, colWidth, props.list],
|
||||
[wrapperWidth, cols, itemHeights, () => props.list],
|
||||
() => {
|
||||
renderer();
|
||||
},
|
||||
{ deep: true }
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 尺寸宽度变化防抖触发
|
||||
const sizeChangeTime = ref(0);
|
||||
// 鍥剧墖鍔犺浇瀹屾垚
|
||||
provide(waterfallImageLoadedKey, renderer);
|
||||
|
||||
provide("sizeChangeTime", sizeChangeTime);
|
||||
|
||||
// 图片加载完成
|
||||
provide("imgLoaded", renderer);
|
||||
|
||||
// 根据选择器获取图片地址
|
||||
// 鏍规嵁閫夋嫨鍣ㄨ幏鍙栧浘鐗囧湴鍧€
|
||||
const getRenderURL = (item: ViewCard): string => {
|
||||
return getValue(item, props.imgSelector)[0];
|
||||
return String(getValue(item, props.imgSelector)[0] ?? "");
|
||||
};
|
||||
|
||||
// 获取唯一值
|
||||
const getKey = (item: ViewCard, index: number): string => {
|
||||
return item[props.rowKey] || index;
|
||||
// 鑾峰彇鍞竴鍊?
|
||||
const getKey = (item: ViewCard, index: number): string | number => {
|
||||
return item[props.rowKey] ?? index;
|
||||
};
|
||||
|
||||
const clearAndReload = () => {
|
||||
const originalList = [...props.list];
|
||||
props.list.length = 0;
|
||||
setTimeout(() => {
|
||||
props.list.push(...originalList);
|
||||
renderer();
|
||||
}, 0);
|
||||
layoutHandle();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
@ -159,42 +201,53 @@ defineExpose({
|
||||
width: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: v-bind(backgroundColor);
|
||||
--waterfall-radius: 22px;
|
||||
--waterfall-border: rgba(255, 255, 255, 0.72);
|
||||
--waterfall-surface: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0.9),
|
||||
rgba(244, 250, 255, 0.72)
|
||||
);
|
||||
--waterfall-shadow: 0 16px 32px rgba(118, 144, 169, 0.12);
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
/* transition: .3s; */
|
||||
/* 初始位置设置到屏幕以外,避免懒加载失败 */
|
||||
transform: translate3d(0, 3000px, 0);
|
||||
visibility: hidden;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* 初始的入场效果 */
|
||||
@-webkit-keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
.waterfall-card {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--waterfall-border);
|
||||
border-radius: var(--waterfall-radius);
|
||||
background: var(--waterfall-surface);
|
||||
box-shadow: var(--waterfall-shadow);
|
||||
backdrop-filter: blur(14px);
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
.waterfall-card :deep(img) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
opacity: 0.01;
|
||||
transform: translate3d(0, 18px, 0) scale(0.985);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translate3d(0, 0, 0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.fadeIn {
|
||||
-webkit-animation-name: fadeIn;
|
||||
animation-name: fadeIn;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user