@rxdrag/website-lib 0.0.133 → 0.0.134
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/Background.astro +114 -109
- package/components/Image.astro +20 -15
- package/components/Video.astro +2 -3
- package/package.json +5 -5
- package/components/Image.type.ts +0 -242
- package/components/Picture.astro +0 -35
|
@@ -1,109 +1,114 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { BackgroundConfig, Locals } from "@rxdrag/website-lib-core";
|
|
3
|
-
import {
|
|
4
|
-
BackgroundVideoPlayer,
|
|
5
|
-
BackgroundHlsVideoPlayer,
|
|
6
|
-
Entify,
|
|
7
|
-
} from "@rxdrag/website-lib-core";
|
|
8
|
-
import
|
|
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
|
-
if (background.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const isHls =
|
|
61
|
-
background.type === "video" &&
|
|
62
|
-
(media?.storageType === "cloudflare_stream" ||
|
|
63
|
-
videoUrl?.endsWith(".m3u8") ||
|
|
64
|
-
videoUrl?.includes("cloudflarestream.com"));
|
|
65
|
-
|
|
66
|
-
// 预计算 Spline URL
|
|
67
|
-
const splineUrl = background.type === "spline" ? background.url : undefined;
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
{background.type === "color" && <div class={className} />}
|
|
71
|
-
|
|
72
|
-
{background.type === "blur" && <div class={className} />}
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
background.type === "image" && background.src && (
|
|
76
|
-
<Image
|
|
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
|
-
|
|
1
|
+
---
|
|
2
|
+
import type { BackgroundConfig, Locals } from "@rxdrag/website-lib-core";
|
|
3
|
+
import {
|
|
4
|
+
BackgroundVideoPlayer,
|
|
5
|
+
BackgroundHlsVideoPlayer,
|
|
6
|
+
Entify,
|
|
7
|
+
} from "@rxdrag/website-lib-core";
|
|
8
|
+
import Image from "./Image.astro";
|
|
9
|
+
|
|
10
|
+
export interface Props {
|
|
11
|
+
background: BackgroundConfig;
|
|
12
|
+
index?: number;
|
|
13
|
+
defaultClass?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { env, imageSizes } = Astro.locals as Locals;
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
background,
|
|
20
|
+
defaultClass = "absolute top-0 left-0 w-full h-full object-cover",
|
|
21
|
+
} = Astro.props;
|
|
22
|
+
|
|
23
|
+
// 安全地提取 mediaRef 和 posterRef(只有部分背景类型有这些属性)
|
|
24
|
+
const mediaRef = background.type === "video" ? background.mediaRef : undefined;
|
|
25
|
+
|
|
26
|
+
const rx = Entify.getInstance(env, imageSizes);
|
|
27
|
+
|
|
28
|
+
// 获取媒体数据
|
|
29
|
+
const media = rx ? await rx.getMedia(mediaRef) : undefined;
|
|
30
|
+
|
|
31
|
+
// 构建 className(同时支持 class 和 className)
|
|
32
|
+
const className = [defaultClass, background.class].filter(Boolean).join(" ");
|
|
33
|
+
|
|
34
|
+
// 预计算视频相关数据
|
|
35
|
+
const videoUrl =
|
|
36
|
+
background.type === "video" ? media?.file?.original : undefined;
|
|
37
|
+
|
|
38
|
+
// 处理视频封面
|
|
39
|
+
let posterUrl: string | undefined;
|
|
40
|
+
if (background.type === "video" && background.poster) {
|
|
41
|
+
if (typeof background.poster === "string") {
|
|
42
|
+
posterUrl = background.poster;
|
|
43
|
+
} else if (typeof background.poster === "object") {
|
|
44
|
+
const poster = background.poster as Partial<ImageMetadata>;
|
|
45
|
+
if (
|
|
46
|
+
typeof poster.src === "string" &&
|
|
47
|
+
typeof poster.width === "number" &&
|
|
48
|
+
typeof poster.height === "number"
|
|
49
|
+
) {
|
|
50
|
+
posterUrl = poster.src;
|
|
51
|
+
} else {
|
|
52
|
+
console.log(
|
|
53
|
+
"[Background] invalid video poster, expect ImageMetadata:",
|
|
54
|
+
background.poster,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const isHls =
|
|
61
|
+
background.type === "video" &&
|
|
62
|
+
(media?.storageType === "cloudflare_stream" ||
|
|
63
|
+
videoUrl?.endsWith(".m3u8") ||
|
|
64
|
+
videoUrl?.includes("cloudflarestream.com"));
|
|
65
|
+
|
|
66
|
+
// 预计算 Spline URL
|
|
67
|
+
const splineUrl = background.type === "spline" ? background.url : undefined;
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
{background.type === "color" && <div class={className} />}
|
|
71
|
+
|
|
72
|
+
{background.type === "blur" && <div class={className} />}
|
|
73
|
+
|
|
74
|
+
{
|
|
75
|
+
background.type === "image" && background.src && (
|
|
76
|
+
<Image
|
|
77
|
+
class={className}
|
|
78
|
+
src={background.src}
|
|
79
|
+
layout={background.layout || "full-width"}
|
|
80
|
+
alt=""
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
{
|
|
86
|
+
background.type === "svg" && background.code && (
|
|
87
|
+
<div class={className} set:html={background.code} />
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
{
|
|
92
|
+
background.type === "video" &&
|
|
93
|
+
(isHls ? (
|
|
94
|
+
<BackgroundHlsVideoPlayer
|
|
95
|
+
className={className}
|
|
96
|
+
src={videoUrl}
|
|
97
|
+
poster={posterUrl}
|
|
98
|
+
client:load
|
|
99
|
+
/>
|
|
100
|
+
) : (
|
|
101
|
+
<BackgroundVideoPlayer
|
|
102
|
+
className={className}
|
|
103
|
+
src={videoUrl}
|
|
104
|
+
poster={posterUrl}
|
|
105
|
+
client:load
|
|
106
|
+
/>
|
|
107
|
+
))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
{
|
|
111
|
+
background.type === "spline" && (
|
|
112
|
+
<iframe class={className} src={splineUrl} title="Spline 3D Scene" />
|
|
113
|
+
)
|
|
114
|
+
}
|
package/components/Image.astro
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { Image as AstroImage, Picture as AstroPicture } from "astro:assets";
|
|
3
|
-
import { type ImageProps, SIZES_MAP } from "
|
|
3
|
+
import { type ImageProps, SIZES_MAP } from "@rxdrag/website-lib-core";
|
|
4
4
|
|
|
5
5
|
interface Props extends ImageProps {}
|
|
6
6
|
|
|
@@ -14,13 +14,15 @@ const {
|
|
|
14
14
|
loading,
|
|
15
15
|
priority,
|
|
16
16
|
quality,
|
|
17
|
-
|
|
17
|
+
widths,
|
|
18
|
+
sizes,
|
|
19
|
+
sizeHint = "auto",
|
|
18
20
|
class: className,
|
|
19
21
|
...restProps
|
|
20
|
-
} = Astro.props;
|
|
22
|
+
} = Astro.props as Props;
|
|
21
23
|
|
|
22
|
-
//
|
|
23
|
-
const actualSizes = SIZES_MAP[
|
|
24
|
+
// 优先使用原生 sizes,否则用 sizeHint 映射
|
|
25
|
+
const actualSizes = sizes ?? SIZES_MAP[sizeHint];
|
|
24
26
|
|
|
25
27
|
// 判断是否使用 Picture(有 formats 时使用)
|
|
26
28
|
const usePicture = formats && formats.length > 0;
|
|
@@ -32,13 +34,13 @@ const isStringSrc = typeof src === "string";
|
|
|
32
34
|
if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
|
|
33
35
|
throw new Error(
|
|
34
36
|
`Image src="${src}" 是本地资产路径,必须使用 import 导入。\n` +
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
`正确用法:\n` +
|
|
38
|
+
` import myImage from "${src}";\n` +
|
|
39
|
+
` <Image src={myImage} />`
|
|
38
40
|
);
|
|
39
41
|
}
|
|
40
|
-
---
|
|
41
42
|
|
|
43
|
+
---
|
|
42
44
|
{
|
|
43
45
|
isStringSrc ? (
|
|
44
46
|
<img
|
|
@@ -46,7 +48,8 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
|
|
|
46
48
|
alt={alt}
|
|
47
49
|
width={width}
|
|
48
50
|
height={height}
|
|
49
|
-
loading={loading}
|
|
51
|
+
loading={priority ? "eager" : loading}
|
|
52
|
+
fetchpriority={priority ? "high" : undefined}
|
|
50
53
|
class={className}
|
|
51
54
|
{...restProps}
|
|
52
55
|
/>
|
|
@@ -59,9 +62,10 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
|
|
|
59
62
|
formats={formats}
|
|
60
63
|
quality={quality}
|
|
61
64
|
loading={priority ? "eager" : loading}
|
|
62
|
-
sizes={actualSizes}
|
|
65
|
+
sizes={layout === "none" ? undefined : actualSizes}
|
|
66
|
+
widths={layout === "none" ? undefined : widths}
|
|
63
67
|
class={className}
|
|
64
|
-
{...(layout ? { layout } : {})}
|
|
68
|
+
{...(layout && layout !== "none" ? { layout } : {})}
|
|
65
69
|
{...restProps}
|
|
66
70
|
/>
|
|
67
71
|
) : (
|
|
@@ -71,11 +75,12 @@ if (isStringSrc && (src.startsWith("/src/") || src.startsWith("src/"))) {
|
|
|
71
75
|
width={width}
|
|
72
76
|
height={height}
|
|
73
77
|
quality={quality}
|
|
74
|
-
loading={loading}
|
|
75
|
-
sizes={actualSizes}
|
|
78
|
+
loading={priority ? "eager" : loading}
|
|
79
|
+
sizes={layout === "none" ? undefined : actualSizes}
|
|
80
|
+
widths={layout === "none" ? undefined : widths}
|
|
76
81
|
class={className}
|
|
77
82
|
{...(priority ? { priority } : {})}
|
|
78
|
-
{...(layout ? { layout } : {})}
|
|
83
|
+
{...(layout && layout !== "none" ? { layout } : {})}
|
|
79
84
|
{...restProps}
|
|
80
85
|
/>
|
|
81
86
|
)
|
package/components/Video.astro
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
type Locals,
|
|
6
6
|
} from "@rxdrag/website-lib-core";
|
|
7
7
|
import { getImage } from "astro:assets";
|
|
8
|
-
import type { ImageMetadata } from "astro";
|
|
9
8
|
|
|
10
9
|
const { env, imageSizes } = Astro.locals as Locals;
|
|
11
10
|
|
|
@@ -47,8 +46,8 @@ if (poster) {
|
|
|
47
46
|
if (typeof poster === "string") {
|
|
48
47
|
posterUrl = poster;
|
|
49
48
|
} else {
|
|
50
|
-
const posterImage = await getImage({ src: poster });
|
|
51
|
-
posterUrl = posterImage.src;
|
|
49
|
+
//const posterImage = await getImage({ src: poster });
|
|
50
|
+
posterUrl = poster.src; //;posterImage.src;
|
|
52
51
|
}
|
|
53
52
|
}
|
|
54
53
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rxdrag/website-lib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.134",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@types/aos": "^3.0.7",
|
|
23
23
|
"@types/react": "^19.1.0",
|
|
24
24
|
"@types/react-dom": "^19.1.0",
|
|
25
|
-
"astro": "^
|
|
25
|
+
"astro": "^5.16.6",
|
|
26
26
|
"eslint": "^9.39.2",
|
|
27
27
|
"gsap": "^3.12.7",
|
|
28
28
|
"typescript": "^5",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"react": "^19.1.0",
|
|
39
39
|
"react-dom": "^19.1.0",
|
|
40
40
|
"vanilla-cookieconsent": "3.1.0",
|
|
41
|
-
"@rxdrag/
|
|
42
|
-
"@rxdrag/
|
|
41
|
+
"@rxdrag/website-lib-core": "0.0.123",
|
|
42
|
+
"@rxdrag/rxcms-models": "0.3.98"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"astro": "^
|
|
45
|
+
"astro": "^5.16.6"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"lint": "eslint . --ext .ts,tsx",
|
package/components/Image.type.ts
DELETED
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import type { HTMLAttributes } from "astro/types";
|
|
2
|
-
import type { ImageMetadata } from "astro";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 响应式布局模式(Astro 原生值)
|
|
6
|
-
* @see https://docs.astro.build/zh-cn/guides/images/
|
|
7
|
-
*/
|
|
8
|
-
export type ImageLayout =
|
|
9
|
-
| "responsive" // 跟随父容器,最大不超过原图(推荐)
|
|
10
|
-
| "full-width" // 填满父容器(100% 宽度)
|
|
11
|
-
| "fixed" // 固定尺寸,不响应
|
|
12
|
-
| "none"; // 不使用响应式
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 图片输出格式
|
|
16
|
-
*/
|
|
17
|
-
export type ImageFormat = "webp" | "avif" | "png" | "jpg" | "gif" | "svg";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* 图片在页面中的布局位置(用于优化下载尺寸)
|
|
21
|
-
*/
|
|
22
|
-
export type ImageSizePreset =
|
|
23
|
-
| "auto" // 自动处理(让 layout 决定)
|
|
24
|
-
| "full" // 全宽内容
|
|
25
|
-
| "half" // 两栏布局(约 50%)
|
|
26
|
-
| "third" // 三栏布局(约 33%)
|
|
27
|
-
| "quarter" // 四栏布局(约 25%)
|
|
28
|
-
| "sidebar"; // 侧边栏内图片
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 布局预设 → sizes 属性映射
|
|
32
|
-
*/
|
|
33
|
-
export const SIZES_MAP: Record<ImageSizePreset, string | undefined> = {
|
|
34
|
-
auto: undefined,
|
|
35
|
-
full: "100vw",
|
|
36
|
-
half: "(max-width: 768px) 100vw, 50vw",
|
|
37
|
-
third: "(max-width: 768px) 100vw, 33vw",
|
|
38
|
-
quarter: "(max-width: 768px) 100vw, 25vw",
|
|
39
|
-
sidebar: "(max-width: 768px) 100vw, 300px",
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* ============================================================
|
|
44
|
-
* 图片组件属性
|
|
45
|
-
* ============================================================
|
|
46
|
-
*
|
|
47
|
-
* 设计原则:
|
|
48
|
-
* 1. Props 只处理图片核心功能(来源、加载、优化)
|
|
49
|
-
* 2. 样式相关属性通过 Tailwind class 设置
|
|
50
|
-
*
|
|
51
|
-
* ============================================================
|
|
52
|
-
* 样式设置指南(通过 class 属性使用 Tailwind)
|
|
53
|
-
* ============================================================
|
|
54
|
-
*
|
|
55
|
-
* 【尺寸】
|
|
56
|
-
* - 宽度:w-full, w-1/2, w-64, w-[300px]
|
|
57
|
-
* - 高度:h-auto, h-64, h-screen, h-[200px]
|
|
58
|
-
* - 最大宽度:max-w-md, max-w-lg, max-w-full
|
|
59
|
-
*
|
|
60
|
-
* 【宽高比】
|
|
61
|
-
* - 正方形:aspect-square
|
|
62
|
-
* - 视频:aspect-video (16:9)
|
|
63
|
-
* - 自定义:aspect-[4/3], aspect-[3/2]
|
|
64
|
-
*
|
|
65
|
-
* 【填充模式】
|
|
66
|
-
* - 裁剪填满:object-cover(推荐用于背景图、卡片图)
|
|
67
|
-
* - 完整显示:object-contain(推荐用于 logo)
|
|
68
|
-
* - 拉伸填满:object-fill
|
|
69
|
-
* - 原始尺寸:object-none
|
|
70
|
-
*
|
|
71
|
-
* 【位置(配合 object-cover/contain 使用)】
|
|
72
|
-
* - 居中:object-center(默认)
|
|
73
|
-
* - 顶部:object-top
|
|
74
|
-
* - 底部:object-bottom
|
|
75
|
-
* - 左右:object-left, object-right
|
|
76
|
-
*
|
|
77
|
-
* 【圆角】
|
|
78
|
-
* - 小:rounded-sm
|
|
79
|
-
* - 中:rounded-md, rounded-lg
|
|
80
|
-
* - 大:rounded-xl, rounded-2xl
|
|
81
|
-
* - 圆形:rounded-full
|
|
82
|
-
*
|
|
83
|
-
* 【阴影】
|
|
84
|
-
* - 小:shadow-sm
|
|
85
|
-
* - 中:shadow, shadow-md
|
|
86
|
-
* - 大:shadow-lg, shadow-xl
|
|
87
|
-
*
|
|
88
|
-
* 【边框】
|
|
89
|
-
* - 细边框:border
|
|
90
|
-
* - 边框颜色:border-gray-200, border-white
|
|
91
|
-
*
|
|
92
|
-
* 【滤镜效果】
|
|
93
|
-
* - 灰度:grayscale
|
|
94
|
-
* - 模糊:blur-sm, blur-md
|
|
95
|
-
* - 亮度:brightness-50, brightness-150
|
|
96
|
-
* - 悬停效果:hover:scale-105, hover:brightness-110
|
|
97
|
-
*
|
|
98
|
-
* 使用示例:
|
|
99
|
-
* ```astro
|
|
100
|
-
* <Image
|
|
101
|
-
* src="/hero.jpg"
|
|
102
|
-
* alt="英雄图"
|
|
103
|
-
* class="w-full aspect-video object-cover rounded-lg shadow-lg"
|
|
104
|
-
* />
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
export interface ImageProps extends Omit<HTMLAttributes<"img">, "src" | "width" | "height"> {
|
|
108
|
-
/**
|
|
109
|
-
* 图片来源
|
|
110
|
-
* - 本地图片:使用 import 导入
|
|
111
|
-
* - 远程图片:使用完整 URL
|
|
112
|
-
* - CMS 图片:使用媒体库路径
|
|
113
|
-
*/
|
|
114
|
-
src: string | ImageMetadata;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* 图片宽度
|
|
118
|
-
* - 本地图片可自动推断,可不填
|
|
119
|
-
* - 本地图片使用 layout="fixed" 时必须填写
|
|
120
|
-
* - 远程图片不需要填写,用 Tailwind 样式代替
|
|
121
|
-
*/
|
|
122
|
-
width?: number;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 图片高度
|
|
126
|
-
* - 本地图片可自动推断,可不填
|
|
127
|
-
* - 本地图片使用 layout="fixed" 时必须填写
|
|
128
|
-
* - 远程图片不需要填写,用 Tailwind 样式代替
|
|
129
|
-
*/
|
|
130
|
-
height?: number;
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 图片描述(用于无障碍访问和 SEO)
|
|
134
|
-
* - 内容图片:填写描述文字
|
|
135
|
-
* - 装饰性图片:设为空字符串 alt="",屏幕阅读器会忽略
|
|
136
|
-
*/
|
|
137
|
-
alt?: string;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* 响应式布局模式(Astro 原生值)
|
|
141
|
-
* - responsive: 跟随容器宽度,最大不超过原图尺寸(推荐)
|
|
142
|
-
* - full-width: 填满父容器(100% 宽度)
|
|
143
|
-
* - fixed: 固定尺寸,不响应屏幕变化
|
|
144
|
-
* - none: 不使用 Astro 响应式处理
|
|
145
|
-
*/
|
|
146
|
-
layout?: ImageLayout;
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* 输出格式(用于 Picture 组件)
|
|
150
|
-
* - 浏览器会自动选择最优格式
|
|
151
|
-
* - 推荐:['avif', 'webp'] 获得最佳压缩
|
|
152
|
-
* @example ['avif', 'webp']
|
|
153
|
-
*/
|
|
154
|
-
formats?: ImageFormat[];
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* 加载策略
|
|
158
|
-
* - lazy: 延迟加载,首屏外的图片使用
|
|
159
|
-
* - eager: 立即加载,首屏图片使用
|
|
160
|
-
* @default "lazy"
|
|
161
|
-
*/
|
|
162
|
-
loading?: "lazy" | "eager";
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* 是否为重要图片(LCP 优化,Astro 原生支持)
|
|
166
|
-
* - 设为 true 会自动设置 loading="eager" 和 fetchpriority="high"
|
|
167
|
-
* - 仅对首屏最大的图片使用
|
|
168
|
-
*/
|
|
169
|
-
priority?: boolean;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* 图片质量(1-100)
|
|
173
|
-
* - 数值越高质量越好,文件越大
|
|
174
|
-
* - 推荐 80-90 平衡质量和大小
|
|
175
|
-
* 备注:这个可以先不提供可视化支持,小白理解不了
|
|
176
|
-
* @default 80
|
|
177
|
-
*/
|
|
178
|
-
quality?: number;
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* 图片在页面中的布局位置(用于优化下载尺寸)
|
|
182
|
-
* - auto: 自动处理(推荐,让 layout 决定)
|
|
183
|
-
* - full: 全宽内容
|
|
184
|
-
* - half: 两栏布局
|
|
185
|
-
* - third: 三栏布局
|
|
186
|
-
* - quarter: 四栏布局
|
|
187
|
-
* - sidebar: 侧边栏内图片
|
|
188
|
-
* @default "auto"
|
|
189
|
-
*/
|
|
190
|
-
sizes?: ImageSizePreset;
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* 样式类(使用 Tailwind)
|
|
194
|
-
*
|
|
195
|
-
* 【避免布局偏移】
|
|
196
|
-
* 设置宽高比可预留空间,防止图片加载时页面跳动:
|
|
197
|
-
* - aspect-square(1:1)
|
|
198
|
-
* - aspect-video(16:9)
|
|
199
|
-
* - aspect-[4/3]、aspect-[3/2] 等自定义比例
|
|
200
|
-
*
|
|
201
|
-
* 【常用宽高比】
|
|
202
|
-
* - 1:1 - 正方形,电商产品、头像、社交卡片
|
|
203
|
-
* - 4:3 - 传统屏幕,博客卡片、新闻缩略图
|
|
204
|
-
* - 3:2 - 单反照片,摄影作品、画廊
|
|
205
|
-
* - 16:9 - 宽屏视频,英雄图、视频封面
|
|
206
|
-
* - 21:9 - 超宽屏,电影海报、横幅广告
|
|
207
|
-
* - 4:5 - 竖版,Instagram、社交媒体
|
|
208
|
-
* - 9:16 - 竖屏视频,Stories、短视频封面
|
|
209
|
-
*
|
|
210
|
-
* 【快捷模板】
|
|
211
|
-
* - 英雄图:w-full aspect-video object-cover
|
|
212
|
-
* - 博客卡片:w-full aspect-[16/9] object-cover rounded-lg
|
|
213
|
-
* - 新闻卡片:w-full aspect-[4/3] object-cover rounded-lg
|
|
214
|
-
* - 电商卡片:w-full aspect-square object-cover rounded-lg
|
|
215
|
-
* - 社交卡片:w-full aspect-[4/5] object-cover rounded-lg
|
|
216
|
-
* - 缩略图:w-32 aspect-square object-cover rounded
|
|
217
|
-
* - 头像(小):w-8 h-8 object-cover rounded-full
|
|
218
|
-
* - 头像(中):w-12 h-12 object-cover rounded-full
|
|
219
|
-
* - 头像(大):w-16 h-16 object-cover rounded-full
|
|
220
|
-
* - Logo:h-8 w-auto object-contain
|
|
221
|
-
* - 产品图:w-full aspect-square object-contain bg-white
|
|
222
|
-
* - 背景图:w-full h-full object-cover absolute inset-0
|
|
223
|
-
* - 画廊图:w-full aspect-[3/2] object-cover hover:scale-105 transition
|
|
224
|
-
* - 横幅广告:w-full aspect-[21/9] object-cover
|
|
225
|
-
*
|
|
226
|
-
* 【常用样式】
|
|
227
|
-
* - 尺寸:w-full, w-1/2, w-32, h-64, max-w-lg, max-w-screen-xl
|
|
228
|
-
* - 填充:object-cover(裁剪填满), object-contain(完整显示), object-fill(拉伸)
|
|
229
|
-
* - 位置:object-center, object-top, object-bottom, object-left, object-right
|
|
230
|
-
* - 圆角:rounded-sm, rounded, rounded-md, rounded-lg, rounded-xl, rounded-full
|
|
231
|
-
* - 阴影:shadow-sm, shadow, shadow-md, shadow-lg, shadow-xl
|
|
232
|
-
* - 边框:border, border-2, border-white, border-gray-200
|
|
233
|
-
* - 滤镜:grayscale, blur-sm, brightness-75, contrast-125
|
|
234
|
-
* - 过渡:transition, hover:scale-105, hover:brightness-110, hover:shadow-xl
|
|
235
|
-
*
|
|
236
|
-
* @example "w-full aspect-video object-cover rounded-lg"
|
|
237
|
-
*/
|
|
238
|
-
class?: string;
|
|
239
|
-
//兼容react组件
|
|
240
|
-
className?: string;
|
|
241
|
-
}
|
|
242
|
-
|
package/components/Picture.astro
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { HTMLAttributes } from "astro/types";
|
|
3
|
-
|
|
4
|
-
type PictureSource = {
|
|
5
|
-
srcset: string;
|
|
6
|
-
media?: string;
|
|
7
|
-
type?: string;
|
|
8
|
-
sizes?: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
interface Props extends HTMLAttributes<"picture"> {
|
|
12
|
-
className?: string;
|
|
13
|
-
sources?: PictureSource[];
|
|
14
|
-
imgProps: Omit<HTMLAttributes<"img">, "src"> & {
|
|
15
|
-
src: string;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const {
|
|
20
|
-
className,
|
|
21
|
-
sources,
|
|
22
|
-
imgProps,
|
|
23
|
-
class: originalClass,
|
|
24
|
-
...rest
|
|
25
|
-
} = Astro.props;
|
|
26
|
-
|
|
27
|
-
const sourceList = sources ?? [];
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
<picture class:list={["picture-block", className, originalClass]} {...rest}>
|
|
31
|
-
{sourceList.map((source) => (
|
|
32
|
-
<source {...source} />
|
|
33
|
-
))}
|
|
34
|
-
<img {...imgProps} />
|
|
35
|
-
</picture>
|