gradient.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. define("dojox/gfx/gradient", ["dojo/_base/lang", "./matrix", "dojo/_base/Color"],
  2. function(lang, m, Color){
  3. // Various utilities to deal with a linear gradient (mostly VML-specific)
  4. var grad = lang.getObject("dojox.gfx.gradient", true);
  5. var C = Color;
  6. /*===== grad = dojox.gfx.gradient; =====*/
  7. grad.rescale = function(stops, from, to){
  8. // summary:
  9. // Recalculates a gradient from 0-1 window to
  10. // "from"-"to" window blending and replicating colors,
  11. // if necessary.
  12. // stops: Array
  13. // input gradient as a list of colors with offsets
  14. // (see dojox.gfx.defaultLinearGradient and dojox.gfx.defaultRadialGradient)
  15. // from: Number
  16. // the beginning of the window, should be less than "to"
  17. // to: Number
  18. // the end of the window, should be more than "from"
  19. var len = stops.length, reverseFlag = (to < from), newStops;
  20. // do we need to reverse the color table?
  21. if(reverseFlag){
  22. var tmp = from;
  23. from = to;
  24. to = tmp;
  25. }
  26. // various edge cases
  27. if(!len){
  28. // no colors
  29. return [];
  30. }
  31. if(to <= stops[0].offset){
  32. // all colors are before the color table
  33. newStops = [
  34. {offset: 0, color: stops[0].color},
  35. {offset: 1, color: stops[0].color}
  36. ];
  37. }else if(from >= stops[len - 1].offset){
  38. // all colors are after the color table
  39. newStops = [
  40. {offset: 0, color: stops[len - 1].color},
  41. {offset: 1, color: stops[len - 1].color}
  42. ];
  43. }else{
  44. // main scanning algorithm
  45. var span = to - from, stop, prev, i;
  46. newStops = [];
  47. if(from < 0){
  48. newStops.push({offset: 0, color: new C(stops[0].color)});
  49. }
  50. for(i = 0; i < len; ++i){
  51. stop = stops[i];
  52. if(stop.offset >= from){
  53. break;
  54. }
  55. // skip this color
  56. }
  57. if(i){
  58. prev = stops[i - 1];
  59. newStops.push({
  60. offset: 0,
  61. color: Color.blendColors(new C(prev.color), new C(stop.color), (from - prev.offset) / (stop.offset - prev.offset))
  62. });
  63. }else{
  64. newStops.push({offset: 0, color: new C(stop.color)});
  65. }
  66. for(; i < len; ++i){
  67. stop = stops[i];
  68. if(stop.offset >= to){
  69. break;
  70. }
  71. newStops.push({offset: (stop.offset - from) / span, color: new C(stop.color)});
  72. }
  73. if(i < len){
  74. prev = stops[i - 1];
  75. newStops.push({
  76. offset: 1,
  77. color: Color.blendColors(new C(prev.color), new C(stop.color), (to - prev.offset) / (stop.offset - prev.offset))
  78. });
  79. }else{
  80. newStops.push({offset: 1, color: new C(stops[len - 1].color)});
  81. }
  82. }
  83. // reverse the color table, if needed
  84. if(reverseFlag){
  85. newStops.reverse();
  86. for(i = 0, len = newStops.length; i < len; ++i){
  87. stop = newStops[i];
  88. stop.offset = 1 - stop.offset;
  89. }
  90. }
  91. return newStops;
  92. };
  93. function getPoint(x, y, matrix, project, shiftAndRotate, scale){
  94. var r = m.multiplyPoint(matrix, x, y),
  95. p = m.multiplyPoint(project, r);
  96. return {r: r, p: p, o: m.multiplyPoint(shiftAndRotate, p).x / scale};
  97. }
  98. function sortPoints(a, b){
  99. return a.o - b.o;
  100. }
  101. grad.project = function(matrix, gradient, tl, rb, ttl, trb){
  102. // summary:
  103. // Returns a new gradient using the "VML algorithm" and suitable for VML.
  104. // matrix: dojox.gfx.Matrix2D|Null:
  105. // matrix to apply to a shape and its gradient
  106. // gradient: Object:
  107. // a linear gradient object to be transformed
  108. // tl: dojox.gfx.Point:
  109. // top-left corner of shape's bounding box
  110. // rb: dojox.gfx.Point:
  111. // right-bottom corner of shape's bounding box
  112. // ttl: dojox.gfx.Point:
  113. // top-left corner of shape's transformed bounding box
  114. // trb: dojox.gfx.Point:
  115. // right-bottom corner of shape's transformed bounding box
  116. matrix = matrix || m.identity;
  117. var f1 = m.multiplyPoint(matrix, gradient.x1, gradient.y1),
  118. f2 = m.multiplyPoint(matrix, gradient.x2, gradient.y2),
  119. angle = Math.atan2(f2.y - f1.y, f2.x - f1.x),
  120. project = m.project(f2.x - f1.x, f2.y - f1.y),
  121. pf1 = m.multiplyPoint(project, f1),
  122. pf2 = m.multiplyPoint(project, f2),
  123. shiftAndRotate = new m.Matrix2D([m.rotate(-angle), {dx: -pf1.x, dy: -pf1.y}]),
  124. scale = m.multiplyPoint(shiftAndRotate, pf2).x,
  125. //comboMatrix = new m.Matrix2D([shiftAndRotate, project, matrix]),
  126. // bbox-specific calculations
  127. points = [
  128. getPoint(tl.x, tl.y, matrix, project, shiftAndRotate, scale),
  129. getPoint(rb.x, rb.y, matrix, project, shiftAndRotate, scale),
  130. getPoint(tl.x, rb.y, matrix, project, shiftAndRotate, scale),
  131. getPoint(rb.x, tl.y, matrix, project, shiftAndRotate, scale)
  132. ].sort(sortPoints),
  133. from = points[0].o,
  134. to = points[3].o,
  135. stops = grad.rescale(gradient.colors, from, to),
  136. //angle2 = Math.atan2(Math.abs(points[3].r.y - points[0].r.y) * (f2.y - f1.y), Math.abs(points[3].r.x - points[0].r.x) * (f2.x - f1.x));
  137. angle2 = Math.atan2(points[3].r.y - points[0].r.y, points[3].r.x - points[0].r.x);
  138. return {
  139. type: "linear",
  140. x1: points[0].p.x, y1: points[0].p.y, x2: points[3].p.x, y2: points[3].p.y,
  141. colors: stops,
  142. // additional helpers (for VML)
  143. angle: angle
  144. };
  145. };
  146. return grad;
  147. });