为了账号安全,请及时绑定邮箱和手机立即绑定
首页 手记 Web App下图片滑动组件是如何开发的?
  • 8
    评论
    45
    分享

Web App下图片滑动组件是如何开发的?

标签:
Html/CSS WebApp

昨天学习了Web App下图片滑动组件,今天做一些总结,记录下自己学到的东西.
1.webAPP的头部

<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no" /> 
<!--定义viewport的大小为屏幕的宽高,初始缩放比为1,最大和最小缩放比都为1,用户不可缩放-->
<meta name="apple-touch-fullscreen" content="YES" />
<!--ios下可以变为全屏-->
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<!--ios状态条的颜色-->

2.div结构和css样式
div结构很简单,只是body中有一个id为canvas的div

<!-- 外层画布 -->
 <div id="canvas"></div>

css样式如下:

body{/*清除默认格式*/
 margin: 0;
 padding: 0;
 background: #333;
 overflow: hidden;
 }
 li,ul{/*清除默认格式,使li高度为全屏*/
 list-style: none;
 margin: 0;
 padding: 0;
 height: 100%;
 overflow: hidden;
 }
 /* 使得图片居中 webkit-box */
 li{
 position: absolute;
 display: -webkit-box;
 -webkit-box-pack: center;
 -webkit-box-align: center;
 }
 li img {
 max-width: 100%;
 max-height: 100%;
 }
 /* 隐藏画布外的内容 */
 #canvas{
 height: 100%;
 width: 100%;
 overflow: hidden;
 }

其中使用了webkit的一些属性来使图片居中,下面介绍另外一些兼容ie的使li中图片居中的方法:

.box {
 /*非IE的主流浏览器识别的垂直居中的方法*/
 display: table-cell;
 vertical-align:middle;
 /*设置水平居中*/
 text-align:center;
 /* 针对IE的Hack */
 *display: block;
 *font-size:262px;
 /*约为高度的0.873,300*0.873 约为262*/
 *font-family:Arial;
 /*防止非utf-8引起的hack失效问题,如gbk编码*/
 width:400px; height:300px; border:1px solid #eee;
}
.box img { 
 /*设置图片垂直居中*/ 
 vertical-align:middle;
}

