关于写代码的思考

最近突然想明白一些事情,比如:苹果为什么会从树上掉下来,又比如:太阳为什么会每天升起落下 :mrgreen:
最近有个需求,一张设计图整体风格,偏科技蓝,里面有一部分是小表格,表格里面内容过多,需要出现滚动条,然而浏览器默认的滚动条和设计图的风格偏差太大,我们开会给了一个自定义滚动条的方案,最后由我来做。
最开始接到需求,开始规划一下,怎么做,首先,需要滚动的内容不止一个,是好几个小表格,那么就需要写成可复用式的,算是一个小小的组件吧。
这个组件的功能包括:可鼠标拖动,鼠标滚轮来控制滑块,拖动滑块的同时内容要移动,样式要可自定义的,还要有滑槽。
计划好了大概功能,我就开始做,首先,先写配置样式什么的,然后判断是否需要创建,再然后开始写事件功能,点击啊,移动啊,滚轮啊。写好以后测试了一下,看着效果还不错,可以拖,可以滚轮,一种舒爽的感觉开始散发全身,然而,当我测试复用,也就是多个滚动条同时存在的时候,看着效果,顿时:一脸遭逼,卧槽,这是什么情况!滚动第一个,第二个动了。
直接上代码:

this.wmscroll = this.wmscroll || {};
(function(){
	//对象
	var init = function init(obj){
		p.start(obj);
	};
	//原型
	var p = init.prototype;
	p.start = function(obj){
		//this.tardom = obj.tardom;//盒子
		//this.condom = obj.condom;//需要滚动的内容
		this.tardom = document.getElementById(obj.tardom);
		this.condom = document.getElementById(obj.condom);
		this.scrollbar_bg = {
			"background":"blue",
			"position":"absolute",
			"top":0,
			"right":0,
			"height":"100%",
			"width":"10px"
		}
		this.scrollbar_styles = {//默认样式
			"position":"absolute",
			"top":0,
			"right":0,
			"background":"red",
			"width":"10px",
			"height":"50px",
			"z-index":2
		};
		this.options = {
			"condis":0,
			"type":"y",
			"dir":0,
			"scrflag":false
		};
		//配置样式覆盖默认样式
		if(obj.styles){
			this.extend(this.scrollbar_styles,obj.styles);
		};
		if(obj.scrollbar_bg){
			this.extend(this.scrollbar_bg,obj.scrollbar_bg);
		}
		this.create();
	};
	p.create = function(){
		if(this.condom.offsetWidth > this.tardom.offsetWidth){
			this.styles(this.tardom,{
				"position":"relative",
				"overflow":"hidden"
			});
			
		this.scrollbar = document.createElement("div");
		this.scrollbar_bgs = document.createElement("div");
		this.styles(this.scrollbar,this.scrollbar_styles);
		this.styles(this.scrollbar_bgs,this.scrollbar_bg);
		this.tardom.appendChild(this.scrollbar_bgs);
		this.tardom.appendChild(this.scrollbar);
		var self = this;
		this.scrollbar.onmousedown = function(ev){
			self.options.scrflag = true;
			var ev = ev || event;
			var dis = ev.clientY - this.offsetTop;
			document.onmousemove = function(ev){
				if(self.options.scrflag){
					var ev = ev || event;
					var disY = ev.clientY - dis;
					//可视区,也就是滚动区域
					var iMax = self.tardom.clientHeight;
					self.move(disY,iMax);
				}
				document.onmouseup = function(){	
					self.options.scrflag = false;
				}
				return false;
			}
			return false;
		}
		if(this.options.type=="y"){
			var evname = navigator.userAgent.indexOf('Firefox') > -1 ? "DOMMouseScroll" : "mousewheel";
			this.bind(this.tardom,evname,function(ev){	
				self.mousewheel(ev);
			},false);
		}
		}
	};
	p.mousewheel = function(ev){
		var ev = ev || event;
		//定义一个 wheel 表示滚动方向,向下是 false,向上是 true
		var wheel = ev.wheelDelta > 0 ? true :false || ev.detail < 0 ? true :false;
		//可视区,也就是滚动区域
		var iMax = this.tardom.clientHeight;
		var dis = wheel ? -33 : 33;
		//移动
		this.move(this.options.dir + dis,iMax);
		this.stop(ev);
	}
		//滚动条移动方法,dir 是方向,dis 是距离
	p.move = function(dis,iMax){
		if(dis<=0){ this.options.dir = 0; }else if(dis>=iMax-this.scrollbar.offsetHeight){
				//clientHeight 不包括边框
				this.options.dir = iMax-this.scrollbar.offsetHeight;
			}else{
				this.options.dir = dis;
			}
			//滚动条的移动距离和整个滚动区域的比例
		var isCale = this.options.dir / (iMax - this.scrollbar.offsetHeight);
		this.options.condis = (iMax - this.condom.offsetHeight) * isCale;
		TweenMax.to(this.scrollbar,0.1,{
			top:this.options.dir
		});
		TweenMax.to(this.condom,0.1,{
			marginTop:this.options.condis
		});
	};
	//继承
	p.extend= function(obj1,obj2){
		for(var attr in obj2){
			obj1[attr] = obj2[attr];
		}
	}
	//滚动条样式配置
	p.styles = function(dom,sty){
		if(typeof sty === "object"){
			for (var obj in sty) {
				//遍历配置参数,并过滤无效样式,将有效样式添加
				if(dom.style[obj]!=undefined){
					dom.style[obj] = sty[obj];
				};
			};
		}else{ 
			alert("参数错误!");
		};
	};
	//通用事件绑定方法
	p.bind = function(obj,events,fn,type){
		if(obj.addEventListener){
			obj.addEventListener(events,fn,type);
		}else{
			obj.attachEvent('on'+events,fn);
		}
	}
	//阻止默认事件,比如复制文字
	p.stop = function(ev){
		var ev = ev || event;
		if (ev.preventDefault) {
			//标准下阻止默认行为
			ev.preventDefault();
		} else {
			//IE 中阻止默认行为
			window.event.returnValue = false;
			return false;
		}
	}
	wmscroll.init = init;
})();

                        //实例化滚动条
			new wmscroll.init({
				"tardom":"box",
				"condom":"con1"
			});
			new wmscroll.init({
				"tardom":"box1",
				"condom":"con1"
			});

这里是 Demo,左边是第一个,右边是第二个。控制第一个,第一个不动,第二个动了!
失败版自定义滚动条
最后看代码,看了几次,没找到原因,重写。结果还是错!
晚上回到家,想了想,我原来写单纯的拖拽的时候咋就没毛病呢,这都是一样的套路来的啊,想来想去,觉得只有写代码的步骤不一样,我再次开始重写滚动条,这次我是先创建元素,样式就用类名控制了, 然后写可拖动功能,先让它动起来,然后控制拖动范围,然后控制内容的移动,然后写滚轮事件,然后写自定义配置样式,最后写好,向外提供一个接口,然后测试。
效果是杠杠的,多个滚动条,互不干扰,样式自定义,还可以控制鼠标滚动一下内容滚动的距离。
直接上代码:

this.wm = this.wm || {};
(function(){
	function scroll(options){
				//构造函数初始化
				this.disY = 0;//鼠标在滚动条的位置
				this.dirY = 0;//滚动条的位置
				this.options = {
					scrollbar_bg:{//滑块轨道样式配置
						"background":"blue",
						"position":"absolute",
						"top":0,
						"right":0,
						"height":"100%",
						"width":"10px"
					},
					scrollbar_styles:{//滑块样式配置
						"position":"absolute",
						"top":0,
						"right":0,
						"background":"red",
						"width":"10px",
						"height":"50px",
						"z-index":2
					},
					//鼠标滚轮滚动一次内容移动的距离
					wheel:33
				}
				this.tardom = document.getElementById(options.tardom);//内容盒子
				this.condom = document.getElementById(options.condom);//内容
				//判断是否需要出现滚动条
				if(this.condom.offsetHeight>this.tardom.offsetHeight){
					this.styles(this.tardom,{
						"position":"relative",
						"overflow":"hidden"
					});
					//参数混入
					this.extend(this.options,options);
					//实例一个滚动条
					this.create();
				}
			};
			var p = scroll.prototype;
			//创建滚动条滑块。滑块轨道
			p.create = function(){
				this.scrollbar = document.createElement("div");//滑块
				this.styles(this.scrollbar,this.options.scrollbar_styles);
				this.tardom.appendChild(this.scrollbar);
				this.scrollbar_bg = document.createElement("div");//滑块轨道
				this.styles(this.scrollbar_bg,this.options.scrollbar_bg);
				this.tardom.appendChild(this.scrollbar_bg);
			}
			//初始化事件
			p.scroll = function(){
				this.init();
			};
			p.init = function(){
				var self = this;
				this.scrollbar.onmousedown = function(ev){
					 var ev = ev || event;
					 self.domDown(ev);
					 return false;
				}
				//滚轮事件
				var evname = navigator.userAgent.indexOf('Firefox') > -1 ? "DOMMouseScroll" : "mousewheel";
				this.bind(this.tardom,evname,function(ev){
					self.mouseWheel(ev);
					self.stop(ev);
				},false);
			};
			p.domDown = function(ev){
				var self = this;
				var ev = ev || event;
				this.disY = ev.clientY - this.scrollbar.offsetTop;
				document.onmousemove = function(ev){
					var ev = ev || event;
					self.domMove(ev);
				}
				document.body.onmouseup = self.domUp;
			}
			p.domMove = function(ev){
				var iMax = this.tardom.clientHeight;
				this.dirY = ev.clientY - this.disY;
				this.move(this.dirY,iMax);
			};
			p.domUp = function(){
				document.onmousemove = null;
				document.body.onmouseup = null;
			}
			//最终处理移动
			p.move = function(dirY,iMax){
				if(dirY<=0){ this.dirY = 0; }else if(dirY>=iMax-this.scrollbar.offsetHeight){
					//clientHeight 不包括边框
					this.dirY = iMax-this.scrollbar.offsetHeight;
				}else{
					this.dirY = dirY;
				}
				var isCale = this.dirY / (iMax - this.scrollbar.offsetHeight);
				TweenMax.to(this.scrollbar,0.1,{
					top:this.dirY
				});
				TweenMax.to(this.condom,0.1,{
					marginTop:(iMax - this.condom.offsetHeight) * isCale
				});
			}
			p.mouseWheel = function(ev){
				var ev = ev || event;
				//定义一个 wheel 表示滚动方向,向下是 false,向上是 true
				var wheel = ev.wheelDelta > 0 ? true :false || ev.detail < 0 ? true :false;
				//可视区,也就是滚动区域
				var iMax = this.tardom.clientHeight;
				var dis = wheel ? -this.options.wheel : this.options.wheel;
				//移动
				this.move(this.dirY + dis,iMax);
			}
			//通用事件绑定方法
			p.bind = function(obj,events,fn,type){
				if(obj.addEventListener){
					obj.addEventListener(events,fn,type);
				}else{
					obj.attachEvent('on'+events,fn);
				}
			}
			//阻止默认事件,比如复制文字
			p.stop = function(ev){
				var ev = ev || event;
				if (ev.preventDefault) {
					//标准下阻止默认行为
					ev.preventDefault();
				} else {
					//IE 中阻止默认行为
					window.event.returnValue = false;
					return false;
				}
			}
			//继承
			p.extend= function(obj1,obj2){
				for(var attr in obj2){
					obj1[attr] = obj2[attr];
				}
			}
			//滚动条演示配置
			p.styles = function(dom,sty){
				if(typeof sty === "object"){
					for (var obj in sty) {
						//遍历配置参数,并过滤无效样式,将有效样式添加
						if(dom.style[obj]!=undefined){
							dom.style[obj] = sty[obj];
						};
					};
				}else{ 
					alert("参数错误!");
				};
			};
			wm.scroll = scroll;
})();


