D3.js条形图

时间:2016-06-11 19:13:33

标签: javascript html css d3.js bar-chart

HTML

<div id="searchVolume"></div>  

CSS

#tooltip {
    position: absolute;
    width: 50px;
    height: auto;
    padding: 10px;
    background-color: white;
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;
    -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
    pointer-events: none;
}
#tooltip.hidden {
    display: none;
}
#tooltip p {
    margin: 0;
    font-family: sans-serif;
    font-size: 12px;
    line-height: 16px;
}
.indent{
    padding-left: 5px;
}

rect {
    -moz-transition: all 0.3s;
    -webkit-transition: all 0.3s;
    -o-transition: all 0.3s;
    transition: all 0.3s;
}
rect:hover{
    fill: orange;
}
.axis path,
.axis line {
    fill: none;
    stroke: black;
    shape-rendering: crispEdges;
}
.axis text {
    font-family: sans-serif;
    font-size: 11px;
}  

脚本

var margin = {top: 25, right: 40, bottom: 35, left: 85},
                w = 500 - margin.left - margin.right,
                h = 350 - margin.top - margin.bottom;
var padding = 10;

var colors =    [ ["Morning", "#F64BEE"],
                  ["Midday", "#25B244"],
          ["Afternoon", "#2BA3F4"],
          ["Evening","#FD7680"]];

var dataset = [
                { "Morning": 1400000, "Midday": 673000, "Afternoon": 43000, "Evening":50000},
                { "Morning": 165000, "Midday": 160000, "Afternoon": 21000, "Evening":23000 },
                {"Morning": 550000, "Midday": 301000, "Afternoon": 34000, "Evening":43000},
        {"Morning": 550320, "Midday": 351000, "Afternoon": 24000, "Evening":38000},
        {"Morning": 55000, "Midday": 3010, "Afternoon": 24000, "Evening":43054},
        {"Morning": 750000, "Midday": 401000, "Afternoon": 84000, "Evening":42100},
        {"Morning": 578000, "Midday": 306000, "Afternoon": 54000, "Evening":43400},
                            ];

var xScale = d3.scale.ordinal()
                .domain(d3.range(dataset.length))
                .rangeRoundBands([0, w], 0.05); 
// ternary operator to determine if global or local has a larger scale
var yScale = d3.scale.linear()
                .domain([0, d3.max(dataset, function(d) { return Math.max(d.Morning,d.Midday,d.Afternoon,d.Evening);})]) 
                .range([h, 0]);
var xAxis = d3.svg.axis()
                .scale(xScale)
                .orient("bottom");
var yAxis = d3.svg.axis()
                .scale(yScale)
                .orient("left")
                .ticks(5);




var commaFormat = d3.format(',');

