小白探索大前端--使用vue实现简单轮播图

最近看完了《Vue实战》这本书,这也是我第一次完整的看完的一本关于前端的书籍(现在还在看的是《CSS世界》,有点想放弃了)。照着书中的demo都写了一遍,虽然很多栗子非常简单,但是值得学习的地方还是挺多的。当然也不得不吐槽很多用法都有点过时,尤其是关于webpack的配置。不过现在的前端发展很快,各种工具版本不断更新,代码过时也是能够理解的。对于学习者来说当然得学习最新的东西。

这篇文章主要是用来记录在自己实现一个【知乎日报移动版】的过程中遇到的一个坑,说坑也算不上,只能叫做坎坷之路吧。因为书中给的实战demo是一个pc版的,自己跟着写了一遍,觉得没有什么难度。然后在github上找了一下,发现用知乎日报来练手的项目很多,原因也很简单--api是现成的。于是我也蠢蠢欲动,决定使用vue亲自撸一个【知乎daily】。

虽然gayhub很多现成的项目,但是我并不打算去抄一遍。我在手机上下载了一个知乎日报的APP,按照不同功能截屏来一一实现。没办法,这就是工(zhuang)匠(bi)情(xin)怀(tai).

开发过程中,整体的框架功能上基本没什么问题,该自己写组件就自己写,绝没有偷懒。也遇到很多问题,比如开发环境下的接口代理、知乎图片的同源策略等。这些问题肯定难不倒我,都不值一提。让我觉得难受的还是首页的轮播图实现。这个问题困扰了我2天(准确时间是一天半,还有半天在工作:逃)。

遇到这个功能的时候第一反应是使用一个开源库,分分钟就搞定了。但是经过10s的思想斗争,工匠情怀终于战胜了理智,下定决心打算自己手动实现。然而,带来的结果就是卡顿了半天,没任何进展。网上也有很多教程,也是很简单的,可以说是基础操作了。可惜我被一篇文章带偏了,走了一点弯路。

这篇文章的思路很常规,很有道理。首先将一个框框用来装你要显示的图片,仅仅只能显示这个框框,而图片呢就放后面排排坐,通过定时器去移动图片,就像一格格的胶卷一样,轮到谁谁就被看到了。

我一开始就是按照这种思路去整,结果怎么整都实现不了(在移动端)。在一筹莫展之际,发现了一个更加屌的思路。这个思路和之前的不一样,区别在于后面的图片不是排排坐好,而是叠加到一块去。下面通过代码去一探究竟。

首先得整一个窗口,简单来说就是用来显示一张图片的容器,它的宽度对移动端而言就是屏幕宽度。他得有一个非常重要的属性overflow:hidden,当子元素尺寸超过其父亲的大小多余内容就会被隐藏。这里用ul标签来装图片,其实使用div也是没问题的。元素liposition属性得设置为absolute.因为使用这个属性就能脱离文档流,也就不会“排排坐”了,而是叠加到一起了,当然,最后的肯定叠在最上面,前提是没有显示的设置z-index属性。还有很重要的一点,子元素定位设置为absolute父元素记得也设置一下定位属性,因为子元素的相对位置是按照第一个祖先元素不为static的元素来的,不然会粗大事。 html代码结构如下:

    <div class="window">
      <ul class="container" ref="imagesWrapper">
        
        <li v-for="(e,i) in imgs" :key="i">
          <img :src="e.image" :alt="e.title">
          <div class="desc">{{e.title}}</div>
        </li>
        
      </ul>

      <ol class="point-wrap">
        <li :class="{active:i==currentIndex}" v-for="(e,i) in imgs" :key="i"></li>
      </ol>

    </div>
  </div>
* {
  padding: 0;
  margin: 0;
  list-style-type: none;
}

img {
  width: 100%;
}
.container {
  position: relative;
  min-height: 100%;
}
.container li {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  transform: translateX(100%);
}

.window {
  position: relative;
  height: 100%;
  margin: 0 auto;
  overflow: hidden;
}

这里有一个小技巧,在静态样式中并没有将图片直接展示出来,而是将所有图片向右偏移一个窗口宽度。这是有玄机的。

说了这么多,还没谈到vue的部分。在container容器中有一个ref标签。这是在vue中用来获取dom元素的。也许在vue中操作dom是不被推荐的,但是对于这种动态生成的li元素我找不到怎么去动态绑定其样式。因此采用了这种比较low的办法。

    init() {
      let wrappers = this.$refs.imagesWrapper;
      let children = wrappers.children;
      let total = this.imgs.length;
      // 纯js操作 只需要先将三张图片位置确定好
      // 最左边按道理说是没有图片的 但是为了无限滚动效果 这里将其置为最后一张
      let left = total - 1;
      let center = 0;
      let right = 1;

      // 初始化的时候将li左移动了一个屏幕宽度,就是为了防止叠加的元素挡住要显示的图
      // 现在第一张显示的图片实际是最后一张 3张轮播起来就行了 没必要对每个图片进行位置计算
      // left处于最左边的位置 不显示
      children[left].style.transform = "translateX(" + -this.distance + "px)";
      // center处于中间位置 显示
      children[center].style.transform = "translateX(" + 0 + "px)";
      // right处于右边 不显示
      children[right].style.transform = "translateX(" + this.distance + "px)";
      this.sliderItem = children;
      // this.play();
    }

