Пишем скрипт анимации цветов

javascript, 22.09.2009 Вторник, 18:40
Теги: скрипт, цвета, HEX, Анимация
     Сегодня мы с вам напишем скрипт цветовой анимации. Цветовая анимация - полезная и интересная штука, которая поможет создать множество красивых эффектов на вашем сайте. Что я подразумеваю под цветовой анимацией? - плавное изменение одного цвета на другой.

     Начнем с того, что создадим объект, в котором и будут хранится все функции нашего будущего скрипта:
Code
<script type="text/javascript">
colors = {
  }
</script>
     Теперь нам необходимо создать функцию, которая бы получала начальный цвет элемента. Так как конструкция element.style.property не возвращает данные, хранящиеся например в css, то мы воспользуемся связкой данной конструкции с конструкцией computedStyle. Так как element.style работает в разы быстрее чем computedStyle, мы сначала будем проверять наличие инлайновых данных. Так как разработчики IE опять наплевали на стандарты пошли своим путем, то я так же добавил использование конструкции currentStyle. Всю функцию я назвал current. Она принимает сам элемент и требуемый стиль и возвращает данные по стилю. Вот что получилось:
Code
<script type="text/javascript">
colors = {
  current: function(el,st){
  var result;
  if(el.style[st] && el.style[st]!=''){result=el.style[st]} else
  if(/*@cc_on!@*/false){result=el.currentStyle[st]} else
  {result=document.defaultView.getComputedStyle(el,null)[st]}
  return result=='transparent' ? '#FFFFFF' : result;
  }
  }
</script>
     Едем дальше. Разные браузеры возвращают информацию о цвете по разному. К примеру IE возвращает именно то, что записано в стиле, а FireFox - цвет, преобразованный в вид rgb(r,g,b). Для того, чтобы свести все различия к единому результату, я написал функцию обработки цвета и перевода его hex значения к развернутому rgb виду. Функцию я назвал просто - torgb. Вот как выглядит эта функция, она добавлена после функции current:
Code
<script type="text/javascript">
colors = {
  current: function(el,st){
  var result;
  if(el.style[st] && el.style[st]!=''){result=el.style[st]} else
  if(/*@cc_on!@*/false){result=el.currentStyle[st]} else
  {result=document.defaultView.getComputedStyle(el,null)[st]}
  return result=='transparent' ? '#FFFFFF' : result;
  },
   
  torgb: function(col){
  var r,g,b,result;
  function rgb(a,b){
  var q = {'A':10,'B':11,'C':12,'D':13,'E':14,'F':15};
  return parseInt(a in q?q[a]:a)*16+parseInt(b in q?q[b]:b);
  }
  result=col.toUpperCase();
  if(/RGB\(\d+,\s?\d+,\s?\d+\)/.test(result)){
  r = result.match(/\d+/g)[0];
  g = result.match(/\d+/g)[1];
  b = result.match(/\d+/g)[2];
  }else
  if(/#([0-9A-F]{2}){3}/.test(result)){
  r = rgb(result.charAt(1),result.charAt(2));
  g = rgb(result.charAt(3),result.charAt(4));
  b = rgb(result.charAt(5),result.charAt(6));
  }else
  if(/#([0-9A-F]{1}){3}/.test(result)){
  r = rgb(result.charAt(1),result.charAt(1));
  g = rgb(result.charAt(2),result.charAt(2));
  b = rgb(result.charAt(3),result.charAt(3));
  }
  return {r:r,g:g,b:b};
  }
  }
</script>
     Кстати, кому интересно, вот самописная функция обратного перевода чисел из десятичной системы в шестнадцатеричную:
Code
<script type="text/javascript">
toHEX = function(N){
            N=parseInt(N);
            if(N==0 || isNaN(N)){return "00"}
            N=Math.round(Math.min(Math.max(0,N),255));
            return "0123456789ABCDEF".charAt((N-N%16)/16)+"0123456789ABCDEF".charAt(N%16);
            }
