0
点赞
收藏
分享

微信扫一扫

【移动端】Flex+vw进行布局

分湖芝蘭 2022-04-07 阅读 70

移动端页面demo

1. 项目介绍

项目共包含三个页面

  • 首页
  • 设备页
  • 我的

image-20210618112814788 image-20210618112837994 image-20210618112853401

每个页面都有一个公共的底部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>

效果:

image-20210618175937757

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>
举报

相关推荐

0 条评论