前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >QQ天气H5-前端完整解析

QQ天气H5-前端完整解析

作者头像
IMWeb前端团队
发布2017-12-29 17:25:54
2.8K0
发布2017-12-29 17:25:54
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

前言: 什么是手Q天气

手Q天气是在手Q 6.0版本以上新增的功能,页面会展现当天的气温情况,已经五天温度折线图以及24小时温度图表等。 并且为了更好的交互效果,天气页面会根据8种不同的天气信息,展现相应的天气动画。如下雨下雪,飘云,日光闪烁等动画效果。

在开发手Q天气的时候,学习到许多,发现有许多地方值得写一下。以下是我的总结。

一、REM整体布局

我们知道对于移动端来说,分辨率适配是个常见的问题,设计师往往给与我们的是iphone6(750px)的视觉稿。而对于天气页面来说采用REM来整体布局是个十分不错的选择。

rem 是什么

rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。rem计算的规则是依赖根元素。

基本写法

如下面定义html的节点

代码语言:javascript
复制
/*定义html根元素字体大小*/
html{
    font-size:10px;   
}
/*定义子元素,采用rem作为单位*/
.sonDom {
    width: 6rem;  /*相当于 6*10=60px*/
    height: 3rem;   /*相当于 3*10=30px*/
    line-height: 3rem;   /*相当于 3*10=30px*/
    font-size: 1.2rem;   /*相当于 1.2*10=12px*/
    border-radius: .5rem;   /*相当于0.5*10=5px*/
}

从上面可以看出,rem其实相当于一个划算单位

举个例子,我们平时理解的 1米=10分米=100厘米

同理可得: 1rem = 根元素font-size的值

这里一开始设置了 html根元素font-size为10px,即 1rem = 10px

通过上面的,我们可以得出

可以通过改变html的font-size的值而等比改变所有用了rem单位的元素

而这个正是rem实现完美的分辨率适配的原理。

如何动态更改根元素font-size值

为了实现分辨率适配,我们需要用根据屏幕的大小动态去计算根元素的font-size的值 目前普遍的是两种方法:

1、通过媒体查询方式

通过媒体查询的方式,能够满足大部分场景,只需要把常用的屏幕宽度考虑进去即可

代码语言:javascript
复制
/*默认为20px*/
html {
    font-size : 20px;
}
/*判断宽度设置不同的html font-size值去覆盖默认值*/
@media only screen and (min-width: 320px){
    html {
        font-size: 10px;
    }
}
@media only screen and (min-width: 375){
    html {
        font-size: 16; 
    }
}
@media only screen and (min-width: 414px){
    html {
        font-size: 20px; 
    }
}
2、通过js设置

如果希望把所有屏幕大小给考虑进去,可以考虑使用js来计算(天气H5也是使用js来换算),如下面的代码

代码语言:javascript
复制
 //设置fontsize
   var doc = document,
   win = window;
   function initFontSize  () {
        var docEl = doc.documentElement,
            resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
            recalc = function() {
                var clientWidth = docEl.clientWidth; //window.innerWidth;
                if (!clientWidth) return;
                fontSizeRate = (clientWidth / 375);
                var baseFontSize = 15 * fontSizeRate;
                docEl.style.fontSize = baseFontSize + 'px';
            };

        recalc();
        if (!doc.addEventListener) return;
        win.addEventListener(resizeEvt, recalc, false);
        doc.addEventListener('DOMContentLoaded', recalc, false);
    };

rem换算

我们在开发的时候,往往不希望涉及到rem的换算,如一个设计稿按钮是120px,如果我们根节点font-size是14px, 计算 120/14=8.571rem。感觉还是挺麻烦得 而这些工作是可以通过sass等预处理器或者构建去完成

如下面 翔神写的fis插件

https://github.com/imweb/fis-parser-rem

效果图

如下面, rem的适配效果还是挺不错的

rem需注意点