3.js代码
我们刚才看到我们的div结构只有一个div,而css中有ul和li这些元素的样式,那么这些元素节点就是通过我们的js动态添加的.下面我们来看下js代码.

 <script type="text/javascript">
 //所有的数据
 var list = [{
 height: 950,
 width: 800,
 img: "imgs/1.jpg"
 },
 {
 height: 1187,
 width: 900,
 img: "imgs/2.jpg"
 },
 {
 height: 766,
 width: 980,
 img: "imgs/3.jpg"
 },
 {
 height: 754,
 width: 980,
 img: "imgs/4.jpg"
 },
 {
 height: 493,
 img: "imgs/5.jpg",
 width: 750
 },
 {
 height: 500,
 img: "imgs/6.jpg",
 width: 750
 },
 { 
 height: 600,
 img: "imgs/7.jpg",
 width: 400
 }];
 //构造函数
 function Slider(opts){
 //构造函数需要的参数
 this.wrap = opts.dom;
 this.list = opts.list;
 //构造三步奏
 this.init();
 this.renderDOM();
 this.bindDOM();
 }
 //第一步 -- 初始化
 Slider.prototype.init = function() {
 //设定窗口比率
 this.radio = window.innerHeight/window.innerWidth;
 //设定一页的宽度
 this.scaleW = window.innerWidth;
 //设定初始的索引值
 this.idx = 0;
 };
 //第二步 -- 根据数据渲染DOM
 Slider.prototype.renderDOM = function(){
 var wrap = this.wrap;
 var data = this.list;
 var len = data.length;
 this.outer = document.createElement('ul');
 //根据元素的
 for(var i = 0; i < len; i++){
 var li = document.createElement('li');
 var item = data[i];
 li.style.width = window.innerWidth +'px';
 li.style.webkitTransform = 'translate3d('+ i*this.scaleW +'px, 0, 0)';
 if(item){
 //根据窗口的比例与图片的比例来确定
 //图片是根据宽度来等比缩放还是根据高度来等比缩放
 if(item['height']/item['width'] > this.radio){
 li.innerHTML = '<img height="'+ window.innerHeight +'" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="'+ item['img'] +'">';
 }else{
 li.innerHTML = '<img width="'+ window.innerWidth +'" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="'+ item['img'] +'">';
 }
 }
 this.outer.appendChild(li);
 }
 //UL的宽度和画布宽度一致
 this.outer.style.cssText = 'width:' + this.scaleW +'px';
 wrap.style.height = window.innerHeight + 'px';
 wrap.appendChild(this.outer);
 };
 Slider.prototype.goIndex = function(n){
 var idx = this.idx;
 var lis = this.outer.getElementsByTagName('li');
 var len = lis.length;
 var cidx;
 //如果传数字 2,3 之类可以使得直接滑动到该索引
 if(typeof n == 'number'){
 cidx = idx;
 //如果是传字符则为索引的变化
 }else if(typeof n == 'string'){
 cidx = idx + n*1;
 }
 //当索引右超出
 if(cidx > len-1){
 cidx = len - 1;
 //当索引左超出 
 }else if(cidx < 0){
 cidx = 0;
 }
 //保留当前索引值
 this.idx = cidx;
 //改变过渡的方式,从无动画变为有动画
 lis[cidx].style.webkitTransition = '-webkit-transform 0.2s ease-out';
 lis[cidx-1] && (lis[cidx-1].style.webkitTransition = '-webkit-transform 0.2s ease-out');
 lis[cidx+1] && (lis[cidx+1].style.webkitTransition = '-webkit-transform 0.2s ease-out');
 //改变动画后所应该的位移值
 lis[cidx].style.webkitTransform = 'translate3d(0, 0, 0)';
 lis[cidx-1] && (lis[cidx-1].style.webkitTransform = 'translate3d(-'+ this.scaleW +'px, 0, 0)');
 lis[cidx+1] && (lis[cidx+1].style.webkitTransform = 'translate3d('+ this.scaleW +'px, 0, 0)');
 };
 //第三步 -- 绑定 DOM 事件
 Slider.prototype.bindDOM = function(){
 var self = this;
 var scaleW = self.scaleW;
 var outer = self.outer;
 var len = self.list.length;
 //手指按下的处理事件
 var startHandler = function(evt){
 //记录刚刚开始按下的时间
 self.startTime = new Date() * 1;
 //记录手指按下的坐标
 self.startX = evt.touches[0].pageX;
 //清除偏移量
 self.offsetX = 0;
 //事件对象
 var target = evt.target;
 while(target.nodeName != 'LI' && target.nodeName != 'BODY'){
 target = target.parentNode;
 }
 self.target = target;
 };
 //手指移动的处理事件
 var moveHandler = function(evt){
 //兼容chrome android,阻止浏览器默认行为
 evt.preventDefault();
 //计算手指的偏移量
 self.offsetX = evt.targetTouches[0].pageX - self.startX;
 var lis = outer.getElementsByTagName('li');
 //起始索引
 var i = self.idx - 1;
 //结束索引
 var m = i + 3;
 //最小化改变DOM属性
 for(i; i < m; i++){
 lis[i] && (lis[i].style.webkitTransition = '-webkit-transform 0s ease-out');
 lis[i] && (lis[i].style.webkitTransform = 'translate3d('+ ((i-self.idx)*self.scaleW + self.offsetX) +'px, 0, 0)');
 }
 };
 //手指抬起的处理事件
 var endHandler = function(evt){
 evt.preventDefault();
 //边界就翻页值
 var boundary = scaleW/6;
 //手指抬起的时间值
 var endTime = new Date() * 1;
 //所有列表项
 var lis = outer.getElementsByTagName('li');
 //当手指移动时间超过300ms 的时候,按位移算
 if(endTime - self.startTime > 300){
 if(self.offsetX >= boundary){
 self.goIndex('-1');
 }else if(self.offsetX < 0 && self.offsetX < -boundary){
 self.goIndex('+1');
 }else{
 self.goIndex('0');
 }
 }else{
 //优化
 //快速移动也能使得翻页
 if(self.offsetX > 50){
 self.goIndex('-1');
 }else if(self.offsetX < -50){
 self.goIndex('+1');
 }else{
 self.goIndex('0');
 }
 }
 };
 //绑定事件
 outer.addEventListener('touchstart', startHandler);
 outer.addEventListener('touchmove', moveHandler);
 outer.addEventListener('touchend', endHandler);
 };
 //初始化Slider 实例
 new Slider({
 dom : document.getElementById('canvas'),
 list : list
 });
 </script>

首先第一步,将所有要显示的图片的信息写入到一个数组中,分别存储每张图片的高,宽,路径.

