26 мая 2011 г.

Выбор цвета как в photoshop на javascript

Всем, здравствуйте!
Сегодня мы напишем скрипт, который позволит нам выбирать цвет, если не как в photoshop, то близкое к тому. Ведь самое главное суть!
Для этого нам понадобятся следующие изображения.













Последнее изображение я делал php-скриптом. Как это реализовано, я писал в предыдущей статье. Выглядеть все это будет следующим образом


Итак, приступим!

<form action="" method="POST">
<table style="margin:auto; border:1px solid #000; position:relative;">
<tr><td>Введите свой цвет или выберите из набора.
 <div class="ppic" id="surname_pic" style="height:20px; width:20px;position:relative;top:0;right:0;margin:0;float:right;padding:0;border:1px solid #000000;z-index:5"></div>
 <div class="all_for_clr" id="surname_main" style="background:#aaaaaa;height:0; width:0;position:relative;top:0;right:0;margin:0;padding:0;overflow:hidden;float:right;border:1px solid #000000;filter:alpha(opacity=0);opacity:0;z-index:0">
 <div class="preview" id="surname_pre" style="height:50px;width:50px;position:relative;float:right;z-index:0;background:#ffffff;border:1px solid #000000;"></div>
 <div class="brightness" id="surname_br" style="height:128px;width:20px;position:relative;float:right;z-index:0;background:url(nasyschen.png);overflow:hidden;border:1px solid #000000;margin:0 5px 0 0;padding:0;"><div id="surname_line" style="height:1px;width:20px;position:absolute;top:0;left:0;background:#ff0000;margin:0;padding:0;border:0"></div></div>
 <div class="gradus" id="surname" style="height:128px;width:128px;background:url(spec.png) no-repeat 0 0;overflow:hidden;position:relative;float:right;z-index:0;margin:0 5px 0 0;padding:0;border:1px solid #000000;"><div id="surname_krest" style="height:15px;width:15px;position:absolute;top:0;left:0;background:url(krest.png);margin:0;padding:0;border:0"></div></div></div></td>
 <td><input type="text" name="surname" /></td>
</tr>
</table>
</form>
это у нас непосредственно сама форма.
div с class="ppic" - это у нас квадратик, в котором будет показан ткущий цвет.
div с классом "all_for_color" - здесь мы разместим спектр, где у нас будет бегать крестик и где мы будем выбирать цвет, а так же полосу, в которой будем выбирать яркость. Здесь же у нас будет окошко, в котором будет отображаться выбираемый цвет.
В этом диве у нас будет:
div с классом "preview" - т.е. предпросмотр цвета
div с классом "gradus" - не знаю почему я его так назвал, здесь у нас спектр, по которому будем кликать, чтобы выбрать цвет
div с классом "drightness" - здесь черно-белый градиент, выбор яркости.
div с ID="surname_line" - это линия, которая будет держать нас в курсе, где мы кликнули последний раз по яркости
и div с ID="surname_krest" - это крестик, который показывает, где мы кликнули последний раз по спектру.

Ну, а теперь самое страшное :)
Только не забудьте подключить библиотеку jQuery!

