使用 timevis 绘制时间轴

· 2937字 · 6分钟

vis.js 是一个 JavaScript 可视化库,其中的 vis-network 模块被封装成了 R 中的 visNetwork 包,而 vis-timeline 模块被封装成了 R 中的 timevis 包。

本文参考了三部分:

  1. timevis 包的官方
  2. vis.js 的官方文档
  3. miscosoft copilot 的答案。

基础时间轴(timevis) 🔗

timevis 绘制时间轴的方式非常简单,加载包以后执行timevis(data)就可以得到一个配置了许多默认选项的时间轴。数据中可输入以下字段,仅 start 和 content 为必选项。

  • id:条目唯一 id,数据框中每一行称为一个条目(item),对应时间轴上一个时点或一个时期。
  • start:开始日期,必须是合法的日期格式,如'yyyy-mm-dd',如果仅写年份的数字会被默认为秒数。
  • end:结束日期,若有则展示时期,没有则展示时点。
  • content:每行数据展示的内容标签,支持纯文本或 HTML 编码。
  • title:鼠标悬停在每个条目时的提示内容,仅支持纯文本。
  • type:条目的类型。可以是“box”(默认)、“point”、“range”或“background”。“box”和“point”类型只需要开始日期,“range”和“background”类型需要开始日期和结束日期。
  • editable:如果为 TRUE,则可以使用鼠标操作项目。如果设置了全局 editable 配置选项,则此设置会覆盖全局设置。可编辑的项目可以通过单击来删除或修改其开始/结束日期。
  • group:组的 id。vis.js 仅支持横向时间轴,组相当于在基础时间轴的基础上增加了一个纵向轴,所有相同组 id 的条目将放在同一行上。更多细节在另一个数据框 groups 中设置。

时点时间轴 🔗

只有开始日期、没有结束日期的条目在时间轴上显示为一个时点,默认类型为“box”。

library(timevis)

df1 <- data.frame(
  id = 1:3,
  start = c("1587-01-01", "1768-01-01", "1984-01-01"),
  content = c('黄仁宇《万历十五年》', '孔飞力《叫魂:1768年中国妖术大恐慌》', '乔治·奥威尔《1984》'),
  title = c('英文原版1981年出版', '英文原版1976年出版', '英文原版1949年出版'),
  type = c('box', 'box', 'point')
)

timevis(
  df1,
  options = list(format = list(
    minorLabels = list(year = "YYYY年"),
    majorLabels = list(year = "YYYY年")
  ))
)

时期时间轴 🔗

既有开始日期、也有结束日期的条目在时间轴上显示为一个时期,默认类型为“默认为range”。

时期时间轴有一个问题,如果某个条目的时期范围相对于时间轴的当前展示范围太小,那么条目的内容会显示不全。

df2 <- data.frame(
  id = 1:5,
  start = c(
    "2025-06-20",
    "2025-08-01",
    "2025-10-01",
    "2025-12-30",
    "2026-02-07"
  ),
  end = c(
    "2025-06-30",
    "2025-08-17",
    "2025-10-08",
    "2026-01-04",
    "2026-02-08"
  ),
  content = c('A事件', 'B事件', 'C事件', "D事件", "E事件")
)

timevis(df2, options = list(format = list(
  minorLabels = list(month = "M月", day = "D日"),
  majorLabels = list(month = "YYYY年MM月")
)))

分组时间轴 🔗

  • 单一分组
df1$group <- c(1, 2, 2)

timevis(df1, groups = data.frame(
  id = 1:2,
  content = c("第一组", "第二组"),
  title = c('第一组的提示', '第二组的提示')
))

  • 嵌套分组
df2$group <- 1:5

timevis(
  df2,
  groups = data.frame(
    id = 1:5,
    content = c("第一大组", "第二大组", "第一大组:小组1", "第一大组:小组2", "第二大组:1"),
    nestedGroups = I(list(c(3, 4), 5, NA, NA, NA))
  ),
  option = list(locale = 'en')
)

可选参数(options) 🔗

vis.js支持的可选参数(options),下面薅几个感兴趣一点的了解一下。关于交互的部分等猴年马月学了 shiny 再看。

  • height 高度/width 宽度/maxHeight 最大高度/minHeight 最小高度
timevis(df1, width = '100%', height = '150px')

日期格式 🔗

  • format:时间轴的标签格式。
    • majorLabels:大刻度标签,在图形容器最下方。
    • minorLabels:小刻度标签,作为时间轴的具体刻度标签。
查看默认的日期格式

日期的格式化语法详见moment.js

