Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functional approach #8

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
.RData
.Ruserdata
.DS_Store
Untitled.Rmd
17 changes: 17 additions & 0 deletions 01-side.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Ah, the famous `diamonds` dataset from **ggplot2**. With the **plotly** package, we can make this graph of interactive

```r
library(plotly)
library(rolldown)

p1 <- ggplot(diamonds) +
geom_freqpoly(
aes(
x = log(price),
color = clarity
)
stat = "density"
) +
facet_wrap(~cut)
ggplotly(p1)
```
8 changes: 8 additions & 0 deletions 02-side.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Residual plots for the model:

$$
log(price) = \beta_0 + \beta_1 * log(carat)
$$


We clearly need to account for the diamond clarity!
2 changes: 2 additions & 0 deletions 03-side.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Thanks to all the organizers and John Deere for hosting!

6 changes: 5 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ Authors@R: c(
)
Description: R Markdown output formats based JavaScript libraries such as
Scrollama for storytelling.
Imports: htmltools, bookdown, jsonlite
Imports:
htmltools,
rmarkdown,
bookdown,
jsonlite
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
Expand Down
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# Generated by roxygen2: do not edit by hand

S3method(print,stickyroll)
export(add_step)
export(rolldown_style_default)
export(scrollama)
export(scrollama_setup)
export(scrollama_sidebar)
export(stickyroll)
importFrom(htmltools,HTML)
importFrom(htmltools,htmlDependency)
importFrom(htmltools,tags)
2 changes: 2 additions & 0 deletions R/imports.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#' @import htmltools
#' @import rmarkdown
97 changes: 97 additions & 0 deletions R/stickyroll.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#' @param
#' @export
#' @examples
#'
#' library(plotly)
#'
#' p1 <- ggplotly(qplot(1:10))
#' p2 <- ggplotly(qplot(1:10, 1:10))
#'
#' rolldown() %>%
#' add_step("Look here", p1) %>%
#' add_step("Another one", p2)

stickyroll <- function() {
structure(list(), class = "stickyroll")
}

#' Add a 'step' to the
#' @param
#' @export

add_step <- function(x, sidebar, main) {
sidebar <- as_html(sidebar)
main <- htmltools::as.tags(main)
if (!inherits(main, c("htmlwidget", "shiny.tag", "shiny.tag.list"))) {
warning("main content must be an htmlwidget or html content")
}

id <- basename(tempfile())
x[[length(x) + 1]] <- list(
sidebar = htmltools::tags$div(id = id, class = "step", sidebar),
main = htmltools::tags$div(
id = paste0("main-", id),
style = "opacity: 0",
main
)
)

x
}

#' @export
print.stickyroll <- function(x, ...) {
content <- htmltools::tags$main(htmltools::tags$section(
id = "scrolly",
do.call(htmltools::tags$article, lapply(x, function(x) x$sidebar)),
do.call(figure_div, lapply(x, function(x) x$main))
))

content <- htmltools::tagList(
content,
rolldown_deps(),
htmltools::tags$script(
paste(readLines(file.path(pkg_resource("stickyroll"), "stickyroll.js")), collapse = "\n")
)
)

print(htmltools::browsable(content))

invisible(content)
}

figure_div <- function(...) {
htmltools::tags$div(..., class = "figure")
}

as_html <- function(x) {
if (!file.exists(x)) stop("File does not exist")
tmpfile <- tempfile(fileext = ".html")
rmarkdown::render(x, output_file = tmpfile, output_format = rmarkdown::html_document())
htmltools::HTML(paste(readLines(tmpfile), collapse = "\n"))
}


rolldown_deps <- function() {
list(
htmltools::htmlDependency(
name = "d3",
version = "5.9.1",
pkg_resource('d3'),
script = "d3.min.js"
),
htmltools::htmlDependency(
'scrollama',
'2.0.0',
pkg_resource('scrollama'),
script = 'scrollama.min.js'
),
htmltools::htmlDependency(
name = "stickyroll",
version = "0.0.1",
pkg_resource('stickyroll'),
stylesheet = "stickyroll.css",
all_files = FALSE
)
)
}
31 changes: 31 additions & 0 deletions example.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
library(plotly)
library(leaflet)
library(rolldown)

p1 <- ggplot(diamonds, aes(x = log(price), color = clarity)) +
geom_freqpoly(stat = "density") +
facet_wrap(~cut)

gg1 <- ggplotly(p1, width = 1200, height = 600) %>%
config(displayModeBar = FALSE)

