巧用JavaScript实现颜色梯度和渐变效果

转帖|其它|编辑:郝浩|2009-04-02 13:57:50.000|阅读 628 次

概述:关于颜色的效果一般就两个,颜色梯度变化和样式的颜色渐变,前者在ie中一般用滤镜实现。

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

关于颜色的效果一般就两个,颜色梯度变化和样式的颜色渐变,前者在ie中一般用滤镜实现。

实例代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>JavaScript 颜色梯度和渐变效果</title>
<script type="text/javascript">
var $ = function (id) {
    return "string" == typeof id ? document.getElementById(id) : id;
};
var Extend = function(destination, source) {
 for (var property in source) {
  destination[property] = source[property];
 }
 return destination;
}
var Map = function(array, callback, thisObject){
 if(array.map){
  return array.map(callback, thisObject);
 }else{
  var res = [];
  for (var i = 0, len = array.length; i < len; i++) { res.push(callback.call(thisObject, array[i], i, array)); }
  return res;
 }
}
var ColorGrads = function(options){
 this.SetOptions(options);
 this.StartColor = this.options.StartColor;
 this.EndColor = this.options.EndColor;
 this.Step = Math.abs(this.options.Step);
};
ColorGrads.prototype = {
  //设置默认属性
  SetOptions: function(options) {
    this.options = {//默认值
  StartColor: "#fff",//开始颜色
  EndColor: "#000",//结束颜色
  Step:  20//渐变级数
    };
    Extend(this.options, options || {});
  },
  //获取渐变颜色集合
  Create: function() {
 var colors = [],
  startColor = this.GetColor(this.StartColor),
  endColor = this.GetColor(this.EndColor),
  stepR = (endColor[0] - startColor[0]) / this.Step,
  stepG = (endColor[1] - startColor[1]) / this.Step,
  stepB = (endColor[2] - startColor[2]) / this.Step;
 //生成颜色集合
 for(var i = 0, n = this.Step, r = startColor[0], g = startColor[1], b = startColor[2]; i < n; i++){
  colors.push([r, g, b]); r += stepR; g += stepG; b += stepB;
 }
 colors.push(endColor);
 //修正颜色值
 return Map(colors, function(x){ return Map(x, function(x){
  return Math.min(Math.max(0, Math.floor(x)), 255);
 });});
  },
  //获取颜色数据
  GetColor: function(color) {
 if(/^#[0-9a-f]{6}$/i.test(color))
 {//#rrggbb
  return Map([color.substr(1, 2), color.substr(3, 2), color.substr(5, 2)],
   function(x){ return parseInt(x, 16); }
  )
 }
 else if(/^#[0-9a-f]{3}$/i.test(color))
 {//#rgb
  return Map([color.substr(1, 1), color.substr(2, 1), color.substr(3, 1)],
   function(x){ return parseInt(x + x, 16); }
  )
 }
 else if(/^rgb(.*)$/i.test(color))
 {//rgb(n,n,n) or rgb(n%,n%,n%)
  return Map(color.match(/\d+(\.\d+)?\%?/g),
   function(x){ return parseInt(x.indexOf("%") > 0 ? parseFloat(x, 10) * 2.55 : x, 10); }
  )
 }
 else
 {//color
  var mapping = {"red":"#FF0000"};//略
  color = mapping[color.toLowerCase()];
  if(color){
   return Map([color.substr(1, 2), color.substr(3, 2), color.substr(5, 2)],
    function(x){ return parseInt(x, 16); }
   )
  }
 }
  }
};
var CurrentStyle = function(element){
 return element.currentStyle || document.defaultView.getComputedStyle(element, null);
}
var Bind = function(object, fun) {
 var args = Array.prototype.slice.call(arguments).slice(2);
 return function() {
  return fun.apply(object, args.concat(Array.prototype.slice.call(arguments)));
 }
}
//渐变对象
var ColorTrans = function(obj, options){
 
 this._obj = $(obj);
 this._timer = null;//定时器
 this._index = 0;//索引
 this._colors = [];//颜色集合
 this._grads = new ColorGrads();
 
 this.SetOptions(options);
 
 this.Speed = Math.abs(this.options.Speed);
 this.CssColor = this.options.CssColor;
 
 this._startColor = this.options.StartColor || CurrentStyle(this._obj)[this.CssColor];
 this._endColor = this.options.EndColor;
 this._step = Math.abs(this.options.Step);
 
 this.Reset();
 this.SetColor();
};
ColorTrans.prototype = {
  //设置默认属性
  SetOptions: function(options) {
 this.options = {//默认值
  StartColor: "",//开始颜色
  EndColor: "#000",//结束颜色
  Step:  20,//渐变级数
  Speed:  20,//渐变速度
  CssColor: "color"//设置属性(Scripting属性)
 };
    Extend(this.options, options || {});
  },
  //重设颜色集合
  Reset: function(color) {
 //修改颜色后必须重新获取颜色集合
 color = Extend({ StartColor: this._startColor, EndColor: this._endColor, Step: this._step }, color || {});
 //设置属性
 this._grads.StartColor = this._startColor = color.StartColor;
 this._grads.EndColor = this._endColor = color.EndColor;
 this._grads.Step = this._step = color.Step;
 //获取颜色集合
 this._colors = this._grads.Create();
 this._index = 0;
  },
  //颜色渐入
  FadeIn: function() {
 this.Stop(); this._index++; this.SetColor();
 if(this._index < this._colors.length - 1){
  this._timer = setTimeout(Bind(this, this.FadeIn), this.Speed);
 }
  },
  //颜色渐出
  FadeOut: function() {
 this.Stop(); this._index--; this.SetColor();
 if(this._index > 0){
  this._timer = setTimeout(Bind(this, this.FadeOut), this.Speed);
 }
  },
  //颜色设置
  SetColor: function() {
 var color = this._colors[Math.min(Math.max(0, this._index), this._colors.length - 1)];
 this._obj.style[this.CssColor] = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";
  },
  //停止
  Stop: function() {
 clearTimeout(this._timer);
  }
};
</script>
</head>
<body>
<style type="text/css">
#idGrads{}
#idGrads div{ font-size:0;height:1px;}
</style>
<div id="idGrads"> </div>
<script>
var forEach = function(array, callback, thisObject){
 if(array.forEach){
  array.forEach(callback, thisObject);
 }else{
  for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array); }
 }
}
var colors = new ColorGrads({ StartColor: "#fff", EndColor: "rgb(20,127,0)" }).Create();
forEach(colors.concat().concat(colors.reverse()), function(x){
 $("idGrads").innerHTML += "<div style=\"background-color:"+"rgb(" + x[0] + "," + x[1] + "," + x[2] + ");\"></div>";
})
</script>
<Br />
<Br />
<style type="text/css">
#idMenu{ background:#DBDBDB;text-align:center;line-height:25px; table-layout:fixed;}
#idMenu td{ cursor:pointer;}
</style>
<table id="idMenu" width="600" border="0" cellspacing="5" cellpadding="0">
 <tr>
  <td onclick="location.href='http://www.cnblogs.com/cloudgamer/archive/2008/07/21/ImgCropper.html'">
