rn-relates
rn-relates copied to clipboard
RN实现长屏截图
当前参与的项目中,个别RN界面需要生成长屏图片进行分享,目前是使用了 react-native-view-shot 。安装之后,简单使用方法:
import ViewShot, {captureRef} from 'react-native-view-shot';
export default class SomeView extends Component {
render() {
return(
<View style={styles.container}>
<ViewShot>
<ScrollView ref={r => this.scrollView = r}>
...
</ScrollView>
</ViewShot>
</View>
)
}
}
调用方法:
handleCaptureImage = () => captureRef(this.scrollView, {snapshotContentContainer: true}).then(uri => this.setState({uri}));
文档中对snapshotContentContainer 有说明,具体可参阅 captureRef(view, options) ,注意这个参数其实是属于options里面的,文档罗列参数的层级容易让人误会,我在尝试时就以为它跟options是同层级关系,导致长屏截图无果。
因为其他原生界面也有截屏需求,且同事已经实现相应界面,所以计划在RN界面截图之后,就将图片uri发送到原生,由UIScrollView配合UIImageView来展示图片,同事的实现代码中,contentSize由截图的size决定。
一切都按部就班的进行,但是图片展示出来之后,发现宽度居然是屏幕两倍,导致UIScrollVeiw可以左右滑动,并且高度值也比预想的大,总之问题表现就是图片变大了。于是断点调试打印下图片对象:
(lldb) po shotImage
<UIImage: 0x1c04b8180> size {750, 2211} orientation 0 scale 1.000000
可以看到图片size的width为750,而自己手机是iPhone 6s,截图宽度是375才正确,这里明显放大了一倍。把展示图片的contentSize的width改为屏幕宽度,图片明显被压缩了,这不是合适的处理方法。
当对一个组件有疑问的时候,自然是去查看源码实现了。见 源码line67 至 源码line92 ,当没有在options中设置宽高时,且截图内容组件为RCTScrollView子类,则截图size为该可滚动组件的contentSize,这没毛病。再看 源码line108,此行即为实际绘制截图的代码,为何导致当前问题,基本可以在该行代码找到答案了。
这里涉及一个绘图方法UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale),各参数说明:
size:很明显,该参数用于设置想要渲染的图片的尺寸opaque:指定所生成图片的背景是否为不透明,YES为黑色,NO为透明scale:表示位图的缩放比例,如果设置为0,则让图片的缩放因子根据屏幕的分辨率而变化
再看组件的实现代码:
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
所以组件在绘制时,位图的缩放比例是由屏幕分辨率决定的,而自己的手机的[UIScreen mainScreen].scale是2,所以图片实际被放大了一倍。注意不要被上面lldb输出的scale给搞混了,那个是生成之后的图片比例。
既然图片被放大,那么显示之前,再做个缩放好了:
...
// 由截图uri生成UIImage
UIImage *shotImage = [[UIImage alloc] initWithContentsOfFile:uri];
CGFloat shotImgW = shotImage.size.width;
CGFloat shotImgH = shotImage.size.height;
CGFloat targetW = SCREEN_WIDTH;
CGFloat targetH =(shotImgH * targetW) / shotImgW;
CGSize targetSize = CGSizeMake(targetW, targetH);
// scale不为1时,采用该方法,否则缩放后不清晰
// 为避免内存占用过高,scale为2或3时,统一设置缩放比例为2
if ([[UIScreen mainScreen] scale] == 2.0 || [[UIScreen mainScreen] scale] == 3.0) {
UIGraphicsBeginImageContextWithOptions(targetSize, NO, 2.0);
} else {
UIGraphicsBeginImageContext(targetSize);
}
[shotImage drawInRect:CGRectMake(0, 0, targetW, targetH)];
// 最终图片
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
...
至此,由RN生成长屏截图,再由原生UIImageView正确展示图片的功能,就完成了☕️。
你好,你这个是iOS平台的,Android平台的有用过吗?会不会出现截图很模糊,是怎么处理的?
@jun58 这个是跨平台的,没有遇到截图模糊的情况
@ljunb 现状是,图越长越模糊
@jun58 看来那年的业务场景,还没覆盖到这个。你可以在issue列表找找看吧,或者反馈下给作者