m <- lm(log(price) ~ log(carat), data = diamonds)
diamonds <- modelr::add_residuals(diamonds, m)
p <- ggplot(diamonds, aes(x = clarity, y = resid, color = clarity)) +
ggforce::geom_sina(alpha = 0.1) +
stat_summary(fun.data = "mean_cl_boot", color = "black") +
facet_wrap(~cut)

gg2 <- ggplotly(p, width = 1200, height = 600) %>%
toWebGL() %>%
config(displayModeBar = FALSE)

map <- leaflet(height = 600) %>%
addTiles(attribution = '') %>%
addMarkers(-93.7418395, 41.6745487, popup = "Thanks John Deere!") %>%
setView(-93.74, 41.675, 15)

stickyroll() %>%
add_step("01-side.Rmd", gg1) %>%
add_step("02-side.Rmd", gg2) %>%
add_step("03-side.Rmd", map)
2 changes: 2 additions & 0 deletions inst/resources/d3/d3.min.js

Large diffs are not rendered by default.

67 changes: 67 additions & 0 deletions inst/resources/rolldown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
* {
box-sizing: border-box;
}

html,
body {
margin: 0;
padding: 0;
}

body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
min-height: 1280px;
color: #3b3b3b;
font-size: 24px;
}

p,
h1,
h2,
h3,
h4,
a {
margin: 0;
font-weight: 400;
}

#scrolly {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
background-color: #f3f3f3;
padding: 1rem;
}

#scrolly > * {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}

#scrolly article {
position: relative;
padding: 0 1rem;
max-width: 30rem;
}

#scrolly div.figure div {
position: -webkit-sticky;
position: sticky;
min-width: 100%;
}

#scrolly .step {
margin: 0 auto 2rem auto;
}

#scrolly .step:last-child {
margin-bottom: 0;
}

#scrolly .step div {
padding: 1rem;
font-size: 1.5rem;
}
66 changes: 66 additions & 0 deletions inst/resources/rolldown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
var main = d3.select('main');
var scrolly = main.select('#scrolly');
var figure = scrolly.select('div.figure');
var article = scrolly.select('article');
var step = article.selectAll('.step');

// initialize the scrollama
var scroller = scrollama();

// generic window resize listener event
function handleResize() {
// 1. update height of step elements
var stepH = Math.floor(window.innerHeight);
step.style('min-height', stepH + 'px');

// 3. tell scrollama to update new element dimensions
scroller.resize();
}

// scrollama event handlers
function handleStepEnter(response) {
console.log("enter", response);
var id = "main-" + response.element.id;
var height = document.getElementById(id).clientHeight;
var top = (window.innerHeight - height) / 2;
var top = Math.max(0, top);

var el = d3.select("#" + id);
el.style("top", top + "px");

el
.transition(400)
.style("opacity", 1);
}

function handleStepExit(response) {
console.log("exit", response);
var id = "main-" + response.element.id;
var el = d3.select("#" + id);

el
.transition(400)
.style("opacity", 0);
}

function init() {
// 1. force a resize on load to ensure proper dimensions are sent to scrollama
handleResize();

// 2. setup the scroller passing options
// this will also initialize trigger observations
// 3. bind scrollama event handlers (this can be chained like below)
scroller.setup({
step: '#scrolly article .step',
offset: 0.5
})
.onStepEnter(handleStepEnter)
.onStepExit(handleStepExit);

// setup resize event
window.addEventListener('resize', handleResize);
}


// kick things off
setTimeout(function() { init(); }, 10);
67 changes: 67 additions & 0 deletions inst/resources/stickyroll/stickyroll.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
* {
box-sizing: border-box;
}

html,
body {
margin: 0;
padding: 0;
}

body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
min-height: 1280px;
color: #3b3b3b;
font-size: 24px;
}

p,
h1,
h2,
h3,
h4,
a {
margin: 0;
font-weight: 400;
}

#scrolly {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
background-color: #f3f3f3;
padding: 1rem;
}

#scrolly > * {
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
}

#scrolly article {
position: relative;
padding: 0 1rem;
max-width: 30rem;
}

#scrolly div.figure div {
position: -webkit-sticky;
position: sticky;
min-width: 100%;
}

#scrolly .step {
margin: 0 auto 2rem auto;
}

#scrolly .step:last-child {
margin-bottom: 0;
}

#scrolly .step div {
padding: 1rem;
font-size: 1.5rem;
}
Loading