2018-05-28


title: 純javascript--簡單的colorPicker取色器
date: 2018-04-26 00:04:45
tags:


功能需求

  • 取色板可點擊遭贸,長按移動火惊,獲取顏色
  • 取色條拖動改變色相
  • 可修改十六進制值隅很,RGB,HSL桐猬,HSV值改變所有顯示數(shù)據(jù)
  • 用戶輸入的數(shù)據(jù)進行校驗(十六進制自動補全冷离;不正確的十六進制自動顯示為黑色;rgb值的極限范圍澡谭,S,V,L 小數(shù)點輸入的實時校驗并糾正)
主思路

一愿题、三部分組成:取色板,取色條蛙奖,輸入框
二潘酗、hsv rgb hsl 三者關(guān)系
HSVL:
H: 色相,0°-360°雁仲,每隔60°有不同的主色相
S: 飽和度崎脉,0-100%,越低越灰暗
V: 明度伯顶,L: 亮度 0-100%
s隨著水平軸的向右從0-1囚灼,v隨著垂直軸的向上從0-1骆膝,所以可以用鼠標(biāo)在取色板中的位置(width, height)與整個面板的width和height的比例求出(s, v),h值可根據(jù)在取色條上的高度灶体,求出hsv值阅签,再來個轉(zhuǎn)換公式rgb,hsl也呼之欲出~

image.png

常規(guī)的取色器
三蝎抽、取色板和取色條的渲染 [ canvas ]
取色條渲染簡單~政钟,用canvas的api addColorStop
取色板看來得用個透明灰板加個主顏色漸變來渲染,不過要注意只要h發(fā)生變化樟结,主顏色就會發(fā)生變化养交,所以得重新渲染主顏色層
四、數(shù)據(jù)變化
觸發(fā)更新的兩種方法:
i. 通過拖動取色板或者取色條瓢宦,通過get方法獲取hsv等
ii. 通過改變輸入板上的值碎连,通過value方法獲取hsv等
更新任務(wù):
* 更新數(shù)據(jù)渲染頁面:
* 獲取當(dāng)前hsv rgb hsl值,
* 實時顯示input中的值
* 實時顯示當(dāng)前顏色
* 更新渲染picker-panel 取色板
* 更新handle的background-color
* 更新handle的位置

取色板驮履,取色條渲染

///先完成取色板鱼辙,取色條的渲染
    <div class="main-panel">
        <div class="main">
            <canvas width="460" height="400" id="selectColor">You browser is not support canvas</canvas>
            <span id="main-handle" class="handle"></span>
        </div>
        <div class="bar">
            <canvas width="12" height="400" id="dragColor"></canvas>
            <span id="drag-handle" class="handle"></span>
        </div>
    </div>
////取色板渲染
        PickerPanel.prototype.render = function (color) {
              ///一層是透明灰層
            var lightLinear = this.ctxPicker.createLinearGradient(0, 0, 0, this.height);
            lightLinear.addColorStop(0, 'rgba(0, 0, 0, 0)');
            lightLinear.addColorStop(1, 'rgba(0, 0, 0, 1)');
              ///一層是顏色層,通過傳進來的color決定色相
            var colorLinear = this.ctxPicker.createLinearGradient(0, 0, this.width, 0);
            colorLinear.addColorStop(0, 'rgba(255, 255, 255, 1)');
            colorLinear.addColorStop(1, color);

            this.ctxPicker.fillStyle = colorLinear;
            this.ctxPicker.fillRect(0, 0, this.width, this.height);
            this.ctxPicker.fillStyle = lightLinear;
            this.ctxPicker.fillRect(0, 0, this.width, this.height);
            
        }


////取色器渲染
        BarPanel.prototype.render = function () {

            var dragLinear = this.ctxPicker.createLinearGradient(0, 0, 0, this.height);
         ////整個取色器分成6等分玫镐,每等分的增量 1 / 6
            dragLinear.addColorStop(0, '#ff0000');
            dragLinear.addColorStop(0.167, '#ffff00');
            dragLinear.addColorStop(0.334, '#00ff00');
            dragLinear.addColorStop(0.501, '#00ffff');
            dragLinear.addColorStop(0.668, '#0000ff');
            dragLinear.addColorStop(0.835, '#ff00ff');
            dragLinear.addColorStop(1, '#ff0000');  

            this.ctxPicker.fillStyle = dragLinear;
            this.ctxPicker.fillRect(0, 0, this.width, this.height);

        }

