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.js库制作条形图。我坚持遵循基本的事情,甚至不需要花费太多时间。我无法掌握d3的功能。你可以玩小提琴,任何帮助都会非常有益:
我想在一个x值上分组四个不同的条形图。就像'0'一样,它们中有四个不同于当前的一个,它将所有东西合并为两个
2.将x轴的内容从数字改为星期一到星期五的天数
3.对于y轴,我试图显示值,而不是20000,它应显示20k,并且栏应该在动态创建时识别它。这可能吗?
任何帮助都会非常有益。我无法理解。
答案 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/(我删除了某些内容,但您可以稍后再添加它们)