Eason
Eason
  • 发布:2015-12-30 01:36
  • 更新:2016-01-18 17:01
  • 阅读:12565

页面切换后才渲染数据【已解决】

分类:MUI
mui

先简单说下页面流:首页(列表)、详情页面。
首页加载时,已经预加载了详情页面。监听列表的tap事件,一触发及通过mui.fire通知详情页面ajax加载数据并渲染,然后mui.openWindow打开新窗口。出现以下问题:流畅的切换到详情页面,这时页面才会渲染一下(执行我ajax成功回调中的方法),而不是显示已经渲染好的页面。就算我使用等待框或者setTimeout也解决不了这个问题。所以我想是否是我渲染数据的代码出了问题,请指教。下面贴上代码。

不必在意setTimeout的方法,这里是为了优化pop-in的效果。50ms的延迟和这个问题没有关系。

列表监听事件:

            // 主列表点击事件  
            mui('#list').on('tap', 'a', function() {  
                //plus.nativeUI.showWaiting("正在加载...");  
                var page = plus.webview.getWebviewById("ratedetail.html");  
                var objId = this.getAttribute('data-id');  
                var id = this.getAttribute('href');  
                var href = this.getAttribute('href');  

                // 通知详情页面去ajax拉取数据(在渲染页面和移动窗体之前)  
                mui.fire(page,'pulldata',{id:objId});  

                // 延迟50毫秒显示窗体。解决被tap元素高亮效果不消失的问题。  
                // 另:为了传参将延迟函数写成该形式:setTimeout(function(){yourFuction(params)},time)  
                setTimeout(function(){openNew(objId,id,href)},50);  
            });  

            function openNew(objId,id,href){  
                var page = mui.openWindow({  
                    id: id,  
                    url: href,  
                    createNew:false,  
                    show: {  
                        aniShow: 'pop-in'  
                    },  
                });   
            }

详情页面(这里只贴上ajax渲染数据部分):

// 成功回调,渲染数据  
            var onSuccess = function (data) {  
                console.log(JSON.stringify(data));  
                var depa = document.getElementById("depa");  
                depa.innerText = data.obj.depa;  
                var dest = document.getElementById("dest");  
                dest.innerText = data.obj.dest;  
                var airline = document.getElementById("airline");  
                airline.innerText = data.obj.airline;  
                var week = document.getElementById("week");  
                week.innerText = data.obj.week;  
                var cargoType = document.getElementById("cargoType");  
                cargoType.innerText = data.obj.cargoType;  
                var currency = document.getElementById("currency");  
                currency.innerText = data.obj.currency;  
                var duration = document.getElementById("duration");  
                duration.innerText = data.obj.duration;  
            };  

2015.12.30更新
如果没有解决方案的话,是否能判断dom何时渲染完毕,再关闭等待框。现在的问题在于连这点也无法实现。

2015.12.31更新
使用pop-in会采用切换前后页面的截图,这个效果的造成是因为ajax是直译式语言,虽然将show出新窗体的代码写在渲染代码之后,但是渲染的效率根据手机的性能不同而不同。所以我的解决方案是,渲染完毕后用定时器延迟50ms,此时再show新窗口就可以了。然后show新窗口直接在新窗口的js代码里写就ok。感谢@maq的积极响应。

2015-12-30 01:36 2 条评论 负责人:无 分享
已邀请:
maq

maq

我的猜测是这样的:pop-in 强制采用了 capture 的方式实现动画过程(除非页面尚未加载完成无法获取 capture),从而导致了问题现象。

想要确保 AJAX 渲染完成后再 show() 很容易,不用定时器延时,只要在 webview 里面渲染完成后再调用 show() 把自己显示出来即可。

但即使这样,由于 render:"onscreen" 的影响,capture 得到的仍然是渲染前的内容。设置 render:"always" 虽然可能有用,但由于机型、版本的原因并不可靠。

唯一可靠的办法就是不要用 pop-in,改用 slide-in-right 好了,也不差多少,hehe

  • Eason (作者)

    pop-in毕竟体验好,还能解决返回字体模糊的问题。在哪里show()没有太大区别,然后目前定时器延时是一定要的,因为javascript是直译式语言,虽然show()写在渲染数据的代码后,但未必已经渲染好了。除非监听dom已经加载完毕的事件,里面写show()这样能做到0延迟。

    2015-12-30 17:17

  • maq

    根据目前推演的情况来看,如果一定要用 pop-in 的话,就只能等官方来确认并修复 BUG 了。


    应该有两个方面需要修复,一个是 pop-in 不要强制采用 capture 方式实现动画过程,要允许 show 的过程中实时渲染。另一个是 capture 之前要给 webview 一次 repaint 的机会,避免获取到过时的页面截图。

    2015-12-30 18:02

maq

maq

好问题!虽然没有经验,也忍不住想参与一下 :)