{
  minorLabels: {
    millisecond:'SSS',
    second:     's',
    minute:     'HH:mm',
    hour:       'HH:mm',
    weekday:    'ddd D',
    day:        'D',
    week:       'w',
    month:      'MMM',
    year:       'YYYY'
  },
  majorLabels: {
    millisecond:'HH:mm:ss',
    second:     'D MMMM HH:mm',
    minute:     'ddd D MMMM',
    hour:       'ddd D MMMM',
    weekday:    'MMMM YYYY',
    day:        'MMMM YYYY',
    week:       'MMMM YYYY',
    month:      'YYYY',
    year:       ''
  }
}
timevis(df2, options = list(
  format = list(
    minorLabels = list(month = "M月", day = "D日"),
    majorLabels = list(month = "YYYY年MM月 -大刻度-")
  )
))

时间轴范围 🔗

  • timeAxis:时间轴的固定刻度
    • scale:固定刻度的单位,可选项有'millisecond'(毫秒), 'second'(秒), 'minute'(分钟), 'hour'(小时), 'weekday', 'week', 'day'(日), 'month'(月), 'year'(年)
    • step:固定刻度的步长。
timevis(df1, options = list(timeAxis = list(scale = 'year', step = 100)))
  • 设定时间轴整体范围

    • max/min:固定时间轴的最大日期/最小日期,无法通过缩放改变。
    • start/end:设定初始的时间范围,可以通过缩放改变。
  • hiddenDates:隐藏特定时间段。 -start/end:指定需要隐藏的时间起点和终点。 -repeat:指定需要隐藏的时间的重复方式,可选的有 daily(按天), weekly(按周), monthly(按月), yearly(按年)。

timevis(df2, options = list(
  locale = 'en',
  hiddenDates = list(
    start = '2025-06-15',
    end = '2025-06-30',
    `repeat`  = 'monthly'
  )
))

布局 🔗

  • orientation:时间轴的相对位置
    • orientation.axis:时间轴轴线相对条目的位置,默认 “bottom” (时间轴轴线在条目下方),可选有 “top”(时间轴轴线在条目上方)、“both”(时间轴轴线在条目上下两边)、 “none”(去掉时间轴轴线)
    • orientation.item:默认 “bottom”,改成 “top” 时条目会更靠近轴线。
timevis(df2, options = list(orientation = list(axis = 'both')))
timevis(df2, options = list(orientation = list(axis = 'top')))

timevis(df2)
timevis(df2, options = list(orientation = list(item = 'top')))
  • stack(堆叠):控制在同一时点或时段有重叠的条目是否自动换行堆叠。
    • stack = TRUE(默认):重叠的事件会自动上下堆叠,减少互相遮挡。
    • stack = FALSE:重叠的内容会被压缩到同一行,可能会互相遮挡。
df <- data.frame(
  id = 1:2,
  start = c(Sys.Date(), Sys.Date()),
  content = c('xxxxx', 'bbbbb')
)

timevis(df, option = list(stack = FALSE))
  • margin:间距
    • axis:条目与时间轴之间的最小间距(像素),默认20。
    • item 条目之间水平和垂直方向的最小间距(像素),默认10。
      • horizontal:条目之间最小水平间距(像素),默认10。
      • vertical:条目之间最小垂直间距(像素),默认10。

自定义样式(HTML/CSS) 🔗

按条目 🔗

  • content:支持引入 HTML 编码。
    • 支持 HTML 的 UNICODE 编码。
    • 支持 JS 的 UNICODE 编码。
    • 不默认引入 font awesome。
    • 支持<b>``<i>这类不改变 CSS 样式的 HTML 元素。
    • 不支持<span>``<div>这类改变 CSS 样式的 HTML 元素。
df <- data.frame(
  id = 1:5,
  start = c(
    Sys.Date(),
    Sys.Date(),
    Sys.Date() + 1,
    Sys.Date() + 2,
    Sys.Date() + 3
  ),
  content = c(
    'A["&#10084"]',
    'B["\u2764"]',
    'C["❤"]',
    "<b>D1</b><br><i>D2</i>",
    "<span style='color:blue'>E1</span>"
  )
)
timevis(df, option = list(locale = 'en'))

  • className/style:支持引入 CSS 样式,且 className 类的优先级更高。
df$style <- c(rep(NA, 3), 'color:blue;', 'color:red;font-size:30px')
timevis(df, option = list(locale = 'en'))

library(htmltools)

df$className <- "myItem"

tagList(
  tags$style(HTML("
    /* 去掉 item 边框 */
    .vis-item.myItem {
      border: none !important;
    }

    .vis-item.myItem .vis-item-content {
      color: green;
      font-size: 20px;
      background: white;
    }

    /* 隐藏竖着的刻度线(grid lines) */
    .vis-time-axis .vis-grid {
      display: none !important;
    }
  ")),
  timevis(df, option = list(locale = 'en'))
)

按组别 🔗

组标签的样式修改方式与按条目一样,这里不展开。

timevis(df1, groups = data.frame(
  id = 1:2,
  content = c("第一组", "第二组"),
  style = c('color:blue;', 'color:red;font-size:30px')
))

R