2014-10-01 2 views
0

Я пытаюсь, чтобы виджет для рикшауграфа менял цвет фона в зависимости от самого высокого значения на самом высоком графике для новейших входящих данных. У меня есть работа для одной серии, но у меня возникают проблемы с ее обработкой нескольких рядов на одном графике. Это фрагмент файла rickshawgraph.coffee. Я знаю, что мне нужен цикл, чтобы получить каждую серию и проверить, какой из них имеет самое высокое значение, а затем выполнить остальную часть логики, но у меня очень сильное время синтаксиса. Любая помощь будет оценена по достоинству. Я включил также все файлы ниже.Dashing Rickshawgraph, чтобы изменить цвет фона на самых высоких рядах данных

node = $(@node) 
    series = @_parseData {points: @get('points'), series: @get('series')} 
    data = series[0].data 
    values = data[data.length - 1].y 
    #cool = parseInt(@get('cool')) 
    cool = parseInt node.data "cool" 
    #warm = parseInt(@get('warm')) 
    warm = parseInt node.data "warm" 
    level = switch 
    when values <= cool then 0 
    when values >= warm then 4 
    else 
     bucketSize = (warm - cool)/3 # Total # of colours in middle 
     Math.ceil (values - cool)/bucketSize 

    backgroundClass = "hotness#{level}" 
    lastClass = @get "lastClass" 
    node.toggleClass "#{lastClass} #{backgroundClass}" 

    @set "lastClass", backgroundClass 

Мой erb-файл вызывает виджет здесь.

</li> 

Мой rickshawgraph.scss СКС здесь.

// ---------------------------------------------------------------------------- 
// Mixins 
// ---------------------------------------------------------------------------- 
@mixin transition($transition-property, $transition-time, $method) { 
    -webkit-transition: $transition-property $transition-time $method; 
    -moz-transition: $transition-property $transition-time $method; 
    -o-transition: $transition-property $transition-time $method; 
    transition: $transition-property $transition-time $method; 
} 

// ---------------------------------------------------------------------------- 
// Sass declarations 
// ---------------------------------------------------------------------------- 
$background-color: #00C176; 

// ---------------------------------------------------------------------------- 
// Widget-graph styles 
// ---------------------------------------------------------------------------- 

.widget-rickshawgraph { 
    background-color: #00C176; 
    position: relative; 
} 
.widget-rickshawgraph .rickshaw_graph { 
    position: absolute; 
    left: 0px; 
    top: 0px; 
} 
.widget-rickshawgraph svg { 
    position: absolute; 
    left: 0px; 
    top: 0px; 
} 
.widget-rickshawgraph .title, .widget-rickshawgraph .value { 
    position: relative; 
    z-index: 99; 
} 
.widget-rickshawgraph .title { 
    color: rgba(126, 126, 126, 0.7); 
} 
.widget-rickshawgraph .more-info { 
    color: rgba(0, 0, 0, 0); 
    font-weight: 600; 
    font-size: 20px; 
    margin-top: 0; 
    opacity: 0; 
} 
.widget-rickshawgraph .x_tick { 
    position: absolute; 
    bottom: 0; 
} 
.widget-rickshawgraph .x_tick .title { 
    font-size: 40px; 
    color: rgba(0, 0, 0, 0.4); 
    opacity: 0.5; 
    padding-bottom: 3px; 
} 
.widget-rickshawgraph .y_ticks { 
    font-size: 40px; 
    fill: rgba(0, 0, 0, 0.4); 
    color: rgba(0, 0, 0, 0.4); 
    font-weight: bold; 
} 
.widget-rickshawgraph .y_ticks text { 
    font-size: 20px; 
    color: rgba(0, 0, 0, 0.4); 
    fill: rgba(0, 0, 0, 0.4); 
    font-weight: bold; 
} 
.widget-rickshawgraph .domain { 
    display: none; 
} 
.widget-rickshawgraph .rickshaw_legend { 
    position: absolute; 
    left: 0px; 
    bottom: 0px; 
    white-space: nowrap; 
    overflow-x: scroll; 
    font-size: 80px; 
    height: 20px; 
} 
.widget-rickshawgraph .rickshaw_legend ul { 
    margin: 0; 
    padding: 0; 
    list-style-type: none; 
    text-align: center; 
} 
.widget-rickshawgraph .rickshaw_legend ul li { 
    display: inline; 
} 
.widget-rickshawgraph .rickshaw_legend .swatch { 
    display: inline-block; 
    width: 14px; 
    height: 14px; 
    margin-left: 5px; 
} 
.widget-rickshawgraph .rickshaw_legend .label { 
    display: inline-block; 
    margin-left: 5px; 
    /*Change the font size and the text size and make sure the label comes to the front for the legend */ 
    font-size: 200%; 
    color: rgba(255, 255, 255, 0.7); 
} 