//实例化滚动条
var box = new wm.scroll({
				tardom:"box",
				condom:"con",
				wheel:33,
				scrollbar_bg:{
						"background":"green",
						"position":"absolute",
						"top":0,
						"right":0,
						"height":"100%",
						"width":"10px"
					},
					scrollbar_styles:{
						"position":"absolute",
						"top":0,
						"right":0,
						"background":"#f90",
						"width":"10px",
						"height":"50px",
						"z-index":2
					}
			});
			box.scroll();
			var box1 = new wm.scroll({
				tardom:"box1",
				condom:"con1",
				wheel:66,
				scrollbar_bg:{
						"background":"#6fcef3",
						"position":"absolute",
						"top":0,
						"right":0,
						"height":"100%",
						"width":"10px"
					},
					scrollbar_styles:{
						"position":"absolute",
						"top":0,
						"right":0,
						"background":"#851bd6",
						"width":"10px",
						"height":"50px",
						"z-index":2
					}
			});
			box1.scroll();

完美兼容IE8,IE7 下面不支持动画,效果略有不足。
成功版自定义滚动条
这段代码写的很顺畅,一步步实现什么功能,下一步干啥,都很轻松写好了,没出问题。
看看两份代码好像没啥很大的区别,但第一份就是有问题(至今我都没找到,如果有人看出来了,请赐教,太蛋疼了)。
现在想想,也明白了一些道理。
就我来说,做一个项目或者做功能写代码之前,我都习惯做个计划,先做什么,后做什么,某一个模块是依赖某一个模块的。等等这些。现在想想也就是因为这些想法耽误了我不少时间,确实浪费时间了。
然而现在我觉得写代码就:应该一针见血,一定要中要害,先抓住主要的,再抓住次要的,写程序就是这样子,开发之道做增删改查,一个表里面总是做验证,登录注册永远做不完,验证确实很重要,那能不能先把登陆注册的数据保存到数据库里面再说,然后再来做收尾工作,验证码什么的,验证一做完,工作就做完了,难道不是吗?,就一味的把验证做好,到了后面呢,基本的功能没做完,和数据库里面的数据查询还没做,数据对接,注册的话,数据库里还没有数据,做注册的目的,就是将用户填写的数据保存到数据库中,就一句话,如果这个用户不进行恶意攻击,我们根本不用做验证。重点是保存数据而不是怎么做验证,先把数据保存了,再来验证数据的完整性和有效性等等,这样,只要验证完了,功能也就完了,搞来搞去,最后本末倒置。
当然,如果说你水平很高,对这些很纯熟了,那你边做验证边写业务,或者就先写验证,再写业务,那我无话可说,毕竟到了一定级别,这些都不是问题了(我以后可能也会这样写的)。
写代码也是一种艺术:
太多的书都讲到一个观点:“代码是写给人阅读的,只不过刚好能被计算机执行”。(代码可读性)
代码格式美观性,就比如我写出来的代码格式整齐,看着就感觉挺舒服的,当然,也可以写完用格式工具整理一下。
还有最近在看 Flex,里面有段 switch 代码:

switch ( Number( item.fileType ) )
                {
                    case 1 : return 'TXT 文件';
                    case 2 : return 'CSV 文件';
                    case 3 : return 'XML 文件';
                    case 4 : return 'XLS 文件';
                }
                    return '未知文件';

刚开始还没想到 return 的时候可以不用 break,毕竟 return 后面的代码都不会执行,一方面我是觉得这种代码,刚开始看还真的可能看不懂,这就降低了可读性,而从代码的角度来说,这个代码很巧妙,虽然没用 break,但是运行结果是一致的。
顺便想到了 for 可以这样写:

			var i = 0;
			for(;;){
				if(i<5){
					console.log(i);
				}else{
					break;
				}
				i++;
			}

判断都拿到里面了,看起来挺怪的是吧。其实这个挺符合 for 循环的执行过程的。
for 循环执行过程:定义 var i = 0;然后判断 i 符合条件,然后执行循环体,最后才是执行的 i++。
主要就是这个滚动条的问题,给了我一些不一样的感觉。 剩下的尽说了一堆废话。。。。。。


a'ゞ『完美』版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明关于写代码的思考
喜欢 (26)
[]
分享 (0)
a'ゞ『完美』
关于作者:
一个前端小白菜
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(1)个小伙伴在吐槽
  1. a'ゞ『完美』
    我擦,这个链接按钮怎么总是出错 :sad:
    TZ-完美2016-07-03 12:21 回复 Windows 7 | Chrome 52.0.2729.4