第二步,定义一个初始化函数Slider,这个初始化函数需要传入一个参数,这个参数中有两个属性,分别为id为canvas的div节点和图片信息数组.然后将该参数的两个属性通过变量声明,分别赋值给wrap和list两个变量.此时,我们会看到对于wrap和list的前面都有一个this.,这个this是指的调用该函数的对象,因为这个函数的调用者为new Slider(),所以wrap和list都为类变量.变量声明完毕后,定义了三个函数来执行初始化过程,分别为this.init()-负责初始化一些数据,this.renderDOM()负责dom结点的渲染,this.bindDOM负责将事件和结点绑定到DOM中.

第三步,我们看到Slider.prototype.init = function()这段代码,对Slider中的init方法进行了定义,分别定义了三个类变量(因为在第二步中讲过,Slider的调用者为new Slider()),radio表示当前屏幕的宽高比,scaleW表示图片一页的宽度,idx表示当前图片滑动到的索引值.

第四步,我们看到Slider.prototype.renderDom= function()这段代码,首先获取id为canvas的那个div,图片的数据源赋值为data,以及图片的个数len.因为我们的是通过ul-li这种结构来展示图片,又因为当我们给li设置定位方式为绝对定位(因为当li设置为绝对定位时,所有的li标签都会定位到屏幕左上角),要实现滑动效果的第一步是将图片按照顺序水平排列.这里我们使用了translate3d这个方法来将图片进行位置的调整.之后设置图片样式,ul的样式,然后添加li到ul,添加ul到canvas.

第五步,通过Slider.prototype.bindDOM= function()绑定事件和处理事件,首先我们看到我们为outer(id为canvas的div)绑定了三个事件,分别对应touchStart,touchMove,touchEnd三个事件,这三个事件有三个回调方法,其中在一次用户滑动的过程中,touchStart回调一次,touchMov回调多次,touchEnd回调一次,分别回调startHandler(),moveHandler(),endHandler()三个方法,值得注意的是,在回调的这三个方法的时候,这三个方法的this对象为outer,因为这三个方法要使用new Slider()这个对象中的一些属性,所以我们在bindDom方法刚开始的时候var self = this,对new Slider()进行了对象保存,方便之后的调用.

在startHandler()中,记录了起始位置和时间,同时清除了偏移量-防止连续滑动时造成偏移量计算错误的情况
在moveHandler()中,首先要调用evt.preventDefault()方法来阻止浏览器的默认滑动事件,然后通过evt.targetTouches[0].pageX获取在移动事件中触摸的x坐标,通过在startHandler()中计算的触摸起始值,计算手指滑动的距离.接下来获取id为canvas的div下的所有li标签,然后根据手指的滑动移动li标签的位置,但是当我们滑动当前图片时,当前图片的旁边两个li标签的位置应该也是跟着变化的,这样才能让图片有整体滑动的效果.其中使用了webkitTransition属性来定义了每次执行滑动的时滑动动画的事件为0s,webkitTransform属性定义了滑动的距离.

lis[i] && (lis[i].style.webkitTransition = '-webkit-transform 0s ease-out');
 lis[i] && (lis[i].style.webkitTransform = 'translate3d('+ ((i-self.idx)*self.scaleW + self.offsetX) +'px, 0, 0)');

这两个&&则是为了防止lis[i]中i的值不合法造成的数组越界产生的错误.
在endHandler()中,提供了两种翻页的逻辑判断,一种是当手指位移超过屏幕的六分之一且滑动时间超过300毫秒,第二种是当手指位移大于50且滑动事件少于300毫秒,当符合任意条件时都会执行翻页操作,调用goIndex()方法.

点击查看更多内容
发表于 2016年06月12日 23:42, 共 18107 人浏览

本文原创发布于慕课网 ,转载请注明出处,谢谢合作

8人点赞

若觉得本文不错,就分享一下吧!

评论
评论

共同学习,写下你的评论

评论加载中...

展开查看更多评论

作者其他优质文章

正在加载中
移动开发工程师
手记
粉丝
5
获赞与收藏
454

关注作者,订阅最新文章

阅读免费教程

  • 8
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的〜
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

慕课手记新用户专享福利
恭喜你,你的运气太好了,居然抽中了 100个积分!
恭喜你,抽中了价值 元的专栏 !
太棒了, 直接落到你账户里!
积分商城里的罗技鼠标、机械键盘、
Kindle 阅读器、小米平衡车
Apple iPad (10.2英寸)、大额优惠券
在等着你去兑换了噢
作者:
免费赠送
兑换码:1111222211
优惠券可用于购买实战课、体系课
无门槛使用
先去看看,有什么好东西 马上兑换 我爱学习,选课去
帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

举报

0/150
提交
取消

AltStyle によって変換されたページ (->オリジナル) /