如何在特定位置获取svg线性渐变的颜色

3

我有一个线性渐变,用作百分比条,带有一个小椭圆形,在该条上移动以显示当前完成百分比。完成百分比通过调用一个AngularJS绑定函数进行更新。

我需要根据当前位置的渐变条颜色更改小椭圆形的颜色。例如,如果百分比是80%,我需要获取80%位置处的渐变颜色。

这是否可能?

<svg height="20px" width="100%">
<defs>
    <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
        <stop offset="50%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
        <stop offset="100%" style="stop-color:rgb(79,189,0);stop-opacity:1" />
    </linearGradient>
</defs>
<rect width="100%" height="3px" y="50%" fill="url(#gradient)" style="stroke-width:0" />
<rect width="30px" height="20px" x="{{calculateProfilePercentage()}}%" rx="8" ry="8" fill="rgb(249,166,31)" style="stroke-width:0" />
</svg>
2个回答

6

据我所知,您无法从渐变中读取值,因为渐变对象没有公开的方法可以实现这一点。

但是,您可以创建一个屏幕外画布,在那里绘制渐变并从那里读取像素值。

例如,让我们创建一个名为GradientReader的对象。这使我们能够实例化它并嵌入方法等:

function GradientReader(colorStops) {

    const canvas = document.createElement('canvas');   // create canvas element
    const ctx = canvas.getContext('2d');               // get context
    const gr = ctx.createLinearGradient(0, 0, 101, 0); // create a gradient
    
    canvas.width = 101;                                // 101 pixels incl.
    canvas.height = 1;                                 // as the gradient

    for (const { stop, color } of colorStops) {               // add color stops
        gr.addColorStop(stop, color);
    }
    
    ctx.fillStyle = gr;                                // set as fill style
    ctx.fillRect(0, 0, 101, 1);                        // draw a single line
    
    // method to get color of gradient at % position [0, 100]
    return {
        getColor: (pst) => ctx.getImageData(pst|0, 0, 1, 1).data
    };
}

现在我们可以为对象设置颜色点。
const gr = new GradientReader([{stop: 0.0, color: '#f00'},
                               {stop: 0.5, color: '#ff0'},
                               {stop: 1.0, color: 'rgb(79,189,0)'}]);

我们现在有一个模拟渐变的对象,我们只需要调用它的方法getColor()并传入百分比值即可:
const col = gr.getColor(pst);
el.style.backgroundColor = `rgb(${col[0]}, ${col[1]}, ${col[2]})`;

FIDDLE

按照您的需求进行修改。

希望这有所帮助!


非常符合我的需求,谢谢。顺便问一下,你似乎对Javascript有很扎实的理解,是如何达到这个水平的呢? - Pankas
1
@Pankas 练习,练习 :) - user1693593
很遗憾,我们不得不使用这样的技巧来实现如此简单的事情。我需要在SVG文档的上下文中获取一个linearGradient的颜色,而我不想为此创建一个离屏画布。似乎编写一个手动插值例程是唯一(合理的,非画布基础的)选择? - Yona Appletree
运行得很好!非常感谢这个“超越常规”的解决方案。 - Pianoman

0

这是我的答案,使用Angular 8和一些ES6技术。新的百分比通过一个observable传入。

HTML文件包含一个SVG,其中有两个与类相关联的属性。类似于这样的东西:

<svg>
 <circle [attr.stroke]='strokeColor' [attr.fill]='fillColor'>
</svg>

应用程序的TypeScript文件:

import {Component, Input, OnInit} from '@angular/core';
import {Observable} from 'rxjs';

@Component({
  selector: 'app-real-time-indicator',
  templateUrl: './real-time-indicator.component.html',
  styleUrls: ['./real-time-indicator.component.css']
})
export class RealTimeIndicatorComponent implements OnInit {

  @Input() percentagePassed$: Observable<number>;
  @Input() minutesRemaining$: Observable<number>;

  public currentPercentage: number;
  public minutesRemaining: number;
  private canvas2dContext: any;
  private canvas: any;

  public fillColor: string;
  public strokeColor: string;

  constructor() {

  }

  ngOnInit(): void {
    this.percentagePassed$.subscribe((newValue) => {
      this.currentPercentage = 100 - newValue;
      let [r, g, b]   = this.getColor( newValue);
      this.fillColor = `rgb(${r},${g},${b})`;

      [r, g, b]   = this.getColor( 100 - newValue);
      this.strokeColor = `rgb(${r},${g},${b})`;
    });
    this.canvas = document.createElement('canvas');     // create canvas element
    this.canvas2dContext = this.canvas.getContext('2d');                 // get context
    const gr = this.canvas2dContext.createLinearGradient(0, 0, 101, 0);   // create a gradient

    this.canvas.width = 101;                                // 101 pixels incl.
    this.canvas.height = 1;                                 // as the gradient

    const colorStops = [
      {stop: 0.0, color: 'lightgreen'},
      {stop: 0.9, color: 'orange'},
      {stop: 1.0, color: 'red'},
    ];

    for (const cs of colorStops)                       // add color stops
    {
      gr.addColorStop(cs.stop, cs.color);
    }

    this.canvas2dContext.fillStyle = gr;                                // set as fill style
    this.canvas2dContext.fillRect(0, 0, 101, 1);                        // draw a single line

  }

  // method to get color of gradient at % position [0, 100]
  getColor(percentage: number) {
    return this.canvas2dContext.getImageData(percentage , 0, 1, 1).data;
  }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接