首先就是初始化显示的内容,之前在静态css样式中将图片移动到右边看不到的地方去了,现在就得手动操作让其可见。刚开始我以为直接将这些图片全部“铺开”,然后滚动,后来发现这么做很笨。直接操作三张就行了!为了实现无限滚动的效果,第一张的逻辑上的前一张是最后一张,因此将第一张的“上一张”给放到屏幕左边,下一张放到屏幕右边,要显示的也就是第一张(下标为0)归位到窗口,这和之前设置的全局的样式transform: translateX(100%)对应起来了。

如此以来,初始化的三张图片就定位好了,逻辑上也是没什么问题的。其余的改不显示还是显示不了,也不会参与移动。下面看看怎么“滚”。

    next() {
      this.currentIndex++;
      // 边界判断
      if (this.currentIndex > this.imgs.length - 1) {
        this.currentIndex = 0;
      }

      // center 为显示的图片
      let center = this.currentIndex;
      // 左边的 如果为负数 就取最后一张图片下标
      let left = center - 1 < 0 ? this.imgs.length - 1 : center - 1;
      // 右边的 如果超过了最大图片数量 取第一张图片下标
      let right = center + 1 == this.imgs.length ? 0 : center + 1;

      let children = this.sliderItem;
      // 给元素添加过渡
      children[center].style.transition = "transform .5s";
      children[left].style.transition = "transform .5s";
      // 右边的图片是替补图片,不需要走过渡
      children[right].style.transition = "none";
      // 3张图片同时移动
      children[left].style.transform = "translateX(" + -this.distance + "px)";
      children[center].style.transform = "translateX(0px)";
      children[right].style.transform = "translateX(" + this.distance + "px)";
    },

currentIndex为全局变量,指当前显示的图片下标,每次调用next会子增,到上限后会回归到0,这些都是很常规的操作。接下来就是计算上一张,下一张图片的下标,也是很容易理解,无非多了一点判断,在最后一张显示的时候下一张的下标得置为0,上一张也是同理,不然就回"空指针"了。接下来就是针对这三张图片改变样式,原则就是移动到哪个下标就显示哪个图片,上一张就移到左边,下一站移动到右边,顺便给加个动画效果。如此而已!

最后就是自动播放的逻辑,也是非常简单,一个定时器就搞定:

    play() {
      if (this.timer) {
        window.clearInterval(this.timer);
        this.timer = null;
      }
      this.timer = window.setInterval(() => {
        this.next();
      }, this.interval);
    }

然后在钩子函数中将这方法加上去就完事了。一个自制的轮播组件就写完了。简陋但是简单。这里有一个不太重要的细节,针对窗口变化的时候得动态改变偏移量。

    // 窗口变化 重新初始化
    windowChange() {
      const that = this;
      window.onresize = () => {
        return (() => {
          window.screenWidth = document.body.clientWidth;
          that.distance = window.screenWidth;
          this.init();
        })();
      };
    },

这样在pc端下也能正常“滚”动了。

最后,做一下小小的总结。这个组件虽然简单,但是也花了一定时间,毕竟踩坑的路是不能跳过的。其中花了很多时间纠结布局和样式,很是难受,都怪我没有把《CSS世界》看完。虽然简单,但是功能也很局限,比如没有实现手动去滑动。APP上是有这个功能的,那是因为我还没学会怎么在vue下使用touch事件(实际上是懒)。比如没有代码优化等等,总不能要求一个新手来造一个完美的轮子吧(给自己一点上升的空间咯)。造轮子不是目的,理解其中的所以然才是目的,现成的库有很多,完成功能也很容易,但是不能仅仅满足于此,我觉得作为手艺人得有一种格(xi)物(huan)致(zhuang)知(B)的精神。

不能动的效果图

参考资料

CSS深入理解之relative定位

几种原生js轮播图

源码地址

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容

  • 学会使用CSS选择器熟记CSS样式和外观属性熟练掌握CSS各种选择器熟练掌握CSS各种选择器熟练掌握CSS三种显示...
    七彩小鹿阅读 6,308评论 2 66
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,465评论 1 45
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,747评论 1 92
  • 闪光的钢轨,如缆索一般 承载着流动的国土,牵引着祖国的命脉 缩短了距离,唤醒了沉睡的土地 一声“大车”,一生的使命...
    兰机小牛阅读 509评论 1 1
  • 【一阶段·片段三】 R · 阅读原文片段 “行动科学管理术”利用“ABC模型”的概念来解释人的行动与结果之间的关系...
    天天一丁点阅读 284评论 2 0