.hotness0 { background-color: #00C176; } 
.hotness1 { background-color: #88C100; } 
.hotness2 { background-color: #FABE28; } 
.hotness3 { background-color: #FF8A00; } 
.hotness4 { background-color: #FF003C; } 

// // More colour-blind friendly palette 
// .hotness0 { background-color: #046D8B; } 
// .hotness1 { background-color: #309292; } 
// .hotness2 { background-color: #2FB8AC; } 
// .hotness3 { background-color: #93A42A; } 
// .hotness4 { background-color: #ECBE13; } 

My rickshawgraph.coffee здесь.

# Rickshawgraphhot v0.1.0 

class Dashing.Rickshawgraphhot extends Dashing.Widget 

    DIVISORS = [ 
     {number: 100000000000000000000000, label: 'Y'}, 
     {number: 100000000000000000000,  label: 'Z'}, 
     {number: 100000000000000000,  label: 'E'}, 
     {number: 1000000000000000,   label: 'P'}, 
     {number: 1000000000000,    label: 'T'}, 
     {number: 1000000000,    label: 'G'}, 
     {number: 1000000,     label: 'M'}, 
     {number: 1000,      label: 'S'}, 
     {number: 1,       label: 'MS'} 
    ] 

    # Take a long number like "2356352" and turn it into "2.4M" 
    formatNumber = (number) -> 
     for divisior in DIVISORS 
      if number > divisior.number 
       number = "#{Math.round(number/(divisior.number/10))/10}#{divisior.label}" 
       break 
      else 
      number = " number + 'ms'" 

     return number 

    getRenderer:() -> return @get('renderer') or @get('graphtype') or 'area' 

    # Retrieve the `current` value of the graph. 
    @accessor 'current', -> 
    answer = null 

    # Return the value supplied if there is one. 
    if @get('displayedValue') != null and @get('displayedValue') != undefined 
     answer = @get('displayedValue') 

    if answer == null 
     # Compute a value to return based on the summaryMethod 
     series = @_parseData {points: @get('points'), series: @get('series')} 
     if !(series?.length > 0) 
     # No data in series 
     answer = '' 

     else 
     switch @get('summaryMethod') 
      when "sum" 
      answer = 0 
      answer += (point?.y or 0) for point in s.data for s in series 

      when "sumLast" 
      answer = 0 
      answer += s.data[s.data.length - 1].y or 0 for s in series 

      when "highest" 
      answer = 0 
      if @get('unstack') or (@getRenderer() is "line") 
       answer = Math.max(answer, (point?.y or 0)) for point in s.data for s in series 
      else 
       # Compute the sum of values at each point along the graph 
       for index in [0...series[0].data.length] 
       value = 0 
       for s in series 
        value += s.data[index]?.y or 0 
       answer = Math.max(answer, value) 

      when "none" 
      answer = '' 

      else 
      # Otherwise if there's only one series, pick the most recent value from the series. 
      if series.length == 1 and series[0].data?.length > 0 
       data = series[0].data 
       answer = data[data.length - 1].y 
      else 
       # Otherwise just return nothing. 
       answer = '' 

     if @get('numformat') == 'ms' 
     answer = formatNumber answer 


    return answer 


    ready: -> 
    @assignedColors = @get('colors').split(':') if @get('colors') 
    @strokeColors = @get('strokeColors').split(':') if @get('strokeColors') 

    @graph = @_createGraph() 
    @graph.render() 

    clear: -> 
    # Remove the old graph/legend if there is one. 
    $node = $(@node) 
    $node.find('.rickshaw_graph').remove() 
    if @$legendDiv 
     @$legendDiv.remove() 
     @$legendDiv = null 

    # Handle new data from Dashing. 
    onData: (data) -> 
    series = @_parseData data 

    if @graph 
     # Remove the existing graph if the number of series has changed or any names have changed. 
     needClear = false 
     needClear |= (series.length != @graph.series.length) 
     if @get("legend") then for subseries, index in series 
     needClear |= @graph.series[index]?.name != series[index]?.name 

     if needClear then @graph = @_createGraph() 

     # Copy over the new graph data 
     for subseries, index in series 
     @graph.series[index] = subseries 

     @graph.render() 

     node = $(@node) 
     series = @_parseData {points: @get('points'), series: @get('series')} 
     data = series[0].data 
     values = data[data.length - 1].y 
     #cool = parseInt(@get('cool')) 
     cool = parseInt node.data "cool" 
     #warm = parseInt(@get('warm')) 
     warm = parseInt node.data "warm" 
     level = switch 
     when values <= cool then 0 
     when values >= warm then 4 
     else 
      bucketSize = (warm - cool)/3 # Total # of colours in middle 
      Math.ceil (values - cool)/bucketSize 

     backgroundClass = "hotness#{level}" 
     lastClass = @get "lastClass" 
     node.toggleClass "#{lastClass} #{backgroundClass}" 

     @set "lastClass", backgroundClass 
    # Create a new Rickshaw graph. 
    _createGraph: -> 
    $node = $(@node) 
    $container = $node.parent() 

    @clear() 

    # Gross hacks. Let's fix this. 
    width = (Dashing.widget_base_dimensions[0] * $container.data("sizex")) + Dashing.widget_margins[0] * 2 * ($container.data("sizex") - 1) 
    height = (Dashing.widget_base_dimensions[1] * $container.data("sizey")) 

    if @get("legend") 
     # Shave 20px off the bottom of the graph for the legend 
     height -= 20 

    $graph = $("<div style='height: #{height}px;'></div>") 
    $node.append $graph 
    series = @_parseData {points: @get('points'), series: @get('series')} 

    graphOptions = { 
     element: $graph.get(0), 
     renderer: @getRenderer(), 
     width: width, 
     height: height, 
     series: series 
    } 

    if [email protected]('stroke') then graphOptions.stroke = true 
    if @get('min') != null then graphOptions.max = @get('min') 
    if @get('max') != null then graphOptions.max = @get('max') 

    try 
     graph = new Rickshaw.Graph graphOptions 
    catch err 
     if err.toString() is "x and y properties of points should be numbers instead of number and object" 
     # This will happen with older versions of Rickshaw that don't support nulls in the data set. 
     nullsFound = false 
     for s in series 
      for point in s.data 
      if point.y is null 
       nullsFound = true 
       point.y = 0 

     if nullsFound 
      # Try to create the graph again now that we've patched up the data. 
      graph = new Rickshaw.Graph graphOptions 
      if [email protected] 
      console.log "#{@get 'id'} - Nulls were found in your data, but Rickshaw didn't like" + 
       " them. Consider upgrading your rickshaw to 1.4.3 or higher." 
      @rickshawVersionWarning = true 
     else 
      # No nulls were found - this is some other problem, so just re-throw the exception. 
      throw err 

    graph.renderer.unstack = [email protected]('unstack') 

    xAxisOptions = { 
     graph: graph 
    } 
    if Rickshaw.Fixtures.Time.Local 
     xAxisOptions.timeFixture = new Rickshaw.Fixtures.Time.Local() 

    x_axis = new Rickshaw.Graph.Axis.Time xAxisOptions 
    y_axis = new Rickshaw.Graph.Axis.Y(graph: graph, tickFormat: Rickshaw.Fixtures.Number.formatMS) 

    if @get("legend") 
     # Add a legend 
     @$legendDiv = $("<div style='position:fixed; z-index:99; width: #{width}px;'></div>") 
     $node.append(@$legendDiv) 
     legend = new Rickshaw.Graph.Legend { 
     graph: graph 
     element: @$legendDiv.get(0) 
     } 


    return graph 

    # Parse a {series, points} object with new data from Dashing. 
    # 
    _parseData: (data) -> 
    series = [] 

    # Figure out what kind of data we've been passed 
    if data.series 
     dataSeries = if isString(data.series) then JSON.parse data.series else data.series 
     for subseries, index in dataSeries 
     try 
      series.push @_parseSeries subseries 
     catch err 
      console.log "Error while parsing series: #{err}" 

    else if data.points 
     points = data.points 
     if isString(points) then points = JSON.parse points 

     if points[0]? and !points[0].x? 
     # Not already in Rickshaw format; assume graphite data 
     points = graphiteDataToRickshaw(points) 

     series.push {data: points} 

    if series.length is 0 
     # No data - create a dummy series to keep Rickshaw happy 
     series.push {data: [{x:0, y:0}]} 

    @_updateColors(series) 

    # Fix any missing data in the series. 
    if Rickshaw.Series.fill then Rickshaw.Series.fill(series, null) 

    return series 

    # Parse a series of data from an array passed to `_parseData()`. 
    # This accepts both Graphite and Rickshaw style data sets. 
    _parseSeries: (series) -> 
    if series?.datapoints? 
     # This is a Graphite series 
     answer = { 
     name: series.target 
     data: graphiteDataToRickshaw series.datapoints 
     color: series.color 
     stroke: series.stroke 
     } 
    else if series?.data? 
     # Rickshaw data. Need to clone, otherwise we could end up with multiple graphs sharing 
     # the same data, and Rickshaw really doesn't like that. 
     answer = { 
     name: series.name 
     data: series.data 
     color: series.color 
     stroke: series.stroke 
     } 
    else if !series 
     throw new Error("No data received for #{@get 'id'}") 
    else 
     throw new Error("Unknown data for #{@get 'id'}. series: #{series}") 

    answer.data.sort (a,b) -> a.x - b.x 

    return answer 

    # Update the color assignments for a series. This will assign colors to any data that 
    # doesn't have a color already. 
    _updateColors: (series) -> 
    # If no colors were provided, or of there aren't enough colors, then generate a set of 
    # colors to use. 
    if [email protected] or @defaultColors?.length != series.length 
     @defaultColors = computeDefaultColors @, @node, series 

    for subseries, index in series 
     # Preferentially pick supplied colors instead of defaults, but don't overwrite a color 
     # if one was supplied with the data. 
     subseries.color ?= @assignedColors?[index] or @defaultColors[index] 
     subseries.stroke ?= @strokeColors?[index] or "#000" 

    # Convert a collection of Graphite data points into data that Rickshaw will understand. 
    graphiteDataToRickshaw = (datapoints) -> 
    answer = [] 
    for datapoint in datapoints 
     # Need to convert potential nulls from Graphite into a real number for Rickshaw. 
     answer.push {x: datapoint[1], y: (datapoint[0] or 0)} 
    answer 

    # Compute a pleasing set of default colors. This works by starting with the background color, 
    # and picking colors of intermediate luminance between the background and white (or the 
    # background and black, for light colored backgrounds.) We use the brightest color for the 
    # first series, because then multiple series will appear to blend in to the background. 
    computeDefaultColors = (self, node, series) -> 
    defaultColors = [] 

    # Use a neutral color if we can't get the background-color for some reason. 
    backgroundColor = parseColor($(node).css('background-color')) or [50, 50, 50, 1.0] 
    hsl = rgbToHsl backgroundColor 

    alpha = if self.get('defaultAlpha')? then self.get('defaultAlpha') else 1 

    if self.get('colorScheme') in ['rainbow', 'near-rainbow'] 
     saturation = (interpolate hsl[1], 1.0, 3)[1] 
     luminance = if (hsl[2] < 0.6) then 0.7 else 0.3 

     hueOffset = 0 
     if self.get('colorScheme') is 'rainbow' 
     # Note the first and last values in `hues` will both have the same hue as the background, 
     # hence the + 2. 
     hues = interpolate hsl[0], hsl[0] + 1, (series.length + 2) 
     hueOffset = 1 
     else 
     hues = interpolate hsl[0] - 0.25, hsl[0] + 0.25, series.length 
     for hue, index in hues 
     if hue > 1 then hues[index] -= 1 
     if hue < 0 then hues[index] += 1 

     for index in [0...series.length] 
     defaultColors[index] = rgbToColor hslToRgb([hues[index + hueOffset], saturation, luminance, alpha]) 

    else 
     hue = if self.get('colorScheme') is "compliment" then hsl[0] + 0.5 else hsl[0] 
     if hsl[0] > 1 then hsl[0] -= 1 

     saturation = hsl[1] 
     saturationSource = if (saturation < 0.6) then 0.7 else 0.3 
     saturations = interpolate saturationSource, saturation, (series.length + 1) 

     luminance = hsl[2] 
     luminanceSource = if (luminance < 0.6) then 0.9 else 0.1 
     luminances = interpolate luminanceSource, luminance, (series.length + 1) 

     for index in [0...series.length] 
     defaultColors[index] = rgbToColor hslToRgb([hue, saturations[index], luminances[index], alpha]) 

    return defaultColors 



# Helper functions 
# ================ 
isString = (obj) -> 
    return toString.call(obj) is "[object String]" 

# Parse a `rgb(x,y,z)` or `rgba(x,y,z,a)` string. 
parseRgbaColor = (colorString) -> 
    match = /^rgb\(\s*([\d]+)\s*,\s*([\d]+)\s*,\s*([\d]+)\s*\)/.exec(colorString) 
    if match 
    return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), 1.0] 

    match = /^rgba\(\s*([\d]+)\s*,\s*([\d]+)\s*,\s*([\d]+)\s*,\s*([\d]+)\s*\)/.exec(colorString) 
    if match 
    return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), parseInt(match[4])] 

    return null 

# Parse a color string as RGBA 
parseColor = (colorString) -> 
    answer = null 

    # Try to use the browser to parse the color for us. 
    div = document.createElement('div') 
    div.style.color = colorString 
    if div.style.color 
    answer = parseRgbaColor div.style.color 

    if !answer 
    match = /^#([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})/.exec(colorString) 
    if match then answer = [parseInt(match[1], 16), parseInt(match[2], 16), parseInt(match[3], 16), 1.0] 

    if !answer 
    match = /^#([\da-fA-F])([\da-fA-F])([\da-fA-F])/.exec(colorString) 
    if match then answer = [parseInt(match[1], 16) * 0x11, parseInt(match[2], 16) * 0x11, parseInt(match[3], 16) * 0x11, 1.0] 

    if !answer then answer = parseRgbaColor colorString 

    return answer 

# Convert an RGB or RGBA color to a CSS color. 
rgbToColor = (rgb) -> 
    if (!3 of rgb) or (rgb[3] == 1.0) 
    return "rgb(#{rgb[0]},#{rgb[1]},#{rgb[2]})" 
    else 
    return "rgba(#{rgb[0]},#{rgb[1]},#{rgb[2]},#{rgb[3]})" 

# Returns an array of size `steps`, where the first value is `source`, the last value is `dest`, 
# and the intervening values are interpolated. If steps < 2, then returns `[dest]`. 
# 
interpolate = (source, dest, steps) -> 
    if steps < 2 
    answer =[dest] 
    else 
    stepSize = (dest - source)/(steps - 1) 
    answer = (num for num in [source..dest] by stepSize) 
    # Rounding errors can cause us to drop the last value 
    if answer.length < steps then answer.push dest 

    return answer 

# Adapted from http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c 
# 
# Converts an RGBA color value to HSLA. Conversion formula 
# adapted from http://en.wikipedia.org/wiki/HSL_color_space. 
# Assumes r, g, and b are contained in the set [0, 255] and 
# a in [0, 1]. Returns h, s, l, a in the set [0, 1]. 
# 
# Returns the HSLA representation as an array. 
rgbToHsl = (rgba) -> 
    [r,g,b,a] = rgba 
    r /= 255 
    g /= 255 
    b /= 255 
    max = Math.max(r, g, b) 
    min = Math.min(r, g, b) 
    l = (max + min)/2 

    if max == min 
    h = s = 0 # achromatic 
    else 
    d = max - min 
    s = if l > 0.5 then d/(2 - max - min) else d/(max + min) 
    switch max 
     when r then h = (g - b)/d + (g < b ? 6 : 0) 
     when g then h = (b - r)/d + 2 
     when b then h = (r - g)/d + 4 
    h /= 6; 

    return [h, s, l, a] 

# Adapted from http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c 
# 
# Converts an HSLA color value to RGBA. Conversion formula 
# adapted from http://en.wikipedia.org/wiki/HSL_color_space. 
# Assumes h, s, l, and a are contained in the set [0, 1] and 
# returns r, g, and b in the set [0, 255] and a in [0, 1]. 
# 
# Retunrs the RGBA representation as an array. 
hslToRgb = (hsla) -> 
    [h,s,l,a] = hsla 
    if s is 0 
    r = g = b = l # achromatic 
    else 
    hue2rgb = (p, q, t) -> 
     if(t < 0) then t += 1 
     if(t > 1) then t -= 1 
     if(t < 1/6) then return p + (q - p) * 6 * t 
     if(t < 1/2) then return q 
     if(t < 2/3) then return p + (q - p) * (2/3 - t) * 6 
     return p 

    q = if l < 0.5 then l * (1 + s) else l + s - l * s 
    p = 2 * l - q; 
    r = hue2rgb(p, q, h + 1/3) 
    g = hue2rgb(p, q, h) 
    b = hue2rgb(p, q, h - 1/3) 

    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), a] 

