指南
图片与资源
<Image> 显示一张位图:随应用一起发布的文件,或从 URL 下载的图片。图片在主线程之外读取和解码,按视图实际显示的尺寸降采样后缓存;主线程只负责显示解码结果。
import { Image } from "nsh/image";
import logo from "./assets/logo@2x.png";
<Image src={logo} />
<Image src="https://picsum.photos/seed/nsh/600/400" style={{ width: "100%", height: 200, borderRadius: 12 }} />
元素的属性列在 image 中。
#应用内的图片
像导入模块一样导入图片文件。导入得到一个 ImageAsset,即 { uri, width, height },打包器会把文件随应用一起发布:
import logo from "./assets/logo@2x.png";
logo; // { uri: "asset:/assets/logo@2x.png", width: 64, height: 64 }
支持的文件:.png、.jpg、.jpeg、.gif、.webp、.heic。
尺寸以点为单位。 文件名中的 @2x 或 @3x 后缀表示文件每个点有 2 或 3 个像素,因此 128×128 像素的 logo@2x.png 是一个 64×64 点的资源。没有后缀时,一个像素就是一个点。nsh 不会在 logo@2x.png 和 logo@3x.png 之间自动选择:你导入的那个文件就是最终发布的文件。请按需要的最高密度导出(@3x 能在所有手机上得到最清晰的效果);解码时图片会降采样到屏幕所需的大小。
自然尺寸。 当样式没有指定宽度或高度时,来自资源的图片按自然尺寸布局,所以 <Image src={logo} /> 不需要任何样式。若要改变大小,设置其中一边加 aspectRatio,或同时设置两边。
资源路径相对于导入它的模块解析,其 uri 相对于应用根目录(入口文件所在目录)。nsh build 会把它们复制到 out/assets/,nsh package 会把它们放进应用,开发时则由开发服务器提供。
#远程图片
给 src 传一个 URL 字符串或 { uri }:
<Image src={user().avatarUrl} style={{ width: 48, height: 48, borderRadius: 24, backgroundColor: "#eaeef2" }} />
- 为远程图片指定尺寸。 加载完成前无法得知它们的尺寸,所以如果没有宽和高(或其中一边加
aspectRatio),它们的高度会塌缩为 0。 - 加载期间会显示背景色,也可以使用
defaultSource(见下文)。 - 下载是共享的:多个视图显示同一个 URL 时只下载一次,即使它们按不同尺寸解码也是如此。没有任何视图再等待的下载会被取消。
- 解码后的图片缓存在内存中(内存紧张时会清理),以 URL、解码尺寸和模糊半径为键。
- 当应用声明了能力时,图片的主机必须在
network中被允许;被拒绝的图片会以onError失败。
#缩放模式
resizeMode 决定图片如何填充形状不同的框:
| 取值 | 效果 |
|---|---|
"cover"(默认) | 保持宽高比填满整个框,裁掉溢出部分 |
"contain" | 保持宽高比完整放入框内,留出空白 |
"stretch" | 精确填满框,图片会变形 |
"center" | 自然尺寸,居中 |
<Image src={photo} resizeMode="contain" style={{ width: 140, height: 140, backgroundColor: "#eaeef2" }} />
Tailwind 类 object-cover、object-contain 等效果相同。
#图片样式
图片也是视图:它们支持 borderRadius(也可按角设置)、边框、阴影、opacity 和变换。圆角会裁剪图片。
<Image src={avatar} style={{ width: 90, height: 90, borderRadius: 45, borderWidth: 3, borderColor: "#8250df" }} />
tintColor(一个样式)会给图片的不透明像素重新着色:适合随主题变色的单色图标。filter(grayscale()、sepia()、blur()等)在两个平台上都可用于图片(滤镜)。
<Image src={icon} style={{ width: 24, height: 24, tintColor: active() ? "#0969da" : "#6e7781" }} />
#占位图、淡入与模糊
| 属性 | 说明 |
|---|---|
defaultSource | src 加载完成前显示的图片;最好是本地资源 |
fadeDuration | 加载完成的图片淡入所用的毫秒数。iOS 默认 0,Android 默认 300 |
blurRadius | 按此半径(点)模糊图片,在解码时一次性应用 |
<Image
src={`https://picsum.photos/seed/${id()}/300/300`}
defaultSource={placeholder}
fadeDuration={250}
style={{ width: 100, height: 100, borderRadius: 12 }}
/>
<Image src={cover} blurRadius={16} style={{ position: "absolute", top: 0, right: 0, bottom: 0, left: 0 }} />
#动图
动态 GIF、WebP 和 APNG 文件会自动播放,无论是资源还是来自 URL。它们支持相同的样式和缩放模式;在 iOS 上,着色后的动图只显示第一帧。
#加载事件
| 事件 | 负载 | 触发时机 |
|---|---|---|
onLoadStart | 开始加载 | |
onProgress | { loaded, total },单位为字节(未知时 total 为 0) | 下载过程中,最多每 100 ms 一次 |
onLoad | { width, height } | 图片就绪;源图的像素尺寸 |
onError | { message } | 加载或解码失败 |
onLoadEnd | 在 onLoad 或 onError 之后 |
const status = signal("loading…");
<Image
src={url}
onLoad={(e) => status.set(`${e.width}×${e.height}`)}
onError={(e) => status.set(e.message)}
style={{ width: 90, height: 60 }}
/>
onLoad 报告的是源图的尺寸,而不是解码时的尺寸。
#Image.getSize 与 Image.prefetch
渲染元素的同一个 Image 也可以处理不在屏幕上的图片:
import { Image } from "nsh/image";
const size = await Image.getSize("https://example.com/photo.jpg"); // { width, height } in pixels
const ok = await Image.prefetch("https://example.com/next.jpg"); // true once the bytes are in memory
| 方法 | 返回值 |
|---|---|
Image.getSize(source) | 图片的像素尺寸。无法加载时 reject |
Image.prefetch(source) | 图片字节加载完成后为 true |
source 是 URL 字符串或 { uri }。两者都会为接下来显示该图片的视图保留已下载的字节,因此先预取再显示的图片无需再次下载。
一个常见用法是按宽高比确定远程图片的尺寸:
function RemoteImage(props: { uri: string }) {
const ratio = signal(1);
Image.getSize(props.uri).then((s) => ratio.set(s.width / s.height));
return <Image src={props.uri} style={{ width: "100%", aspectRatio: ratio() }} />;
}
#其他资源
| 导入 | 得到 | 页面 |
|---|---|---|
.ttf、.otf | 字体的字体族名,在第一帧之前注册 | 字体 |
.json | 解析后的值,打包进脚本 | 项目结构 |
图片也可以用 drawImage 绘制在画布上(Canvas),而 backgroundImage 的渐变能替代许多装饰性图片(样式)。