有没有试过把 50 毫秒加大,大到足够让“AJAX+渲染”全部完成?

猜测可能仅仅是 AJAX 响应不够快而已。

  • Eason (作者)

    加长多久还是这个效果。和@clender说的一样

    2015-12-30 09:57

上交国家

上交国家

你这个明明是打开详情页后才去请求数据,当然需要一个请求的时间差了,预加载并没有加载数据没什么卵用啊。

  • Eason (作者)

    1、预加载的代码在首页打开前我就已经执行了。

    2、我先用mui.fire请求了数据才打开的页面。

    3、预加载本来就不是加载数据,而是加载页面模版。

    2015-12-30 09:56

chender

chender - 与人为善

部分手机上无法在webview未显示时进行渲染(虽然创建openview的时候一个参数,控制webview的渲染方式),所以你的问题不一定是ajax异步导致的,因为一般开发环境都是局域网,ajax请求一般都比较快,50ms完全够了;
我之前也遇到过你这样的情况,那怕延迟1分钟再将webview显示出来(1分钟足够webview进行内容的渲染了吧),在动画的过程中,webview还是白的,没有内容;

  • Eason (作者)

    50ms是为了解决另一个问题的,不用考虑。我也换了三台手机都是这样的效果。那你现在解决了这个问题了吗?

    2015-12-30 10:14

maq

maq

嗯,很可能是 WebviewStyle.render 设置的问题,可以设置成 'always' 再观察一下。

  • Eason (作者)

    这个我也试过。小米4有时可以,再老一点的安卓机完全不行了。现在主要问题是让显示等待框。等全部加载完毕再去掉等待框。虽然我代码是这么写的,但是总是等待框消失后页面才开始渲染,体验不好。

    2015-12-30 10:13

我勒个去

我勒个去

哈哈,我以前也是这么想的,但实际是不可以的,不是延迟的问题,压根就是需要在窗口show完成了,才能ajax,昨天现场直播里面chb好像也说到这个问题,虽然是建议显示完以后ajax,但实际是无法后台ajax,这点也一直困扰着我,

因为如果能在后台ajax,就可以做到窗口动画完成,数据就显示在面前,而不是窗口动画完,去ajax,然后数据突然跳出来

chender

chender - 与人为善

ajax是可以后台做的,这个我很确定,你可以打日志跟踪一下;根本原因是界面必须要webview显示出来后才能进行渲染(事实上你回调函数已经执行,你要动态显示的数据也已经添加到页面中,只是浏览器还没对页面进行渲染而已);
WebviewStyle.render 这个属性在部分手机上没有效果,这部分手机目前还没有什么解决方案

  • Eason (作者)

    ajax可以后台跑是毋庸置疑的。现在问题就和你说的一样。这样体验太糟糕了,为什么没人提。坐等官方给个说法啊。

    2015-12-30 10:30

maq

maq

分析渲染的过程,很可能是这样的:当一个 webview 处于未显示状态的时候,reflow 已经全部完成了,只差一个 repaint 的机会。(关于 reflow 和 repaint,推荐一篇非常好的文章《网页性能管理详解》)

如果直接启动 webview.show() 过程,想必系统会以 webview 中残留的显示内容来显示动画过程(其实如果系统能在这个过程中实现 repaint 也基本上能解决问题了),所以导致后面的重绘过程。

所以我想到一个方法:先调用一个无动画效果的 show() 把 webview 显示出来,但控制它的位置在屏幕之外,也就是实际上是看不到的。如果这样能骗过系统,让它给 webview 一个 repaint 的机会,那么接下来再调用一次 show() 把这个 webview 显示到屏幕上来,也许就可以了。

这个方法说实话很烂,但如果真能有效的话,也算是一个补救措施。

  • maq

    如果系统足够聪明,能够判断出 webview 在屏幕之外,并因此拒绝 repaint,那就没办法了。

    2015-12-30 11:07

  • Eason (作者)

    回复 maq:方向是对的,render:“always”应该就是解决类似的问题的,但是它应该也是异步的。所以性能好的手机这个问题不是很明显。

    2015-12-30 11:16

  • maq

    回复 Eason:结合你提出的这个问题,我有点觉得 render:"always" 这个设计并不科学。开启了这个选项(即便对这个选项的支持在所有机型都没有 BUG),虽然能避免渲染延迟导致的问题,但由此带来的额外开销并不合算。其实只要能有办法给 webview 一次额外的 repaint 机会就能解决问题了。

    2015-12-30 11:38

maq

maq

看到文档 《WebviewExtraOptions

里面提到【当窗口执行动画操作时,使用图片会加速窗口动画效率,提升用户体验。 如果未指定窗口动画使用的图片,则根据动画效果默认使用窗口的实时动画效果。】

按这个意思,在 show() 的过程中,webview 应该是有机会渲染的呀……难道是基座程序的 BUG?

maq

maq

