仿去哪儿网webapp(一)

一,利用FastClick第三方插件来解决移动端click事件300毫秒延迟情况

1,安装,npm  i  fastclick  -S

在main.js中引入

import FastClick from "fastclick";
FastClick.attach(document.body);

2,安装stylus

npm i stylus stylus-loader --save-dev

3.关于移动端的尺寸计算,设计图为750px, 以苹果6开发(设备宽度375)

在reset.css重置样式样式表中可看到font-size大小, 在main.js引入reset.css

1rem = html font-size = 50px

在组件的样式表中,可用rem来样式作为尺寸单位

如果一个height为86px, 因为移动端一般用二倍图来画,所以height是43px, 再除以50, 也就是0.86rem,

 

4,阿里图标本地引入

将阿里图标下载到本地,在vue项目中,assets文件-style文件夹-新建iconfont文件夹,将图标字体文件放入,在assets-stylus文件中放入iconfont.css,然后在main.js引入iconfont.css

import "./assets/style/iconfont.css";

最后,在iconfont.css中修改图标字体的路径(五个都要修改)

 

 

 

 

@font-face {font-family: "iconfont";
  src: url('./iconfont/iconfont.eot?t=1612861657067'); /* IE9 */
  src: url('./iconfont/iconfont.eot?t=1612861657067#iefix') format('embedded-opentype'), /* IE6-IE8 */
  url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAANIAAsAAAAAB1QAAAL6AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqCCIF9ATYCJAMQCwoABCAFhG0HTht1BsgeB+XGM0sZRXL+HcHX2G/f3X0VSJJEI0PFO4npJqERSWKx0SiJ7CXwk13+y02MdiKdupif3OzO7E4Ws/wCOrmT2lz3jUrqkoC///9zTb4SX307m0tUU0Ta0d4Ax60Dimoa2SYyMZ8T8NZlHCfQNKNK1G5n/0SIV1jjAnHNZAHicy5FEaJ1oVqztIhXiFFPd74RAC/h9+MPwhFPUsmsk/ZPOyRo/iz6tAGtGAUz82pYMZ8VwioyVoFCnNfaD6E4tAppar2jrQN1dYm52sKfNozOGNkxz/7LIySZqLIy22CNCWc+i1yS4DNMkvjckCHDDzHUvVFkD7hiiSlZQDNKtu7u3kinVpSTvZsPr8yl+1bmyfsPnRAfPZr8+PGUhw8nPXgQcbMcSCait7Dv8MnjOZP2Hjh2IlfsuP/ZpLSH0yuypT2bqsT51+8rzQtuXpmf+z1nfjBduXHvzLJP8DTpHPr8GZ1H5zjpOXBTLjiIP9+0A925gzzJdp4LHsuBZAdKt9QcqhxzZ2HzYHArvD06dWok/2D65awFBZHJU0cKwAT45/uPVvNdQx5qBiDf+ugXUQjef/btm5NW/yc+qYCPbuspWf9F9zQAfEdBb8EHF7BlRRdZhlJUscs3nMXsZQcSmraJzxxj6NUcyonemYS6iRSSmmnI6maRQqxCRcsaVNVtQNOKrtUtIzYkSgPL1hKEvouQdL1B1hdBCvESKiZ9gap+IGg6iKwtW+aDJbbZRHCJhIcWYiZygzomNzHKHk9kTxPsrCikTSa2w7w45A8Wo13EIPYcM5x5cth1KaY213EnnUY0jWPT5sNEdP2K65q1gQCte5Nf5Dok2mEjBC5Cgg1ZCGNEnIH61V2Jpc+PR8g8GoHdQqmxT0bYHKxzLMQv2APpkhu9KPfyimMeWZjLRWGUjdNhnTSI0Gg4zKwfNIwQufyUEQFTrYBdifaV+9dX6l+4Bpqsq0ukyFGionoqGIrHYh3uOR7PFGybz6+W3H5GNWWaxgwZAAAA') format('woff2'),
  url('./iconfont/iconfont.woff?t=1612861657067') format('woff'),
  url('./iconfont/iconfont.ttf?t=1612861657067') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
  url('./iconfont/iconfont.svg?t=1612861657067#iconfont') format('svg'); /* iOS 4.1- */
}

.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.iconfanhui:before {
  content: "\e624";
}

.iconsousuo:before {
  content: "\e632";
}

.iconarrow-down-filling:before {
  content: "\e688";
}

 

 然后在模板中引入字体图标类

