移动端页面demo
1. 项目介绍
项目共包含三个页面
- 首页
- 设备页
- 我的
每个页面都有一个公共的底部Tab,由于我们还没有讲到Vue和路由,所以这里不做成SPA(单页应用),而是把footer放到每一个页面中。
这是一个移动端项目,为了适应移动端设备,需要首先自适应的布局,布局方式,主要使用了flex弹性盒和vw作为单位。
项目开发分为两个阶段,第一阶段,实现页面布局,第二阶段,通过jQuery的ajax,请求数据,然后使用art-template模板引擎渲染。
2. 页面布局实现
2.1单位
页面布局主要使用了flex和vw技术。flex这里不多说,主要讲下vw。
vw也是一种相对单位,它把页面宽度分成100份,每一份的大小就是1vw。这样当所有元素大小都是用vw作为单位时,页面缩放的时候,元素大小也就会实现同步缩放。
具体到本项目来说,我们按照宽度为750px来计算。
100vw = 750px
所以: 1vw = 7.5px
比如,设计图中,某个元素的宽度为30px,那么使用vw表示就是 4vw,某个文字大小为24px,则使用vw表示为3.2vw。
2.2 ACSS
随着项目的开发,样式代码也会越来越多,为了规范css命名方式,出现了很多css命名规范,例如:BEM, OOCSS,AMCSS,SMACSS,SUITCSS,ITCSS,ACSS等。
关于BEM命名规范,大家可以参考这个链接学习:https://bemcss.com/
这里我们重点说下ACSS。
什么是ACSS
ACSS 即: Atomic CSS,是一套将样式原子化的方式。
什么意思呢?首先让我们思考这样一个场景:
比如要定义一双球鞋,球鞋有很多属性,比如:品牌,尺码,颜色,重量,季节,材料等。
如果用以前的方式定义样式,那写出来就是:
球鞋A{
品牌:"李宁";
尺码:42;
颜色:红色;
重量:300g;
季节:夏季;
材料:䨻;
}
好,然后又有第二双鞋:
球鞋B{
品牌:"匹克";
尺码:42;
颜色:红色;
重量:280g;
季节:夏季;
材料:态极;
}
我们发现在这两双球鞋的属性中有很多相同的属性,比如颜色,季节等,但是在定义每双鞋的时候要重复写一遍,如果还有第三双,第四双鞋,任然有可能有重复的属性,可能是这两个属性,也可能是其他属性,那么有没有办法更好的复用这些属性呢?
当然有,就是我们接下来要介绍的ACSS。
ACSS这种方式将CSS属性进行原子化,也就是一个属性定义成一个类!
比如定义球鞋的时候,有品牌,尺码,颜色,重量,季节,材料等这些属性,那就可以定义为:
.brand-ln{
品牌:李宁;
}
.brand-pk{
品牌:匹克;
}
.size-42{
尺码:42;
}
.size-43{
尺码:43;
}
.color-red{
颜色:红色;
}
.seazon-sum{
季节:夏天;
}
.material-beng{
材料:䨻;
}
.material-tj{
材料:态极;
}
当这些原子化的类定义好之后,一双李宁,红色,夏天穿的,采用了䨻材料的42码球鞋就表示为:
<球鞋 class="brand-ln color-red seazon-sum material-beng size-42" />
而一双匹克,同样红色,夏天穿的,采用了太极科技材料的43码鞋就表示为:
<球鞋 class="brand-pk color-red seazon-sum material-tj size-43" />
看明白了吗?
这样虽然貌似定义了很多类,但是可以在最大程度上复用这些类,而且不用在费尽心机去想元素的类名了。还有就是,之前我们会经常采用父子嵌套的方式定义样式,比如: .banner div ul li h1
。这种方式一但DOM结构发生变化,那么样式就很容易出问题,给后期迭代维护造成不便。
在本次项目中,我们就讲采用ACSS的方式来定义样式。
ACSS 在项目的应用
比如下面的样式,我们就采用了ACSS的方式定义了一组文本大小的样式:
/* 按照屏幕宽度 750px 计算 1vw = 7.5px */
.f-60 {
font-size: 8vw;
}
.f-54 {
font-size: 7.2vw;
}
.f-40 {
font-size: 5.3vw;
}
.f-34 {
font-size: 4.5vw;
}
.f-32 {
font-size: 4.3vw;
}
.f-30 {
font-size: 4vw;
}
.f-28 {
font-size: 3.7vw;
}
.f-26 {
font-size: 3.5vw;
}
.f-24 {
font-size: 3.2vw;
}
.f-20 {
font-size: 2.7vw;
}
.f-22 {
font-size: 2.9vw;
}
在.f-60
这个类中,定义了font-size
等于8vw
,1vw = 7.5px,所以8vw就是60px,其它以此类推。
这样我们就是可以使用这些类来给元素添加样式了:
<div class="block-icon">
<div class="flex f-weight j-s-b">
<p class="f-28 black-1">环境检测</p>
<p class="f-24 gray-90">客厅</p>
</div>
<div class="flex j-s-b al-center mt-40">
<img class="source-1" src="./images/furnishing_1.png" alt="">
<div class="f-weight">
<p class="f-50">52 <span class="blue-1 f-26">良</span></p>
<p class="f-24 gray-90">PM2.5/1h</p>
</div>
</div>
</div>
效果:

