【D3.js in Action 3 精译】1.2.2 可缩放矢量图形(三)

当前内容所在位置

  • 第一部分 D3.js 基础知识
    • 第一章 D3.js 简介
      • 1.1 何为 D3.js?
      • 1.2 D3 生态系统——入门须知
        • 1.2.1 HTML 与 DOM
        • 1.2.2 SVG - 可缩放矢量图形 ✔️
          • 第一部分
          • 第二部分
          • 【第三部分】✔️
        • 1.2.3 Canvas 与 WebGL(精译中 ⏳)
        • 1.2.4 CSS(待翻译)
        • 1.2.5 JavaScript(待翻译)
        • 1.2.6 Node 与 JavaScript 框架(待翻译)
        • 1.2.7 Observable 记事本(待翻译)

译注
(上接 1.2.2 小节(二))
…… 关于 SVG 描边(strokes)的位置

当对齐可视化项目中的图形时,需要特别注意:SVG 图形绘制出的描边是在内外边界上平均展布的。如下图所示,已知一个 width 属性为 40px 的矩形,令 stroke-width 的值为 1,则在视觉效果上会在矩形的左右两边各增加宽度为 0.5px 的描边(而不是下意识地以为的那样在各边均增加 1px),最终实际的总宽度为 41px;若令 stroke-width 的值为 2,则左右两边各增加 1px,以此类推。

描边宽度 stroke-width 对 SVG 图形实际宽度的影响描边宽度 stroke-width 对 SVG 图形实际宽度的影响

(……关于 SVG 描边(strokes)的位置)

5 圆与椭圆

在数据可视化中常常会用到圆形。它们天然吸引眼球,使可视化效果看起来更友好、更有趣。SVG 的圆是通过 <circle /> 元素绘制的,其必选属性包括圆心位置(cx, cy)与半径(r),如图 1.17 所示。圆的半径是从圆心到圆周上任意一点绘制的线的长度。将以下圆形添加到示例中,令圆心位于 (530, 80) 处,且半径为 50px

<circle cx="530" cy="80" r="50" />

图 1.17
图 1.17 在 SVG 容器的坐标系中定位圆和椭圆并调整其大小

还可以为圆设置填充(fill)与描边(stroke)属性(attribute)。要生成图 1.17 中的圆,令填充色为透明(transparent)、描边宽度为 3px、描边颜色为 #81c21c(译注:这里应该参考的是最终效果图 1.8,图 1.17 只是示意图)。

同理,<ellipse /> 元素必须指定圆心坐标(cx, cy),但不像圆有固定半径,椭圆的半径会变化,使其呈扁平状——这是通过声明椭圆的水平半径(rx)和垂直半径(ry)来实现的。将如下代码片段添加到示例中,将在圆的下方绘制一个椭圆,其水平半径为 50px、垂直半径为 30px

<ellipse cx="530" cy="205" rx="50" ry="30" />
6 路径

SVG 的路径(path)元素是目前为止所有 SVG 元素中最灵活的一种。在 D3 中,路径被广泛用于绘制几乎所有的复杂形状和曲线。这些形状和曲线是无法用目前讨论过的图形基元(线、矩形、圆和椭圆)来表示的。

路径元素通过声明 d 属性(这里的“d”代表“draw”,即“绘制”)来指示浏览器进行绘制。d 属性包含一系列命令:从开始绘制路径的位置,到使用的一系列曲线类型,一直到确定该路径是否为一个封闭图形为止。例如,在图形画廊示例中添加以下路径元素:

<path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" fill="none" stroke="#773b9a" stroke-width="3" />

如图 1.18 所示, d 属性以 M680 150 开头,表示“移动(M)到坐标(680,150)处”。接着从起点坐标(680 150)到 d 属性最后一个坐标(840 150)所指定的终点,绘制一条三阶贝塞尔曲线(cubic Bézier curve)。三阶贝塞尔曲线还需要设置控制点来定义曲线的陡峭程度和弯曲方向。这些控制点从字母 C(这里的“C”代表“cubic curve”,即“三次方曲线”)之后开始,直到字母 S(这里的“S”代表“stop”,即“停止”)之后结束。

图 1.18 一条简单的 SVG 路径及其控制点
图 1.18 一条简单的 SVG 路径及其控制点

注意

