笔记
前言
最近在开发IM通讯系统的时候有发送并渲染图片的问题。因为发送的图片宽高不一,所以给了图片一个计算过的width和mode="widthFix" 属性。
但是,Taro的Image的标签的widthFix 属性存在一个问题(当然其他非裁剪的属性存在同样的问题,比如heightFix,)。就是在数据从头上开始重新渲染的时候,整个Image标签的告诉都会被重新计算。不管有没有加上key。
这个问题导致原因是,mode属性并不是在优先计算图片的宽高然后再渲染视图,而是先进行渲染然后再进行自适应宽高的计算。
PS:这个问题只有在往数组的前方添加内容的时候才会出现。
问题模拟
注意到了吗,图片会瞬间拉长然后恢复到自适应高度。实际上每次添加节点都会有这个情况,gif中点击没有出现的只是录制的时候帧数没有捕捉到。
实际上网上有很多资料会告诉你,这种闪烁是因为Image有一个固定240px的高度,给Image设定一个height:"auto"就好了。
实际上效果并没有想的那么顺利。
确实,图片突然拉长然后回到一个正常的高度的闪烁确实是因为图片在一开始width:xxx,height:320的情况下因为mode:widthFix的问题height的高度在渲染完成以后突然变成另一个高度导致的。但是把height调整为"auto"也并不能改变这个bug。无非就是让图片从height:320到自适应高度的闪烁变成了从height:0到自适应高度的闪烁。
效果如下图
这个问题就有意思了。以上两个区别就是从320-自适应高度和从0-自适应高度的区别。当然,在视觉效果上,后者显然会比前者更好一些。
所以,如何解决这个问题?
方案
当我发现不论我缓存组件(memo)还是添加key都不能避免这张图片的高度被重新计算的时候。我就放弃了避免他被重新渲染的方案。其实在这个问题最麻烦是高度的变化,而不是图片本身渲染的问题。
既然无法避免他被重新渲染。而问题的原因又是高度的变动导致的。所以我改变了策略,我让他记住这个图片的高度。会不会更好些?
解决方法一
使用mode中除去自适应宽高以外的属性,比如aspectFit ,aspectFill等裁剪属性,这些属性都是需要提前设定宽高的。所以不会出现高度闪烁或者宽度闪烁的情况。
这种方法适用的场景比较局限: 当我们的项目在渲染列表图片的时候允许同一固定宽高的情况下。不需要每一张图片都有自己特定的宽高的时候。
解决方法二
封装一个自定义的Image组件。
js
import { Image } from "@tarojs/components";
import { FC, useEffect, useState } from "react";
import Taro, { pxTransform } from "@tarojs/taro";
import { memo } from "react";
interface Props {
item: {
url: string;
key: string;
};
}
export const ImageCom: FC<Props> = memo(({ item }) => {
const [imageHW, setImageHW] = useState({
width: 0,
height: 0,
});
/**
* 计算图片相对于屏幕的比例
* @param res
* @returns
*/
const computeImageScale = (res: any) => {
const { width, height } = res;
const screenWidth = Taro.getSystemInfoSync().windowWidth * 2 * 0.7;
if (screenWidth < width) {
const scale = screenWidth / width;
return {
width: screenWidth,
height: height * scale,
};
} else {
return {
width,
height,
};
}
};
/**
* 获取并计算图片的宽高
*/
const handelGetImageInfo = () => {
Taro.getImageInfo({
src: item.url,
success: (res) => {
const elInfo = computeImageScale(res);
setImageHW({
height: elInfo.height,
width: elInfo.width,
});
},
});
};
useEffect(() => {
handelGetImageInfo();
});
return (
<>
<Image
src={item?.url}
//一开始就给他一个固定的经过计算的宽高,不给他自适应的机会
style={{ width: pxTransform(imageHW.width),height:pxTransform(imageHW.height) }}
key={item.key}
/>
</>
);
});
第二种方法就是放弃mode属性,自己去计算图片在窗口中该有的宽和高。只要让图片在重新渲染的时候就保持他该有的高度,就不会出现闪烁的情况。
结尾
给大家参考两个API
第一个: Taro.getSystemInfoSync() 获取设备信息
第二个: Taro.getImageInfo({...}) 获取图片信息