I am working on implementing some D3.js visualizations in a Shiny App through the r2d3 package.
I have some text and a d3Output() in the same fluidRow, when I run the Shiny App, the text & the bar do not get outputted on the same row. See here: Shiny App Output
Upon further inspection - I see that the div container created by the d3Output() function contains a margin extending to the end of the row. See here: div margin
I would like to understand if its possible to get both the text "fluidRow with Bar" and the d3Ouput() to print on the same row.
Is it possible to remove the margin created by the d3Output()'s div container?
Please find some minimal reproduceable code below
library(r2d3)
library(shiny)
ui <- fluidPage(
fluidRow('Some text in first fluidRow'),
fluidRow('fluidRow with Bar', d3Output('d', width = '400px', height = '30px'))
)
server <- function(input, output, session) {
output$d <- renderD3({
r2d3(data=25, script = "chart.js")
})
}
shinyApp(ui, server)
chart.js
var t1Bar = 400; //width
var t2Bar = data * 4; // data is value between 0 & 100
var t1Color = '#D5D7D2';
var t2Color = '#75DF0A';
svg.attr('width', 400);
svg.attr('height', 30);
// Right to Left
svg.append('rect')
.attr('id', 't1')
.attr('width', 0)
.attr('x', t1Bar)
.attr('y', 0)
.attr('height', 30)
.attr('fill', t1Color);
// Left to Right
svg.append('rect')
.attr('id', 't2')
.attr('width', 0)
.attr('x', 0)
.attr('y', 0)
.attr('height', 30)
.attr('fill', t2Color);
// Transitions
svg.select('#t1')
.attr("x", t1Bar)
.transition()
.delay(0)
.duration(500)
.attr("width", t1Bar - t2Bar)
.attr("x", t2Bar)
svg.select('#t2')
.transition()
.delay(0)
.duration(500)
.attr('width', t2Bar);
Related
I am writing a report in R Markdown, it contains multiple animated highcharts.
The animations work fine, however they all run when the html page loads (after knitting), instead of when the user scrolls to it, so essentially the animation is pointless as the user never sees it.
An example of an animated chart is at the bottom of this question.
Is there a way to make it animate when it appears? All the examples I have found use jsfiddle and I am using R Markdown.
Many thanks
library(dplyr)
library(stringr)
library(purrr)
n <- 5
set.seed(123)
df <- data.frame(x = seq_len(n) - 1) %>%
mutate(
y = 10 + x + 10 * sin(x),
y = round(y, 1),
z = (x*y) - median(x*y),
e = 10 * abs(rnorm(length(x))) + 2,
e = round(e, 1),
low = y - e,
high = y + e,
value = y,
name = sample(fruit[str_length(fruit) <= 5], size = n),
color = rep(colors, length.out = n),
segmentColor = rep(colors2, length.out = n)
)
hcs <- c("line") %>%
map(create_hc)
hcs
Ok, I worked out how to do it myself, going to post the answer here in case someone stumbles across this post in the future.
First of all, I found NOTHING on how to do this in R.
So, I decided to do this in JS, AFTER I had knitted the R Markdown document to HTML, as it wouldn't work in R Markdown.
Once it is a HTML file, open it in TextEdit or Notepad, and add the following code just before one of the charts:
<script>
(function (H) {
var pendingRenders = [];
// https://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport/7557433#7557433
function isElementInViewport(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (
window.innerHeight ||
document.documentElement.clientHeight
) &&
rect.right <= (
window.innerWidth ||
document.documentElement.clientWidth
)
);
}
H.wrap(H.Series.prototype, 'render', function deferRender(proceed) {
var series = this,
renderTo = this.chart.container.parentNode;
// It is appeared, render it
if (isElementInViewport(renderTo) || !series.options.animation) {
proceed.call(series);
// It is not appeared, halt renering until appear
} else {
pendingRenders.push({
element: renderTo,
appear: function () {
proceed.call(series);
}
});
}
});
function recalculate() {
pendingRenders.forEach(function (item) {
if (isElementInViewport(item.element)) {
item.appear();
H.erase(pendingRenders, item);
}
});
}
if (window.addEventListener) {
['DOMContentLoaded', 'load', 'scroll', 'resize']
.forEach(function (eventType) {
addEventListener(eventType, recalculate, false);
});
}
}(Highcharts));
</script>
The charts then animate when you scroll to them, rather than when you open the HTML file.
Note: The JSFIDDLE I got the code from was from here:
https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/studies/appear/
I need to add multiple arcs to one svg elements (every one got different animation). I need to fill them with radial gradients, but now, the center of radial gradient is not in the centre of whole svg element, but in the centre of specific arc. How it looks now. First i make all of the gradients i need in defs.
var tmpgrad=null;
for( var k = 0; k<data.length;k++){
tmpgrad = grads
.append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("r", "50%")
.attr("id", function(d, i) { return "grad" + k; });
tmpgrad
.append("stop")
.attr("offset", "0%")
.style("stop-color", data[k].endColor)
.style("stop-opasity", 0);
tmpgrad
.append("stop")
.attr("offset", "100%")
.style("stop-color",data[k].startColor)
.style("stop-opasity", 1);
}
Then I make my arcs with deferent options:
var width= 1200;
var height=360;
var oArc = d3.svg.arc()
.innerRadius(iRadius)
.outerRadius(oRadius);
var oPie = d3.layout.pie()
.startAngle( sA )
.endAngle( eA )
.sort(null);
var group = svgDrawer.append("g");
var oPath = group.selectAll("g")
.data(oPie([0,200]))
.enter()
.append("path");
oPath
.attr("fill",function(d, i){ if(i==1){ i=0; } else { i = id; } return "url(#grad" + i +")"; })
.attr("d",oArc)
.each(function(d){
this._current = d;
});
But in the result as i said before i got the separated arcs with gradient starting in the middle of every arc element (path actually). How can i solve that.
I am the beginner in svg and javascript subject.
I started to use dc.js Library to create all kinds of graphs and I bumped into a problem when I was trying to create a Geo Choropleth map using dc.js and couldn't add the ability to zoom and move the map.
All the examples I saw were using d3 and svg.. but once I used those examples, I couldn't use the data of dc.dimention and all the crossfilter calculations.
for example my code is:
d3.json("world-countries.json", function (statesJson) {
geoChart.width(1000)
.height(600)
.dimension(countryDim)
.projection(d3.geo.mercator()
.scale((960 + 1) / 4 )
.translate([960 / 4, 960 / 4])
.precision(.1))
.group(countryGroup)
.colors(d3.scale.quantize().range(["#E2F2FF","#C4E4FF","#9ED2FF","#81C5FF","#6BBAFF","#51AEFF","#36A2FF","#1E96FF","#0089FF","#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function(d){ returnd ?geoChart.colors()(d) :'#ccc'; })
.overlayGeoJson(statesJson.features,"state",function(d){
return d.properties.name;
})
.title(function (d) {
return "State: " + d.key + (d.value ? d.value : 0) + "Impressions";
});
Which works nicely, but I want to add the zoom effect and to be able to move the map. how can I do that?!?!
thanks in advance!
So, the answer is:
var width = 960,
height = 400;
var projection = d3.geo.mercator()
.scale(200)
.translate([width/2, height]);
function zoomed() {
projection
.translate(d3.event.translate)
.scale(d3.event.scale);
geoChart.render();
}
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scale(projection.scale())
.scaleExtent([height/2, 8 * height])
.on("zoom", zoomed);
var svg = d3.select("#geo-chart")
.attr("width", width)
.attr("height", height)
.call(zoom);
geoChart
.projection(projection)
.width(1000)
.height(400)
.transitionDuration(1000)
.dimension(countryDim)
.group(ctrGroup)
.filterHandler(function(dimension, filter){
dimension.filter(function(d) {return geoChart.filter() != null ? d.indexOf
(geoChart.filter()) >= 0 : true;}); // perform filtering
return filter; // return the actual filter value
})
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF",
"#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? geoChart.colors()(d) : '#ccc'; })
.overlayGeoJson(statesJson.features, "state", function (d) { return d.id; })
.title(function (d) {
return "State: " + d.key + " " + (d.value ? d.value : 0) + " Impressions";
});
I am using the blur effect on the d3 map as given here: http://geoexamples.blogspot.in/2014/01/d3-map-styling-tutorial-ii-giving-style.html?
But after using this method (because of how the data is loaded..using datum) my zoom functionality behaves randomly. Irrespective of where I click it zooms to the same point. Also, the animations have become very slow after using the filter.
Is there any other way to achieve blur? Or a solution to this problem?
Any help?
Thanks.
This is the code for the world creation in case when filtering is required (use of datum as per the code on the above site).
d3.json("world-110m2.json", function(error, world) {
g.insert("path")
.datum(topojson.feature(world, world.objects.land))
.attr("d", path);
g.insert("path")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("d", path)
.append("path");
g.selectAll("path")
.on("click", click);})
This is the code used in case filtering is not required (No use of datum - maybe the datum is causing the issue)
d3.json("world-110m2.json", function(error,topology) {
g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries)
.geometries)
.enter()
.append("path")
.attr("d",path)
.on("click", click);)}
This is the zoom function: got the code from here: http://bl.ocks.org/mbostock/2206590
function click(d) {
var x, y, k;
var centered;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
if (active === d) return reset();
g.selectAll(".active").classed("active", false);
d3.select(this).classed("active", active = d);
var b = path.bounds(d);
g.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
The blur filter consumes lots of resources, as indicated in the post. Speciallly if you combine it with other filters.
One solution would be using Canvas instead of SVG. Here you have some filters using the Canvas element. It should be possible to achieve the same result.
I can't find why the zoom stops working, but the performance is slower because you use all the data, so you are applying the filter to all the data instead of using only the part of the word you are showing, so you are using a much bigger image when you zoom.
I have this Line Chart here:
jsfiddle.net/yfqQ4/
My Problem now is, that I don't get a proper grid in the background working with a legend (y and x-Axis) like this: http://lab.creativebrains.net/linechart.png
Can anybody can post me a code snippet how I should implement it or something like that?
Thanks!
I would suggest to use d3.svg.axis().scale() to tie up the grid to your coordinates. I drew a quick example based on your code: http://jsfiddle.net/yfqQ4/5/
The gist is to use the existing scales, x and y, and to use ticks as grid. Notice that height and width are the variable defining the size of your container. Here is the relevant code:
var numberOfTicks = 6;
var yAxisGrid = d3.svg.axis().scale(y)
.ticks(numberOfTicks)
.tickSize(width, 0)
.tickFormat("")
.orient("right");
var xAxisGrid = d3.svg.axis().scale(x)
.ticks(numberOfTicks)
.tickSize(-height, 0)
.tickFormat("")
.orient("top");
svg.append("g")
.classed('y', true)
.classed('axis', true)
.call(yAxisGrid);
svg.append("g")
.classed('x', true)
.classed('axis', true)
.call(xAxisGrid);
You can draw background grid like this:
//vertical lines
svg.selectAll(".vline").data(d3.range(26)).enter()
.append("line")
.attr("x1", function (d) {
return d * 20;
})
.attr("x2", function (d) {
return d * 20;
})
.attr("y1", function (d) {
return 0;
})
.attr("y2", function (d) {
return 500;
})
.style("stroke", "#eee");
// horizontal lines
svg.selectAll(".vline").data(d3.range(26)).enter()
.append("line")
.attr("y1", function (d) {
return d * 20;
})
.attr("y2", function (d) {
return d * 20;
})
.attr("x1", function (d) {
return 0;
})
.attr("x2", function (d) {
return 500;
})
.style("stroke", "#eee");
You can see here how it works with your jsfiddle (updated):
http://jsfiddle.net/cuckovic/Phzvy/