如需深入了解 SVG 路径,请参阅 MDN 的教程:https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths。

手动设置 d 属性仅适用于简单的图形,随着图形复杂度增加,纯手动编写将会变得非常繁琐。所幸 D3 提供了强大的图形生成工具来专门处理 d 属性的计算。本书第 4 章将深入讨论。

路径还有一个需要牢记的重要知识点:无论路径元素是否闭合,只要其 fill 属性值不为 nonetransparent,浏览器就会默认填充为黑色,如本例所示。

7 文本

内联 SVG 图形的最大优势之一在于支持屏幕阅读器访问其包含的文本内容。这对于可访问性而言是一大优势。由于数据可视化通常包含多个标签,因此有必要了解如何使用 <text> 元素来操作 SVG 文本。通过往示例中添加标签可以了解 SVG 文本的基本工作原理。

目前为止我们讨论的 SVG 图形都使用了自闭合标签(如 <line /><rect /><path /> ……)。在使用 SVG 的 <text> 元素时,需要同时使用开标签和闭标签,并将待显示文本放置在这两个标签之间。例如,添加一个内容为“line”的文本元素:

<text>line</text>

保存并重新加载页面。您可能以为文本会出现在 SVG 容器的左上角,但却看不到……这是为什么呢?默认情况下,SVG 文本的位置是参照其基线计算得来的,通过 dominant-baseline 属性控制。如果文本基线的坐标是 (0, 0),如图 1.19 所示,实际文本最终将位于 SVG 容器之外。由于任何位于 SVG 容器之外的元素都是不可见的,所以看不到文本。

图 1.19 在 SVG 容器外定位文本
图 1.19 在 SVG 容器外定位文本

在使用 SVG 文本元素时,另一个需要考虑的问题是文本如何流动(flow)。常规 HTML 元素在页面上的位置是按照控制内容流动的特定规则来确定的。如果在页面中插入大量 <div> 元素,它们会自然地堆叠在一起,其内容也会回流(reflow),不会超出容器的范围;而 SVG 文本根本不会流动,开发人员必须单独设置每个文本元素的 xy 属性。例如通过它们将文本放在坐标点 (60, 260) 处,标签“line”才会出现在图形画廊示例中 SVG 直线段的下方:

<text x="60" y="260">line</text>

作为练习,再创建一个新的文本元素,并将标签“rect”置于矩形和正方形的下方。

至此,我们通过 xy 属性声明了文本元素 左下角 的位置。怎样设置文本中点位置呢?通过属性 text-anchor 来实现:令其值为 middle 即可,如图 1.20 所示。再比如,利用该属性还可以将圆形的文本标签居中。

<text x="530" y="155" text-anchor="middle">circle</text>

图 1.20图 1.20 text-anchor 属性对 SVG 文本对齐方式的影响。默认值为 start;根据其中间位置对齐,值为 middle;设置末尾对齐,值为 end

最后,分别为椭圆和路径元素各添加一个文本标签。SVG 文本默认为黑色,可以通过 fill 属性(attribute)进行更改。

8 分组元素

本小节要讨论的最后一个 SVG 元素为分组元素(group element)。分组元素(即 <g> 元素)与之前讨论过的其他 SVG 元素有所不同。它既没有视觉上的图形表示,也不存在具有边界的空间;相反,它是 SVG 各元素的逻辑分组,广泛应用于由多个图形和标签组成的可视化效果中。

如果希望正方形标签和“rect”文本标签一同显示,并在 SVG 容器中整体平移,可以如以下示例将它们放到一个 <g> 元素中。注意,<rect> 元素的左上角已变为了坐标原点 (0, 0)<text> 位于 (0, 85) 处,保持在 <rect> 的下方:

<g><rect x="0" y="0" width="60" height="60" /><text x="0" y="85">rect</text>
</g>

此时,包含正方形及其文本标签的组出现在了 SVG 容器的左上角。我们可以在 SVG 容器中任意移动这个组及其包含的所有元素,始终保持正方形与文本标签之间的对齐方式不变。

使用 transform 属性可以在 SVG 容器中平移该分组。transform 属性比之前学过的其他属性略显复杂,但与 CSS 中 transform 属性的用法相同。它接受一个或一系列变换(transformation,如平移、旋转、缩放等)作为属性值。平移一个分组使用 translate(x, y) 变换。如果要将 <rect><text> 元素移回原位,需要令分组元素右移 260px 并下移 175px,即令 <g> 元素 transform 属性的值为 transform="translate(260,175)"