</script>
     Не важно, забейте. =) Едем дальше.

     Пришло время добавить собственно саму функцию-аниматор.я не буду подробно описывать её и её элементы. Скажу только что обкатывал и дописывал её приличное количество времени. "Функция" эта на самом деле состоит из двух функций и двух параметров. Первая функция animtype отвечает за тип анимации (всего я сделал 4 типа). Вторая функция animate является функцией-аниматором и создает саму анимацию, а так же определяет состояние анимируемых элементов. Масив animator хранит все анимации, воспроизводящиеся в данный момент, а параметр anielems считает число анимируемых элементов. Вот весь скрипт анимации в сборе:
Code
<script type="text/javascript">
colors = {
  current: function(el,st){
  var result;
  if(el.style[st] && el.style[st]!=''){result=el.style[st]} else
  if(/*@cc_on!@*/false){result=el.currentStyle[st]} else
  {result=document.defaultView.getComputedStyle(el,null)[st]}
  return result=='transparent' ? '#FFFFFF' : result;
  },
  torgb: function(col){
  var r,g,b,result;
  function rgb(a,b){
  var q = {'A':10,'B':11,'C':12,'D':13,'E':14,'F':15};
  return parseInt(a in q?q[a]:a)*16+parseInt(b in q?q[b]:b);
  }
  result=col.toUpperCase();
  if(/RGB\(\d+,\s?\d+,\s?\d+\)/.test(result)){
  r = result.match(/\d+/g)[0];
  g = result.match(/\d+/g)[1];
  b = result.match(/\d+/g)[2];
  }else
  if(/#([0-9A-F]{2}){3}/.test(result)){
  r = rgb(result.charAt(1),result.charAt(2));
  g = rgb(result.charAt(3),result.charAt(4));
  b = rgb(result.charAt(5),result.charAt(6));
  }else
  if(/#([0-9A-F]{1}){3}/.test(result)){
  r = rgb(result.charAt(1),result.charAt(1));
  g = rgb(result.charAt(2),result.charAt(2));
  b = rgb(result.charAt(3),result.charAt(3));
  }
  return {r:r,g:g,b:b};
  },
  animtype: function(p,ani){
  switch(ani){
  case 'simple':return p;break;
  case 'compress':var x=2;return Math.pow(p,2)*((x+1)*p-x);break;
  case 'smooth':function d(p){return Math.pow(p,3)};if(p<0.5){return d(2*p)/2;}else{return (2-d(2*(1-p)))/2;};break;
  case 'jump':function c(p){for(var a=0,b=1;1;a+=b,b/=2){if(p>=(7-4*a)/11)return -Math.pow((11-6*a-11*p)/4,2)+Math.pow(b,2);}};return 1-c(1-p);break;
  default:return p;break;
  }
  },
  animator:{},
  anielems:0,
  animate: function(ani,obj,v,time,callback){
  for(key in v){anima(key,v[key])}
  function anima(param,to){
  var from,aID;
  to = colors.torgb(to);  
  from = colors.torgb(colors.current(obj,param));
  for(key in colors.animator){
  if(colors.animator[key].o==obj && colors.animator[key].p==param){
  clearInterval(colors.animator[key].timer)
  }
  }
  aID=colors.anielems;
  colors.animator[aID]={
  o:obj,
  p:param,
  start: new Date().getTime(),
  timer: setInterval(function(){
  var now,progress,u,r,g,b;
  now=(new Date().getTime())-colors.animator[aID].start;
  progress=now/time;
  u = colors.animtype(progress,ani);
  r=parseInt((parseInt(to.r)-parseInt(from.r))*u+parseInt(from.r));
  g=parseInt((parseInt(to.g)-parseInt(from.g))*u+parseInt(from.g));
  b=parseInt((parseInt(to.b)-parseInt(from.b))*u+parseInt(from.b));  
  obj.style[param]='rgb('+r+','+g+','+b+')';
  if(progress>=1){
  clearInterval(colors.animator[aID].timer);
  obj.style[param]='rgb('+to.r+','+to.g+','+to.b+')';
  if(callback){callback()}
  delete colors.animator[aID];
  }
  },10)
  }
  colors.anielems++;
  }
  }
  }
