欢迎来到 jackNEss'窝窝
I like simple mind

slide box for mobile

2012年06月27日

功能分析

其实大致是与web版的一样,就是多了几个在智能手机上用到的功能:

基本功能

首先分析下与web版一致的基本功能。

  • 自动轮播
  • 轮播时指示性按钮状态相应地跟着改变
  • 带前一张,后一张功能按钮
  • 滚动时带有过度效果

触摸版slidebox特有功能

考虑到手机与传统web版在交互上的不同,我决定在触摸版上加入只能手机上的一些交互功能

  • 手指拖动图片进行图片切换
  • 手指按住图片,自动轮播功能停止

需要思考的问题

在这里所写需要思考的问题主要是手机方面要考虑到的问题,web版已经有的功能在这里就不加细说分析了。

手机横竖屏切换特性

由于触摸版手机支持根据重力感应横竖屏切换操作,这种操作给我们带来的影响就是控件的宽度发生了改变(当然,如果你控件不做自动铺满等宽度弹性控制的话可以无视),所以我们要保证当触发 onresize事件时,我们的控件运作正常。

关于手指滑动问题

刚开始的时候我是做当手指拖动图片超过图片大小一半时,图片自动切换到下一张/上一张,但是实际在手机上测试时发现,其实很难切换过去,手的弧度拖动图片到他大小的0.3倍幅度已经是极限了,我们首饰平时一划也就这个幅度,超过了手指滑动就变得很不舒服,用户体验很差,所以最后改为超过0.2倍大小就自动切换到下一张。

重构方面注意事项

如果想做轮播并且自适应屏幕的话,我建议,用我以下的做法:

假设是4个图片进行轮播,我建议按以下写法定制自适应图片轮播框主体部分

<!-- html document-->
<div class="slidebox_area">
	<ul class="slidebox" >
		<li>我是图片一</li>
		<li>我是图片一</li>
		<li>我是图片一</li>
		<li>我是图片一</li>
	</ul>
</div>

css 我建议这样写

/*css document*/
.slidebox_area{ max-width:630px; width:100%; margin:0 auto; overflow:hidden;}
.slidebox{ width:400%;}
.slidebox li{ float:left; display:inline; width:25%;}

在这里我要提醒一下的就是 max-width 属性 必须写心爱 width 属性之前,否则会引起 浏览器的一个bug,就是在获取 offsetWidth的时候直接获取到浏览器的宽度,而不是target的宽度。具体原因还不清楚。

代码部分

/*
 * jnSlidebox(target,option)
 * target            slide框对象(滚动内容 的 父级)
 * option ={
	effectArea:      slide框对象(滚动内容 的 父级)
	rebuild:         是否重构对象:如果这个值为false,函数不再创建额外的元素,直接操作effectArea对象,此时 effectArea 应为包着滚动内容的一个层,且其父级为最外层
	leftBtn:         向左按钮
	rightBtn:        向右按钮
	instructBtns:    指示性质按钮集合
	isAuto:          是否自动滚动,默认为true
	autoInterval:    自动循环间隔,默认值为 3000
	transition:      动画过度时间,默认值为500
	slideDirection:  滚动方向,"x" 或者 "y",默认值为 “x”
	effectType:      触发方式, "mouseover" 或者 "click"
	btnsToggleClass: 设置当事件触发时简易的按钮样式添加, 默认值为 ""
	interval:        每帧动画播放间隔 默认值为 20
 }
 * 方法
 * jnSlidebox().setBeforeSlideEvent(func)  设置事件触发前发生的事件
 * jnSlidebox().setAfterSlideEvent(func)   设置事件触发后发生的事件
 * jnSlidebox().setOption(option)          属性设置
 * jnSlidebox().init()                     函数初始化
 *
 * example:
 * --------------------------------------------
 完整版:
 jnSlidebox("#slidebox",{
 	leftBtn:"#lt_btn",
 	rightBtn:"#rt_btn",
 	instructBtns:"#btn_area a",
 	slideDirection:"x",
 	rebuild:false,
 	btnsToggleClass:"cur",
 	isAuto:true,
 	autoInterval:3000,
 	transition:500,
 	effectType:"mouseover",
 	interval:20
 }).setBeforeSlideEvent(function(){
	
 }).setAfterSlideEvent(function(){
	
 }).init();

 简版:
 jnSlidebox("#slidebox",{leftBtn:"#lt_btn",rightBtn:"#rt_btn",instructBtns:"#btn_area a",slideDirection:"x",rebuild:false,btnsToggleClass:"cur"}).init();
 * -------------------------------------------
 * author:jackNEss Lau
 * date: 2012-3-10
 * ver:1.0
 */