<g transform="translate(260,175)"><rect x="0" y="0" width="60" height="60" /><text x="0" y="85">rect</text>
</g>

<g> 元素的另一个特点,是其子元素可以继承它的样式属性(styling attributes)。下面举例说明。由于前面“rect”文本标签已经和正方形归为同一组,故先将其余文本元签统一放到另一个 <g> 元素内:

<g><text x="60" y="260">line</text><text x="530" y="155" style="text-anchor:middle">circle</text><text x="530" y="260" style="text-anchor:middle">ellipse</text><text x="730" y="260">path</text>
</g>

如果令 <g>fill 属性值为 #636466,则里面的每个 <text> 元素都将继承相同的颜色;同理,如果添加的是 font-familyfont-size 属性,同一组内的文本元素也将继承这些样式属性(properties)。

<g fill="#636466" style="font-size:16px; font-family:monospace"><text x="60" y="260">line</text><text x="530" y="155" style="text-anchor:middle">circle</text><text x="530" y="260" style="text-anchor:middle">ellipse</text><text x="730" y="260">path</text>
</g>

最后一次再重新加载页面,注意观察分组内的标签继承的颜色和字体效果;再看看分组外的标签是否保持原来的外观不变。像这样在分组元素上应用共享样式的做法非常方便,可以帮助您在工作中遵循 DRY(即 Don’t Repeat Yourself,不要重复自己)原则。需要更新属性时,在分组容器上操作也会很简单。

恭喜您完成了本书的第一个练习!您可以在代码清单 1.2 及源码文件的 end 文件夹找到 SVG 图形画廊的完整代码。在构建首个 D3 项目时,该练习可以作为参考。

代码清单 1.2 SVG 图形画廊示例的完整 HTML 代码

<!DOCTYPE html>
<html>
<head> <!-- [略] --> </head>
<body><div style="width:100%; max-width:1200px; margin:0 auto;"><svg viewBox="0 0 900 300" style="border:1px solid black;"><line x1="50" y1="45" x2="140" y2="225" stroke="black" /><rect x="260" y="25" width="120" height="60" fill="#6ba5d7" /><rect x="260" y="100" width="120" height="60" rx="20" ry="20" fill="#6ba5d7" /> <g transform="translate(260, 175)"><rect x="0" y="0" width="60" height="60" fill="transparent"  stroke="#6ba5d7" /><text x="0" y="85">rect</text></g><circle cx="530" cy="80" r="50" fill="none" stroke="#81c21c" stroke- width="3" /><ellipse cx="530" cy="205" rx="50" ry="30" fill="#81c21c" /><path d="M680 150 C 710 80, 725 80, 755 150 S 810 220, 840 150" fill="none" stroke="#773b9a" stroke-width="3" /><g fill="#636466" style="font-size:16px; font-family:monospace"><text x="60" y="260">line</text><text x="530" y="155" style="text-anchor:middle">circle</text><text x="530" y="260" style="text-anchor:middle">ellipse</text><text x="730" y="260">path</text></g></svg></div>
</body>
</html>

强化练习:创建 SVG 图形

现在该您小试牛刀了——创建如下图所示的 SVG 图形。您可以在源码文件夹 02_SVG_exercise/start 中进行操作。具体要求如下:

  • 创建一个响应式 SVG 容器,宽高均为 400px(屏幕上留足空间)。
  • 绘制一个宽高均为 200px 的正方形。将其置于 SVG 容器的中心位置,并指定透明色填充及 5px 的黑色描边。
  • 在 SVG 容器的中心添加一个半径为 100px 的圆。将其 fill 属性设置为 CSS 颜色名称“plum”。
  • 绘制两条描边为 5px 的黑色对角线:一条从正方形的左上角画到右下角;另一条从正方形的右上角画到左下角。
  • 在正方形上方添加文本“SVG is awesome!”字样,并将其置于 SVG 容器的中心。其他文本样式:字号 18px、无衬线字体。

SVG 强化练习项目效果图
强烈建议通过练习该 SVG 图形来强化本小节介绍的知识点

