jjzjj

javascript - Accessibility: d3 brush/zoom 可以获得焦点并用键盘控制

coder 2024-07-25 原文

关于如何使用键盘控制 d3 笔刷/缩放的任何提示: 1. 专注笔刷控制能力 2. 能够使用键盘改变笔刷区域

是否支持开箱即用?

更新:显然没有开箱即用的解决方案(希望 d3 会在某个时候提供)。这意味着自定义解决方案将取决于可视化/场景。发布实际的用户体验和要求,并将针对此特定案例提供解决方案。

为了满足可访问性要求,任务是修改下面的图表控件,以便能够使用键盘进行缩放/画笔。这包括:1) 能够设置焦点; 2) 能够使用左右箭头键进行控制。

最佳答案

我要用这个 bl.ock作为引用。我相信这是您图像的来源。

缩放和画笔功能比较

我们对这个 block 中的一些东西感兴趣,缩放代码和刷亮代码:

function brushed() {
  if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
  var s = d3.event.selection || x2.range();
  x.domain(s.map(x2.invert, x2));
  focus.select(".area").attr("d", area);
  focus.select(".axis--x").call(xAxis);
  svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
      .scale(width / (s[1] - s[0]))
      .translate(-s[0], 0));
}

function zoomed() {
  if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
  var t = d3.event.transform;
  x.domain(t.rescaleX(x2).domain());
  focus.select(".area").attr("d", area);
  focus.select(".axis--x").call(xAxis);
  context.select(".brush").call(brush.move, x.range().map(t.invertX, t));

两个函数:

  • 检查是否应该执行函数的主体
  • 设置一个新的 x 尺度域
  • 更新面积和轴

区别很重要:

画笔函数使用 d3.zoomIdentity 更新比例,它必须这样做,因为它需要更新缩放函数以反射(reflect)当前缩放比例和变换。

缩放功能手动设置画笔,必须这样做,因为需要更新画笔。

在没有事件的情况下放大并刷“入”

要通过键盘控制它,使用 brushed() 函数作为模板可能更容易。这是因为当前的缩放变换可能很难检索,而欺骗画笔中的更改相对容易。

在 brushed 函数中,d3.event.selection 中的值是一个数组,其中包含画笔所包含的值范围(范围内的值,而不是域中的值)。它是画笔覆盖的引用/上下文比例 x2 中最小和最大范围值的数组。这是我们唯一需要同时更新缩放和画笔的东西。

要放大,我们可以采用焦点 x 尺度的域并找到域的最小值和最大值。然后我们可以重新设置 focus x scale 的域稍微小一点,有效地放大。下面的代码将域转换为一个范围,并在将其转换回域之前缩小该范围 - 这是不需要的,但更紧密地遵循 brushed() 函数,意味着不必处理日期。

var xMin = x2(x.domain()[0]);
var xMax = x2(x.domain()[1]);

var currentDifference = Math.abs(xMin-xMax);

xMin += currentDifference / 2 / 3   // increase the minimum value of the domain
xMax -= currentDifference / 2 / 3   // decrease the maximum value of the domain
x.domain([xMin,xMax].map(x2.invert, x2));

我们也可以这样设置缩放比例:

var identity = d3.zoomIdentity
  .scale(width/ (xMax - xMin))

我们还想修改缩放的变换,以便我们放大到先前较大域的中心。以下只是示例 block 中使用的代码的复制,但为了说明起见使用了更清晰的名称:

var identity = d3.zoomIdentity
  .scale(width/ (xMax - xMin))
  .translate(-xMin, 0);

如果我们使用 brushed 函数作为模板,我们可能会得到:

var xMin = x2(x.domain()[0]); // minimum value in x range currently
var xMax = x2(x.domain()[1]); // maximum value in x range currently

var currentDifference = Math.abs(xMax-xMin); // center point of range

xMin += currentDifference / 2 / 3  // reduce the distance between center point and end points
xMax -= currentDifference / 2 / 3

x.domain([xMin,xMax].map(x2.invert, x2));  // convert the range to a domain
focus.select(".area").attr("d", area); // redraw the chart
focus.select(".axis--x").call(xAxis);  // redraw the axis

var identity = d3.zoomIdentity
  .scale(width/ (xMax - xMin))
          .translate(-xMin, 0);          // update the zoom factor

context.select(".brush").call(brush.move, x.range().map(identity.invertX, identity)); // update the brush
svg.select(".zoom").call(zoom.transform, identity); // apply the zoom factor

这会将焦点区域放大到以当前域的中心为中心的区域。使用上面的代码,域名将缩小三分之一,但可以根据您的需要进行更改。

与原始刷函数相比,唯一真正的区别是我们:

  • 手动计算笔刷范围
  • 使用缩放功能中使用的方法更新画笔。

就是这样。

其他操作

您可以通过扩大而不是缩小域来缩小,只需在定义新端点时切换符号即可:

xMin -= currentDifference / 2 / 3  
xMax += currentDifference / 2 / 3

向左移动看起来像:

xMin -= currentDifference / 2 / 3  
xMax -= currentDifference / 2 / 3

而自然地向右移动则相反。

添加键盘

现在您所要做的就是设置一个监听器来监听击键:

d3.select("body")
  .on("keypress", function() {
    if (d3.event.key == "a") {
      // one of zoom in/out/pan
    }
    else if (d3.event.key == "b" {
      //...
    }
});

将它们放在一起

我组装了一个 block 来显示所有内容,我使用 asdw 进行关键输入:

  • a:向左平移
  • d:向右平移
  • w:放大
  • s:缩小。

最后一点:我已经包含了一项检查以确保新域在边界内:我们不想超出我们的数据域。

Here's例子。

关于javascript - Accessibility: d3 brush/zoom 可以获得焦点并用键盘控制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47296060/

有关javascript - Accessibility: d3 brush/zoom 可以获得焦点并用键盘控制的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  3. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  4. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

  5. ruby - 匹配大写字母并用后续字母填充,直到一定的字符串长度 - 2

    我有一个驼峰式字符串,例如:JustAString。我想按照以下规则形成长度为4的字符串:抓取所有大写字母;如果超过4个大写字母,只保留前4个;如果少于4个大写字母,则将最后大写字母后的字母大写并添加字母,直到长度变为4。以下是可能发生的3种情况:ThisIsMyString将产生TIMS(大写字母);ThisIsOneVeryLongString将产生TIOV(前4个大写字母);MyString将生成MSTR(大写字母+tr大写)。我设法用这个片段解决了前两种情况:str.scan(/[A-Z]/).first(4).join但是,我不太确定如何最好地修改上面的代码片段以处理最后一种

  6. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  7. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  8. ruby - 我可以将我的 README.textile 以正确的格式放入我的 RDoc 中吗? - 2

    我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:

  9. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  10. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

随机推荐