刚做了一个试验,代码如下。

测试结果:在动画过程中,webview 中的内容是可以实时动态渲染的。

测试环境:Genymotion 模拟器,手机型号【Samsung Galaxy S3 - 4.2.2 - API 17】

// index.html  
var embed = plus.webview.create('sub-webview.html', '', {top:"150px",bottom:"50px"});  
embed.show('pop-in', 5000);  

// sub-webview.html  
var cnt = 0;  
setInterval(function() {  
    mui('#my_counter')[0].innerText = cnt;  
    console.log(cnt++);  
}, 500);

按这个测试结果的话,原帖问题就不应该存在呀……晕了……

  • chender

    和手机有关系,有一些手机必须要webview显示出来后(并且必须是显示在屏幕上)才进行渲染

    2015-12-30 12:32

  • maq

    不一定是手机型号的差别,可能跟调用方式有关,请看后面的报告 :)

    2015-12-30 12:34

maq

maq

最新测试报告:如果我把 index.html 里面的代码改成下面的样子,问题就出现了,show() 动画过程中,页面内容是凝固的!而当时 js 一直在持续运行。

var embed = plus.webview.create('sub-webview.html', '', {top:"150px",bottom:"50px"});  
setTimeout(function() {  
    embed.show('pop-in', 5000);  
}, 3000);

哈哈,怎么看都像是个 BUG。看来得官方大神出场了 ^_^

  • Eason (作者)

    我之前用了50ms的setTimeout原因就是切换前默认用的是截图,目前猜测之后也是先show新窗口的截图,再渲染。如果是截图,可以自定义截图的时间点就可以解决这个问题。已经提交该issue到github上了,坐等大神回复。

    2015-12-30 13:55

  • Eason (作者)

    预截图参照:http://ask.dcloud.net.cn/article/25,全文搜索一下

    2015-12-30 13:57

  • Eason (作者)

    果然是动画的问题,已经解决了。稍后发一个传送门。

    2015-12-30 14:22

chender

chender - 与人为善

楼上说的这种现象我以前遇到过,和pop-in动画有关系,pop-in动画有很多诡异的现象;
比如你的这个例子里面,如果你把embed的上下边距设置为0px,就不会出现你说的这个问题了;
还有我感觉pop-in的动画,即使没有用预截图进行加速,在一些情况下移动的时候也只是webview的一个副本,所以界面花“卡住”

maq

maq

回复 chender:
我按照你说的把 top 和 bottom 都设置为 0px 了,可是结果是一样的,show() 的动画过程中页面内容一样是凝固的。到目前为止我的实验观察结果是:show() 的动画过程中页面内容能否实时动态渲染,只跟 show() 调用的时机有关。

  • 如果是在 create() 之后紧跟着 show(),页面就是活的;
  • 如果 create() 之后等待一段时间(估计是页面加载完成之后)再 show(),页面就是凝固的。
  • chender

    那就是手机型号的问题,我这边刚才测了一下,华为P6和索尼L36H,就用的你上面贴的代码,如果top和bottom为0px或者不设置,embed页面在动画的过程中是会进行实时渲染的

    2015-12-30 15:09

  • chender

    有些手机上即使不用pop-in,用slide,也会出现你说的这种现象

    2015-12-30 15:11

maq

maq

更新测试报告:如果把 'pop-in' 换成 'slide-in-right' 的话,就不会凝固了!

呵呵,我感觉都快猜出 BUG 是怎么来的了 ^o^

  • Eason (作者)

    哈哈,你猜对了,就是pop-in。pop-in之所以体验好,是因为它将切换前和切换后的两个页面都截了图,然后动画是用截图显示的。在渲染前截图了,无论你延迟多久,截的图是不会变的。所以解决方案是:ajax渲染后通知打开新的窗口前,先setTimeout延迟个50-100ms,然后再打开就非常完美了。

    2015-12-30 15:29

  • Eason (作者)

    现在就想知道延迟多久是否能确定下来。比如dom渲染完毕后再通知。

    2015-12-30 15:30

  • maq

    是不是只要放弃 'pop-in' 就百无禁忌?

    2015-12-30 15:37

我勒个去

我勒个去

哦?改下就能实时渲染了?

我勒个去

我勒个去

我也改用slide-in-right 窗口动画以后,基本达到要求了, 我点击的时候,先执行自定义事件,去获取数据,然后窗口开始滑动, 等窗口出现的时候,页面+数据都渲染好了,基本达到要求,

fightforh5

fightforh5

我也碰到这个问题,关注下

云海帆

云海帆 - 咨询问题请+Q1395641578

我遇到一个类似的问题 http://ask.dcloud.net.cn/question/14836
不知道和你这个是不是一个问题。
你用的Android版本是多少?
我今天发现只有4.3 会出现问题。测试了4.4 和5.1 没有问题。

该问题目前已经被锁定, 无法添加新回复