Мой rickshawgraph.html находится здесь.

<h1 class="title" data-bind="title" style="color:white;"> </h1> 
<h2 class="value" data-bind="current | prepend prefix"></h2> 
<p class="more-info" data-bind="moreinfo"></p> 

ответ

2

Предлагаю вам сделать это вместо этого. В «assets/stylesheets/application.scss» указаны цвета предупреждений и опасности. Вы можете добавить туда новые цвета.

В вашей рикше графа виджета добавить

onData: (data) -> 
if data.status 
    # clear existing "status-*" classes 
    $(@get('node')).attr 'class', (i,c) -> 
     c.replace /\bstatus-\S+/g, '' 
    # add new class 
    $(@get('node')).addClass "status-#{data.status}" 

В .rb вашей работы, установите статус и отправить его.

Например:

if count < 50 
    status = 'warning' 
else 
    status = 'danger' 
end 

send_event('thread-count', { value: count, status: status }) 

В приведенном выше случае, если мой счетчик меньше чем 50, мигает желтым или же его красный цвет.

ПРИМЕЧАНИЕ: анимация не работает в Firefox. Работает только в Safari и Chrome.

0

Ответ здесь простой и приятный. Мне пришлось сделать несколько настроек, хотя в моих диаграммах использовались цвета данных и цвета данных. Проводка для использования образца.