<script type="text/javascript">
open_color=false;
function findX(el){
 var curX=0;
 if(el.offsetParent){
  while(el.offsetParent){
   curX+=el.offsetLeft;
   el=el.offsetParent;
  }
 }
 else if(el.x) curX+=el.x;
 return curX;
}
function findY(el){
 var curY=0;
 if(el.offsetParent){
  while(el.offsetParent){
   curY+=el.offsetTop;
   el=el.offsetParent;
  }
 }
 else if(el.y) curY+=el.y;
 return curY;
}
function getColor(x,y){
 var dec_r;
 var dec_g;
 var dec_b;
 if(y<22){
  dec_g=(256-y*12)/128;
  dec_r=0;
  dec_b=2;
 }
 else if(y>21&&y<43){
  dec_g=0;
  dec_r=(y*12-256)/128;
  dec_b=2;
 }
 else if(y>42&&y<65){
  dec_r=2;
  dec_b=(768-y*12)/128;
  dec_g=0;
 }
 else if(y>64&&y<86){
  dec_b=0;
  dec_g=(y*12-768)/128;
  dec_r=2;
 }
 else if(y>85&&y<107){
  dec_g=2;
  dec_r=(1280-y*12)/128;
  dec_b=0;
 }
 else if(y>106){
  dec_r=0;
  dec_b=(y*12-1280)/128;
  dec_g=2;
 }
 red=Number(Math.abs(Math.round(255-x*dec_r))).toString(16);
 green=Number(Math.abs(Math.round(255-x*dec_g))).toString(16);
 blue=Number(Math.abs(Math.round(255-x*dec_b))).toString(16);
 if(red.length<2) red="0"+red;
 if(green.length<2) green="0"+green;
 if(blue.length<2) blue="0"+blue;
 return '#'+red+green+blue;
}
function chColor(x,y,k){
 var dec_r;
 var dec_g;
 var dec_b;
 if(y<22){
  dec_g=(256-y*12)/128;
  dec_r=0;
  dec_b=2;
 }
 else if(y>21&&y<43){
  dec_g=0;
  dec_r=(y*12-256)/128;
  dec_b=2;
 }
 else if(y>42&&y<65){
  dec_r=2;
  dec_b=(768-y*12)/128;
  dec_g=0;
 }
 else if(y>64&&y<86){
  dec_b=0;
  dec_g=(y*12-768)/128;
  dec_r=2;
 }
 else if(y>85&&y<107){
  dec_g=2;
  dec_r=(1280-y*12)/128;
  dec_b=0;
 }
 else if(y>106){
  dec_r=0;
  dec_b=(y*12-1280)/128;
  dec_g=2;
 }
 red=Number(Math.abs(Math.round((255-x*dec_r)*k))).toString(16);
 green=Number(Math.abs(Math.round((255-x*dec_g)*k))).toString(16);
 blue=Number(Math.abs(Math.round((255-x*dec_b)*k))).toString(16);
 if(red.length<2) red="0"+red;
 if(green.length<2) green="0"+green;
 if(blue.length<2) blue="0"+blue;
 return '#'+red+green+blue;
}
mmousedown=false;
bmousedown=false;
$(document).ready(function(){
 $(".gradus").mousedown(function(){
  mmousedown=true;
 });
 $(".gradus").mouseup(function(){
  mmousedown=false;
 });
 $(".gradus").mousemove(function(e){
  if(!mmousedown) return;
  var x=e.pageX-findX(this);
  var y=e.pageY-findY(this);
  if(x>127 || x<0 || y>127 || y<0){
   mmousedown=false;
   return;
  }
  var color=getColor(x,y);
  var field=$(this).attr("id");
  $("#"+field+"_krest").css("top",y-7);
  $("#"+field+"_krest").css("left",x-7);
  $("[name="+field+"]").val(color);
  $("#"+field+"_pre").css("background",color);
  $("#"+field+"_pic").css("background",color);
  $("#"+field+"_line").css("top",0);  
 });
 $(".gradus").click(function(e){
  var x=e.pageX-findX(this);
  var y=e.pageY-findY(this);
  if(x>127 || x<0 || y>127 || y<0){
   mmousedown=false;
   return;
  }
  var color=getColor(x,y);
  var field=$(this).attr("id");
  $("#"+field+"_krest").animate({top:y-7, left:x-7},"fast");
  $("[name="+field+"]").val(color);
  $("#"+field+"_pre").css("background",color);
  $("#"+field+"_pic").css("background",color);
  $("#"+field+"_line").css("top",0);  
 });
 $(".ppic").mouseenter(function(e){
  if(open_color) return;
  open_color=true;
  var len=$(this).attr("id");
  len=len.length-4;
  var id=$(this).attr("id").substr(0,len);
  $(this).css("z-index","-1");
  $("#"+id+"_main").css("position","absolute");
  $("#"+id+"_main").css("left",e.pageX-100);
  $("#"+id+"_main").css("width","215px");
  $("#"+id+"_main").css("height","130px");
  $("#"+id+"_main").css("padding","5px");
  $("#"+id+"_main").css("z-index","0");
  $("#"+id+"_main").animate({opacity:1},"slow");
  open_color=false;
 });
 $(".all_for_clr").mouseleave(function(){
  if(open_color) return;
  open_color=true;
  $(this).animate({opacity:0},"slow",function(){
   $(this).css("position","relative");
   $(this).css("left","0");
   $(this).css("width","0");
   $(this).css("height","0");
   $(this).css("padding","0");
   $(this).css("z-index","-1");
   $(".ppic").css("z-index","5");
   open_color=false;
  });
 });
 $(".brightness").mousedown(function(){
  bmousedown=true;
 });
 $(".brightness").mouseup(function(){
  bmousedown=false;
 });
 $(".brightness").mousemove(function(e){
  if(!bmousedown) return;
  var y=e.pageY-findY(this);
  var x=e.pageX-findX(this);
  if(x>19 || x<0 || y>127 || y<0){
   bmousedown=false;
   return;
  }
  var k=(127-y)/127;
  var len=$(this).attr("id");
  len=len.length-3;
  var id=$(this).attr("id").substr(0,len);
  var spec=document.getElementById(id);
  var krest=document.getElementById(id+"_krest");
  var x_color=findX(krest)+7-findX(spec);
  var y_color=findY(krest)+7-findY(spec);
  var color=chColor(x_color,y_color,k);
  $("#"+id+"_line").css("top",y);
  $("[name="+id+"]").val(color);
  $("#"+id+"_pre").css("background",color);
  $("#"+id+"_pic").css("background",color);
 });
 $(".brightness").click(function(e){
  var y=e.pageY-findY(this);
  var x=e.pageX-findX(this);
  if(x>19 || x<0 || y>127 || y<0){
   bmousedown=false;
   return;
  }
  var k=(127-y)/127;
  var len=$(this).attr("id");
  len=len.length-3;
  var id=$(this).attr("id").substr(0,len);
  var spec=document.getElementById(id);
  var krest=document.getElementById(id+"_krest");
  var x_color=findX(krest)+7-findX(spec);
  var y_color=findY(krest)+7-findY(spec);
  var color=chColor(x_color,y_color,k);
  $("#"+id+"_line").animate({top:y},"fast");
  $("[name="+id+"]").val(color);
  $("#"+id+"_pre").css("background",color);
  $("#"+id+"_pic").css("background",color);
 });
});
</script>