var jnSlidebox = function(){
	
	var option ={
		
		//slide框对象(滚动内容 的 父级)
		effectArea:null,
		
		//是否重构对象:如果这个值为false,函数不再创建额外的元素,直接操作effectArea对象,此时 effectArea 应为包着滚动内容的一个层,且其父级为最外层
		rebuild:true,
		
		//向左按钮
		leftBtn:null,
		
		//向右按钮
		rightBtn:null,
		
		//指示性质按钮集合
		instructBtns:null,
		
		//是否自动滚动
		isAuto:true,
		
		//自动循环间隔
		autoInterval:3000,
		
		//动画过度时间
		transition:500,
		
		//滚动方向
		slideDirection:"x",
		
		//触发方式
		effectType:"mouseover",
		
		//设置当事件触发时简易的按钮样式添加
		btnsToggleClass:"",
		
		//每帧动画播放间隔
		interval:20
	}
	
	var op;
	arguments[0]? option.effectArea = jnSelector( arguments[0] ):"";
	arguments[1]? op = arguments[1]:"";
	
	var _outset,_box;
	var _beforeEvent,_afterEvent;
	
	function addEvent(elm,type,func){
		if(elm.addEventListener){
			elm.addEventListener(type,_addEvent,false);
		}
		else if(elm.attachEvent){
			elm.attachEvent("on" + type,_addEvent);
		}
		
		
		function _addEvent(){
			func.call(elm);
		}
		
		return _addEvent;
	}
	
	function removeEvent(elm,type,func){
		if(elm.removeEventListener){
			elm.removeEventListener(type,func,false);
		}
		else if(elm.detachEvent){
			elm.detachEvent("on" + type,func);
		}
	}
	
	//console for ie7+
	function jnConsole(){
		if(typeof console !="undefined"){return console;}
	}
	
	// selector
	function jnSelector(source){
		var dc = document;
		
		if( typeof source == "Object"){
			return source;
		}
		else if( typeof source == "string"){
			
			var strGroup = source.split(" ");
			var strGroup_len = strGroup.length;
			var fragSource;
			var fragSource_len;
			var result = dc;
			if(strGroup_len == 1 && strGroup[0].substring(0,1) == "#"){
				return dc.getElementById( strGroup[0].substring(1) )
			}
			else{
				if(dc.querySelectorAll){
					result = dc.querySelectorAll(source); 
				}
				else{
					for( var i = 0; i < strGroup_len; i++){
						fragSource = strGroup[i];
						fragSource_len = fragSource.length;
						if(fragSource_len == 0){continue;}
						switch(fragSource.substring(0,1)){
							case "#": result = idSelector(result,fragSource.substring(1)); break;
							case ".": result = classSelector(result,fragSource.substring(1)); break;
							default : result = tagSelector(result,fragSource); break;
						}
					}
					result = [].concat(result);
					
				}
				if(result.length == 0){
					jnConsole("can't find out" + source);
					return false;
				}
				else if(result.length == 1){
					return result[0];
				}
				else{
					return result;
				}
			}
		}
		
		function idSelector(target,idStr){
			return dc.getElementById(idStr);
		}
		
		function classSelector(target,className){
			var targetGroup = [].concat(target);
			var targetGroup_len = targetGroup.length;
			var result = [];
			var fragTarget;
			var fragGroup;
			var fragTarget_cells;
			var fragTarget_cells_len;
			var fragClassNames;
			var fragClassNames_len;
			for( var i = 0; i < targetGroup_len; i++){
				fragTarget = targetGroup[i];
				if(!fragTarget){continue;}
				fragTarget_cells = fragTarget.getElementsByTagName("*");
				fragTarget_cells_len = fragTarget_cells.length;
				
				for( var k = 0; k < fragTarget_cells_len; k++){
					fragClassNames = fragTarget_cells[k].className.split(" ");
					fragClassNames_len = fragClassNames.length;
					
					for( var j = 0; j < fragClassNames_len; j++){
						if(fragClassNames[j] == className){
							result = result.concat(fragTarget_cells[k]);
							break;
						}
					}
				}
				
				
			}
			return result;
		}
		
		function tagSelector(target,tagStr){
			var targetGroup = [].concat(target);
			var targetGroup_len = targetGroup.length;
			var result = [];
			var fragTarget;
			var fragGroup;
			var fragGroup_len;
			for( var i = 0; i < targetGroup_len; i++){
				fragTarget = targetGroup[i];
				if(!fragTarget){continue;}
				fragGroup = fragTarget.getElementsByTagName(tagStr);
				fragGroup_len = fragGroup.length;
				for( var j = 0; j < fragGroup_len;j++){
					result = result.concat(fragGroup[j]);
				}
			}
			return result;
		}
	}

	//惯性运动
	function inertia_Motion(So,St,T){
		var S = Math.abs(St - So);
		var a = S/Math.pow(T/2,2);
		var Vt = a*T/4;
		
		return{
			Sn:function(Tn){
				var _Sn;
				Tn < T/2?(
					_Sn = So + a*Math.pow(Tn,2)/2 * ( parseInt( (St - So)/Math.abs(St - So) )||0 )
				):(
					Tn < T?(
						_Sn = So +  ( a*Math.pow(T/2,2)  - a*Math.pow(T - Tn,2)/2 )*( parseInt( (St - So)/Math.abs(St - So) )||0 )
					):(
						_Sn =  St
					)
				);
				
				return _Sn;
			},
			Vn:function(Tn){
				var _Vn;
				Tn < T/2?(
					_Vn = a*Tn/2
				):(
					Tn < T?(
						_Vn = Vt - a*(Tn - T/2)/2
					):(
						_Vn = 0
					)
				);
				return _Vn;
			}	
		}
	}
	
	//初始化
	function areaReset(){
		
		var dc = document;
		var target = option.effectArea;
		if(option.rebuild){
			var outsetCssText = "";
			_outset = dc.createElement("outset");
			outsetCssText += ";display:block";
			outsetCssText += "; width:100%";
			outsetCssText += "; height:100%";
			outsetCssText += "; overflow:hidden";
			
			_outset.style.cssText = outsetCssText;
			
			_box = dc.createElement("box");
			var boxCssText = "";
			boxCssText += ";display:block";
			boxCssText += ";height:" + (option.slideDirection == "x"?"100%":"auto");
			boxCssText += ";width:" + (option.slideDirection == "x"?"999em":"100%");
			_box.style.cssText = boxCssText;
			
			var fragment = dc.createDocumentFragment();
			while(target.children.length > 0){
				
				fragment.appendChild(target.children[0]);
			}
			
			target.appendChild(_outset);
			_outset.appendChild(_box);
			_box.appendChild(fragment);
		}
		else{
			_box = target;
			
		}
		//slideEvent variable reset;
		slideEvent.tarIndex = 0;
		var direction = option.slideDirection;
		var boxChildren_len = _box.children.length
		
		
		var distance = (boxChildren_len > 0?(
			(direction == "x"?(
				_box.children[0].offsetWidth
			):(
				_box.children[0].offsetHeight
			))
		):0);
		
		var transition = option.transition;
		var interval = option.interval;
		var sTarget = _box;
		if(direction =="x"){
			sTarget.scrollLeft = 0;
		}
		else{
			sTarget.scrollTop = 0;
		}
		var cellLength = boxChildren_len;
		var beforeCallback,afterCallback;
		
		if(option.isAuto){
			beforeCallback = function(){
				typeof _beforeEvent == "function"?_beforeEvent():"";
				autoIntervalEvent.remove();
			}
			afterCallback = function(){
				typeof _afterEvent == "function"?_afterEvent():"";
				autoIntervalEvent();
			}
			autoIntervalEvent();
			addEvent(_box,"mouseover",function(){
				autoIntervalEvent.isHover = true;
			});
			addEvent(_box,"mouseout",function(){
				autoIntervalEvent.isHover = false;
			})
			
			
		}
		else{
			beforeCallback = _beforeEvent;
			afterCallback = _afterEvent;
		}
		
		//1234
		var istBtns = option.instructBtns;
		var istBtns_len;
		var istBtnsCell;
		if(istBtns){
			istBtns_len = istBtns.length;
			for( var i = 0; i < istBtns_len; i++){
				istBtnsCell = istBtns[i];
				if(istBtnsCell){
					(function(ix){
						addEvent(istBtnsCell,option.effectType,function(){
							slideEventInit(ix);
						});
						if(!option.isAuto){return;}
						
						addEvent(istBtnsCell,"mouseover",function(){
							autoIntervalEvent.isHover = true;
						});
						
						addEvent(istBtnsCell,"mouseout",function(){
							autoIntervalEvent.isHover = false;
						});
					})(i)
				}
			}
		}
		
		//left/right button
		var leftBtn = option.leftBtn;
		var rightBtn = option.rightBtn;
		if(leftBtn){
			addEvent(leftBtn,"click",function(){
				var targetIndex = indexCheck((slideEvent.tarIndex||0) - 1,cellLength);
				slideEventInit(targetIndex);
			})
			
		}
		if(rightBtn){
			addEvent(rightBtn,"click",function(){
				var targetIndex = indexCheck((slideEvent.tarIndex||0) + 1||0,cellLength);
				slideEventInit(targetIndex);
			})
		}
		
		function slideEventInit(index){
			slideEvent(
				sTarget,
				index,
				distance,
				direction,
				cellLength,
				transition,
				interval,
				beforeCallback,
				afterCallback
			);
			if(option.btnsToggleClass !=""){
				toggleClassEvent(istBtns,istBtns[index],option.btnsToggleClass);
			}
		}
		
		// auto interval
		function autoIntervalEvent(){
			var targetIndex = indexCheck((slideEvent.tarIndex||0) + 1||0,cellLength);
			autoIntervalEvent.intervalKey = setTimeout(function(){
				if(autoIntervalEvent.isHover){
					autoIntervalEvent();
				}
				else{
					slideEventInit(targetIndex);
				}
				
			},option.autoInterval)
			
		}
		autoIntervalEvent.isHover = false;
		autoIntervalEvent.remove = function(){
			if(autoIntervalEvent.intervalKey){
				clearTimeout(autoIntervalEvent.intervalKey);
			}
		}
		
		
		//mobile only
		var touchInit = {};
		touchInit.So = 0;
		touchInit.Sn = 0;
		touchInit.St = 0;
		if(direction == "x"){
			touchInit.styleKey = "marginLeft";
			touchInit.touchKey = "pageX"
		}
		else{
			touchInit.styleKey = "marginTop";
			touchInit.touchKey = "pageY"
		}
		addEvent(_box,"touchstart",function(e){
			e = e||window.event;
			e.preventDefault();
			if(!e.touches.length){return;}
			if(slideEvent.transitionKey){
				clearTimeout(slideEvent.transitionKey);
			}
			var touch = e.touches[0];
			
			autoIntervalEvent.isHover = true;
			autoIntervalEvent.remove();
			
			touchInit.So = touch[touchInit.touchKey] - (parseInt(_box.style[touchInit.styleKey]||0)); 
			
			
		})
		
		addEvent(_box,"touchmove",function(e){
			e = e||window.event;
			e.preventDefault();
			if(!e.touches.length){return;}
			var touch = e.touches[0];
			touchInit.Sn = touch[touchInit.touchKey];
			touchInit.St = touchInit.Sn - touchInit.So;
			touchInit.St > 0?(
				touchInit.St = 0
			):(
				touchInit.St < -(cellLength - 1)*distance?(
					touchInit.St = -(cellLength - 1)*distance
				):""
			)
			this.style[touchInit.styleKey] = touchInit.St + "px";
		})
		
		addEvent(_box,"touchend",function(e){
			e = e||window.event;
			e.preventDefault();
			autoIntervalEvent.isHover = false;
			var orgIndex = slideEvent.tarIndex;
			var effectDistance = 0.2;
			frag = -touchInit.St/distance - orgIndex;
			frag > 0?(
				frag > effectDistance? frag = Math.ceil(frag):frag = Math.floor(frag)
			):(
				frag < -effectDistance? frag = Math.floor(frag):frag = Math.ceil(frag)
			);
			var index = orgIndex + frag
			
			slideEventInit(index);
			
		})
		
		addEvent(window,"orientationchange",function(){
			distance = (boxChildren_len > 0?(
				(direction == "x"?(
					_box.children[0].offsetWidth
				):(
					_box.children[0].offsetHeight
				))
			):0);
		})
	}
	
	// oh,there are so many variable you need to fill in...
	function slideEvent(target,index,distance,direction,cellLength,transition,interval,beforeCallback,afterCallback){
		
		//reset
		if(typeof slideEvent.tarIndex == "undefined"){
			slideEvent.tarIndex = 0;
		}
		
		//if(index == slideEvent.tarIndex){return;}
		
		if(typeof slideEvent.transitionKey != "undefined"){
			clearTimeout(slideEvent.transitionKey);
		}
		
		slideEvent.tarIndex = index;
		
		var dirKey =(direction == "x"?"marginLeft":"marginTop");
		var So = parseInt(target.style[dirKey])||0;
		var St = -slideEvent.tarIndex*distance;
		var T = transition/interval ||0;
		var Tn = 0;
		
		var transInertia = inertia_Motion(So,St,T);
		
		typeof beforeCallback == "function"?beforeCallback():"";
		
		(function _transitonEvent(){
			if(Tn < T){
				target.style[dirKey] = transInertia.Sn(Tn) + "px";
				
				Tn++;
				slideEvent.transitionKey = setTimeout(_transitonEvent,interval);
			}
			else{
				target.style[dirKey] = St + "px";
				typeof afterCallback == "function"?afterCallback():"";
			}
		})();
	}
	
	
	
	function indexCheck(index,length){
		if( index >= 0 ){
			if( index < length ){
				return index;
			}
			else{
				return 0;
			}
		}
		else{
			return length - 1;
		}
	}
	
	// className exchange
	function toggleClassEvent(group,target,className){
		if(!className||!group||!target){return;}
		
		var group_len = group.length;
		var fragCell;
		var fragClassNames;
		var fragClassNames_len;
		var classNameIndex = -1;
		for( var i = 0; i < group_len; i++){
			fragCell = group[i];
			fragClassNames = fragCell.className.split(" ");
			fragClassNames_len = fragClassNames.length;
			classNameIndex = -1;
			
			for( var j = 0; j < fragClassNames_len; j++ ){
				if(fragClassNames[j] == className){
					
					classNameIndex = j;
					break;
				}
			}
			
			fragCell == target?(
				classNameIndex == -1?fragClassNames.push(className):""
			):(
				classNameIndex == -1?"":fragClassNames.splice(classNameIndex,1)
			);
			fragCell.className = fragClassNames.join(" ");
		}
	}
	
	return{
		//设置事件触发前发生事件
		setBeforeSlideEvent:function(func){
			_beforeEvent = func;
			return this;
		},
		
		//设置事件触发后发生事件
		setAfterSlideEvent:function(func){
			_afterEvent = func;
			return this;
		},
		
		//属性设置
		setOption:function(op){
			op.effectArea?option.effectArea = jnSelector( op.effectArea ):"";
			op.leftBtn?option.leftBtn = jnSelector( op.leftBtn ):"";
			op.rightBtn?option.rightBtn = jnSelector( op.rightBtn ):"";
			op.instructBtns?option.instructBtns = jnSelector( op.instructBtns ):"";
			
			typeof op.isAuto == "boolean"?option.isAuto = op.isAuto:"";
			typeof op.rebuild == "boolean"?option.rebuild = op.rebuild:"";
			
			op.autoInterval?option.autoInterval = op.autoInterval:"";
			op.transition?option.transition = op.transition:"";
			op.slideDirection?option.slideDirection = op.slideDirection:"";
			op.effectType?option.effectType = op.effectType:"";
			op.btnsToggleClass?option.btnsToggleClass = op.btnsToggleClass:"";
			
			op.interval?option.interval = op.interval:"";
		},
		init:function(){
			op?this.setOption(op):"";
			if(!option.effectArea){return;}
			areaReset();
		}
	}
}

示例可点击 demo 查看

分类HTML5 + CSS3
标签,
阅读 685
  • 评论加载中...

标签云

分类目录

最新留言

  • 评论加载中...

与我联系

如有疑问or建议可通过以下方式跟我取得联系.

Q Q:373435871
Email:jackness1208@gmail.com
© Copyright 2011 - 2014 jackNEss.org All Rights Reserved 粤ICP备14065612号
首页 | 关于我 | 网站地图 | RSS