<div class="header-left  ">h
      <span class="iconfont iconfanhui"></span>
    </div>

 

 5.将主题颜色抽离成变量,以后主题颜色修改更加方便

assets文件夹-style文件夹- 新建varibles.styl文件,写入主题颜色

$bgColor = #00bcd4

在header组件中,引入该样式文件,在样式中写入该颜色变量

<style lang="stylus"  scoped>
@import '~assets/style/varibles.styl'

.header
    display :flex
    height :0.86rem
    line-height : .86rem
    background : $bgColor
    color : #fff
    .header-left
      flex :0 0 .64rem
      color : #fff
      text-align : center
      font-size: .4rem

 

6.移动端1px像素问题

assets文件夹-style文件夹- 新建mixins.styl文件, 写入1px的样式, 然后组件中引入

@import "~assets/style/mixins.styl"
// 伪类+transform
// 原理:把原先元素的border去掉,然后利用:before或者:after重做border,并 transform的scale缩小一半,
// 原先的元素相对定位,新做的border绝对定位。

$color = blue

.border-1px-bottom, .border-1px-top,.border-1px-left,.border-1px-right
  position: relative


// 边框一像素,利用伪类设置一像素
.border-1px::after
    content: "";
    box-sizing: border-box;
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    border: 1px solid $color

// 下边框
.border-1px-bottom::after
    position :absolute
    left: 0
    bottom: 0
    width: 100%
    border-bottom: 1px solid $color
    content: ' '
    
// 上边框
.border-1px-top::before
    position :absolute
    left: 0
    top: 0
    width: 100%
    border-top: 1px solid $color
    content: ' '


// 左边框
.border-1px-left::before
    position :absolute
    left: 0
    top: 0
    height: 100%
    border-left: 1px solid $color
    content: ' '


// 右边框
.border-1px-right::after
    position :absolute
    right: 0
    top: 0
    height: 100%
    border-right: 1px solid $color
    content: ' '


/*设备像素比*/
/*显示屏最小dpr为1.5*/
@media (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)
  .border-1px-bottom::after,.border-1px-top::before
    // Y轴压缩0.7,  1.5*0.7 约等于1
    -webkit-transform: scaleY(0.7)
    transform: scaleY(0.7)
  
  .border-1px-left::before,.border-1px-right::after
  // X轴压缩0.7,  1.5*0.7 约等于1
    -webkit-transform: scaleX(0.7)
    transform: scalexX(0.7)

  .border-1px::after 
    width: 150%;
    height: 150%;
    transform: scale(0.7);
    transform-origin: 0 0;
    


@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2)
  .border-1px-bottom::after,.border-1px-top::before
    // Y轴压缩0.5,  2*0.5 等于1
    -webkit-transform: scaleY(0.5)
    transform: scaleY(0.5)
  
  .border-1px-left::before,.border-1px-right::after
  // X轴压缩0.5,  2*0.5 等于1
    -webkit-transform: scaleX(0.5)
    transform: scaleX(0.5)

  .border-1px::after 
    width: 200%;
    height: 200%;
    transform: scale(0.5);
    transform-origin: 0 0;


@media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3)
  .border-1px-bottom::after,.border-1px-top::before
    -webkit-transform: scaleY(0.333)
    transform: scaleY(0.5)
  
  .border-1px-left::before,.border-1px-right::after
    -webkit-transform: scaleX(0.333)
    transform: scaleX(0.333)

  .border-1px::after 
    width: 300%;
    height: 300%;
    transform: scale(0.333);
    transform-origin: 0 0;

// 去除边框   // 
border-none()
  &:after
    display: none

 

7.使用vue封装的vue-awesome-swiper轮播图插件,不是原生的swiper插件

安装,npm install vue-awesome-swiper@4.1.1  --save

npm install swiper@5  --save

在main.js全局注册

import VueAwesomeSwiper from "vue-awesome-swiper";

import "swiper/css/swiper.min.css";

Vue.use(VueAwesomeSwiper);

新建swiper组件

<template>
  <swiper ref="mySwiper" :options="swiperOptions">
    <swiper-slide>Slide 1</swiper-slide>
    <swiper-slide>Slide 2</swiper-slide>
    <swiper-slide>Slide 3</swiper-slide>
    
    <div class="swiper-pagination" slot="pagination"></div>
  </swiper>
</template>

<script>
export default {
  data() {
    return {
      swiperOptions: {
        pagination: {
          el: ".swiper-pagination"
        }
      }
    };
  }
};
</script>

<style scoped lang="stylus"></style>

在home父组件中引入和注册swiper组件。

<template>
  <div>
    <HomeHeader></HomeHeader>
    <HomeSwiper></HomeSwiper>
  </div>
</template>

<script>
import HomeHeader from "components/HomeHeader/HomeHeader";
import HomeSwiper from "components/Swiper/Swiper";

8, 此时有个小bug,如果在网速条件差的情况,图片还没有加载完成的时候,轮播图结构下方的结构会先占用轮播图的位置,直到轮播图加载完成后,才会到他正确的位置(称为抖动情况)

原因,当图片没加载完成,此时图片高度为0,下面的结构会撑上去,此时只需要计算宽高比,计算padding-bottom即可解决

 

 

解决方法,在swiper组件,新增一个根标签div ,计算图片的宽高比例,用样式来解决

<template>
  <div class="wrapper">
    <swiper ref="mySwiper" :options="swiperOptions">
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>

      <div class="swiper-pagination" slot="pagination"></div>
    </swiper>
  </div>
</template>

<script>
export default {
  data() {
    return {
      swiperOptions: {
        pagination: {
          el: ".swiper-pagination"
        }
      }
    };
  }
};
</script>

<style scoped lang="stylus">
.wrapper
  overflow :hidden
  width:100%
  height:0
  padding-bottom :31.25%
.item
  width:100%
</style>

 

 9,利用深度选择器去修改轮播图的圆点颜色,默认是蓝色,修改成红色
直接通过.swiper-pagination-bullet-active类修改颜色是无效的,因为scoped只能修改当前组件的样式,而swiper组件里头的子组件是不会有效果的
需要通过深度选择器来改变swiper子组件里头的样式
#Scoped CSS
当 <style> 标签有 scoped 属性时,它的 CSS 只作用于当前组件中的元素。这类似于 Shadow DOM 中的样式封装。
vue-loader官网介绍;https://vue-loader.vuejs.org/zh/guide/scoped-css.html#%E6%B7%B7%E7%94%A8%E6%9C%AC%E5%9C%B0%E5%92%8C%E5%85%A8%E5%B1%80%E6%A0%B7%E5%BC%8F
#子组件的根元素
使用 scoped 后,父组件的样式将不会渗透到子组件中。不过一个子组件的根节点会同时受其父组件的 scoped CSS 和子组件的 scoped CSS 的影响。
这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。 #深度作用选择器 如果你希望 scoped 样式中的一个选择器能够作用得“更深”,例如影响子组件,你可以使用 >>> 操作符:
<template>
  <div class="wrapper">
    <swiper ref="mySwiper" :options="swiperOptions">
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>
      <swiper-slide>
        <img class="item" src="@/assets/logo.png" alt="" />
      </swiper-slide>

      <div class="swiper-pagination" slot="pagination"></div>
    </swiper>
  </div>
</template>

<script>
export default {
  data() {
    return {
      swiperOptions: {
        pagination: {
          el: ".swiper-pagination",
      loop:true
        }
      }
    };
  }
};
</script>

<style scoped lang="stylus">
.wrapper
  & >>> .swiper-pagination-bullet-active
    background: red
  overflow :hidden
  width:100%
  height:0
  padding-bottom :100%
  .item
    width:100%

</style>

 

 

 

 

 10.商品图标区域布局,一个图标

 

 

  <div class="icons">
        <div class="icon">
          <div class="icon-img">
            <img
              class="icon-img-content"
              src="//s.qunarzz.com/homenode/images/touchheader/hotel.png"
              alt=""
            />
          </div>
          <p class="icon-desc">热门景点</p>
        </div>

样式布局,同理,慢速网络下对于图片下的结构抖动情况,对用padding-bottom来代替height, 一行八个图标,一个weidth占25%

<style scoped lang="stylus">

.icons
  margin-top:.1rem
  .icon
    float:left
    width:25%
    height :0
    padding-bottom :25%
    position : relative
    .icon-img
      position: absolute
      top: 0
      left: 0
      right: 0
      bottom: .44rem
      padding :.1rem
      box-sizing:border-box
      .icon-img-content
        display :block
        margin :0 auto
        height :100%
        width:100%
    .icon-desc
      position: absolute
      bottom: 0
      left: 0
      right: 0
      line-height .44rem
      height .44rem
      text-align :center

