Skip to content

Commit

Permalink
update doc《基础组件封装-网络图片组件》
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaoyang committed Nov 21, 2023
1 parent b103711 commit a0c4b17
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 59 deletions.
274 changes: 274 additions & 0 deletions content/posts/rn/base-components/BaseImage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import { Image, View } from "react-native";
import FastImage from "react-native-fast-image";

// aliyun oss image format
// error image url: https://cdn.xxx.com/pic/illustrationstory/default/default_cover_720-1080.png?x-oss-process=image/resize,w_150/quality,q_85/format,webp
// normal image url: https://cdn.xxx.com/pic/illustrationstory/custom/scene/202310/2721/1698415166233-0yZkuCBu9I_960-960.png?x-oss-process=image/resize,w_150/quality,q_85/format,webp

/**
* 图片组件
* @param {require('xxx') | {uri: string}} errorSource 加载失败的图片
*/
export default React.memo((props) => {
const {
source,
style,
resizeMode,
onLoad,
errorSource,
// todo: 加载中的图片
// pendingSource, // loading状态的图片、加载失败的图片
// pendingStyle, // loading状态的图片样式
} = props;

const { loadStatus, onLoadStart, onLoadEnd, onLoadError } = useWebImage({
source,
});
// console.log("props.source: ", source?.uri);
// console.log("loadStatus:", loadStatus);

if (!source) {
return null;
}

const _style = amendStyle(style);

const isWebImage = useMemo(() => source?.uri?.startsWith("http"), [source]);
console.log("isWebImage:", isWebImage);

// const renderPending = () => {
// return pendingSource ? (
// <View style={{ position: "absolute" }}>
// <Image source={pendingSource} style={pendingStyle} />
// </View>
// ) : (
// <View
// style={[_style, { position: "absolute", backgroundColor: "#ccc" }]}
// />
// );
// };

console.log("_style:", _style);
const renderError = () => {
console.log("renderError:", source?.uri, _style);

let { width, height } = getWH(_style);

console.log("width height", width, height);

let w = width; // 340 / 3
let h = height; // 260 / 3;
if (!width || !height) {
//兜底一个宽高
w = 340 / 3;
h = 260 / 3;
}

return (
<View
style={[
_style,
{
position: "absolute",
justifyContent: "center",
alignItems: "center",
zIndex: 10,
},
]}
>
<Image
source={errorSource ?? require("./load_error.png")}
style={{
width: w,
height: h,
}}
resizeMode={errorSource ? "cover" : "contain"}
/>
</View>
);
};

return (
<FastImage
style={_style}
resizeMode={resizeMode ?? FastImage.resizeMode.cover}
onLoad={(e) => {
//console.log("onLoad::", source?.uri);
onLoad?.(e);
}}
onLoadStart={onLoadStart}
onLoadEnd={onLoadEnd}
onError={onLoadError}
source={source}
onLayout={(e) => {
//console.log("onLayout:", e.nativeEvent.layout);
}}
>
{props.children}
{/* {isWebImage && loadStatus == kLoadStatus.PENDING && renderPending()} */}
{isWebImage && loadStatus == kLoadStatus.ERROR && renderError()}
</FastImage>
);
});

const kLoadStatus = {
IDLE: "IDLE",
PENDING: "PENDING",
SUCCESS: "SUCCESS",
ERROR: "ERROR",
};

function useWebImage({ source }) {
const uri = source?.uri;

const [loadStatus, setLoadStatus] = useState(kLoadStatus.IDLE);
const isLoadEndRef = useRef(false); //用于解决onLoadError回调后,居然还会走onLoadEnd回调的问题 (判断error走了 end方法就不处理state了)

useEffect(() => {
console.log("setLoadStatus(kLoadStatus.IDLE):", uri);
setLoadStatus(kLoadStatus.IDLE);
isLoadEndRef.current = false;
}, [uri]);

const onLoadStart = () => {
// console.log("onLoadStart~");
setLoadStatus(kLoadStatus.PENDING);
};

const onLoadEnd = (e) => {
// console.log("onLoadEnd~ loadStatus", e, loadStatus);
!isLoadEndRef.current && setLoadStatus(kLoadStatus.SUCCESS);
isLoadEndRef.current = true;
};

const onLoadError = () => {
// console.log("onLoadError~");
setLoadStatus(kLoadStatus.ERROR);
isLoadEndRef.current = true;
};

return {
loadStatus,
onLoadStart,
onLoadEnd,
onLoadError,
};
}

// 对style修复 (未设置宽高的,设置兜底宽高20)
const amendStyle = (style) => {
//对于style未给宽高的,甚至style都undefined,设置兜底宽高20
const extraStyle = { minWidth: 20, minHeight: 20 };

let _style;
const { width, height } = getWH(_style);
if (!width || !height) {
_style = Array.isArray(style)
? [...style, extraStyle]
: [style, extraStyle];
}
if (!_style) {
_style = style;
}

return _style;
};

// 从style获取宽高
const getWH = (style) => {
let w = 0;
let h = 0;
if (Array.isArray(style)) {
// 从后往前遍历,取最后一个有宽高的对象
for (let index = style.length - 1; index >= 0; index--) {
let obj = style[index];
if (!w && obj?.width) {
w = obj.width;
}
if (!h && obj?.height) {
h = obj.height;
}
if (w && h) {
break;
}
}
} else {
let obj = style;
if (obj?.width) {
w = obj.width;
}
if (obj?.height) {
h = obj.height;
}
}
return { width: w, height: h };
};


/*
使用示例
```javascript
import HBImage from "app/components/image/HBImage";
import { getSize, parseSize } from "app/utils/image/measurer";
const FeedItem = (props) => {
const coverUrl = useMemo(
() => getImageUrl(item.coverUrl, imageWidth), //oss resize 拼接url
[item.coverUrl]
);
const imageHeightFromUrl = useMemo(() => {
return getHeightFromSize(parseSize(coverUrl)); // parseSize从url(..._720-1080.png...)中解析出宽高 【正常100%都能取出】
}, [coverUrl]);
const [imageHeight, setImageHeight] = useState(imageHeightFromUrl);
useEffect(() => {
if (!(imageHeightFromUrl > 0)) {
//若从url中未能解析出宽高,就通过RN官方Image.getSize方式获取宽高 【正常100%不可能走这里头】
let h = imageSize;
getSize(coverUrl)
.then((size) => {
if (size) {
h = getHeightFromSize(size);
}
setImageHeight(h);
kImageHeightCache[coverUrl] = h;
})
.catch((err) => {
setImageHeight(h);
});
} else {
kImageHeightCache[coverUrl] = imageHeightFromUrl;
}
}, [coverUrl])
return <>
{imageHeightFromUrl || imageHeight ? (
<HBImage
style={[
localStyles.image,
{
height: imageHeightFromUrl > 0 ? imageHeightFromUrl : imageHeight,
},
]}
source={{ uri: coverUrl }}
resizeMode={"contain"}
errorSource={require("app/components/image/default_cover_720-1080.png")}
/>
) : null}
</>
}
const getHeightFromSize = (size) => {
if (!(size?.width > 0) || !(size?.height > 0)) return 0;
return Math.min((imageSize * size.height) / size.width, maxImageHeight);
};
```
*/

83 changes: 77 additions & 6 deletions content/posts/rn/base-components/index.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
---
title: "封装常用基础组件"
date: 2023-11-01T12:00:11+08:00
draft: false
draft: true
---

# 瀑布流列表base组件

* 瀑布流列表base组件
源码:[BaseList.js](./BaseList.js)

源码[BaseList.js](./BaseList.js)
优点:将下拉刷新、上拉加载更多相关的 pageNo逻辑,封装到BaseList中,使用方仅需关注 数据请求的api,让使用方代码量降到最少。

> 将下拉刷新、上拉加载更多相关的 pageNo逻辑,封装到BaseList中,使用方仅需关注 数据请求的api,让使用方代码量降到最少。
使用示例
### 使用示例
```javascript
import BaseList from "app/components/list/base";
import { getFanList } from "./service";
Expand Down Expand Up @@ -46,3 +45,75 @@ const FanList = (props) => {

```


# 图片base组件

源码:[BaseImage.js](./BaseImage.js)

优点: 使用了高性能图片库FastImage,封装为 可配置图片加载失败后展示的占位error图


使用示例
```javascript
// aliyun oss image format
// error image url: https://cdn.xxx.com/pic/illustrationstory/default/default_cover_720-1080.png?x-oss-process=image/resize,w_150/quality,q_85/format,webp
// normal image url: https://cdn.xxx.com/pic/illustrationstory/custom/scene/202310/2721/1698415166233-0yZkuCBu9I_960-960.png?x-oss-process=image/resize,w_150/quality,q_85/format,webp

import HBImage from "app/components/image/HBImage";
import { getSize, parseSize } from "app/utils/image/measurer";

const FeedItem = (props) => {

const coverUrl = useMemo(
() => getImageUrl(item.coverUrl, imageWidth), //oss resize 拼接url
[item.coverUrl]
);

const imageHeightFromUrl = useMemo(() => {
return getHeightFromSize(parseSize(coverUrl)); // parseSize 从url(..._720-1080.png...)中解析出宽高 【正常100%都能取出】
}, [coverUrl]);

const [imageHeight, setImageHeight] = useState(imageHeightFromUrl);

useEffect(() => {
if (!(imageHeightFromUrl > 0)) {
//若从url中未能解析出宽高,就通过RN官方Image.getSize方式获取宽高 【正常100%不可能走这里头】
let h = imageSize;
getSize(coverUrl)
.then((size) => {
if (size) {
h = getHeightFromSize(size);
}
setImageHeight(h);
kImageHeightCache[coverUrl] = h;
})
.catch((err) => {
setImageHeight(h);
});
} else {
kImageHeightCache[coverUrl] = imageHeightFromUrl;
}
}, [coverUrl])

return <>
{imageHeightFromUrl || imageHeight ? (
<HBImage
style={[
localStyles.image,
{
height: imageHeightFromUrl > 0 ? imageHeightFromUrl : imageHeight,
},
]}
source={{ uri: coverUrl }}
resizeMode={"contain"}
errorSource={require("app/components/image/default_cover_720-1080.png")}
/>
) : null}
</>
}

const getHeightFromSize = (size) => {
if (!(size?.width > 0) || !(size?.height > 0)) return 0;
return Math.min((imageSize * size.height) / size.width, maxImageHeight);
};
```
6 changes: 0 additions & 6 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -219,12 +219,6 @@ <h1 class="f3 fw1 athelas mt0 lh-title">
<h1 class="f3">More</h1>


<h2 class="f5 fw4 mb4 dib mr3">
<a href="/zyestin/posts/rn/base-components/" class="link black dim">
封装常用基础组件
</a>
</h2>

<h2 class="f5 fw4 mb4 dib mr3">
<a href="/zyestin/posts/hugo-usage/" class="link black dim">
hugo &#43; github pages usage
Expand Down
Loading

0 comments on commit a0c4b17

Please sign in to comment.