Skip to content

Image

Image 组件为图片展示提供了更稳健的封装,支持加载态、回退内容、响应式尺寸以及多种图片来源。它基于 GPUI 原生图片能力构建,可处理 URL、本地文件和 SVG 等资源,并便于结合主题与布局系统做统一样式控制。

导入

rust
use gpui::{img, ImageSource, ObjectFit};
use gpui_component::{v_flex, h_flex, div, Icon, IconName};

用法

基础图片

rust
// 来自 URL 的图片
img("https://example.com/image.jpg")

// 本地图片文件
img("assets/logo.png")

// SVG 图片
img("icons/star.svg")

设置尺寸

rust
// 固定尺寸
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))

// 响应式宽度并限制最大宽度
img("https://example.com/banner.jpg")
    .w(relative(1.))
    .max_w(px(800.))
    .h(px(400.))

// 正方形图片
img("https://example.com/avatar.jpg")
    .size(px(100.))

Object Fit 选项

用于控制图片在容器中的缩放与定位方式:

rust
// Cover:填满容器,可能裁剪
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))
    .object_fit(ObjectFit::Cover)

// Contain:完整显示,保持比例
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))
    .object_fit(ObjectFit::Contain)

// Fill:拉伸填满,可能变形
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))
    .object_fit(ObjectFit::Fill)

// ScaleDown:类似 contain,但不会放大
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))
    .object_fit(ObjectFit::ScaleDown)

// None:保持原始尺寸
img("https://example.com/photo.jpg")
    .w(px(300.))
    .h(px(200.))
    .object_fit(ObjectFit::None)

回退内容

rust
fn image_with_fallback(src: &str, alt_text: &str) -> impl IntoElement {
    div()
        .w(px(300.))
        .h(px(200.))
        .bg(cx.theme().surface)
        .border_1()
        .border_color(cx.theme().border)
        .rounded(px(8.))
        .overflow_hidden()
        .child(
            img(src)
                .w_full()
                .h_full()
                .object_fit(ObjectFit::Cover)
                // 实际项目中可在这里补充错误处理
        )
}

fn image_with_icon_fallback(src: &str) -> impl IntoElement {
    div()
        .size(px(200.))
        .bg(cx.theme().surface)
        .border_1()
        .border_color(cx.theme().border)
        .rounded(px(8.))
        .flex()
        .items_center()
        .justify_center()
        .child(
            img(src)
                .size_full()
                .object_fit(ObjectFit::Cover)
                // 加载失败时可改为显示图标占位
        )
}

加载状态

rust
fn image_with_loading(src: &str, is_loading: bool) -> impl IntoElement {
    div()
        .w(px(400.))
        .h(px(300.))
        .rounded(px(8.))
        .overflow_hidden()
        .map(|this| {
            if is_loading {
                this.bg(cx.theme().muted)
                    .flex()
                    .items_center()
                    .justify_center()
                    .child("Loading...")
            } else {
                this.child(
                    img(src)
                        .w_full()
                        .h_full()
                        .object_fit(ObjectFit::Cover)
                )
            }
        })
}

fn progressive_image(src: &str, placeholder_src: &str) -> impl IntoElement {
    div()
        .relative()
        .w(px(400.))
        .h(px(300.))
        .rounded(px(8.))
        .overflow_hidden()
        .child(
            img(placeholder_src)
                .absolute()
                .inset_0()
                .w_full()
                .h_full()
                .object_fit(ObjectFit::Cover)
                .opacity(0.5)
        )
        .child(
            img(src)
                .absolute()
                .inset_0()
                .w_full()
                .h_full()
                .object_fit(ObjectFit::Cover)
        )
}

响应式图片

rust
fn responsive_image_grid() -> impl IntoElement {
    div()
        .grid()
        .grid_cols(3)
        .gap_4()
        .child(
            img("https://example.com/photo1.jpg")
                .w_full()
                .aspect_ratio(1.0)
                .object_fit(ObjectFit::Cover)
                .rounded(px(8.))
        )
        .child(
            img("https://example.com/photo2.jpg")
                .w_full()
                .aspect_ratio(1.0)
                .object_fit(ObjectFit::Cover)
                .rounded(px(8.))
        )
        .child(
            img("https://example.com/photo3.jpg")
                .w_full()
                .aspect_ratio(1.0)
                .object_fit(ObjectFit::Cover)
                .rounded(px(8.))
        )
}

fn hero_image() -> impl IntoElement {
    div()
        .relative()
        .w_full()
        .h(px(500.))
        .rounded(px(12.))
        .overflow_hidden()
        .child(
            img("https://example.com/hero-image.jpg")
                .absolute()
                .inset_0()
                .w_full()
                .h_full()
                .object_fit(ObjectFit::Cover)
        )
        .child(
            div()
                .absolute()
                .inset_0()
                .bg(rgba(0, 0, 0, 0.4))
                .flex()
                .items_center()
                .justify_center()
                .child(
                    v_flex()
                        .items_center()
                        .gap_4()
                        .child("Hero Title")
                        .child("Subtitle text here")
                )
        )
}

图片画廊

rust
fn image_gallery(images: Vec<&str>) -> impl IntoElement {
    v_flex()
        .gap_6()
        .child(
            div()
                .w_full()
                .h(px(400.))
                .rounded(px(12.))
                .overflow_hidden()
                .child(
                    img(images[0])
                        .w_full()
                        .h_full()
                        .object_fit(ObjectFit::Cover)
                )
        )
        .child(
            h_flex()
                .gap_3()
                .children(
                    images.iter().map(|src| {
                        div()
                            .size(px(80.))
                            .rounded(px(6.))
                            .overflow_hidden()
                            .border_2()
                            .border_color(cx.theme().border)
                            .cursor_pointer()
                            .hover(|this| this.border_color(cx.theme().primary))
                            .child(
                                img(*src)
                                    .size_full()
                                    .object_fit(ObjectFit::Cover)
                            )
                    })
                )
        )
}

SVG 图片

rust
img("assets/icons/logo.svg")
    .size(px(64.))
    .text_color(cx.theme().primary)

img("data:image/svg+xml;base64,...")
    .w(px(32.))
    .h(px(32.))

img("assets/spinner.svg")
    .size(px(24.))
    .text_color(cx.theme().primary)
    // 实际使用中可叠加旋转动画

API 参考

核心函数

函数说明
img(source)基于 ImageSource 创建图片元素

图片来源(ImageSource)

类型说明示例
String / &strURL 或文件路径"https://example.com/image.jpg"
SharedUri共享 URI 引用SharedUri::from("file://path")
Local Path本地文件系统路径"assets/logo.png"
Data URIBase64 编码图片"data:image/png;base64,..."

尺寸方法

方法说明
w(length)设置宽度
h(length)设置高度
size(length)同时设置宽高
w_full()占满容器宽度
h_full()占满容器高度
size_full()占满容器尺寸
max_w(length)设置最大宽度
max_h(length)设置最大高度
min_w(length)设置最小宽度
min_h(length)设置最小高度

Object Fit 选项

说明
ObjectFit::Cover填满容器,可能裁剪
ObjectFit::Contain完整显示在容器内
ObjectFit::Fill拉伸填满容器
ObjectFit::ScaleDown类似 contain,但不会放大
ObjectFit::None保持原始尺寸

样式方法

方法说明
rounded(radius)设置圆角
border_1()1px 边框
border_color(color)设置边框颜色
opacity(value)设置透明度(0.0-1.0)
shadow_sm()小阴影
shadow_lg()大阴影

最佳实践

图片优化

  • 根据展示尺寸提供合适分辨率的图片
  • 在保证质量的前提下尽量压缩资源
  • 优先考虑 WebP、AVIF 等现代格式
  • 为不同屏幕尺寸准备响应式图片

错误处理

  • 为加载失败提供明确回退内容
  • 使用骨架屏保持布局稳定
  • 对临时网络错误考虑重试机制
  • 对永久失败给出可理解的用户提示

性能

  • 对首屏外图片使用懒加载
  • 配合缓存策略减少重复请求
  • 加载期间可使用低质量占位图
  • 图片尺寸应与实际展示上下文匹配

用户体验

  • 图片网格中保持一致的宽高比
  • 使用平滑的加载过渡
  • 根据内容类型选择合适的 object-fit
  • 细节图可考虑提供缩放能力