一、image 组件基础
image 是小程序的图片组件,负责把一张图片按指定方式显示在页面里。它支持JPG、PNG、SVG、WEBP、GIF等格式,从基础库 2.3.0 起也支持直接使用云文件 ID。
它的用法非常简单,只有一个必须关心的属性src和一个真正决定效果的属性mode。
1.1 最简用法
<image src="/images/helo.jpeg" mode="aspectFit"/>
src支持本地路径和网络地址。本地路径有两种写法:以/开头表示从项目根目录算起,不以/开头则表示相对于当前文件。本项目用的是绝对写法/images/helo.jpeg,这样无论文件被放在哪一层页面目录里都能正确找到图片。
1.2 两个必须先知道的默认值
项目 | 默认值 | 含义 |
mode | scaleToFill | 不写 mode 时默认拉伸填满,图片会变形 |
组件尺寸 | 320px × 240px | 不写样式时组件就是这个大小,图片会被限制在里面 |
这两个默认值一起造成了新手最常见的问题:图片要么变形,要么只显示一角。所以实际使用时,既要显式设置宽高,也要显式指定mode。
1.3 src 与 mode 的关系
src决定显示哪张图,mode决定这张图如何塞进组件的框里。组件最终显示的尺寸是由 CSS 决定的,mode只控制图片在框内的缩放与裁剪方式——理解这一点,后面 14 种模式的差别就都能推出来了。
二、两种模式:缩放与裁剪
14 个 mode 值分成两类,这是理解它们的第一个分水岭:
类别 | 数量 | 是否改变图片大小 | 渲染框架支持 |
缩放模式 | 5 个 | 会缩放图片,让它适应组件框 | Webview 与 Skyline 都支持 |
裁剪模式 | 9 个 | 不缩放图片,组件框当作取景窗口 | 仅 Webview 支持 |
2.1 五种缩放模式
mode | 含义 |
scaleToFill | 不保持纵横比,把图片的宽高完全拉伸至填满组件(会变形) |
aspectFit | 保持纵横比,让图片的长边完整显示,整张图都能看到 |
aspectFill | 保持纵横比,只保证短边完整显示,另一个方向会被裁掉 |
widthFix | 宽度不变,高度按原图比例自动变化(基础库支持) |
heightFix | 高度不变,宽度按原图比例自动变化(2.10.3 起支持) |
2.2 九种裁剪模式
mode | 含义 | 取景位置 |
top | 只显示图片顶部区域 | 上中 |
bottom | 只显示图片底部区域 | 下中 |
center | 只显示图片中间区域 | 正中 |
left | 只显示图片左边区域 | 左中 |
right | 只显示图片右边区域 | 右中 |
top left | 只显示左上区域 | 左上角 |
top right | 只显示右上区域 | 右上角 |
bottom left | 只显示左下区域 | 左下角 |
bottom right | 只显示右下区域 | 右下角 |
以上九个均为裁剪模式,官方文档标注「仅 Webview 支持」。
2.3 为什么本项目把渲染器改成了 webview
这是整个项目里最关键的一处配置。因为九个裁剪模式只在Webview渲染器下生效,而微信开发者工具新建项目时默认可能启用 Skyline,所以作者在 app.json 里明确写了:
"renderer": "webview",
如果不写这一行,前 5 种缩放模式看起来正常,后 9 种裁剪模式则不会按预期工作——这是学习这 14 种模式时最容易被绊住的地方。
官方文档把这 9 个裁剪模式都标注为「仅 Webview 支持」,也就是说在 Skyline 渲染器下它们不在支持范围内。需要用到这些模式时,就要像本项目一样显式指定 renderer。
三、本项目做了什么
这个项目的思路很干净:把一张图片用 14 种不同的mode各显示一次,每张上面配上这个模式的说明文字,方便一眼对比差别。
3.1 目录结构
image/ ├── app.js ├── app.json # 关键:renderer 设为 webview ├── app.wxss ├── components │ └── navigation-bar │ ├── navigation-bar.js │ ├── navigation-bar.json │ ├── navigation-bar.wxml │ └── navigation-bar.wxss ├── images │ └── helo.jpeg # 被演示的图片,1422 x 800 像素 ├── pages │ └── index │ ├── index.js # 14 种模式的数据 │ ├── index.json │ ├── index.wxml │ └── index.wxss # 固定 image 的宽高 ├── project.config.json ├── project.private.config.json └── sitemap.json3.2 页面数据(index.js,节选)
页面把所有模式放在一个数组里,每项含mode和text两个字段:
Page({ data: { src:'/images/helo.jpeg', imgArray:[{ mode:'scaleToFill', text:'scaleToFill:缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素' },{ mode:'aspectFit', text:'aspectFit:缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来' },{ mode:'aspectFill', text:'aspectFill:缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来' }, /* ……中间省略,数组共 14 项,完整清单见第四章的表格…… */ { mode: 'bottom right', text: 'bottom right:裁剪模式,不缩放图片,只显示图片的右下边区域。仅 Webview 支持' } ] })为便于阅读,此处省略了中间若干项;数组实际共 14 项,顺序与第二章表格一致。
3.3 页面结构(index.wxml)
<!--index.wxml--> <navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar> <scroll-view class="scrollarea" scroll-y type="list"> <view class="box"> <view class="title">图片的不同显示模式</view> <block wx:for="{{imgArray}}"> <view>显示模式编号{{index+1}}</view> <view>{{item.text}}</view> <view class="img-layout"> <image src="{{src}}" mode="{{item.mode}}"/> </view> </block> </view> </scroll-view>这里有两个细节值得注意:
<image>的 src 是固定的,只有mode跟着循环变量走——所以 14 张图其实是同一张图的不同显示方式。
外层用<block>而不是<view>包循环,block不会渲染成真实节点,因此不会多出一层容器影响布局。
3.4 页面样式(index.wxss)
page { height: 100vh; display: flex; flex-direction: column; } .scrollarea { flex: 1; overflow-y: hidden; } .img-layout{ text-align: center; margin-top: 20rpx; margin-bottom: 40rpx; } image{ width: 480rpx; height: 400rpx; background-color: #eee; }这段样式做了两件重要的事:
把image固定成 480rpx × 400rpx,给所有模式一个统一的对照框,否则组件会用默认的 320px × 240px,对照就没有意义了。
给image加了background-color: #eee浅灰底。这看着不起眼,却让aspectFit的留白一眼可见——这是个很实用的做法。
3.5 全局样式(app.wxss)
除了页面自身的样式,项目还在app.wxss中定义了一些全局样式,供所有页面共用。全局样式通常用来统一字体、间距、背景色等基础视觉规范,避免在每个页面里重复书写相同的样式规则。这样既减少了冗余代码,也让整个小程序的视觉风格保持一致。
/**app.wxss**/ .box{ margin: 20rpx; padding: 20rpx; border: 2rpx solid silver; } .title{ font-size: 40rpx; font-weight: bolder; text-align: center; margin-bottom: 30rpx; color: red; }四、用真实尺寸算一遍
上面只是文字描述,要真正理解这 14 种模式,最有效的办法是拿本项目的真实数字算一次。
4.1 两个关键比例
对象 | 尺寸 | 宽高比 |
原图 helo.jpeg | 1422 × 800 像素 | 1422 ÷ 800 = 1.78 |
组件框(wxss 设定) | 480rpx × 400rpx | 480 ÷ 400 = 1.20 |
两个比例不相等(1.78 比 1.20 更「扁长」),所以图片比框更宽。这一个事实就决定了所有缩放模式的表现——这是本节的核心。
补充:750rpx 等于屏幕宽度。在 375px 宽的手机上,480rpx 约等于 240px,400rpx 约等于 200px。所以这个组件框实际只有 240 × 200 像素大小。
4.2 五种缩放模式的推算结果
mode | 实际显示尺寸 | 怎么来的 | 视觉效果 |
scaleToFill | 480 × 400 rpx | 强制拉伸到框的大小 | 变形。原比例 1.78 被压成 1.20,图被压扁 |
aspectFit | 480 × 270 rpx | 宽贴满(480),高 = 480 ÷ 1.78 | 整图完整可见,上下各留约 65rpx 灰底 |
aspectFill | 711 × 400 rpx | 高贴满(400),宽 = 400 × 1.78 | 撑满整个框,左右各有约 115rpx 被裁掉 |
widthFix | 480 × 270 rpx | 宽度保持 480,高度自动算 | 效果同 aspectFit,但组件高度不再是 400 |
heightFix | 711 × 400 rpx | 高度保持 400,宽度自动算 | 效果同 aspectFill,但组件宽度超出 480 |
尺寸按原图 1422×800 与组件框 480×400rpx 计算,与官方对五种模式的描述一致。
4.3 为什么只有 aspectFit 能看见灰底
aspectFit是唯一一个让组件框比图片大的模式:图片按宽度贴满后只有 270rpx 高,而框有 400rpx 高,多出来的 130rpx 就露出了background-color: #eee的浅灰底。其余四种模式的图片都会把框填满或撑破,所以看不到灰底。
反过来说,如果你想确认一张图有没有被裁切,加一句浅灰底色是最省事的办法。
4.4 九种裁剪模式的实际效果
裁剪模式与缩放模式有一个本质区别:它不缩放图片。图片按原始尺寸渲染,组件框只是一个取景窗口,框外的一律看不见。
按 4.1 的换算,组件框约 240 × 200 像素,而原图是 1422 × 800 像素,于是:
方向 | 可见比例 | 说明 |
水平 | 240 ÷ 1422 ≈ 17% | 只能看到图片左右方向约六分之一 |
垂直 | 200 ÷ 800 = 25% | 只能看到图片上下方向约四分之一 |
也就是说,每种裁剪模式都只露出原图很小的一块。这正好展示了裁剪模式的特点,但如果希望每种模式的差别更明显,可以换一张更小的原图,或者把组件框调大一些。
五、完整属性表
5.1 通用属性
属性 | 类型 | 默认值 | 说明 |
src | string | — | 图片资源地址,支持本地路径、网络地址、云文件 ID |
mode | string | scaleToFill | 图片裁剪、缩放的模式,共 14 个合法值 |
show-menu-by-longpress | boolean | false | 长按图片显示菜单(发送给朋友、保存图片、识别码等) |
bind:load | eventhandle | — | 图片载入完毕时触发,e.detail = { height, width } |
bind:error | eventhandle | — | 错误发生时触发,e.detail = { errMsg } |
5.2 分渲染框架的特有属性
属性 | 框架 | 默认值 | 说明 |
fade-in | Skyline | false | 是否渐显 |
preload | Skyline | false | 是否预加载图片(3.15.0 起) |
lazy-load | WebView | false | 图片懒加载,进入上下三屏范围才加载;Skyline 默认懒加载 |
webp | WebView | false | 是否解析 webP 格式,只支持网络资源 |
forceHttps | WebView | false | 自动把 http 链接替换为 https(3.9.1 起) |
属性、默认值与说明取自微信官方 image 组件文档。
5.3 用 bind:load 拿到图片的真实尺寸
bind:load会在图片载入完成时告诉你图片的真实宽高,这在需要按原图比例做布局时很有用:
<image src="{{src}}" bindload="onImageLoad"/>
onImageLoad(e){
console.log(e.detail.width, e.detail.height) // 例如 1422 800
}
六、通读项目后发现的问题
6.1 wx:for 缺少 wx:key(建议修)
index.wxml 里的循环没有写wx:key:
<block wx:for="{{imgArray}}">
这不会导致报错,但微信开发者工具会给出警告。wx:key的作用是帮助框架在数据变化时正确复用节点,用法是填一个数据里唯一且不变的字段名。本项目每项的mode正好唯一,可以直接用它:
<block wx:for="{{imgArray}}" wx:key="mode">
6.2 renderer 与 rendererOptions.skyline 同时存在(建议清理)
app.json 里既写了"renderer": "webview",又保留了rendererOptions.skyline配置块:
"renderer": "webview", "rendererOptions": { "skyline": { ... } // 渲染器已是 webview,这段配置不会生效 }功能上没有问题——渲染器既然是 webview,skyline 的那段参数就被忽略了,但留在文件里会让人误以为当前用的是 Skyline。建议删掉,或补一句注释说明。
6.3 本地配置里的 skylineRenderEnable 开关
project.private.config.json 里有一项"skylineRenderEnable": true。这是开发者工具侧的编译开关,和 app.json 里显式指定的renderer: webview是两回事:真正决定渲染器的是 app.json。不过两者看起来方向相反,建议确认一下本地开关是否有意保留。
6.4 说明文字的大小写不统一
数组中各条text描述里,「Webview」出现了三种写法:仅webview支持(1 处)、仅Webview支持(3 处)、仅 Webview 支持(5 处)。内容都对,但同一份数据里混用三种写法会显得随意,建议统一成一种。
6.5 其他小问题
位置 | 现状 | 建议 |
navigation-bar | title="Weixin" | 改成页面实际标题,如「图片显示模式」 |
index.wxml | 文字写成「左上边区域/右下边区域」 | 官方用语是「左上角区域」,可统一 |
app.js | App({}) 空实现 | 保持空即可,无需修改 |
组件尺寸 | 固定 480 × 400 rpx | 建议补一句注释说明这个尺寸与图片比例不同,是有意为之 |
七、易错点汇总
现象 | 原因 | 解决办法 |
图片变形了 | 没写 mode,用了默认的 scaleToFill | 按需指定 mode,如 aspectFit |
图片只显示一角 | 用了裁剪模式,或组件尺寸太小 | 改用缩放模式,或放大组件与图片的适配 |
裁剪模式完全没效果 | 渲染器是 Skyline | 在 app.json 里设置 "renderer": "webview" |
aspectFit 上下有空白 | 图片比例与组件比例不一致,这是正常现象 | 如不想要留白改用 aspectFill,或让组件比例贴近原图 |
组件大小不对 | 没写样式,用了默认的 320px × 240px | 在 wxss 里显式设置宽高 |
widthFix 设置了高度但没生效 | widthFix 会接管高度,按比例自动算 | 用 widthFix 时只设宽度,不要设高度 |
列表渲染有警告 | wx:for 没写 wx:key | 补上唯一字段作为 wx:key |
图片加载失败没有提示 | 没有监听 bind:error | 绑定 bind:error 做兜底提示 |
八、小结
image 组件只有src和mode两个核心属性,但mode的 14 个取值分属两个体系:5 个缩放模式会改变图片大小来适应组件框,9 个裁剪模式不缩放图片、把组件框当成取景窗口。
使用时记住三件事:一要显式设置宽高(默认是 320×240),二要显式指定mode(默认会拉伸变形),三要注意裁剪模式仅Webview支持——需要它们时,就要像本项目这样把 app.json 的 renderer 设为 webview。
理解了原图比例与组件框比例的关系,14 种模式的效果其实都能提前推算出来,不必逐个去试。