штекер в rickhawgraph.coffee в верхней части секции Ondata:

onData: (data) -> 
    if data.status 
     # clear existing "status-*" classes 
     $(@get('node')).attr 'class', (i,c) -> 
     c.replace /\bstatus-\S+/g, '' 
     @assignedColors = "" 
     @strokeColors = "" 
     # add new class 
     $(@get('node')).addClass "status-#{data.status}" 
    else 
     @assignedColors = @get('colors').split(':') if @get('colors') 
     @strokeColors = "" 

HTML-

<li data-row="1" data-col="1" data-sizex="2" data-sizey="4"> 
    <div data-id="apdex_score_stage" data-view="Rickshawgraph" data-bind-data-min="0" data-max="1" data-title="Apdex Score (1-Excellent)" class="" data-colors="#4D4D94" data-stroke-colors="#707070" data-unstack="false" data-stroke="true" data-default-alpha="0.5" data-legend="false" data-summary-method="last"></div> 
</li> 

в работе

apdex_status="" 

     if apdex_score_values_array_min.min < 1 #yellow if one of the values is less than 1 
    apdex_status="danger" 
end 

print apdex_score_values_array_min[1] 

if apdex_score_values_array_min[1] < 1 #red if last value is less than 1 
    apdex_status="warning" 
end 

#red if last value is less than 1 
if apdex_score_values_array_min.min == 1.0 #nothing to worry, no status 
    apdex_status="" 
end 


send_event("apdex_score_stage", min: apdex_score_values_array_min.round(2), status: apdex_status, points: apdex_score_array) 

привязки данных также широко используется здесь для графика рикши.