每個瀏覽器都有自己的特點(diǎn),比如今天要做的colorpicker就是,一千個瀏覽器,一千個哈姆雷特,一千個colorpicker。今天canvas系列就用canvas做一個colorpicker。
**********************************************************************
效果圖和demo
突然翻到了之前用js和dom寫的一個colorpicker,比較挫,扔張圖就好(old)
這個真的很挫,性能很差,因為每一個可選的顏色值都是一個dom,如果要實現(xiàn)256*256,那瀏覽器就爆了~~~~~
好,回到今天的demo(new)
demo鏈接: https://win7killer.github.io/can_ps/src/demo/color_picker.html
沒錯,就是照著PS的顏色選擇器的樣子仿的。
**********************************************************************
實現(xiàn)
首先我們來看效果圖分析怎么做:
1.左側(cè)colorbar
左側(cè)提供一系列過渡色,不難看出,這個是“紅黃綠青藍(lán)紫”這六種顏色,然后加以過渡色處理來的。最后紫色還要過渡回到紅色。
另外換成環(huán)狀的可能更加好識別,如下圖:
那么,我們就可以用canvas的過渡色來實現(xiàn)左側(cè)這個區(qū)域,
代碼如下:
1 function colorBar() { 2 var gradientBar = ctx.createLinearGradient(0, 0, 0, width); 3 gradientBar.addColorStop(0, '#f00'); 4 gradientBar.addColorStop(1 / 6, '#f0f'); 5 gradientBar.addColorStop(2 / 6, '#00f'); 6 gradientBar.addColorStop(3 / 6, '#0ff'); 7 gradientBar.addColorStop(4 / 6, '#0f0'); 8 gradientBar.addColorStop(5 / 6, '#ff0'); 9 gradientBar.addColorStop(1, '#f00');10 11 ctx.fillStyle = gradientBar;12 ctx.fillRect(0, 0, 20, width);13 }
這里涉及到canvas的fillStyle或者strokenStyle的填充對象,可以使用過渡色對象(自己瞎叫的名字),了解更多可以去w3cschool。
2.中間顏色區(qū)
中間這塊乍看很簡單,再看有點(diǎn)蒙bi,三看才搞清楚怎么搞。
乍看:其實就是左側(cè)選中的那個顏色(比如A),然后進(jìn)行過渡處理,不還是過渡么。
再看:恩,顏色,然后黑色,白色,三種顏色三個角怎么過渡~~~~(如果有快捷的過渡實現(xiàn)方式請留言告知我,THX)。
三看:那么,拆解一下,比如紅色到白色過渡,然后加一層黑色到透明過渡?是滴,就是這么個方案。(我自己之前彎路到了紅色到黑色,白色到透明)
那么就是借助兩次過渡色的填充,實現(xiàn)中間色塊區(qū)域。
代碼如下:
1 function colorBox(color) { 2 // 底色填充,也就是(舉例紅色)到白色 3 var gradientBase = ctx.createLinearGradient(30, 0, width + 30, 0); 4 gradientBase.addColorStop(1, color); 5 gradientBase.addColorStop(0, 'rgba(255,255,255,1)'); 6 ctx.fillStyle = gradientBase; 7 ctx.fillRect(30, 0, width, width); 8 9 // 第二次填充,黑色到透明10 var my_gradient1 = ctx.createLinearGradient(0, 0, 0, width);11 my_gradient1.addColorStop(0, 'rgba(0,0,0,0)');12 my_gradient1.addColorStop(1, 'rgba(0,0,0,1)');13 ctx.fillStyle = my_gradient1;14 ctx.fillRect(30, 0, width, width);15 }
需要注意,第一次填充,是從橫向填充,這時候中間色塊的左邊已經(jīng)不是canvas的原點(diǎn),所以加了偏移量30px
第二次填充縱向,Y軸還是0。
這個在實際應(yīng)用中要注意。
到這里,左側(cè)canvas繪制的東西就差不多了。
3. 顏色選擇事件處理
首先明確交互事件:
選擇左側(cè)colorbar(比如#ff0),中間base顏色要跟著變化,右上角也要是對應(yīng)顏色(#ff0)【這個時候其實也可以得到選擇的顏色,可以結(jié)束交互】;
選擇中間區(qū)域的顏色,左側(cè)不變,可以獲取到對應(yīng)的顏色值,結(jié)束交互。
最終就是在右側(cè)的dom區(qū)域展示所選到的顏色。
canvas中沒有dom對象,所以鼠標(biāo)點(diǎn)擊事件要靠鼠標(biāo)的位置來確定是否進(jìn)行相應(yīng)處理。而且我們繪制的不是path對象,也無法使用inpath之類的方法來判斷。
點(diǎn)擊事件代碼:
1 can.addEventListener('click', function(e) { 2 var ePos = { 3 x: e.offsetX || e.layerX, 4 y: e.offsetY || e.layerY 5 } 6 var rgbaStr = '#000'; 7 if (ePos.x >= 0 && ePos.x < 20 && ePos.y >= 0 && ePos.y < width) { 8 // in 9 rgbaStr = getRgbaAtPoint(ePos, 'bar');10 colorBox('rgba(' + rgbaStr + ')');11 } else if (ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) {12 rgbaStr = getRgbaAtPoint(ePos, 'box');13 } else {14 return;15 }16 outColor(rgbaStr.slice(0, 3).join());17 cur.style.left = ePos.x + 'px';18 cur.style.top = ePos.y + 'px';19 cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '#000' : '#fff';20 });
其中,getRgbaAtPoint是最終的獲取顏色值的方法,需要根據(jù)不同的鼠標(biāo)位置傳參來決定選取左側(cè)還是右側(cè)圖像
獲取顏色就比較簡單了,就是拿到對應(yīng)區(qū)域的imageData,然后從顏色數(shù)組中獲取到對應(yīng)位置的顏色值即可。
做過canvas像素處理的同學(xué)會比較明白,不明白的建議先去把getImageData方法看一看,了解一下
獲取顏色代碼:
1 function getRgbaAtPoint(pos, area) { 2 if (area == 'bar') { 3 var imgData = ctx.getImageData(0, 0, 20, width); 4 } else { 5 var imgData = ctx.getImageData(0, 0, can.width, can.height); 6 } 7 8 var data = imgData.data; 9 var dataIndex = (pos.y * imgData.width + pos.x) * 4;10 return [11 data[dataIndex],12 data[dataIndex + 1],13 data[dataIndex + 2],14 (data[dataIndex + 3] / 255).toFixed(2),15 ];16 }
這時候拿到的就是rgba顏色對應(yīng)的值。
需要注意,最后一個數(shù)據(jù)時alpha通道,canvas的imageData里是0-255【沒記錯的話】,而不是我們平常用的0-1,所以要做轉(zhuǎn)換。
顏色輸出&轉(zhuǎn)換:
拿到顏色后就可以輸出到右側(cè)了。
右側(cè)只是用了rgb三通道,所以取數(shù)組前三位就好。
至于hex顏色,則用rgb來轉(zhuǎn)換。
轉(zhuǎn)換代碼如下:
1 function rgb2hex(rgb) { 2 var aRgb = rgb instanceof Array ? rgb : (rgb.split(',') || [0, 0, 0]); 3 var temp; 4 return [ 5 (temp = Number(aRgb[0]).toString(16)).length == 1 ? ('0' + temp) : temp, 6 (temp = Number(aRgb[1]).toString(16)).length == 1 ? ('0' + temp) : temp, 7 (temp = Number(aRgb[2]).toString(16)).length == 1 ? ('0' + temp) : temp, 8 ].join(''); 9 }10 11 function hex2rgb(hex) {12 if (hex.length == 3) {13 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];14 }15 return [16 parseInt(hex[0] + hex[1], 16),17 parseInt(hex[2] + hex[3], 16),18 parseInt(hex[4] + hex[5], 16),19 ].join();20 }
簡單來說,就是10進(jìn)制與16進(jìn)制的轉(zhuǎn)換。
有個點(diǎn),就是rgb的三個值,分別對應(yīng)的是hex的每兩個值,比如rgb(255,0,255)對用到hex則分別是 “ff,00,ff”,綜合起來就是“#ff00ff”,可以簡寫“#f0f”。
額外效果:
中間的顏色選擇還有個效果,就是鼠標(biāo)拖拽到哪里,就選中相應(yīng)的顏色。
鼠標(biāo)拖拽事件大家都不陌生,直接上代碼,不廢話
1 can.addEventListener('mousedown', function(e) { 2 var ePos = { 3 x: e.layerX || e.offsetX, 4 y: e.layerY || e.offsetY 5 } 6 if (ePos.x >= 30 && ePos.x < 30 + width && ePos.y >= 0 && ePos.y < width) { 7 document.onmousemove = function(e) { 8 var pos = { 9 x: e.clientX,10 y: e.clientY11 }12 13 pos.x = pos.x < 30 ? 30 : pos.x && (pos.x > (30 + width - 1) ? (30 + width - 1) : pos.x);14 pos.y = pos.y < 0 ? 0 : pos.y && (pos.y > (width - 1) ? (width - 1) : pos.y);15 16 rgbaStr = getRgbaAtPoint(pos, 'box');17 cur.style.left = pos.x + 'px';18 cur.style.top = pos.y + 'px';19 cur.style.outlineColor = (rgbaStr[0] > 256 / 2 || rgbaStr[1] > 256 / 2 || rgbaStr[2] > 256 / 2) ? '#000' : '#fff';20 outColor(rgbaStr.slice(0, 3).join());21 };22 document.onmouseup = function() {23 // outColor(rgbaStr.slice(0, 3).join());24 document.onmouseup = document.onmousemove = null;25 }26 }27 28 });
這樣,每段代碼拼湊起來,就是整體的架子了,附上最終代碼(比較長,折疊了):
View Code
**********************************************************************
寫在最后:
最終寫完效果在自己玩耍的過程中,發(fā)現(xiàn)瀏覽器對于canvas的過渡色實現(xiàn)有點(diǎn)問題。chrome很明顯,F(xiàn)F稍微好一點(diǎn)。
如圖: 按道理來說,最下邊選到的顏色應(yīng)該都是rgb(0,0,0)才對,但是圖上可見,有些地方并不是~~~
大多數(shù)還是000,某些點(diǎn)某個通道有可能會出現(xiàn)1。原因未知。
嘗試了email給chrome郵箱,可能我英語比較差人家沒看懂,也可能我問題沒描述清楚,反正后來沒有回復(fù),之后的瀏覽器更新也沒有處理。
相應(yīng)的,css3的過渡色則沒有一丁點(diǎn)問題。
**********************************************************************
圖片、代碼啥的貼了一堆,其中涉及的知識點(diǎn)可能有點(diǎn)多。看到這里的同學(xué),建議回過頭再看一遍哈。需要注意的我盡量特殊顏色標(biāo)出來了。
最后,歡迎留言提建議什么的。
http://www.cnblogs.com/ufex/p/6382982.html