</script>
А вот его, сжатая с помощью YUI Compressor, версия:
Code
<script type="text/javascript">
colors={current:function(el,st){var result;if(el.style[st]&&el.style[st]!=""){result=el.style[st]}else{if(/*@cc_on!@*/false){result=el.currentStyle[st]}else{result=document.defaultView.getComputedStyle(el,null)[st]}}return result=="transparent"?"#FFFFFF":result},torgb:function(e){var h,f,c,a;function d(i,g){var j={A:10,B:11,C:12,D:13,E:14,F:15};return parseInt(i in j?j[i]:i)*16+parseInt(g in j?j[g]:g)}a=e.toUpperCase();if(/RGB\(\d+,\s?\d+,\s?\d+\)/.test(a)){h=a.match(/\d+/g)[0];f=a.match(/\d+/g)[1];c=a.match(/\d+/g)[2]}else{if(/#([0-9A-F]{2}){3}/.test(a)){h=d(a.charAt(1),a.charAt(2));f=d(a.charAt(3),a.charAt(4));c=d(a.charAt(5),a.charAt(6))}else{if(/#([0-9A-F]{1}){3}/.test(a)){h=d(a.charAt(1),a.charAt(1));f=d(a.charAt(2),a.charAt(2));c=d(a.charAt(3),a.charAt(3))}}}return{r:h,g:f,b:c}},animtype:function(e,b){switch(b){case"simple":return e;break;case"compress":var a=2;return Math.pow(e,2)*((a+1)*e-a);break;case"smooth":function f(c){return Math.pow(c,3)}if(e<0.5){return f(2*e)/2}else{return(2-f(2*(1-e)))/2}break;case"jump":function g(h){for(var d=0,c=1;1;d+=c,c/=2){if(h>=(7-4*d)/11){return -Math.pow((11-6*d-11*h)/4,2)+Math.pow(c,2)}}}return 1-g(1-e);break;default:return e;break}},animator:{},anielems:0,animate:function(a,e,b,d,f){for(key in b){c(key,b[key])}function c(h,j){var i,g;j=colors.torgb(j);i=colors.torgb(colors.current(e,h));for(key in colors.animator){if(colors.animator[key].o==e&&colors.animator[key].p==h){clearInterval(colors.animator[key].timer)}}g=colors.anielems;colors.animator[g]={o:e,p:h,start:new Date().getTime(),timer:setInterval(function(){var n,m,l,p,o,k;n=(new Date().getTime())-colors.animator[g].start;m=n/d;l=colors.animtype(m,a);p=parseInt((parseInt(j.r)-parseInt(i.r))*l+parseInt(i.r));o=parseInt((parseInt(j.g)-parseInt(i.g))*l+parseInt(i.g));k=parseInt((parseInt(j.b)-parseInt(i.b))*l+parseInt(i.b));e.style[h]="rgb("+p+","+o+","+k+")";if(m>=1){clearInterval(colors.animator[g].timer);e.style[h]="rgb("+j.r+","+j.g+","+j.b+")";if(f){f()}delete colors.animator[g]}},10)};colors.anielems++}}};
</script>
     Скрипт запускается следующим образом:
Code
<script type="text/javascript">
colors.animate(тип анимации,элемент,хэш значений,время,колбэк);

// пример

colors.animate("smooth",this,{"backgroundColor":"#FF9900"},100,function(){alert('Готово!')})
</script>
Где тип анимации - строка, которая может принимать значения
  1. 'simple' - простая, линейная анимация
  2. 'smooth' - сглаженная анимация, замедленная в начале и в конце.
  3. 'jump' - анимация прыжком, после достижения финиш позиции анимация пару раз повторит последние шаги.
  4. 'compress' -  сжимающая имация, то же что и прыжок, но анимация переходит финиш позицию, затем в несколько колебаний устанавливает нужное значение.
Элемент - сам анимируемый элемент. Хэш значений - хэш, содержащий все названия анимируемых параметров и нужные значения. Одновременно можно указать несколько анимируемых параметров:
{'color':'#000','borderColor':'#FFF','backgroundColor':#123123} и так далее. Время - время выполнения анимации в милисекундах. Колбэк - функция, выполняемая после завершения анимации (не обязательный параметр).

     Данный скрипт полностью кроссбраузерен и работает во всех версиях IE начиная с 5.5, в Opera 9 и 10, в Mozilla FireFox, в Safari и в Google Chrome. Ни в одном браузере не было обнаружено торможения анимации или каких либо багов.

     Так как же можно применять данный скрипт? По разному! Смотрим примеры: color_animation.html.

     Если вы увидели в скрипте какое либо пространство для оптимизации - пишите в комменты, я с удовольствием учту ваши замечания на будущее и исправлю скрипт.


ВИДЕОДЕСЕРТ

    Так как я достаточно давно не выкладывал видео десертов, то на этот раз исправлюсь. В десерте - сам ролик и история его создания:
Если вам понравилась музыка - её название написано в титрах первого ролика.

Жми на пятую!
17, 13, 7327
№13
Спасибо отличный скрипт, буду юзать на своем форуме для создания оффтопа ( при наведение на текст он становится более темным и читаемым ) :))
№12
[sayto]Nailed[/sayto], я учусь, вот и всё)
№11
[sayto]Anonymus[/sayto], насчет Visual не знаю. Скрипты я пишу в dreamweaver и то, только потому, что там подсвечивается код (удобнее, когда участки кода выделяются). Если бы подсветка кода была в блокноте - писал бы в нем.
№10
IETester это не IE. По некоторым отзывам, то, что корректно отображается в IETester'е, в настоящем IE выглядит совсем не так радужно.
Ответ: Возможно. Но и наоборот тоже верно. Многое, отображающееся с ошибками в тестере, ie кушает нормально. У кого есть возможность проверить кроссбраузерность скрипта? Был бы благодарен.
№9
[sayto]DSC[/sayto], и там и там проверял тестовую страницу с помощью IETester. Всё работает.
№8
Спасибо, не знал, что так можно.
№7
В том, что регэкспы будут применяться только один раз вместо четырех. А регэкспы штука медленная.
if(/RGB\((\d+),\s?(\d+),\s?(\d+)\)/.test(result)){
r = RegExp.$1;
g = RegExp.$2;
b = RegExp.$3;
}
№6
Так в чем оптимизация? Можете предложить лучше?
№5
Оптимизация? Да пожалуйста:
if(/RGB\(\d+,\s?\d+,\s?\d+\)/.test(result)){
r = result.match(/\d+/g)[0];
g = result.match(/\d+/g)[1];
b = result.match(/\d+/g)[2];
к чему 4 вызова разбора одной и той же строки регэкспами? Достаточно будет и одного.

Это то, что сразу бросилось в глаза. Более подробно не рассматривал.

№4
но зачем делать скрипты цветным

Это подсветка кода от studio ad если ты еще не знаешь

№3
Понравилось, главное, что код не так много весит, в некоторых случаях скрипт может пригодиться. Благодарю.
№2
Класс, но зачем делать скрипты цветным
№1
куль
    © Блог StudioAD.ru 2024 год нашей эры. Не все права защищены... Копирование любой информации и материалов с обратной ссылкой приветствуется! Хостинг от uCoz.

    Если вам пришлись по душе материалы моего блога - подпишитесь на RSS дабы получать обновления незамедлительно! Я рад что вы читаете и комментируете мои экзерсисы, приятного времяпрепровождения.