零、問題的由來
一般在前端展示圖片時都會碰到這兩個常見的需求:
- 圖片未載入完成時先展示佔點陣圖,等到圖片載入完畢後再展示實際的圖片。
- 假如圖片連結有問題(比如 404),依然展示佔點陣圖。甚至你還可以增加點選圖片再次載入的功能。(例如知乎)
然鵝,小程式原生元件 image 並沒有提供這些常用功能...
注:這裡加了 2s 的延遲
一、常規操作
在小程式沒還沒推出自定義元件功能時,只能通過改變 Page 中的 data 來展示兜底的佔點陣圖,所以當時的處理方式十分蛋疼...
1.1.相同預設圖
由於需要知道這個圖片的資料路徑,所以不得不在每個 image
上加上類似 data-img-path
的東西。
<view
wx:for="{{ obj.arr }}"
wx:key="imgSrc"
wx:for-item="item"
wx:for-index="itemIdx"
>
<image
src="{{ item.imgSrc }}"
binderror="onImageError"
data-img-path="obj.arr[{{ itemIdx }}].imgSrc"
/>
</view>
複製程式碼
const DEFAULT_IMG = '/assets/your_default_img'
Page({
data: {
obj: {
arr: [
{ imgSrc: 'your_img1' },
{ imgSrc: 'your_img2' },
],
},
},
onImageError ({
target: { dataset: { imgPath } },
}) {
this.setData({
[imgPath]: DEFAULT_IMG,
})
},
})
複製程式碼
1.2.不同預設圖
如果預設圖片不同呢?例如球員、球隊和 feed 的預設圖片一般都是不同的。
那麼一般只好再增加一個屬性例如 data-img-type
來標識預設圖的型別。
<!-- 球隊圖 -->
<image
...
data-img-type="team"
/>
<!-- 球員圖 -->
<image
...
data-img-type="player"
/>
複製程式碼
const DEFAULT_IMG_MAP = {
feed: '/assets/default_feed',
team: '/assets/default_team',
player: '/assets/default_player',
}
Page({
data: {
obj: {
arr: [
{ imgSrc: 'your_img1' },
{ imgSrc: 'your_img2' },
],
},
},
onImageError ({
target: { dataset: { imgPath, imgType } },
}) {
this.setData({
[imgPath]: DEFAULT_IMG_MAP[imgType],
})
},
})
複製程式碼
1.3.圖片在模板中
頁面層級淺倒還好,如果跨模板了,那麼模板就可能要用一個類似於 pathPrefix
的屬性來傳遞模板資料的路徑字首。
<!--
球員排行模板
pathPrefix: String
playerList: Array
...
-->
<template name="srPlayerRank">
<view
wx:for="{{ playerList }}"
wx:key="imgSrc"
wx:for-item="item"
wx:for-index="itemIdx"
>
<image
src="{{ item.imgSrc }}"
binderror="onImageError"
data-img-type="player"
data-img-path="{{ pathPrefix }}.playerList[{{ itemIdx }}].imgSrc"
/>
</view>
</template>
複製程式碼
最後在失敗回撥裡呼叫 setData({ [path]: DEFAULT_IMG })
重新設定圖片地址。
就問你蛋不蛋疼?這一坨 data-img-path="{{ pathPrefix }}.playerList[{{ itemIdx }}].imgSrc"
程式碼真讓人無發可脫...
二、自定義元件
有了自定義元件後,用領袖【竊·格瓦拉】的話來說的話就是:“感覺好 door 了~”
2.1.原生自定義元件
原生寫法一般要寫4個檔案:.json
/.wxml
/.js
/.wxss
- TuaImage.json
{
"component": true
}
複製程式碼
- TuaImage.wxml
<!-- 載入中的圖片 -->
<image
hidden="{{ !isLoading }}"
src="{{ errSrc }}"
style="width: {{ width }}; height: {{ height }}; {{ styleStr }}"
mode="{{ imgMode }}"
/>
<!-- 實際載入的圖片 -->
<image
hidden="{{ isLoading }}"
src="{{ imgSrc || src }}"
mode="{{ imgMode }}"
style="width: {{ width }}; height: {{ height }}; {{ styleStr }}"
bindload="_onImageLoad"
binderror="_onImageError"
lazy-load="{{ true }}"
/>
複製程式碼
- TuaImage.js
const DEFAULT_IMG = '/assets/your_default_img'
Component({
properties: {
// 圖片地址
src: String,
// 圖片載入中,以及載入失敗後的預設地址
errSrc: {
type: String,
// 預設是球隊圖示
value: DEFAULT_IMG,
},
width: {
type: String,
value: '48rpx',
},
height: {
type: String,
value: '48rpx',
},
// 樣式字串
styleStr: {
type: String,
value: '',
},
// 圖片裁剪、縮放的模式(詳見文件)
imgMode: {
type: String,
value: 'scaleToFill',
},
},
data: {
imgSrc: '',
isLoading: true,
},
methods: {
// 載入圖片出錯
_onImageError (e) {
this.setData({
imgSrc: this.data.errSrc,
})
this.triggerEvent('onImageError', e)
},
// 載入圖片完畢
_onImageLoad (e) {
this.setData({ isLoading: false })
this.triggerEvent('onImageLoad', e)
},
},
})
複製程式碼
布吉島大家使用原生寫法時有木有一些感覺不方便的地方:
- 4個檔案:
.json
/.wxml
/.js
/.wxss
,這樣老需要切來切去的降低效率 properties
是什麼鬼?大家(React/Vue)一般不都用props
麼?style="width: {{ width }}; height: {{ height }}; {{ styleStr }}"
樣式字串怎麼辣麼長...
2.2.TuaImage.vue
所以以下是一個使用單檔案元件封裝原生 image 元件的例子。
- 使用單檔案元件將配置、模板、指令碼、樣式寫在一個檔案中,方便維護。
- 使用計算屬性
computed
將樣式字串寫在 js 中。 - 使用
this.imgSrc = this.errSrc
而不是this.setData
來改變data
。
<config>
{
"component": true
}
</config>
<template lang="wxml">
<!-- 載入中的圖片 -->
<image
hidden="{{ !isLoading }}"
src="{{ errSrc }}"
style="{{ imgStyleStr }}"
mode="{{ imgMode }}"
/>
<!-- 實際載入的圖片 -->
<image
hidden="{{ isLoading }}"
src="{{ imgSrc || src }}"
mode="{{ imgMode }}"
style="{{ imgStyleStr }}"
bindload="_onImageLoad"
binderror="_onImageError"
lazy-load="{{ true }}"
/>
</template>
<script>
/**
* 圖片元件,能夠傳遞備用圖片以防圖片失效
* https://developers.weixin.qq.com/miniprogram/dev/component/image.html
*/
// 也可以設定為網路圖片如: https://foo/bar.png
const DEFAULT_IMG = '/assets/your_default_img'
export default {
props: {
// 圖片地址
src: String,
// 圖片載入中,以及載入失敗後的預設地址
errSrc: {
type: String,
// 預設是球隊圖示
default: DEFAULT_IMG,
},
width: {
type: String,
default: '48rpx',
},
height: {
type: String,
default: '48rpx',
},
// 樣式字串
styleStr: {
type: String,
default: '',
},
// 圖片裁剪、縮放的模式(詳見文件)
imgMode: {
type: String,
default: 'scaleToFill',
},
},
data () {
return {
imgSrc: '',
isLoading: true,
}
},
computed: {
// 樣式字串
imgStyleStr () {
return `width: ${this.width}; height: ${this.height}; ${this.styleStr}`
},
},
methods: {
// 載入圖片出錯
_onImageError (e) {
this.imgSrc = this.errSrc
this.$emit('onImageError', e)
},
// 載入圖片完畢
_onImageLoad (e) {
this.isLoading = false
this.$emit('onImageLoad', e)
},
},
}
</script>
<style lang="scss">
</style>
複製程式碼
採用框架是 tua-mp:
相關文章: