[javascript]web216安全色拾色器colorpicker 支持浏览器边缘碰撞翻转(原生js)

html5改进最大的是form表单控件

拾色器作为其中一种已经被opera 和chrome21支持了,但是UI非常不尽人意。而且比较多的旧浏览器还是需要这个功能的。所以自己就做了这样一个控件,控件原型主要来自著名的web开发软件dreamweaver的web安全色拾色器。

html的拾色器

<input type="color" />

 

 

色彩生成算法来自【蜃楼寻梦】特别感谢。

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
<style type="text/css">
        #abc-colorPicker .colorPick-close,#filter-colorPicker .colorPick-close{position:absolute;right:0;}
        #abc-colorPicker a,#filter-colorPicker a,#colorPicker a{border-bottom:1px solid #000;border-left:1px solid #FFF;border-right:1px solid #000;border-top:1px solid #FFF;cursor:pointer;display:block;float:left;height:15px;overflow:hidden;width:15px;}
        #abc-colorPicker a:hover,#filter-colorPicker a:hover,#colorPicker a:hover{opacity:.5;}
        #abc-colorPicker li,#filter-colorPicker li,#colorPicker li{display:inline;list-style:none;overflow:hidden;}
        #abc-colorPicker ul,#filter-colorPicker ul,#colorPicker ul{-moz-transition:all .8s ease-in 1s;-ms-transition:all .8s ease-in 1s;-o-transition:all .8s ease-in 1s;-webkit-transition:all .8s ease-in 1s;float:left;margin:0;overflow:hidden;transition:all .8s ease-in 1s;width:102px;}
        #abc-colorPicker,#filter-colorPicker,#colorPicker{background:#FFF;border:solid 1px #eee;display:none;padding:5px;width:312px;}
        #demo{height:600px;margin:0 auto;width:800px;}
        *{margin:0;padding:0;}
        .colorPicker-container a:hover{border-bottom:1px solid #000;border-top:1px solid #000;}
        .colorPicker-preview{color:#FFF;display:inline-block;height:30px;line-height:30px;width:60px;}
        .colorPicker-title{height:30px;overflow:hidden;}
    </style>
</head>

<body>
<div style="height:500px;"></div>
<div id="demo">
    <input type="text" id="filter"/>
    <input type="text" id="abc"/>
</div>
<script type="text/javascript">
 /*
  * Copyright 2012 enix@foxmail.com
  */

        function ColorPicker(k,j){var i=this,h=arguments.callee,l,b,c;if(!(this instanceof h))return i=new h(k,j);b=h.prototype;b.constructor=h;h.cache||(h.cache=[]);c={createColorData:function(){var a=h.cache,e,d,g,f=0,b,c=0;for(e=0;6>e;e++){a.push("<ul>");for(d=0;6>d;d++){a.push("<li>");for(g=0;6>g;g++)b=("000"+f.toString(16)).slice(-3).toUpperCase(),c++,i.color.push(b),a.push('<a index="'+c+'" style="background: #'+b+'" title="#'+b+'" javascript="void (0);"></a>'),f+=3;f+=30;a.push("</li>")}a.push("</ul>");
            f+=480}"function"===typeof this.ongetcolorstring&&this.ongetcolorstring.call(this.color)},getHtmlElement:function(a){this.element||(this.element={});this.element[a]||(this.element[a]=l.createElement(a));return this.element[a].cloneNode(!0)},each:function(a,e){var d,b=0,f=a.length>>>0;if("Object"==={}.toString.call(a).slice(8,-1))for(d in a){if(!1===e.call(a[d],d,a[d]))break}else for(;b<f&&!1!==e.call(a[b],b,a[b++]););},getViewportSize:function(){var a=[0,0];void 0!==window.innerWidth?a=[window.innerWidth,
            window.innerHeight]:a=[document.documentElement.clientWidth,document.documentElement.clientHeight];return a},getClinetRect:function(a){a=a.getBoundingClientRect();return a=a={left:a.left,right:a.right,top:a.top,bottom:a.bottom,height:a.height||a.bottom-a.top,width:a.width||a.right-a.left}},getScrollPosition:function(){var a=[0,0];window.pageYOffset?a=[window.pageXOffset,window.pageYOffset]:"undefined"!=typeof document.documentElement.scrollTop&&0<document.documentElement.scrollTop?a=[document.documentElement.scrollLeft,
            document.documentElement.scrollTop]:"undefined"!=typeof document.body.scrollTop&&(a=[document.body.scrollLeft,document.body.scrollTop]);return a},addEvent:function(a,e,b,c){a.attachEvent&&function(a,b,e){a.attachEvent("on"+b,e)}.apply(this,arguments);a.addEventListener&&function(a,b,e){a.addEventListener(b,e,c||!1)}.apply(this,arguments);a["on"+e]&&function(a,b,e){a["on"+b]=function(){e()}}.apply(this,arguments)}};b.Init=function(){l=document;this.color=[];this.offset=k;this.offsetSize=c.getClinetRect(this.offset);
            j||(this.prefix="colorPicker");j&&Object&&(c.each(j,function(a,b){i[a]||(i[a]=b)}),this.prefix=j.prefix?j.prefix+"-colorPicker":this.prefix);this.isInputText="input"===this.offset.nodeName.toLowerCase()&&"text"===this.offset.getAttribute("type");0==h.cache.length&&c.createColorData();this.createPacker().addEvent().render()};b.createPacker=function(){var a=[["div","id#"+this.prefix+",class#colorPicker"],["div","class#colorPicker-title"],["span","class#colorPicker-preview"],["input","type#button,class#colorPick-close,value#\u5173\u95ed"],
            ["div","class#colorPicker-container"]],b,d;c.each(a,function(g,f){b=c.getHtmlElement(f[0]);c.each(f[1].split(","),function(a,c){d=c.split("#");d[0]&&b.setAttribute(d[0],d[1])});a.splice(g,1,b)});this.elem=a;return this};b.addEvent=function(){c.addEvent(i.elem[4],"mouseover",function(a){i.eventContrl(a)},!1);c.addEvent(this.elem[4],"click",function(a){i.eventContrl(a)},!1);c.addEvent(this.elem[3],"click",function(){i.kill()},!1);return this};b.getColor=function(a){this.current&&this.current.constructor===
                String&&(a.className=this.current);this.elem[2].innerHTML=this.fixColorValue(a.title);this.elem[2].style.background=a.title};b.eventContrl=function(a){var a=a||window.event,b=this.getCurrent(a);b&&("mouseover"==a.type?this.getColor(b):this.outputValue(b))};b.getCurrent=function(a){a=a||window.event;for(a=a.target||a.srcElement;a&&(3==a.nodeType||"a"!==a.nodeName.toLowerCase());)a=a.parentNode;return this.currentElem=a};b.fixColorValue=function(a){return!a?-1:a.replace(/(\w)/g,function(){return RegExp.$1+
                RegExp.$1})};b.outputValue=function(a){a=this.fixColorValue(a.title);this.kill();!0===this.isInputText&&(this.offset.value=a);"function"===typeof this.ongetvalue&&this.ongetvalue.call([a]);return this};b.render=function(){var a=this.elem;a[1].appendChild(a[2]);a[1].appendChild(a[3]);a[0].appendChild(a[1]);a[0].appendChild(a[4]);this.offset.parentNode.appendChild(a[0]);a[4].innerHTML=h.cache.join("");"function"===typeof this.onrendered&&this.onrendered.call(this);return this};b.checkPosition=function(a,b){var a=this.elem[0],d=c.getClinetRect(a),g=c.getViewportSize(),f=c.getScrollPosition();if(!(d.height>g[1]||d.width>g[0]))return 0<d.right-g[0]&&(a.style.left=b.right-d.width+"px"),0<d.bottom-g[1]&&(a.style.top=b.top-d.height+f[1]+"px"),this};b.kill=function(){this.elem[0].style.display="none";return this};b.display=function(){var a=c.getClinetRect(this.offset),b=this.elem[0];b.style.display="block";b.style.cssText="position:absolute;top:"+this.offsetSize.bottom+"px;left:"+this.offsetSize.left+"px; display:block;";this.checkPosition(b,a);return this};this.Init()};
</script>

<script type="text/javascript">
!function(window,doc,undefined){
    var a,b;
   a
= ColorPicker(doc.getElementById('filter'), {prefix:'filter', ongetvalue:function () { //console.log(this) }, bdf:'xdfdf'}); doc.getElementById('filter').onfocus = function () { a.display() } /*b = ColorPicker(doc.getElementById('abc'),{prefix:'abc', ongetvalue:function () { console.log(this) }})*/ b = ColorPicker(doc.getElementById('abc')); doc.getElementById('abc').onfocus = function () { b.display() } }(this,document) </script> <div style="height:600px;"></div> </body> </html>

API
        ColorPicker (node,Option)
        属性:
             node : html node //必须
             Option : {'prefix':string,ongetcolorstring:fn,ongetvalue:fn,onrendered:fn} //可选

                        prefix: 控件id前缀,无prefix属性控件自动添加id为colorPicker
                        ongetcolorstring : 在生成全部色值对应的16进制数字的数组后调用
                        ongetvalue : 在点击色块获得色之后调用,回调函数内的this指向['#000000']这样的色值
                        onrendered : 渲染完成控件后调用

        方法 :
             display : 控制控件显示
             kill : 控制控件隐藏

 

核心问题:

 生成色值

为了使数据简单易用,所以使用了数据扁平化。生成一个数组,并且在需要的地方插入合适的html标签,把颜色值从在实例的属性上以备用。

节点管理

由于色值有216个,加上结构所以节点数目非常多,所以一个个去创建节点是非常耗费资源的。所以色值对应的节点都有字符串拼装,外围结构节点使用dom创建。结构节点储存与实例的属性上,以便于及时访问。结构节点为了避免多次调用documentCreateElment 也采取了创建没有的节点,使用时已经创建那么通过克隆的方式去实现。

事件委托

这么多的节点如果每个都绑定事件,过于浪费资源。采取了在容器节点上绑定事件做委托,减少资源的浪费。

控件与浏览器碰撞

控件如果超出浏览器,用户就无法正常使用功能了,为了最大限度的保证可用性。当控件超出浏览器时要进行翻转处理,如果视口尺寸小于控件尺寸,那么该控件自动隐藏。

怎样确定控件与窗口关系。

1.先确定控件是不是能显示。

获取视口大小

getViewportSize:function () {
    var value = [0, 0];
    undefined !== window.innerWidth ? value = [window.innerWidth, window.innerHeight] : value = [document.documentElement.clientWidth, document.documentElement.clientHeight];
    return value;
}

 

获取元素在视口中的位置

getClinetRect:function (f) {
    var d = f.getBoundingClientRect(), e = (e = {left:d.left, right:d.right, top:d.top, bottom:d.bottom, height:(d.height || (d.bottom - d.top)), width:(d.width || (d.right - d.left))});
    return e;
}

 

      这个方法主要封装了getBoundingClientRect方法,这个是微软原创ie4开始支持,最晚支持的是ff3,ff3.6已经被mozilla放弃支持所以可以大胆使用。

其他浏览器会返回元素在浏览器窗口中的位置,ff除了会返回这四个值还会额外返回宽高共计6个值,所以要进行修正。这样当display方法被调用时,去比较控件与窗口的位置关系,然后确定控件如何摆放。

 

 

这个控件准备用在canvas的文字处理功能上

posted @ 2012-05-31 13:23  小玉西瓜  阅读(1561)  评论(0编辑  收藏  举报