3. 前后端交互
首页的设备数据我们定义为:
"data":[
{
"id":0,
"title":"环境检测",
"pos":"客厅",
"wz":4,
"source":"./images/furnishing_1.png",
"info":{
"PM2dot5":30,
"level":"优"
},
"status":true
}
使用 jQuery请求数据:
$.ajax({
//记得开启服务器
//接口地址
url: "./data/data.json",
method: "GET",
success: function (res) {
// console.log(res);//检查是否请求成功
render(res);
},
});
4. 模板引擎
数据获取之后,需要渲染到页面上,通常我们可能会使用字符串拼接的方式来渲染一组数据,但是当页面结构比较复杂的时候,甚至包含判断逻辑的时候,字符串拼接就显得不那么方便和优雅了。这时可以使用模板引擎。
4.1 art-template模板引擎
官网: https://aui.github.io/art-template/zh-cn/index.html
art-template支持以插值的方式输出值
{{if user}}
<h2>{{user.name}}</h2>
{{/if}}
并且可以很方便的实现条件判断和循环
条件判断:
{{if value}} ... {{/if}}
{{if v1}} ... {{else if v2}} ... {{/if}}
循环:
{{each target}}
{{$index}} {{$value}}
{{/each}}
4.2 安装
在使用之前,首先要进行安装:
下载 template-web.js
然后使用script标签引入。
4.3 定义模板
在页面中使用script标签定义模板,模板需要有id属性
<!--定义模板-->
<script id="tpl" type="text/html">
<!--遍历-->
{{each data}}
<div class="block-icon" wz="{{$value.wz}}">
<div class="flex f-weight j-s-b">
<p class="f-28 black-1">{{$value.title}}</p>
<p class="f-24 gray-90">{{$value.pos}}</p>
</div>
<div class="flex j-s-b ai-center mt-30">
<img class="source" src="{{$value.source}}" alt="" />
{{if $value.info}}
<div class="f-weight">
<p class="f-50">
{{$value.info.PM2dot5}}
<span class="blue-1 f-26">{{$value.info.level}}</span>
</p>
<p class="f-24 gray-90">PM2.5/h</p>
</div>
{{else}}
<div class="close-btn {{$value.status?'is-open':''}}">
<img
src="{{$value.status?'./images/close_1_icon.png':'./images/close_2_icon.png'}}"
alt=""
/>
</div>
{{/if}}
</div>
</div>
{{/each}}
</script>
使用模板生成字符串,把模板的id和数据传入template
函数,然后就会返回即将渲染的字符串,在插入到容器元素中即可。
function render(res) {
var html = template("tpl", res);
$(".block-list").html(html);
}
4.4 按钮交互
实现导航栏的切换功能,以及点击开关能够展示开启关闭的效果
<!--导航栏切换和设备开关-->
<script>
$(".index-nav").on("click", function () {
var tab_list = document.querySelector(".index-nav ");
var lis = tab_list.querySelectorAll(".index-nav p");
var items = document.querySelectorAll(".block-icon");
//for循环绑定事件
for (var i = 0; i < lis.length; i++) {
//给小p设置索引号
lis[i].setAttribute("data-index", i); //以data-开头的均为自定义属性,自己设置也要规范书写
lis[i].onclick = function () {
//1.点击某个,当前这个底色变蓝色,排他思想
for (var i = 0; i < lis.length; i++) {
//干掉所有人,清除p的class类
lis[i].className = "";
}
//留下自己
this.className = "nav-active";
//2.显示下面内容模块
var index = this.getAttribute("data-index"); //得到当前点击的index号
//干掉所有人
console.log(index);
for (var i = 0; i < items.length; i++) {
items[i].style.display = "none";
}
//留下我自己
if (index == 0) {
for (var i = 0; i < items.length; i++) {
items[i].style.display = "block";
}
} else {
for (var i = 0; i < items.length; i++) {
var wz = items[i].getAttribute("wz");
console.log(wz);
if (wz == index) {
items[i].style.display = "block";
}
}
}
};
}
});
// 设备开关
$(".block-list").on("mousemove", function () {
//鼠标移动触发绑定
var btnlist = document.querySelectorAll(".close-btn");
var imglist = document.querySelectorAll(".close-btn img");
for (var i = 0; i < btnlist.length; i++) {
btnlist[i].onclick = function () {
var btn = this.className;
//var img = this.children[0].getAttribute("src");//只能获取不能修改
if (btn == "close-btn is-open") {
this.className = "close-btn";
this.children[0].src = "./images/close_2_icon.png"; //修改孩子的src
} else {
this.className = "close-btn is-open";
this.children[0].src = "./images/close_1_icon.png";
}
};
}
});
</script>