在移动端使用rem的话,兼容性没问题的。但还是存在着一些需要注意的地方:

1、小数数值处理

不同浏览器计算rem转换为px数值时,对于小数点后的数值的处理是有所偏差,rem计算偏差的根源是浏览器内核数字类型的区别,如果浏览器的内核数字类型是float类型,能够较好地支持有小数点的数值。当浏览器内核数字类型是int类型,不支持小数点,会对数字进行四舍五入,这样就会有偏差。如果元素越大偏差得就越明显!

2、雪碧图rem

使用rem的同时又涉及到雪碧图时,由上面我们可以得知,rem的换算成px的尺寸非严格精确尺寸,如果雪碧图如果图标之间的距离过小,就可能导致图标过界,因此图与图之间的间隙需要留相应大一点。

3、单纯的rem没解决高度适配的问题。

单纯的rem没解决高度适配的问题,当然目前也没有特别多高度适配的场景,因此建议如果需要在使用rem基础上还做相应的高度适配,就要通过相应的js去辅助啦。

2、弹性盒子局部布局

我们发现,过去对于页面均分的布局时,我们比较常用下面的方式:

  • 使用float浮动子元素的方法,但需要注意清除浮动
  • 或者是设置子元素为inline-block,但需要注意设置子元素margin值

或许我们可以考虑下css3弹性盒子模型。

兼容性

让人惊喜的是目前的主流智能移动设备操作系统Android和ios的内嵌浏览器对其也有不错的支持。对移动开发来说这真是太美好了,至少对于不太喜欢使用float,padding的我来说是这样的。

手Q天气的使用

如下面这样的布局整个div分成5个部分,每个部分占据同样的宽度。

上面的html结构如下
通过弹性盒子模型,设置其css代码如下:
代码语言:javascript
复制
/*设置容器为盒子*/
.info-day-list {
    display: -webkit-box;
    padding: 1rem 0;
}
/*设置item*/
.info-day-item {
    -webkit-box-flex: 1;  /*设置item占据的比例*/
    width: 1%;
    font-size: 1.4rem;
    line-height: 3rem;
    text-align: center;
}

通过上面,可以发现,弹性盒子将模块的所拥有的空间进行我们自定义比例去分配。上面每个item设置的 box-flex都为1,故其都有父容器剩余空间1比重的宽度。

需注意点

1、弹性盒子模型div块因为文字内容不同而不均分

在开发的时候,我发现在使用弹性盒子模型时,如果涉及到文字的时候需要注意

由于天气的描述文字长度不同,如西南风和微风,分别是三个字和两个字。会有不同的宽度而导致不均分

如上面css所示,我设置了子元素width为1%(只有设置了item是统一的width就行,不一定需要是1%)就可以解决这个问题

HTML5 canvas

我们可以看到在页面中带有温度折线图以及下雪下雨的动画,这个时候我们发现使用dom去绘制这样稍微复杂的动画时,性能并不好也不好操作。这时候我们可以考虑使用到HTML5的canvas画布去实现了。这样可规避渲染树的计算,使渲染更快

由于代码比较篇幅较长,这里只给最终生成效果哈。

折线图表

下雨下雪动画

效果如下, 发现使用canvas在绘制这些动画的时候,还是十分方便的。

具体实现可以看下面这个文章 - 前端如何呼风唤雨

canvas需注意点

1、canvas高清屏模糊

在绘制折线图的时候,我们发现,折线图在高清屏下十分模糊,这是为什么呢?

熟悉retina屏的同学应该都知道,在浏览器的window变量中有一个devicePixelRatio的属性,该属性决定了浏览器会用几个(通常是2个)像素点来渲染1个像素,举例来说,假设devicePixelRatio的值为2,一张100x100像素大小的图片,在retina屏幕下,会用2个像素点的宽度去渲染图片的1个像素点,因此该图片在retina屏幕上实际会占据200x200像素的空间,相当于图片被放大了一倍,因此图片会变得模糊。