该练手项目的完整参考代码,请参阅 附录 DD.1.1 小节或随书源码文件中的 02_SVG_exercise/end 文件夹中。建议尽量独立完成。

(SVG 基础知识译完了,内容有点多,再自己做个小结吧)

本节 SVG 基础知识要点梳理
  • 圆(circle)与椭圆(ellipse):都具有圆心坐标(cx / cy)和半径,只是椭圆的半径有两个(rxry),圆只有一个(r);圆其实是椭圆的 特殊情况
  • 路径(path)是 SVG 图形中最复杂的几何基元,其属性 d 包含一系列命令,可借助 D3 的生成工具代替纯手工赋值;
  • 文本(text)——
    • 在 SVG 中不随常规 HTML 内容流动;
    • 其位置是基于文本基线计算得来的,需要手动设置(xy);
    • 文本对齐通过 text-anchor 属性调整,可选值:start | middle | end
  • 分组(g)元素——
    • 没有视觉图形表示,也不占据空间;
    • 放入同一分组的元素可整体移动;
    • 设置在 g 上的样式可被子元素继承:如 fillfont-sizefont-family 等;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/39496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

独立站新风口:TikTok达人带货背后的双赢合作之道

TikTok以其庞大的用户基础、高度互动性和创新的内容形式&#xff0c;为独立站带来了前所未有的发展机遇。独立站与TikTok达人的合作&#xff0c;不仅能够帮助独立站快速提升品牌知名度和销售额&#xff0c;还能为TikTok达人带来更多商业机会和影响力。本文Nox聚星将和大家探讨独…

Android sdk 安装已经环境配置

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Android ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 一、下载 二、安装 三、环境配置 我的其他博客 正文 一、下载 1、大家可去官网下载 因为需要魔法 所以就不展示了 2、去下面这…

【JS】纯web端使用ffmpeg实现的视频编辑器-视频合并

纯前端实现的视频合并 接上篇ffmpeg文章 【JS】纯web端使用ffmpeg实现的视频编辑器 这次主要添加了一个函数&#xff0c;实现了视频合并的操作。 static mergeArgs(timelineList) {const cmd []console.log(时间轴数据,timelineList)console.log("文件1",this.readD…

Vue+ElementUi实现录音播放上传及处理getUserMedia报错问题

1.Vue安装插件 npm install --registryhttps://registry.npmmirror.com 2.Vue页面使用 <template><div class"app-container"><!-- header --><el-header class"procedureHeader" style"height: 20px;"><el-divid…

vue2 接口文档

const assetmanagementIndex (params) > getAction("/asset/assetmanagementsystem/page", params); //资产管理制度表分页列表 const assetmanagementPost (params) > postAction("/asset/assetmanagementsystem", params); //资产管理制度表新增…

维护Nginx千字经验总结

Hello , 我是恒 。 维护putty和nginx两个项目好久了&#xff0c;用面向底层的思路去接触 在nginx社区的收获不少&#xff0c;在这里谈谈我的感悟 Nginx的夺冠不是偶然 高速:一方面&#xff0c;在正常情况下&#xff0c;单次请求会得到更快的响应&#xff1b;另一方面&#xff0…

从零开始学量化~Ptrade使用教程——安装与登录

PTrade交易系统是一款高净值和机构投资者专业投资软件&#xff0c;为用户提供普通交易、篮子交易、日内回转交易、算法交易、量化投研/回测/实盘等各种交易工具&#xff0c;满足用户的各种交易需求和交易场景&#xff0c;帮助用户提高交易效率。 运行环境及安装 操作系统&…

昇思25天学习打卡营第3天 | 数据集 Dataset

数据是深度学习的基础&#xff0c;高质量的数据输入将在整个深度神经网络中起到积极作用。MindSpore提供基于Pipeline的数据引擎&#xff0c;通过数据集&#xff08;Dataset&#xff09;和数据变换&#xff08;Transforms&#xff09;实现高效的数据预处理。其中Dataset是Pipel…

将数据切分成N份,采用NCCL异步通信,让all_gather+matmul尽量Overlap

将数据切分成N份,采用NCCL异步通信,让all_gathermatmul尽量Overlap 一.测试数据二.测试环境三.普通实现四.分块实现 本文演示了如何将数据切分成N份,采用NCCL异步通信,让all_gathermatmul尽量Overlap 一.测试数据 1.测试规模:8192*8192 world_size22.单算子:all_gather:0.035…

代理IP的10大误区:区分事实与虚构

在当今的数字时代&#xff0c;代理已成为在线环境不可或缺的一部分。它们的用途广泛&#xff0c;从增强在线隐私到绕过地理限制。然而&#xff0c;尽管代理无处不在&#xff0c;但仍存在许多围绕代理的误解。在本博客中&#xff0c;我们将探讨和消除一些最常见的代理误解&#…

人脑网络的多层建模与分析

摘要 了解人类大脑的结构及其与功能的关系&#xff0c;对于各种应用至关重要&#xff0c;包括但不限于预防、处理和治疗脑部疾病(如阿尔茨海默病或帕金森病)&#xff0c;以及精神疾病(如精神分裂症)的新方法。结构和功能神经影像学方面的最新进展&#xff0c;以及计算机科学等…

OBS 免费的录屏软件

一、下载 obs 【OBS】OBS Studio 的安装、参数设置和录屏、摄像头使用教程-CSDN博客 二、使用 obs & 输出无黑屏 【OBS任意指定区域录屏的方法-哔哩哔哩】 https://b23.tv/aM0hj8A OBS任意指定区域录屏的方法_哔哩哔哩_bilibili 步骤&#xff1a; 1&#xff09;获取区域…

012-GeoGebra基础篇-构造圆的切线

前边文章对于基础内容已经悉数覆盖了&#xff0c;这一篇我就不放具体的细节&#xff0c;若有需要可以复刻一下 目录 一、成品展示二、算式内容三、正确性检查五、文章最后 一、成品展示 二、算式内容 A(0,0) B(3,0) c: Circle(A,B) C(5,4) sSegment(A,C) DMidpoint(s) d: Circ…

k8s部署单节点redis

一、configmap # cat redis-configmap.yaml apiVersion: v1 kind: ConfigMap metadata:name: redis-single-confignamespace: redis data:redis.conf: |daemonize nobind 0.0.0.0port 6379tcp-backlog 511timeout 0tcp-keepalive 300pidfile /data/redis-server.pidlogfile /d…

全网小视频去水印接口使用说明

一、请求地址&#xff1a; https://www.lytcreate.com/api/qsy/ 二、请求方式&#xff1a;POST 三、请求体&#xff1a;JSON body {"token": "个人中心的token","url": "视频分享地址"} token获取地址&#xff0c;访问&#xff…

uniapp微信小程序使用xr加载模型

1.在根目录与pages同级创建如下目录结构和文件&#xff1a; // index.js Component({properties: {modelPath: { // vue页面传过来的模型type: String,value: }},data: {},methods: {} }) { // index.json"component": true,"renderer": "xr-frame&q…

软降工程学系统实现

一、程序编码 程序编码是设计的继续&#xff0c;将软件设计的结果翻译成用某种程序设计语言描述的源代码。 程序编码涉及到方法、工具和过程。 程序设计风格和程序设计语言的特性会深刻地影响软件的质量和可维护性。 要求源程序具有良好的结构性和设计风格。 程序设计风格…

【web3】分享一个web入门学习平台-HackQuest

前言 一直想进入web3行业&#xff0c;但是没有什么途径&#xff0c;偶然在电鸭平台看到HackQuest的共学营&#xff0c;发现真的不错&#xff0c;并且还接触到了黑客松这种形式。 链接地址&#xff1a;HackQuest 平台功能 学习路径&#xff1a;平台有完整的学习路径&#xff…

【聊聊原子性,中断,以及nodejs中的具体示例】

什么是原子性 从一个例子说起&#xff0c; x &#xff0c;读和写 &#xff0c; 如图假设多线程&#xff0c;线程1和线程2同时操作变量x&#xff0c;进行x的操作&#xff0c;那么由于写的过程中&#xff0c;都会先读一份x数据到cpu的寄存器中&#xff0c;所以这个时候cpu1 和 c…

MyBatis-plus(下)

目录 静态工具 逻辑删除 枚举处理器 ​编辑​编辑JSON处理器 分页插件 案例 静态工具 只有save与update不需要传class字节码 UserController: MyServiceImpl: 改造根据id批量查询用户的接口&#xff0c;查询用户的同时&#xff0c;查询出用户对应的所有地址 Overrid…