確定顏色坐標(biāo)(HSV)

///通過取色條的位置與總高度比獲取h
///通過鼠標(biāo)x與總width之比獲取s倒戏,鼠標(biāo)y與總height之比獲取v
    function getHSV() {
        var h = barIns.y * 360 / barIns.height,
            s = pickerIns.x * 1 / pickerIns.width,
            v = 1 - ( pickerIns.y * 1 / pickerIns.height );
        return {
            h: parseInt(h) === 360? 0 : parseInt(h),
            s: s.toFixed(2),
            v: v.toFixed(2)
        }
    }

HSV 轉(zhuǎn)換成 RGB HSL

var colorConvert = {
    hsv2rbg: function (h, s, v) {
        var r, g, b;

        var i = Math.floor(h / 60),
            f = h / 60 - i,
            p = v * (1 - s),
            q = v * (1 - f * s),
            t = v * (1 - (1 - f) * s);

        switch (i % 6) {
            case 0:
                r = v, g = t, b = p;
                break;
            case 1:
                r = q, g = v, b = p;
                break;
            case 2:
                r = p, g = v, b = t;
                break;
            case 3:
                r = p, g = q, b = v;
                break;
            case 4:
                r = t, g = p, b = v;
                break;
            case 5:
                r = v, g = p, b = q;
                break;
        }

        return {
            r: parseInt(r * 255),
            g: parseInt(g * 255),
            b: parseInt(b * 255)
        }
    },

    rgb2hsv: function (r, g, b) {
        r /= 255, g /= 255, b /= 255;

        var max = Math.max(r, g, b),
            min = Math.min(r, g, b);
        var h, s, v;
        v = max;

        var d = max - min;
        s = max == 0 ? 0 : d / max;

        if (max == min) {
            h = 0;
        } else {
            switch (max) {
                case r:
                    h = 60 * ((g - b) / d + (g < b ? 6 : 0));
                    break;
                case g:
                    h = 60 * ((b - r) / d + 2);
                    break;
                case b:
                    h = 60 * ((r - g) / d + 4);
                    break;
            }
        }

        return {
            h: parseInt(h),
            s: s.toFixed(2),
            v: v.toFixed(2)
        }
    },

    hsl2rgb: function (h, s, l) {
        h /= 360;

        var r, g, b;

        if (s == 0) {
            r = g = b = l; // achromatic
        } else {
            function hue2rgb(p, q, t) {
                if (t < 0) t += 1;
                if (t > 1) t -= 1;
                if (t < 1 / 6) return p + (q - p) * 6 * t;
                if (t < 1 / 2) return q;
                if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
                return p;
            }

            var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
            var p = 2 * l - q;

            r = hue2rgb(p, q, h + 1 / 3);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1 / 3);
        }   

        return {
            r: parseInt(r * 255),
            g: parseInt(g * 255),
            b: parseInt(b * 255)
        }   
    },

    rgb2hsl: function (r, g, b) {
        r /= 255, g /= 255, b /= 255;

        var max = Math.max(r, g, b),
            min = Math.min(r, g, b);
        var h, s, l = (max + min) / 2;

        if (max == min) {
            h = s = 0; // achromatic
        } else {
            var d = max - min;
            s = l > 0.5 ? d / (2 - max - min) : d / (max + min);

            switch (max) {
                case r:
                    h = 60 * ((g - b) / d + (g < b ? 6 : 0));
                    break;
                case g:
                    h = 60 * ((b - r) / d + 2);
                    break;
                case b:
                    h = 60 * ((r - g) / d + 4);
                    break;
            }
        }

        return {
            h: parseInt(h),
            s: s.toFixed(2),
            l: l.toFixed(2)
        }
    },

    hsv2hsl: function (h, s, v) {
        var rgb = this.hsv2rbg(h, s, v);
        return this.rgb2hsl(rgb.r, rgb.g, rgb.b);
    },

    hsl2hsv: function (h, s, l) {
        var rgb = this.hsl2rgb(h, s, l);
        return this.rgb2hsv(rgb.r, rgb.g, rgb.b);
    }
}