Cropper</td>
  <td onclick="location.href='http://www.cnblogs.com/cloudgamer/archive/2009/01/06/Tween.html'">
Tween</td>
  <td onclick="location.href='http://www.cnblogs.com/cloudgamer/archive/2008/12/24/Slider.html'">
Slider</td>
  <td onclick="location.href='http://www.cnblogs.com/cloudgamer/archive/2008/12/03/Resize.html'">
Resize</td>
  <td onclick="location.href='http://www.cnblogs.com/cloudgamer/archive/2008/11/17/Drag.html'">
Drag</td>
 </tr>
</table>
<script>
forEach($("idMenu").getElementsByTagName("td"), function(x, i){
 var ct1 = new ColorTrans(x, { StartColor: "#666", EndColor: "#fff" });
 var ct2 = new ColorTrans(x, { StartColor: "#f6f6f6", EndColor: "rgb(20,150,0)", CssColor: "backgroundColor" });
 
 x.onmouseover = function(){ ct1.FadeIn(); ct2.FadeIn(); }
 x.onmouseout = function(){ ct1.FadeOut(); ct2.FadeOut(); }
})
</script>
<Br />
<Br />
<div id="idRandom" style="padding:10px;color:#fff; background-color:#CCC;">点击随机换颜色</div>
<script>
var ctRandom = new ColorTrans("idRandom", { EndColor: "#CCC", CssColor: "backgroundColor" })
$("idRandom").onclick = function(){
 var rgb = Map([1,1,1], function(){ return Math.floor((Math.random() * 255)); } );
 ctRandom.Reset({ StartColor: this.style.backgroundColor, EndColor: "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")" })
 ctRandom.FadeIn();
}
</script>
</body>
</html>

  【ColorGrads颜色梯度】

  程序ColorGrads的作用是通过StartColor和EndColor生成颜色梯度集合。颜色都可以用红(r)、绿(g)、蓝(b)三个颜色来表示。程序中先通过GetColor把一般的颜色表示形式转化成一个用红(r)、绿(g)、蓝(b)三个颜色值作元素的集合。那就首先要知道有什么颜色表示形式,从w3c的Colors部分(http://www.w3.org/TR/CSS21/syndata.html#color-units)可以知道有以下形式:

关键词模式:
em { color: red }
RGB颜色模式:
em { color: #f00 }
em { color: #ff0000 }
em { color: rgb(255,0,0) }     
em { color: rgb(100%, 0%, 0%) }

  以上都是表示同一种颜色(红色)。获取颜色属性的形式在ie和ff并不同,ff统一返回RGB颜色模式的第三种形式,ie则按照设置时的形式返回。

  先说说RGB颜色模式,前两种比较常用应该都明白他们的区别吧,它用的是16进制表示形式,而我们想要10进制的。把一个16进制表示的字符转成10进制数字,一般用parseInt,在substr截取字符之后就可以用parseInt转换。对于#ff0000这个形式可以这样转换:

return Map([color.substr(1, 2), color.substr(3, 2), color.substr(5, 2)],
       function(x){ return parseInt(x, 16); }
)

  parseInt的第二个参数就是第一个参数的进制值。对于#f00形式,跟上一个差不多,只是转换之前要先换成完整表示形式:

return Map([color.substr(1, 1), color.substr(2, 1), color.substr(3, 1)],
       function(x){ return parseInt(x + x, 16); }
)

  后面两种可能用的就比较少了,一个用10进制的rgb颜色值表示,另一个用百分比来表示。ff严格按照那样的格式来表示,而ie就“放松”很多,例如:ie可以允许数字百分比混用,ff不可以;ff必须有逗号分隔,ie可以只用空格分隔;当然我们使用时最好是按照w3c的标准来设置了。

  那个DHTML 手册上写的 EM { color: rgb 1.0 0.0 0.0 } 根本不能用的,不要被误导了。对这个形式,程序用正则取得数值,如果有%就根据百分比计算出对应数值:

return Map(color.match(/\d+(\.\d+)?\%?/g),
       function(x){ return parseInt(x.indexOf("%") > 0 ? parseFloat(x, 10) * 2.55 : x, 10); }
)

  而关键词大家都很熟悉,要转化却很麻烦,因为没有一定规律只能一个一个对应:

var mapping = {"red":"#FF0000"};//略
color = mapping[color.toLowerCase()];
if(color){
       return Map([color.substr(1, 2), color.substr(3, 2), color.substr(5, 2)],
              function(x){ return parseInt(x, 16); }
       )
}

  在Create创建颜色集合程序中获得开始颜色和结束颜色的数据后,再根据Step(多少步)就可以获得步长了:

startColor = this.GetColor(this.StartColor),
endColor = this.GetColor(this.EndColor),
stepR = (endColor[0] - startColor[0]) / this.Step,
stepG = (endColor[1] - startColor[1]) / this.Step,
stepB = (endColor[2] - startColor[2]) / this.Step;

  再根据步长生成集合:

for(var i = 0, n = this.Step, r = startColor[0], g = startColor[1], b = startColor[2]; i < n; i++){
       colors.push([r, g, b]); r += stepR; g += stepG; b += stepB;
}
colors.push(endColor);

  正确的颜色值是在0到255之间的,而且是不带小数的,所以最好修正一下:

return Map(colors, function(x){ return Map(x, function(x){
       return Math.min(Math.max(0, Math.floor(x)), 255);
});});

  【ColorTrans颜色渐变】

  有了颜色梯度集合,只需要设个定时器把集合的值依次显示就是一个渐变效果了。这个渐变一般是分两个步骤,分别是(FadeIn)和渐出(FadeOut)。原理就是通过改变_index集合索引,渐入时逐渐变大,渐出时逐渐变小:

  //颜色渐入
  FadeIn: function() {
       this.Stop(); this._index++; this.SetColor();
       if(this._index < this._colors.length - 1){
              this._timer = setTimeout(Bind(this, this.FadeIn), this.Speed);
       }
  },
  //颜色渐出
  FadeOut: function() {
       this.Stop(); this._index--; this.SetColor();
       if(this._index > 0){
              this._timer = setTimeout(Bind(this, this.FadeOut), this.Speed);
       }
  },

  在SetColor设置样式程序中,通过CssColor来设置要修改的样式属性,例如字体颜色是"color",背景色是"backgroundColor":

var color = this._colors[Math.min(Math.max(0, this._index), this._colors.length - 1)];
this._obj.style[this.CssColor] = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ")";

  由于颜色集合是根据开始颜色、结束颜色和步数生成的,所以如果要修改这些属性必须重新生成过集合。Reset程序就是用来修改这些属性并重新生成集合的,集合重新生成后索引也要设回0:

//修改颜色后必须重新获取颜色集合
color = Extend({ StartColor: this._startColor, EndColor: this._endColor, Step: this._step }, color || {});
//设置属性
this._grads.StartColor = this._startColor = color.StartColor;
this._grads.EndColor = this._endColor = color.EndColor;
this._grads.Step = this._step = color.Step;
//获取颜色集合
this._colors = this._grads.Create();
this._index = 0;

  使用技巧

  在颜色渐变菜单中,并没有使用链接标签a,原因是a的伪类的颜色并不能直接用js来修改(除非改class)。暂时没想到很好的方法,只好用onclick跳转代替了。

  在测试过程中还发现一个关于数组的问题,在ie和ff运行alert([,,].length)会分别显示3和2。最后一个元素不写的话ff就会忽略这个元素,只要写的话就不会忽略即使是undefined和null,看了下文档也找到原因。所以这个情况还是插一个东西进去,觉得不好看就new Array好了。

  测试中还发现chrome(1.0.154.48)的map一个问题,map是js1.6的Array的方法,ff和chrome都支持(具体看这里)。在ff中[,,1].map(function(){return 0})返回的是[0,0,0],但chrome却返回[,,0]。
即在chrome中如果元素是空(不包括null和undefined)的话就一律返回空,用new Array来创建也一样。感觉这样不太合理,应该以后会改进吧。

使用说明

ColorGrads只有3个属性设置:

StartColor:       "#fff",//开始颜色
EndColor:       "#000",//结束颜色
Step:              20//渐变级数

设置好属性后用Create生成集合就行了。

ColorTrans只要一个参数,要实现渐变的对象,可设置以下属性:

StartColor:       "",//开始颜色
EndColor:       "#000",//结束颜色
Step:              20,//渐变级数
Speed:              20,//渐变速度
CssColor:       "color"//设置属性(Scripting属性)

如果不设置StartColor会自动使用CurrentStyle获取的样式值。其中StartColor、EndColor和Step在实例化后要重新设置的话需要用Reset来设置。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com

文章转载自:蓝色理想

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP