2014-03-06 53 views

回答

15

上下文的StrokeStyle可以是梯度:

// create a gradient 

gradient = ctx.createLinearGradient(xStart, yStart, xEnd, yEnd); 
gradient.addColorStop(0.0,"blue"); 
gradient.addColorStop(1.0,"purple"); 


// stroke using that gradient 

ctx.strokeStyle = gradient; 

實施例的代碼和使用梯度的StrokeStyle一個演示:http://jsfiddle.net/m1erickson/w46ps/

enter image description here

<!doctype html> 
<html> 
<head> 
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css --> 
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script> 
<style> 
    body{ background-color: ivory; } 
    #canvas{border:1px solid red;} 
</style> 
<script> 
$(function(){ 

    var canvas=document.getElementById("canvas"); 
    var ctx=canvas.getContext("2d"); 

    function drawMultiRadiantCircle(xc, yc, r, radientColors) { 
     var partLength = (2 * Math.PI)/radientColors.length; 
     var start = 0; 
     var gradient = null; 
     var startColor = null, 
      endColor = null; 

     for (var i = 0; i < radientColors.length; i++) { 
      startColor = radientColors[i]; 
      endColor = radientColors[(i + 1) % radientColors.length]; 

      // x start/end of the next arc to draw 
      var xStart = xc + Math.cos(start) * r; 
      var xEnd = xc + Math.cos(start + partLength) * r; 
      // y start/end of the next arc to draw 
      var yStart = yc + Math.sin(start) * r; 
      var yEnd = yc + Math.sin(start + partLength) * r; 

      ctx.beginPath(); 

      gradient = ctx.createLinearGradient(xStart, yStart, xEnd, yEnd); 
      gradient.addColorStop(0, startColor); 
      gradient.addColorStop(1.0, endColor); 

      ctx.strokeStyle = gradient; 
      ctx.arc(xc, yc, r, start, start + partLength); 
      ctx.lineWidth = 30; 
      ctx.stroke(); 
      ctx.closePath(); 

      start += partLength; 
     } 
    } 

    var someColors = []; 
    someColors.push('#0F0'); 
    someColors.push('#0FF'); 
    someColors.push('#F00'); 
    someColors.push('#FF0'); 
    someColors.push('#F0F'); 

    drawMultiRadiantCircle(150, 150, 120, someColors); 

}); // end $(function(){}); 
</script> 
</head> 
<body> 
    <canvas id="canvas" width=300 height=300></canvas> 
</body> 
</html> 
1

幾天前我也需要這種效果,並且我設法創建了一個變通辦法來實現它。

我所做的是一個覆蓋梯度比其他使用是這樣的:

var ic = [ 
     /*0*/{ a:"#FEC331" ,b:"#FB1E24"  ,r1:0 ,r2:1 ,x0:0 ,y0:rd*0.5 ,x1:0 ,y1:-rd}, 
     /*1*/{ a:"#FEC331" ,b:"#FB1E24"  ,r1:0.5 ,r2:0.5 ,x0:0 ,y0:rd*0.3 ,x1:0 ,y1:-rd}, 
     /*2*/{ a:"#EA6F2B" ,b:"transparent" ,r1:0 ,r2:1 ,x0:-rd ,y0:0  ,x1:rd ,y1:0 } 
    ]; 

下面是的jsfiddle完整的代碼和演示:

https://jsfiddle.net/flamedenise/n9no9Lgw/33/

希望它能幫助。

0

在我的情況下,我需要填充整個圓圈而不是圍繞圓周的中風。使用上面的答案並將線寬設置爲半徑的兩倍給出了不希望的結果,所以我寫了自己的。

/** 
* @description Options used when calling CanvasRenderingContext2D.strokeArcGradient() and 
*    CanvasRenderingContext2D.fillArcGradient(). 
* @property {Boolean} useDegrees Whether the specified angles should be interpreted as degrees rather than radians. 
*        (default: false) 
* @property {Number} resolutionFactor The number of lines to render per pixel along the arc. A higher number produces 
*          a cleaner gradient, but has worse performance for large radii. Must be greater 
*          than 0. (default: 8) 
*/ 
class ArcGradientOptions { 
    constructor(options) { 
     function validateParam(test, errorMessage, fatal = false) { 
      if (!test) { 
       if (fatal) { 
        throw new Error(errorMessage); 
       } else { 
        console.assert(false, errorMessage); 
       } 
      } 
     } 

     options = Object.assign({ 
      useDegrees: false, 
      resolutionFactor: 8, 
     }, options); 

     validateParam(
      (options.resolutionFactor instanceof Number | typeof options.resolutionFactor === 'number') && 
       options.resolutionFactor > 0, 
      `ArcGradientOptions.resolutionFactor must be a Number greater than 0. Given: ${options.resolutionFactor}`, 
      true); 

     Object.assign(this, options); 
    } 
}; 

(function() { 
    /** 
    * @description Strokes an arc using a linear gradient. 
    * @param {number} x The x-component of origin of the arc. 
    * @param {number} y The y-component of the origin of the arc. 
    * @param {number} radius The radius of the arc. 
    * @param {number} startAngle Where in the circle to begin the stroke. 
    * @param {number} endAngle Where in the circle to end the stroke. 
    * @param {ArcGradientOptions} options Additional options. 
    */ 
    CanvasRenderingContext2D.prototype.strokeArcGradient = function (x, y, radius, startAngle, endAngle, colorStops, 
      options) { 
     options = new ArcGradientOptions(options); 
     let lineWidth = this.lineWidth; 
     this.fillArcGradient(x, y, startAngle, endAngle, colorStops, radius + lineWidth/2, radius - lineWidth/2, 
      options); 
    } 

    /** 
    * @description Fills a sector or a portion of a ring with a linear gradient. 
    * @param {number} x The x-component of origin of the arc 
    * @param {number} y The y-component of the origin of the arc 
    * @param {number} startAngle Where in the circle to begin the fill. 
    * @param {number} endAngle Where in the circle to end the fill. 
    * @param {number} outerRadius The radius of the arc. 
    * @param {number} innerRadius The radius of the arc that won't be filled. An innerRadius = 0 will fill the whole 
    *        arc. (default: 0) 
    * @param {ArcGradientOptions} options Additional options. 
    */ 
    CanvasRenderingContext2D.prototype.fillArcGradient = function (x, y, startAngle, endAngle, colorStops, outerRadius, 
      innerRadius = 0, options) { 
     options = new ArcGradientOptions(options); 

     let oldLineWidth = this.lineWidth, 
      oldStrokeStyle = this.strokeStyle; 

     if (options.useDegrees) { 
      startAngle = startAngle * Math.PI/180; 
      endAngle = endAngle * Math.PI/180; 
     } 

     let deltaArcAngle = endAngle - startAngle; 
      gradientWidth = Math.floor(outerRadius * Math.abs(deltaArcAngle) * options.resolutionFactor), 
      gData = generateGradientImgData(gradientWidth, colorStops).data; 

     this.lineWidth = Math.min(4/options.resolutionFactor, 1); 

     for (let i = 0; i < gradientWidth; i++) { 
      let gradi = i * 4, 
       theta = startAngle + deltaArcAngle * i/gradientWidth; 

      this.strokeStyle = `rgba(${gData[gradi]}, ${gData[gradi + 1]}, ${gData[gradi + 2]}, ${gData[gradi + 3]})`; 

      this.beginPath(); 
      this.moveTo(x + Math.cos(theta) * innerRadius, y + Math.sin(theta) * innerRadius); 
      this.lineTo(x + Math.cos(theta) * outerRadius, y + Math.sin(theta) * outerRadius); 
      this.stroke(); 
      this.closePath(); 
     } 

     this.lineWidth = oldLineWidth; 
     this.strokeStyle = oldStrokeStyle; 
    } 

    function generateGradientImgData(width, colorStops) { 
     let canvas = document.createElement('canvas'); 
     canvas.setAttribute('width', width); 
     canvas.setAttribute('height', 1); 
     let ctx = canvas.getContext('2d'), 
      gradient = ctx.createLinearGradient(0, 0, width, 0); 

     for (let i = 0; i < colorStops.length; i++) { 
      gradient.addColorStop(colorStops[i].offset, colorStops[i].color); 
     } 

     ctx.fillStyle = gradient; 
     ctx.fillRect(0, 0, width, 1); 
     return ctx.getImageData(0, 0, width, 1); 
    } 
})(); 

此方法從圓的中心繪製線條,沿着它的邊緣繪製每個像素。這樣就可以得到更清晰的漸變效果。

Circles side-by-side

對於大型線的厚度,它仍然是清潔的。

Rings side-by-side

它的一個主要缺點是性能。如果你的半徑非常大,產生一個很好的圓所需的線數約爲半徑的50倍。

jsFiddle