//SVG element
var svg = d3.select("#searchVolume")
            .append("svg")
            .attr("width", w + margin.left + margin.right)
            .attr("height", h + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Graph Bars
var sets = svg.selectAll(".set") 
    .data(dataset) 
    .enter()
    .append("g")
    .attr("class","set")
    .attr("transform",function(d,i){
         return "translate(" + xScale(i) + ",0)";
     })
    ;

sets.append("rect")
    .attr("class","Morning")
    .attr("width", xScale.rangeBand()/4)
    .attr("y", function(d) {
        return yScale(d.Morning);
    })
    .attr("x", xScale.rangeBand()/4)
    .attr("height", function(d){
        return h - yScale(d.Morning);
    })
    .attr("fill", colors[0][1])
    .append("text")
   .text(function(d) {
        return commaFormat(d.Morning);
   })
   .attr("text-anchor", "middle")
   .attr("x", function(d, i) {
        return xScale(i) + xScale.rangeBand() / 4;
   })
   .attr("y", function(d) {
        return h - yScale(d.Morning) + 14;
   })
   .attr("font-family", "sans-serif") 
   .attr("font-size", "11px")
   .attr("fill", "black")
    ;

sets.append("rect")
    .attr("class","Midday")
    .attr("width", xScale.rangeBand()/4)
    .attr("y", function(d) {
        return yScale(d.Midday);
    })
    .attr("height", function(d){
        return h - yScale(d.Midday);
    })
    .attr("fill", colors[1][1])
    .append("text")
    .text(function(d) {
        return commaFormat(d.Midday);
    })
    .attr("text-anchor", "middle")
    .attr("x", function(d, i) {
        return xScale(i) + xScale.rangeBand() / 4;
    })
    .attr("y", function(d) {
        return h - yScale(d.Midday) + 14;
    })
    .attr("font-family", "sans-serif") 
    .attr("font-size", "11px")
    .attr("fill", "red")
    ;

  sets.append("rect")
    .attr("class","Afternoon")
    .attr("width", xScale.rangeBand()/4)
    .attr("y", function(d) {
        return yScale(d.Afternoon);
    })
    .attr("height", function(d){
        return h - yScale(d.Afternoon);
    })
    .attr("fill", colors[2][1])
    .append("text")
    .text(function(d) {
        return commaFormat(d.Afternoon);
    })
    .attr("text-anchor", "middle")
    .attr("x", function(d, i) {
        return xScale(i) + xScale.rangeBand() / 4;
    })
    .attr("y", function(d) {
        return h - yScale(d.Afternoon) + 14;
    })
    .attr("font-family", "sans-serif") 
    .attr("font-size", "11px")
    .attr("fill", "red")
    ; 

  sets.append("rect")
    .attr("class","Evening")
    .attr("width", xScale.rangeBand()/4)
    .attr("y", function(d) {
        return yScale(d.Evening);
    })
    .attr("height", function(d){
        return h - yScale(d.Evening);
    })
    .attr("fill", colors[3][1])
    .append("text")
    .text(function(d) {
        return commaFormat(d.Evening);
    })
    .attr("text-anchor", "middle")
    .attr("x", function(d, i) {
        return xScale(i) + xScale.rangeBand() / 4;
    })
    .attr("y", function(d) {
        return h - yScale(d.Evening) + 14;
    })
    .attr("font-family", "sans-serif") 
    .attr("font-size", "11px")
    .attr("fill", "red")
    ;
// xAxis
svg.append("g") // Add the X Axis
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (h) + ")")
    .call(xAxis)
        ;
// yAxis
svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(0 ,0)")
    .call(yAxis)
    ;
// xAxis label
svg.append("text") 
    .attr("transform", "translate(" + (w / 4) + " ," + (h + margin.bottom - 5) +")")
    .style("text-anchor", "middle")
    .text("Keyword");
//yAxis label
svg.append("text")
        .attr("transform", "rotate(-90)")
        .attr("y", 0 - margin.left)
        .attr("x", 0 - (h / 4))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
        .text("Searches");

// Title
svg.append("text")
        .attr("x", (w / 2))
        .attr("y", 0 - (margin.top / 2))
        .attr("text-anchor", "middle")
        .style("font-size", "16px")
        .style("text-decoration", "underline")
        .text("Weekly Consumption");


// add legend   
var legend = svg.append("g")
        .attr("class", "legend")
        //.attr("x", w - 65)
        //.attr("y", 50)
        .attr("height", 100)
        .attr("width", 100)
        .attr('transform', 'translate(-20,50)');

var legendRect = legend.selectAll('rect').data(colors);

legendRect.enter()
    .append("rect")
    .attr("x", w - 65)
    .attr("width", 10)
    .attr("height", 10);

legendRect
    .attr("y", function(d, i) {
        return i * 20;
    })
    .style("fill", function(d) {
        return d[1];
    });

var legendText = legend.selectAll('text').data(colors);

legendText.enter()
    .append("text")
    .attr("x", w - 52);

legendText
    .attr("y", function(d, i) {
        return i * 20 + 9;
    })
    .text(function(d) {
        return d[0];
    });  

D3 Fiddle

