色彩選擇器(colorPicker)
跟隨網上教程學習前端知識迄今為止3個月,這個色彩選擇器是根據網上教程的任務所做的,在此之前,我完全不知道也沒想過JavaScript居然還能做這樣一個東西,所以剛開始看到這個任務的時候是一頭霧水。以下是我做這個色彩選擇器的過程。
- .我學過用HTML和CSS做一個靜態的網頁,但是我是不瞭解HTML5的,我大概瀏覽了一下別人所寫的程式碼,看到了
<canvas>
這個元素標籤,查詢之後,知道它是一個畫布,那麼如何產生色彩選擇器的顏色就確定了。
(我在剛開始學的時候有上網瞭解過,他人的建議大概是:要自己多思考怎麼寫,不是拿別人的程式碼。而我碰到不會的任務時,我的做法是,比如這次的畫布,我先看了別人的程式碼,然後我會記錄下來別人程式碼裡出現的我所沒見過的元素標籤、JS物件的屬性和方法,但不是直接採用別人的想法(實際上我不太看的懂別人的程式碼,有些也沒有註釋,有時候自己有頭緒寫速度反而比看別人的更快),然後去查詢,這會讓我有一個頭緒,接下來自己慢慢寫,我不知道這樣一個方法是不是比較好的學習方法,希望有好方法的人看到這段文字並且恰巧又有時間的話,可以幫忙提供給我,先說聲謝謝! - 做顏色選擇器得了解顏色的知識,在那個任務下有學員提供了一份筆記,我自己理解了後,如下(我寫的時候是用chorm看效果的):
色彩選擇器demo
R——紅色,G——綠色,B——blue,也就是三原色; H——色相,S——飽和度,L——明度;以及將RGB或者HSL轉為十六進位制來代表顏色。PS上的色彩選擇器有一個色相條,它是將12色相環(所以H的取值就是0°~360°)頭尾拆開變成條形,在色相環的畫布上利用漸變來”上色“;
//顏色棒的漸變
var context2=bar.getContext("2d");
var grd2 = context2.createLinearGradient(0,0,0,600);
grd2.addColorStop(0,"rgb(255,0,0)");
grd2.addColorStop(1/6,"rgb(255,255,0)");
grd2.addColorStop(1/3,"rgb(0,255,0)");
grd2.addColorStop(1/2,"rgb(0,255,255)");
grd2.addColorStop(2/3,"rgb(0,0,255)");
grd2.addColorStop(5/6,"rgb(255,0,255)");
grd2.addColorStop(1,"rgb(255,0,0)");
context2.fillStyle=grd2;
context2.fillRect(0,0,10,600)
而左邊大畫布上的顏色變化是隨著色相的變化而變化的,也就是H值0°~360°。在色相條上滑動圓形選擇器時,利用getImageData()
的方法來獲取發生變化時相應的顏色值,但是不是利用putImageData()
將顏色填充到畫布上,因為畫布上的顏色還有一個飽和度和明度的變化,所以畫布上的顏色應該是從左至右有一個飽和度的變化,即從白色漸變到色相條上被選擇的顏色,這個顏色是變化的,所以可以在其變化時將其作為一個函式的引數傳遞到函式,執行函式,然後進行漸變;同時畫布上明度的變化,也可以被包括在該函式中,下面是從色相條上獲取顏色的方法,有一點值得注意的是,色相條上的顏色變化來自色相條上顏色的變化,而右下角方框的顏色變化來自畫布上圓形選擇器的顏色變化,所以色相條上的顏色變化導致的一系列變化有:色相條上圓形選擇器的背景色變化和畫布上顏色的變化 → 畫布上顏色發生變化,那麼位於畫布上圓形選擇器選中顏色的變化 → RGB和HSL值的變化,展示方框顏色的變化
move.onmousedown=function(event){ flag=true; //這裡的全域性變數是讓滑鼠移動的時候可以取點選時的值 moveTop=move.offsetTop; nowY=event.clientY; } document.addEventListener("mousemove",function(event){ var moveY=event.clientY; dis=moveY-nowY; finalTop=moveTop+dis; if(flag){ if(bar.offsetTop+bar.offsetHeight-(move.offsetHeight/2)-1>=finalTop && finalTop>=bar.offsetTop- (move.offsetHeight/2)){ //if裡面的條件是色相條上的圓形選擇器從圓中心開始計算選色的位置,所以要減去它高度的一般,另外,前面的再減去1,是因為getImageData()不能從色相的最底部取一畫素的值,這樣取來的是瀏覽器的背景色,所以應該讓圓心的位置上移一個畫素,即減1。 move.style.top=finalTop+"px"; colorH=finalTop+move.offsetHeight/2-barTop; var imgData=context2.getImageData(0,colorH,1,1); //將rgb轉為16進位制 var strHex="#"; for(var i=0 ; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ //基於rgb轉16進位制的原理,如果長度為1,那麼在前面加0 hex="0"+hex; } strHex += hex; } bgcolor=strHex; render(bgcolor); //渲染主色板 move.style.backgroundColor=bgcolor; //顏色棒上的圓形選擇器移動時,以下函式改變RGB和Hsl pickRenderShow(); } } })
render()的函式如下:
function render(color){ //水平漸變 var context1=myCanvas.getContext("2d"); var grd1 = context1.createLinearGradient(0,0,600,0); grd1.addColorStop(0,'rgba(255,255,255,1)'); grd1.addColorStop(1,color); context1.fillStyle=grd1; context1.fillRect(0,0,600,600); //垂直漸變 var grd3 = context1.createLinearGradient(0,0,0,600); grd3.addColorStop(0,'rgba(0, 0, 0, 0)'); grd3.addColorStop(1,'rgba(0, 0, 0, 1)'); context1.fillStyle=grd3; context1.fillRect(0,0,600,600); }
另外關於顏色之間轉換的公式,我是抄的別人的程式碼,即javascript HEX十六進位制與RGB, HSL顏色的相互轉換
求出RGB值和HSL值的函式pickRenderShow()如下:
//通過渲染板上圓形選擇器移動而產生的拾色,右下角展示板渲染,各個值的顯示變化 function pickRenderShow(){ //渲染板上的圓形擇色器 circleCenterY=circleSelect.offsetTop-myCanvas.offsetTop+circleSelect.offsetWidth/2; circleCenterX=circleSelect.offsetLeft-myCanvas.offsetLeft+circleSelect.offsetWidth/2; //用來到達渲染板上圓形選擇器中心點位置,然後取寬高各為一畫素的畫素點var imageData=context1.getImageData(circleCenterX,circleCenterY,1,1); showBGcolorR=imageData.data[0]; showBGcolorG=imageData.data[1]; showBGcolorB=imageData.data[2]; colorR.value=showBGcolorR; colorG.value=showBGcolorG; colorB.value=showBGcolorB; //根據渲染板上圓形擇色器的rgb,轉為16進位制,將值填入右邊一欄 var str="#"; for(var i=0; i〈3; i++){ var hex = Number(imageData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; //基於rgb轉16進位制的原理,如果長度為1,那麼在前面加0 } str += hex; } //將rgb轉換為Hsl showBGcolorR /= 255, showBGcolorG /= 255, showBGcolorB /= 255; var max=Math.max(showBGcolorR,showBGcolorG,showBGcolorB); var min=Math.min(showBGcolorR,showBGcolorG,showBGcolorB); L=(max+min)/2; if(max==min){ H=0; S=0; }else{ var d=max-min; S=L>0.5 ? d/(2-max-min):d/(max+min); switch(max){ case showBGcolorR: H=60*((showBGcolorG - showBGcolorB)/d+(showBGcolorG 〈showBGcolorB ? 6 : 0)); break; case showBGcolorG: H=60*( (showBGcolorB - showBGcolorR) / d + 2); break; case showBGcolorB: H=60*((showBGcolorR - showBGcolorG) / d + 4); break; } } colorh.value=Math.round(H); colorS.value=Number(S.toFixed(2)); //將小數點轉換為2位,再將字串轉換為數字 colorL.value=Number(L.toFixed(2)); //使右邊顏色展示板能夠隨左邊圓形擇色器的改變而改變 var context3=show.getContext("2d"); context3.fillStyle=str; context3.fillRect(0,0,100,100); colorHex.value=str; }
3.如果移動畫布上圓形選擇器,那麼RGB和HSL,顏色展示框都會隨之變化,這和上面的pickRenderShow()一樣,就是多加了一個,不能讓圓形選擇器移出畫布之外:
//渲染板上圓形選擇器的移動
circleSelect.addEventListener("mousedown",function(event){
selectflag=true;
downX=event.clientX;
downY=event.clientY;
circleTop=circleSelect.offsetTop;
circleLeft=circleSelect.offsetLeft;
})
document.addEventListener("mousemove",function(event){
var moveX=event.clientX;
var moveY=event.clientY;
var circleNowL=(moveX-downX)+circleLeft;
var circleNowT=(moveY-downY)+circleTop;
if(selectflag){
//這裡計算的原理和色相條上圓形選擇器的位置計算原理是一樣的
if(circleNowL>=myCanvas.offsetLeft-circleSelect.offsetWidth/2 && circleNowL<=myCanvasWidth+myCanvas.offsetLeft-(circleSelect.offsetWidth/2)-1){
circleSelect.style.left=circleNowL+"px";
}
if(circleNowT>=myCanvas.offsetTop-circleSelect.offsetHeight/2 && circleNowT<=myCanvasHeight+myCanvasHeight+myCanvas.offsetTop-(circleSelect.offsetHeight/2)-1){
circleSelect.style.top=circleNowT+"px";
}
pickRenderShow();
}
})
4.如果改變RGB和HSL值的任意一個值,那麼色相條上的圓形選擇器和畫布上的圓形選擇器的位置都會發生改變,畫布顏色,色相條上圓形選擇器背景色,顏色展示框的背景色都將發生改變,首先是RGB輸入值發生改變:
//獲取輸入的RGB值,並作出相應變化 var inputNum=[]; for(var j=0;j〈3;j++){ input[j].addEventListener("keydown",function(){ setTimeout(function(){ //setTimeout方法暫定,這是為了在鍵盤按下後,能夠更新rgb,獲得最新值,從而改變HSL的值,不然鍵盤按下的一瞬間,獲得的資料是上一次的資料; //文字一發生改動,將原先陣列的值清空,避免了留下的j變數值,在原有陣列的基礎上繼續新增陣列 inputNum.splice(0,3); for(var i=0;i〈 3;i++){ inputNum.push(input[i].value); } //利用延遲而獲得的最新數值,來改變HSL和十六進位制 if(inputNum[0]〈=255&& inputNum[1]〈=255 && inputNum[2]〈=255){ rgbTOhex(inputNum[0],inputNum[1],inputNum[2]); rgbTOhsl(inputNum[0],inputNum[1],inputNum[2]); var moveCurrentH=(600*colorh.value)/360-move.offsetHeight/2+bar.offsetTop;//利用返回的HSL中的H值,按照比列來確定move的位置 move.style.top=moveCurrentH+"px"; var imgData=context2.getImageData(0,moveCurrentH-bar.offsetTop+move.offsetHeight/2,1,1); //變化move的背景色 //將rgb轉為16進位制 var strHex="#"; for(var i=0; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; } strHex += hex; } bgcolor=strHex; move.style.backgroundColor=bgcolor; render(bgcolor); //渲染主色板 //RGB改變時同時改變渲染板上圓形擇色器的位置,根據HSV的原理好定位 var max=Math.max(inputNum[0],inputNum[1],inputNum[2]); var min=Math.min(inputNum[0],inputNum[1],inputNum[2]); var V=Number((max/255).toFixed(2)); var S=Number(((max-min)/max).toFixed(2)); //S的值是畫布從左至右,取值為0-1,那麼它本身的值可以視為在畫布中的一個位置比列,所以要乘以畫布的寬,然後減掉它的半徑,以圓心為位置,還要加上畫布的左邊距;同理V的值,只不過V的值是從下開始計算,所以要1減去相應的比例算出相反的比例,才能得出圓形選擇器的上偏移量 var circleL=S*myCanvasWidth-1-circleSelect.offsetWidth/2+myCanvas.offsetLeft; var circleT=(1-V)*myCanvasHeight-1-circleSelect.offsetHeight/2+myCanvas.offsetTop; circleSelect.style.top=circleT+"px" circleSelect.style.left=circleL+"px" //渲染板上圓形擇色器的位置改變時,改變展示板的顏色 var context3=show.getContext("2d"); context3.fillStyle=colorHex.value; context3.fillRect(0,0,100,100); }else{ return; } },500) }) }
以下是改變HSL值發生的變化:
//獲取輸入的HSL值,並作出相應變化 var inputHSL=[]; for(var j=3;j〈6;j++){ input[j].addEventListener("keydown",function(){ setTimeout(function(){ inputHSL.splice(0,3); for(var i=3;i〈 6;i++){ inputHSL.push(input[i].value); } if(inputHSL[0]〈=360 &&inputHSL[1]〈=1 && inputHSL[1]〉=0 && inputHSL[2]〈=1 && inputHSL[2]〉=0){ hslToRgb(Number(inputHSL[0]),Number(inputHSL[1]),Number(inputHSL[2])); var moveCurrentH=(600*colorh.value)/360-15+18; move.style.top=moveCurrentH+"px";//變化move位置 var imgData=context2.getImageData(0,moveCurrentH-18+15,1,1); //變化move的背景色 var strHex="#"; for(var i=0; i〈3; i++){ var hex = Number(imgData.data[i]).toString(16); if(hex.length===1){ hex="0"+hex; } strHex += hex; } bgcolor=strHex; move.style.backgroundColor=bgcolor; render(bgcolor); //渲染主色板 //RGB改變時同時改變渲染板上圓形擇色器的位置,根據HSV的原理好定位 var max=Math.max(colorR.value,colorB.value,colorG.value); var min=Math.min(colorR.value,colorB.value,colorG.value); var V=Number((max/255).toFixed(2)); var S=Number(((max-min)/max).toFixed(2)); var circleL=S*myCanvasWidth-1-10+18; var circleT=(1-V)*myCanvasHeight-1-10+18; circleSelect.style.top=circleT+"px" circleSelect.style.left=circleL+"px" //渲染板上圓形擇色器的位置改變時,改變展示板的顏色 var context3=show.getContext("2d"); context3.fillStyle=colorHex.value; context3.fillRect(0,0,100,100); }else{ return; } },500); }) }
上面rgbTOhex();rgbTOhsl();是轉換公式的函式,可以參考上面給出連結,也可以自己上網查,我把js的整個內容都寫到html頁面中了,在demo中也可以檢視轉換的函式;
以上就是我的色彩選擇器的製作過程,歸納為以下:
- 色相條的漸變
- 色相條上圓形選擇器的移動導致的:1.圓形選擇器背景色的變化。2.畫布背景色的漸變(包括飽和度和明度)。3.畫布當時圓形選擇器中的顏色改變帶來的RGB,HSL,顏色展示框的變化。
- 畫布上圓形選擇器移動導致的:1.RGB,HSL,十六進位制,顏色展示框的變化。2.考慮圓形選擇器的位置。
- RGB輸入值的變化導致的:1.HSL,十六進位制值變化。2.色相選擇器位置和背景色的變化。3.畫布上圓形選擇器的位置變化。4.顏色展示框的變化。
- HSL輸入值變化導致的:1.RGB,十六進位制值變化。2.色相選擇器位置和背景色的變化。3.畫布上圓形選擇器的位置變化。4.顏色展示框的變化。
希望看到這篇文章恰巧又有時間的話,能給我多提提意見,比如我這樣寫的程式碼有哪些缺點,這樣的方法有哪些缺點,應該用哪種方法更好,因為我只是用我想的方法去實現這樣一個功能,沒有考慮到其他因素,比如
1.那個setTimeout的方法,我是為了取的當前的值而不是以前的值push到陣列中去,我試過,如果發生keydown事件,直接將值push到陣列中,是前面的值,不是我當下輸入的值,但是我自己也覺得這個方法有點牽強。
2.每次一輸入陣列的值就得push一遍,是不是一個很麻煩的方法(我找不到其他的詞語來形容了),而且為了保證是當前最新的三個數值,我還得將以前的陣列清除一遍。。。
非常感謝看完一篇這麼囉嗦的文章!!
2017-5-31