Первая переменная нам необходима, чтобы контролировать панель выбора цвета. В начале она у нас содержит значение false. Т.е. панель не отображается.
Функция findX() и findY() возвращают значение X и Y от верхнего левого угла страницы.
getColor() возвращает значение цвета в формате #xxxxxx. Функция работает по той же логике, что и php-скрипт, которым мы нарисовали спектр.
chColor() возвращает измененный цвет, т.е. меняет яркость выбранного цвета. k это коэффициент.
mmousedown - переменная, которая требуется нам для отслеживания, где нажата кнопка мыши. Она принимает значение true, если кнопка мыши была нажата на спектре. Когда пользователь отпускает кнопку, она принимает значение false. Также ведет себя переменная bmousedown по нажатию/отпусканию кнопки мыши на яркости. Это нам необходимо, чтобы при нажатой кнопке, цвет изменялся при каждом смещении мыши.
Дальше мы прикрепляем обработчики событий на каждое важное для нас поле. Т.е. если курсор попадает на маленький квадрат с текущим цветом, то мы отображаем панель выбора цвета. Если курсов покидает панель, она скрывается. Такие же действия мы поделываем и со всем остальным.
Думаю остальное понятно. Если будут вопросы, задавайте, буду рад ответить!
А пока до скорых встреч!

5 комментариев:

  1. А можете выложить всё это готовым архивом? А то у меня не получается(

    ОтветитьУдалить
  2. С удовольствием, Илья! :) https://sites.google.com/site/webkoding/arhiv-web-razrabotok/color.zip?attredirects=0&d=1
    Надеюсь пригодится :) Только заметил сейчас один косяк: в Chromium страдает позиционирование. Надо стили немного подправить будет.

    ОтветитьУдалить
  3. Спасибо, что заморочился )
    Самому писать ой-как нихателось. Ибо задача второстепенная, а времени убил бы... ну, сам знаешь )
    РЕкомендация: сделай привязку на соответствие: Id div = id text;
    Скажем так:
    [div class="ppic_01" id="surname_pic" style="..."][/div]
    [input type="text" name="surname_01" /]
    [div class="ppic_02" id="surname_pic" style="..."][/div]
    [input type="text" name="surname_02" /]
    Дабы в случае необходимости размещения более одного поля выбора цвета на странице небыло лишних запарок...
    Тебе, правда, придется для этого менять весь принцип позицинирования скрытых полей... Ну, да это уже софтвере проблем ))

    ОтветитьУдалить
  4. Ну, вообще-то каждый ID состоит из двух частей соединенных "_". Так вот первая часть, в нашем случае это "surname", содержит имя поля, к которому все они привязаны. Просто забыл описать эту особенность. Поэтому скрипт легко используется при наличии нескольких полей. А в целом, большое спасибо за то, что указал на недочеты в статье.

    ОтветитьУдалить