前言
最近的几个项目中都需要加载大量的HTML,需要将HTML中的图片在点击时实现原生预览。由于HTML的来源很复杂,包括有后台框架编辑的、第三方提供的、网络抓取的等等。所以只能采取获取HTML之后前端注入JS事件来触发图片点击事件,获取当前页面的所有图片地址。由于实现的原理很简单(两行代码),就不需要使用JavaScriptCore或者其他第三方框架了。
实现效果
框架整体介绍
- 该框架为一个通用HTML图片预览框架,旨在提供一种简单快捷的调用方式来native预览网页图片。
- 同时支持UIWebView与WKWebView。
- 支持网页图片自定义过滤规则。过滤“头像、广告”之类的小图标。
- 支持用户自定义核心抓取图片JS、自定义解析规则。
- 支持配置参数确定是否仅抓取正文(conent)部分图片。
- SDK支持解析懒加载类型HTML网页(ps:简书更新以后图片加载就是采用的滚动加载模式)
实现原理
- HTML加载完成之后注入图片点击的JS。
- 截取JS的点击事件并拆分出所有图片URL和当前点击图片URL。
- 通过OPTION配置项、配置相关业务参数。
- 自动解析懒加载类型HTML网页、允许用户自定义懒加载核心属性。
注入图片点击JS需要完成的功能
- 点击图片能够响应图片的点击事件。
- 能够获取当前点击图片的URL。
- 能够获取当前HTML中的所有图片URL。
- 能够将图片点击事件与网页自带点击事件区分开来。
- 能够将当前点击图片URL与所有图片URL区分开来。
截取JS的点击事件解析URL
- 根据标识符判断点击是否执行图片预览功能
- 根据分隔符拆分当前点击图片URL与所有图片URL。
- 根据分隔符将所有图片URL合并成图片数组。
- 具体业务过滤不合法图片URL(eg:包含logo等)。
核心JS代码实现
// 通知 iPhone UIWebView 加载 url 对应的资源
//PhoneGap处理方式
function loadURL(url) {
var iFrame;
iFrame = document.createElement("iframe");
iFrame.setAttribute("src", url);
iFrame.setAttribute("style", "display:none;");
iFrame.setAttribute("height", "0px");
iFrame.setAttribute("width", "0px");
iFrame.setAttribute("frameborder", "0");
document.body.appendChild(iFrame);
// 发起请求后这个 iFrame 就没用了,所以把它从 dom 上移除掉
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
}
function zwPreviewImageClickAction(){
var imgs=document.getElementsByTagName('img');
var length=imgs.length;
var allSrc='';
for(var i=0;i<length;i++){
var img=imgs[i];
var imaSrc = '';
if (img.src.length){
imaSrc = img.src;
}else{
imaSrc = img.getAttribute('data-original-src');
}
if (allSrc.length) {
allSrc = allSrc+'^^^'+imaSrc;
}else{
allSrc = imaSrc;
}
}
for(var i=0;i<length;i++){
var img=imgs[i];
img.onclick=function(){
// window.location.href='zw-image-preview:'+allSrc + '###'+this.src;
loadURL("zw-image-preview:"+allSrc+ '###'+this.src);
}
}
}
zwPreviewImageClickAction();
截取JS事件并解析URL
- UIWebView在shouldStartLoadWithRequest代理方法中
- WKWebView在decidePolicyForNavigationAction代理方法中
if ([request.URL.scheme isEqualToString:@"zw-image-preview"]) {
NSString *urlPath = [request.URL.absoluteString substringFromIndex:[@"zw-image-preview:" length]];
NSArray *mixURLArray = [urlPath componentsSeparatedByString:@"###"];
//图片地址合集
NSString *allImageURL = [mixURLArray firstObject];
//当前实际点击图片的地址
NSString *indexImageURL = [mixURLArray lastObject];
}
实际项目中遇到的问题
- HTML来源与展示界面比较复杂,无法统一WebView界面。导致以上代码需要重复调用多次。
- HTML的格式比较奇葩,鬼知道后台使用的什么转换器。
- 图片地址命名会出现与分隔符冲突等等。
- HTML中混杂了小图标(logo、头像等)需要过滤。
封装JS与URL解析
为了避免重复调用,快速实现功能。对上述功能做了简单的封装。
- UIWebView实现以下两行代码即可
-(void)webViewDidFinishLoad:(UIWebView *)webView{
self.htmlSDK = [ZWHTMLSDK zw_loadBridgeJSWebview:webView];
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ([self.htmlSDK zw_handlePreviewImageRequest:request]) {
return NO;
}
return YES;
}
- WKWebView实现以下两行代码
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
self.htmlSDK = [ZWHTMLSDK zw_loadBridgeJSWebview:webView];
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
[self.htmlSDK zw_handlePreviewImageRequest:navigationAction.request];
}
效果展示
SDK源码解析
- 加载HTML中的所有图片,包括推广广告图、logo、用户评论头像等等。参照上图:包括了简书的logo、下文的推广链接图。
self.htmlSDK = [ZWHTMLSDK zw_loadBridgeJSWebview:webView];
- 仅加载标准正文content中的所有图片(例如简书中正文文章中所有图片),增加了过滤条件OPTION_StandardCoreJS。
self.htmlSDK = [ZWHTMLSDK zw_loadStandardBridgeJSWebview:webView];
- 如果以上还是不能满足需求,需要过滤更多的HTML图片信息,则需要自定义option过滤器。eg:仅加载正文HTML图片、过滤掉所有图片URL包含有logo、avaters的图片。
ZWHTMLOption *option = [[ZWHTMLOption alloc] init];
option.getAllImageCoreJS = OPTION_StandardCoreJS;
option.filterURL = @[@"logo",@"avaters"];
self.htmlSDK = [ZWHTMLSDK zw_loadBridgeJSWebview:webView withOption:option];
- 需要加载执行自定义的JS
ZWHTMLOption *option = [[ZWHTMLOption alloc] init];
option.zwPreviewJS = @"自定义的JS";
self.htmlSDK = [ZWHTMLSDK zw_loadBridgeJSWebview:webView withOption:option];
图片预览显示
- 按照上面两行代码实现调用即可预览显示。SDK内部已经封装了图示的显示模式。
//预览视图、已集成SDK中
ZWPreviewImageView *showView = [ZWPreviewImageView showImageWithArray:allImageArray withShowIndex:index];
[showView showRootWindow];
- 如果用户需要自定义(嫌弃>_<)图片浏览器,只需要实现调用以下的block。allImageArray:过滤后所有URL数组。index:当前操作图片的序号。
self.htmlSDK.blockHandlePreview = ^(NSArray *allImageArray, NSInteger index) {
//自定义图片预览
};
如何使用SDK
强烈建议您使用pod导入,节省导入依赖的时间。
- 使用cocoaPods导入(搜索不到请更新本地仓库)
pod 'ZWHTMLImage', '~> 0.0.2'
- 直接将文件ZWHTMLImage拖入工程中
依赖ZWPhotoPreview图片显示框架。
#import "ZWHTMLSDK.h"
关于图片保存权限
- 长按保存功能需要用户info.plist中配置权限
Privacy - Photo Library Additions Usage Description
源码
源码放在GitHub上,欢迎指正,记得star哦!
v0.0.2版本更新记录
【新增】: 支持懒加载类型网页图片的读取功能。
【修改】: 重新构造图片预览功能、更新图片预览框架ZWPhotoPreview最新版本。
【新增】: 提供图片快速预览、手势拖放动画、手势缩放、长按保存、页码选择等最新功能。
v0.0.1版本更新记录
- 【新增】: 提供简便的HTML图片放大预览功能。
- 【新增】: 提供简便的HTML图片过滤功能、用户自定义过滤参数。
- 【新增】: cocoapods支持。