var DecimalConvert = {
    d2h: function (value) {
        var hArr = [], a = ['A', 'B', 'C', 'D', 'E', 'F'];
        if (value === 0) {
            hArr.unshift(0);
        }
        while (value !== 0) {
            var mol = value % 16;
            mol = mol - 9 > 0? a[mol - 10] : mol;
            hArr.unshift(mol);
            value = parseInt(value / 16);
        }

        ////////////////////////////
        //////////應(yīng)用于顏色十進制轉(zhuǎn)換成十六進制 //
        /////////////////////////s///
        if (hArr.length < 2) {
            hArr.unshift(0);
        }

        return hArr.join('');
    },
    h2d: function (value) {
        var hArr = value.split(/(\d\d)|(\w\w)/).filter(h => !!h).map(h => parseInt(h, 16));
        return {
            r: hArr[0],
            g: hArr[1],
            b: hArr[2]
        }
    }

}

輸入數(shù)據(jù)的校驗,用正則表達(dá)式處理

        InputPanel.prototype.dataFormat = function(e, id, value) {

            switch (id) {
                case 'R':
                case 'G':
                case 'B':
                    value = Number(value);
                    if (value > 255) value = 255;
                    e.target.value = isNaN(value)? 0 : value;
                    pickByRGB();
                    break;
                case 'H':
                    value = Number(value);
                    if (value >= 360) value = 360;
                    e.target.value = isNaN(value)? 0 : value;
                    pickByHSL();
                    break;
                case 'S':
                case 'L':
                    value = value.replace(/^[^01]/, '').replace(/([01])\d?(\.)*(\d)?(\d)?.?/, '$1$2$3$4');
                    e.target.value = (value * 100) > 100? '1.00' : value;
                    if (value.charAt(value.length - 1) !== '.') {
                        pickByHSL();
                    }
                    break;
                default: break;
            }
        }

剩下的就主要是一些dom的事件綁定和數(shù)據(jù)的更新~


1.gif

gif質(zhì)量有點差哈哈恐似,各位看官將就看吧~

參考:https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末杜跷,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子矫夷,更是在濱河造成了極大的恐慌葛闷,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件口四,死亡現(xiàn)場離奇詭異孵运,居然都是意外死亡,警方通過查閱死者的電腦和手機蔓彩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門治笨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赤嚼,你說我怎么就攤上這事旷赖。” “怎么了更卒?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵等孵,是天一觀的道長。 經(jīng)常有香客問我蹂空,道長俯萌,這世上最難降的妖魔是什么果录? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮咐熙,結(jié)果婚禮上弱恒,老公的妹妹穿的比我還像新娘。我一直安慰自己棋恼,他們只是感情好返弹,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著爪飘,像睡著了一般义起。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上师崎,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天默终,我揣著相機與錄音,去河邊找鬼抡诞。 笑死穷蛹,一個胖子當(dāng)著我的面吹牛土陪,可吹牛的內(nèi)容都是我干的昼汗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼鬼雀,長吁一口氣:“原來是場噩夢啊……” “哼顷窒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起源哩,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤鞋吉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后励烦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谓着,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年坛掠,在試婚紗的時候發(fā)現(xiàn)自己被綠了赊锚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡屉栓,死狀恐怖舷蒲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情友多,我是刑警寧澤牲平,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站域滥,受9級特大地震影響纵柿,放射性物質(zhì)發(fā)生泄漏蜈抓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一昂儒、第九天 我趴在偏房一處隱蔽的房頂上張望资昧。 院中可真熱鬧,春花似錦荆忍、人聲如沸格带。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叽唱。三九已至,卻和暖如春微宝,著一層夾襖步出監(jiān)牢的瞬間棺亭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工蟋软, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镶摘,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓岳守,卻偏偏與公主長得像凄敢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子湿痢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 會改變自身的方法 array.pop()刪除一個數(shù)組中的最后一個元素涝缝,并且返回這個元素 array.push(el...
    209bd3bc6844閱讀 573評論 0 1
  • 放下面子 什么事都要面子一定會獲得很累拒逮,一個不會低頭的人一定會錯過很多風(fēng)景。所以臀规,你想要強大之前一定要學(xué)會放下面子...
    喬自定義閱讀 138評論 0 0