在上面的小提琴中,我尝试使用d3.js库制作条形图。我坚持遵循基本的事情,甚至不需要花费太多时间。我无法掌握d3的功能。你可以玩小提琴,任何帮助都会非常有益:
我想在一个x值上分组四个不同的条形图。就像'0'一样,它们中有四个不同于当前的一个,它将所有东西合并为两个 2.将x轴的内容从数字改为星期一到星期五的天数 3.对于y轴,我试图显示值,而不是20000,它应显示20k,并且栏应该在动态创建时识别它。这可能吗?

任何帮助都会非常有益。我无法理解。

1 个答案:

答案 0 :(得分:2)

主要问题是您开始正确地加入数据,但之后您开始使用package tonnyferiandi.lastproject; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Base64; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; public class tab_feed extends Fragment { private List<tab_feedItem> feedsList; private RecyclerView mRecyclerView; private tab_feedAdapter adapter; private ProgressBar progressBar; private Bitmap photone; private Bitmap photono; private boolean load = true; public int page=0; public View v; public String url; LinearLayoutManager layoutManager; int pastVisiblesItems, visibleItemCount, totalItemCount; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { v = inflater.inflate(R.layout.fragment_tab_feed, container, false); mRecyclerView = (RecyclerView) v.findViewById(R.id.recycler_view); mRecyclerView.setHasFixedSize(true); layoutManager = new LinearLayoutManager(getActivity()); mRecyclerView.setLayoutManager(layoutManager); progressBar = (ProgressBar) v.findViewById(R.id.progress_bar); progressBar.setVisibility(View.VISIBLE); feedsList = new ArrayList<>(); // Downloading data from below url url = "http://tukoe.com/feed/getfeed.php?page="+String.valueOf(page); new AsyncHttpTask().execute(url); return v; } public class AsyncHttpTask extends AsyncTask<String, Void, Integer> { @Override protected void onPreExecute() { //setProgressBarIndeterminateVisibility(true); progressBar.setVisibility(View.VISIBLE); } @Override protected Integer doInBackground(String... params) { Integer result = 0; HttpURLConnection urlConnection; try { URL url = new URL(params[0]); urlConnection = (HttpURLConnection) url.openConnection(); int statusCode = urlConnection.getResponseCode(); // 200 represents HTTP OK if (statusCode == 200) { BufferedReader r = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = r.readLine()) != null) { response.append(line); } parseResult(response.toString()); result = 1; // Successful } else { result = 0; //"Failed to fetch data!"; } } catch (Exception e) { Log.d("RecyclerView", e.getLocalizedMessage()); } return result; //"Failed to fetch data!"; } @Override protected void onPostExecute(Integer result) { // Download complete. Let us update UI progressBar.setVisibility(View.GONE); if (result == 1) { scroll(); page++; } else { Toast.makeText(getActivity().getApplicationContext(), "Failed to fetch data!", Toast.LENGTH_SHORT).show(); } } } private void parseResult(String result) { try { JSONObject response = new JSONObject(result); JSONArray posts = response.optJSONArray("result"); for (int i = 0; i < posts.length(); i++) { JSONObject post = posts.optJSONObject(i); tab_feedItem item = new tab_feedItem(); if(!post.getString("title").equals(null)){ load = true; } item.setTitle(post.optString("title")); photone = decodeBase64(post.optString("photo")); int outWidth; int outHeight; int inWidth = photone.getWidth(); int inHeight = photone.getHeight(); if(inWidth > inHeight){ outWidth = 768; outHeight = (inHeight * 768) / inWidth; } else { outHeight = 768; outWidth = (inWidth * 768) / inHeight; } photono = Bitmap.createScaledBitmap(photone, outWidth, outHeight, false); item.setThumbnail(photono); feedsList.add(item); } } catch (JSONException e) { e.printStackTrace(); } } public static Bitmap decodeBase64(String input){ byte[] decodeByte = Base64.decode(input, 0); return BitmapFactory.decodeByteArray(decodeByte, 0, decodeByte.length); } public void scroll(){ adapter = new tab_feedAdapter(getActivity(), feedsList); mRecyclerView.setAdapter(adapter); mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (dy > 0) //check for scroll down { visibleItemCount = mRecyclerView.getChildCount(); totalItemCount = layoutManager.getItemCount(); pastVisiblesItems = layoutManager.findFirstVisibleItemPosition(); if (load) { if ((visibleItemCount + pastVisiblesItems) >= totalItemCount) { load = false; url = "http://tukoe.com/feed/getfeed.php?page="+String.valueOf(page); new AsyncHttpTask().execute(url); } } } } }); } }函数将数据连接到子元素中,从而开始执行一些手动操作。让我解释一下:

首先,我们需要两个x轴刻度,一个用于保存我们的日期域,另一个用于保持我们的时域使用data天数。

rangeRoundBands

让我们在设置比例时解决x轴格式问题。我创建了一个天数组,在var day_scale = d3.scale.ordinal() .domain(d3.range(dataset.length)) .rangeRoundBands([0, w], 0.05); var time_scale = d3.scale.ordinal(); time_scale.domain(['Morning', 'Midday', 'Afternoon', 'Evening']) .rangeRoundBands([0, day_scale.rangeBand()]); 函数中,我们可以根据传递的数据索引返回数组中的值。

tickFormat

现在是y轴格式,我们可以使用var days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; var day_axis = d3.svg.axis() .scale(day_scale) .orient("bottom") .tickFormat(function(d,i) { return days[i]; }); 更多信息来解决这个问题here

d3.formatPrefix

现在让我们跳过我们的svg和axis配置,以获得数据连接问题:

var prefix = d3.formatPrefix(1.21e9);
var searches_axis = d3.svg.axis()
  .scale(searches_scale)
  .orient("left")
  .ticks(5)
  .tickFormat(function(d) {
        var prefix = d3.formatPrefix(d);
        return prefix.scale(d) + prefix.symbol;
  });

现在我们的日期组正确定位,我们可以附加我们的时间数据。

var day_groups = svg.selectAll(".day-group")
  .data(dataset) // join data to our selection
  .enter().append("g")
  .attr("class", function(d, i) {
    return 'day-group day-group-' + i;
  })
  .attr("transform", function(d, i) {
    // position a g element with our day_scale and data index
    return "translate(" + day_scale(i) + ",0)";
  });

现在让我们添加我们的作品!

var times_g = day_groups.selectAll(".time-group")
  .data(function(d) {
    // this is the tricky part, we are creating an array of
    // objects with key (time...'Morning', 'Midday', 'Afternoon', 'Evening')
    // and value (the value of the time)
    // in order to create a time group for each time event
    return Object.keys(d).map(function(key) {
      return {
        key: key,
        value: d[key]
      }
    });
  })
  .enter().append("g")
  .attr("class", function(d) {
    return 'time-group time-group-' + d.key;
  })
  .attr("transform", function(d) {
    // use our time scale to position
    return "translate(" + time_scale(d.key) + ",0)";
  });

映射颜色对象:

var rects = times_g.selectAll('.rect')
  .data(function(d) {
    // use as data our object
    return [d];
  })
  .enter().append("rect")
  .attr("class", "rect")
  .attr("width", time_scale.rangeBand()) // get width of rect based in our time_scale
  .attr("x", function(d) {
    return 0; // returning 0 since the group is in charge of positioning
  })
  .attr("y", function(d) {
    return searches_scale(d.value); // use our y_scale
  })
  .attr("height", function(d) {
    return h - searches_scale(d.value); // use our y_scale
  })
  .style("fill", function(d) {
    return colors[d.key]; // map colors by using an object
  });

如果您对此处的工作原理有任何疑问,请更新jsfiddle:https://jsfiddle.net/706gsjfg/3/(我删除了某些内容,但您可以稍后再添加它们)

相关问题