因此我们的解决方案时:更加屏幕像素比devicePixelRatio的小同比方法canvas

如下面代码

代码语言:javascript
复制
  //兼容高清屏幕,canvas画布像素也要相应改变
  var c = document.getElementById("canvas");
  //获取devicePixelRatio
  var DPR = window.devicePixelRatio;画布宽高
  //同比设置画板宽高
  c.width = = canvasWidth * DPR;
  c.height = canvasHeight * DPR;

2、内存占用

canvas对内存的消耗是挺大的,如非必要还是不要使用多个canvas

css3 transition animation

我们可以使用CSS3的transition和animation来实现许多交互效果。

使用transition实现滑动Slider

在天气内页有个星座slider,如下面

通过设置每个卡片的类名,使其切换不同的位置

代码语言:javascript
复制
.star-icon-outside-l {
    z-index: 20;
    -webkit-transform: translateX(-18.8rem) scale(.673);
            transform: translateX(-18.8rem) scale(.673);
}
.star-icon-outside-r {
    z-index: 20;
    -webkit-transform: translateX(8.5rem) scale(.673);
            transform: translateX(8.5rem) scale(.673);
}
.star-icon-beside-l {
    z-index: 40;
    -webkit-transform: translateX(-12.5rem) scale(.851);
    -webkit-transform: translateX(-12.5rem) scale(.851);
}
.star-icon-beside-r {
    z-index: 40;
    -webkit-transform: translateX(2.35rem) scale(.851);
            transform: translateX(2.35rem) scale(.851);
}
.star-icon-hide-l {
    z-index: 10;
    -webkit-transform: translateX(-25rem) scale(.673);
            transform: translateX(-25rem) scale(.673);
}
.star-icon-hide-r {
    z-index: 10;
    -webkit-transform: translateX(17rem) scale(.673);
            transform: translateX(17rem) scale(.673);
}
.star-icon-cur {
    z-index: 90;
    -webkit-transform: translateX(-5.05rem) scale(1);
            transform: translateX(-5.05rem) scale(1);
}

animation循环动画

1、渐隐渐现
代码语言:javascript
复制
@-webkit-keyframes toggleShow
{
    0% {
        opacity: 0;
    }
    11% {
        opacity: 0;
    }
    12.5% {
        opacity: 1;
    }
    20%{
        opacity: 0;
    }
    100%{
        opacity: 0;
    }
}
2、放大收缩
代码语言:javascript
复制
@-webkit-keyframes shine
{
    0% {
        -webkit-transform: scale(1,1);
    }
    50% {
        -webkit-transform: scale(1.2,1.2);
    }
    100% {
        -webkit-transform: scale(1,1);
    }
}
3、飘动
代码语言:javascript
复制
@-webkit-keyframes moveToLeft /* Safari 和 Chrome */
{
    from {-webkit-transform: translate(0);}
    to {-webkit-transform: translate(-33.33333333%);}
}

优化

我们知道,在移动端开发,性能和加载速度是十分重要的,这里我们就需要考虑所有前端能优化的点,做好了优化, 才能时你这个页面更好的展示。以下是相关的优化内容

基本动画优化

基本的动画优化,如使用transform的translate来代替left等位移操作 3D加速等,

合理使用RAF(requestAnimationFrame)

使用raf能解决脚本问题引起的丢帧,卡顿问题,并且支持中间状态监听

代码语言:javascript
复制
//首页判断是否可以使用requestAnimFrame来替换setTimeout
window.requestAnimFrame =
    window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    function(callback) {
        return window.setTimeout(callback, 1000 / 30);
    };

canvas优化

使用canvas实现下雨下雪效果,是通过一帧一帧地去重绘下落的雨滴或者雪花。这里雪花雨滴越多,对手机的性能要求就越高。