如果,图片图标很多,需要用到swiper轮播图来处理

  <div class="icons">
    <swiper>
      <swiper-slide>
        <div class="icon">
          <div class="icon-img">
            <img
              class="icon-img-content"
              src="//s.qunarzz.com/homenode/images/touchheader/hotel.png"
              alt=""
            />
          </div>
          <p class="icon-desc">热门景点</p>
        </div>
      </swiper-slide>
      <swiper-slide>
        <div class="icon">
          <div class="icon-img">
            <img
              class="icon-img-content"
              src="//s.qunarzz.com/homenode/images/touchheader/hotel.png"
              alt=""
            />
          </div>
          <p class="icon-desc">热门景点</p>
        </div>
      </swiper-slide>
    </swiper>

  </div>

此时一排图片图标高度占25%,两排就占50%,而加入swiper组件后,左右滑动的高度距离只有25%, 我们需要增大他的高度为50%, 此时利用到深度选择器

 

 样式

.icons
  margin-top:.1rem
  & >>>.swiper-container
    padding-bottom : 50%
    height :0
  .icon
    float:left
    width:25%
    height :0
    padding-bottom :25%
    position : relative

 

11. 对于图片图标轮播的页数逻辑计算以及样式抽离公共代码

图片图标的数据

 data() {
    return {
      iconList: [
        {
          id: "0001",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/54/ace00878a52d9702.png",
          desc: "景点门票"
        },
        {
          id: "0002",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1711/df/86cbcfc533330d02.png",
          desc: "滑雪季"
        },
        {
          id: "0003",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1710/a6/83f636bd75ae6302.png",
          desc: "泡温泉"
        },
        {
          id: "0004",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/35/2640cab202c41b02.png",
          desc: "动植园"
        },
        {
          id: "0005",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/d0/e09575e66f4aa402.png",
          desc: "游乐园"
        },
        {
          id: "0006",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/59/569d3c096e542502.png",
          desc: "必游榜单"
        },
        {
          id: "0007",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/17/4bd370f3eb1acd02.png",
          desc: "演出"
        },
        {
          id: "0008",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/7f/b1ea3c8c7fb6db02.png",
          desc: "城市观光"
        },
        {
          id: "0009",
          imgUrl:
            "http://img1.qunarzz.com/piao/fusion/1611/a9/ffc620dbda9b9c02.png",
          desc: "一日游"
        }
      ]

模板数据填充,每页轮播图图片的数量是8个,第九个图标需要在第二页,此时根据iconList数据去计算页数

 

 

 <div class="icons">
    <swiper>
      <!-- 轮播图页数 -->
      <swiper-slide >
        <div class="icon" v-for="(icon, index) in iconList" :key="icon.id">
          <div class="icon-img">
            <img class="icon-img-content" :src="icon.imgUrl" />
          </div>
          <p class="icon-desc">{{icon.desc}}</p>
        </div>
      </swiper-slide>
    </swiper>

计算页数,用computed

  computed: {
    // 计算轮播图每行图片的页数
    pages() {
      const pages = [];
      const { iconList } = this;
      iconList.forEach((item, index) => {
        // 计算当前图标在第几页
        const page = Math.floor(index / 8);
        // 判断第0个索引没有数据,内嵌一个数组
        if (!pages[page]) {
          pages[page] = [];
        }
        // 有数据
        pages[page].push(item);
      });

      return pages;
    }
  }

在swiper模板中填充

<div class="icons">
    <swiper>
      <!-- 轮播图页数 -->
      <swiper-slide v-for="(page, index) in pages" :key="index">
        <div class="icon" v-for="(icon, index) in page" :key="icon.id">
          <div class="icon-img">
            <img class="icon-img-content" :src="icon.imgUrl" />
          </div>
          <p class="icon-desc">{{icon.desc}}</p>
        </div>
      </swiper-slide>
    </swiper>
  </div>

优化效果,如果图片下方文字的数量过多,需要有省略情况

在mixins.styl中定义样式,然后在该组件中引入,

ellipsis()
  overflow: hidden
  white-space: nowrap
  text-overflow: ellipsis    
@import '~assets/style/mixins.styl'
.icon-desc
      position: absolute
      bottom: 0
      left: 0
      right: 0
      line-height .44rem
      height .44rem
      text-align :center
      ellipsis()

 

 

 

 

 

 

 

 

 
posted @ 2021-02-09 16:44  全情海洋  阅读(503)  评论(0编辑  收藏  举报