因此我们需要对不同的手机进行处理,对于一些稍微低端的手机进行一些降级处理和优化。 根据渲染情况,相应的减少雨滴和雪花的个数,减少渲染计算时间

代码语言:javascript
复制
//判断每次update的时间差,如果发现时间长过长,则相应地减少动画的最大雪花个数
 if (new Date - lastTime > 30 && drops.length < OPTS.maxNum && OPTS.maxNum > 21) {
      OPTS.maxNum -= 10;
 }

内存优化

由于我们打开天气广告时,是新开一个webview的 因此我们需要暂停被遮住的天气webview的天气动画,减少内存消耗

代码语言:javascript
复制
if(mqq&&mqq.iOS&&mqq.addEventListener){
      mqq.addEventListener("qbrowserVisibilityChange", function(e){
          cancelAnimationFrame(drp_ticker);
          if(!e.hidden){
              update();
          }
      });
  }

合理使用缓存

为了加快二次加载页面的速度,我们就需要使用好缓存。

天气的数据,都会用localstorage缓存起来

第二次短时间加载则会使用localstorage的数据,加快二次加载速度。

代码语言:javascript
复制
 //判断是否有可用的缓存
if (checkCache('weather-local_weather_info')) {
    //有则使用缓存
    Page.processData(local_weather_info);
    weather_info = local_weather_info;
} else {
    //没有缓存则去请求
    Vinda.getData('data-weatherInfo', function(data) {
        if (data && data.retcode == 0) {
            Page.processData(data);

            //将天气信息存储进来,新增时间戳,用于缓存新鲜度的判断
            $.extend(data.result, {
                searchTime: +Date.now(),
                city: city
            });
            //每次获取都会更新缓存
            $S.save({
                key: 'local_weather_info',
                value: data
            });
    });
}

预加载

DNS预解析

我们可以通过dns 预解析prefetch,提前解析,减少dns请求时间

代码语言:javascript
复制
<link rel="dns-prefetch" href="//pub.idqqimg.com" />
CGI预加载

由于天气页面是强数据页面,对于cgi数据是强依赖的。因为提前预先加载cgi能够使我们更快地去渲染页面而不是等先拉取页面js再去执行页面js去请求cgi的这样的顺序。

代码优化

dom对象池复用

在天气内页有个星座slider,如下面

我们知道总共有12个星座,但我这里却只使用了7个dom(5个可见,2个分别是隐藏的),通过复用来实现循环的12个月。虽然这里并没有减少很多dom的数量,但我觉得dom对象池复用的思想能给多dom节点的的场景带来质的飞跃。

异步加载权重较低的模块

由于整个天气又有折线图,又有动画,又有下雨下雪等东西。因此我们需要对页面进行模块划分。每个部分都是一个模块 我i将天气页面大致分成下面几个模块

代码语言:javascript
复制
//页面总模块
var Page = {/**/};

//头部模块
var headerMod = {/**/};
//时间维度的温度变化图
var timeDegreeMod = {/**/};
//广告模块
var adsMod = {/**/};
//一周天气情况图
var detailMod = {/**/};
//天气动画模块
var AnimationMod ={/**/}

然后通过总模块去管理子模块,由于模块的划分,我们可以很轻松的根据页面展示权重和先后顺序,分别去渲染和异步加载相应的模块

代码语言:javascript
复制
var Page = {
    render: function(){
        //渲染基本页面
        headerMod.render();
        timeDegreeMod.render();
        detailMod.render();
        //加载完天气信息才去加载广告;
        adsMod.getAds();
        //由于权重较低,因此异步加载下雨下雪的动画部分组件
        require.async('./setAnimation', function(AMod) {
            AnimationMod = AMod;
            AnimationMod.init();
        });
    }
}

其余基本优化

雪碧图,文件合并等减少请求数

资源压缩,代码压缩减少请求体积。

内联css, js置后等渲染无阻塞

兼容点

在开发手Q天气时,还遇到下面一些需要兼容和注意的点:

ios 广点通app广告处理逻辑兼容

由于手Q天气涉及到广告,大部分广点通广告是只需要点击链接跳转就可以了

但有些广告由于是app广告,需要引导用户去下载,故在ios上则需要做些兼容。在ios手机需要通过以下判断,改为呼起app store下载页面

代码语言:javascript
复制
  //判断是否为手Q打开且为ios且为app下载广告
var isIosAppAds = mqq.iOS && mqq.device.isMobileQQ() && producttype == 19;
//如果是app ios 广告,
//则jsonp请求广点通给的跳转链接,获取跳转appstore的tencent串
if (self.isIosAppAds) {
    $.ajax({
        url: adsMod.jump_url,
        data: {
            acttype: 1,
        },
        dataType: 'jsonp',
        success: function(o) {
            if (o && o.ret >= 0) {
                var data = o.data || {};
                if (data.dstlink) {
                    //通过手Q接口呼起app
                    mqq.app.launchApp({
                        name: data.dstlink //self.dstlink
                    });
                }
            }
        }
    });
} else {
    //默认打开新webview即可
    mqq.ui.openUrl({
        url: adsMod.jump_url,
        target: 1,
        style: 0
    });
}

X5内核兼容点

由于我们的场景是在手Q上打开,故需要兼容X5内核上的规范。

X5 tbs.1x版本时,伪元素是不能做动画的。

X5 tbs.1x版本时,不支持transition-timing-function 的ease-out曲线

目前了解,貌似到了X5 tbs.2x版本正在开始灰度支持。

更多x5上的问题,可以通过以下链接去查看QQ浏览器官网的 X5技术指南

非a标签跳转 bug

因为天气页面有许多跳转上报,需要先上报再跳转,然后我之前是这样写的

代码语言:javascript
复制
<div id="js-jump-xxx" onclick="reportAndJump();"></div>

但这样写发现在低版本的android机上,只能发起上报请求而不能进行链接跳转

后来经排查,发现低端android机只能使用a标签进行跳转操作

代码语言:javascript
复制
<a id="js-jump-xxx" onclick="reportAndJump();"></a>

不足

  • 由于页面涉及到比较多的动画和canvas,故对内存的消耗比较高,这方面一直没有很好去解决低内存手机的内存消耗问题
  • 页面动画渲染和代码仍需雕琢。当时刚入职,许多方面还是一知半解。故自我觉得仍然有许多可以优化的地方。。。

总结

QQ天气H5是我毕业来到腾讯的第一个独立开发的项目。虽然现在已经交接了。但我时不时都会去看下这个项目的动态和代码提交记录。QQ天气H5这个项目,让我在刚入职时学会了许多。虽然写得并不是很好啦。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、REM整体布局
    • rem 是什么
      • 基本写法
        • 如何动态更改根元素font-size值
          • rem换算
            • 效果图
              • rem需注意点
              • 2、弹性盒子局部布局
                • 兼容性
                  • 手Q天气的使用
                    • 需注意点
                    • HTML5 canvas
                      • 折线图表
                        • 下雨下雪动画
                          • canvas需注意点
                            • 2、内存占用
                            • css3 transition animation
                              • 使用transition实现滑动Slider
                                • animation循环动画
                                • 优化
                                  • 基本动画优化
                                    • 合理使用RAF(requestAnimationFrame)
                                      • canvas优化
                                        • 内存优化
                                          • 合理使用缓存
                                            • 预加载
                                              • 代码优化
                                                • 其余基本优化
                                                • 兼容点
                                                  • ios 广点通app广告处理逻辑兼容
                                                    • X5内核兼容点
                                                      • 非a标签跳转 bug
                                                      • 不足
                                                      • 总结
                                                      相关产品与服务
                                                      容器服务
                                                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                                                      领券
                                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档