D3.js 学习 - 0 - SVG 基础回顾
说明
D3 并没有一如一种新的视觉展现形式,图形方面的描述都是来源自 web 标准:HTML、SVG、CSS
图表仅仅是内部有一些形状的矩形,而D3提供的是一种让你通过操作图标或者你自己定义的图形来表达你想要展示数据的方式,它让你可以轻易的为图形添加可视化交互,定义你的 图形有怎样的行为。
SVG 是可缩放矢量图形,是一种用来描述二维矢量图形的 XML 标记语言,简单的说 SVG 面向图形,HTML面向文本;SVG 是一个 W3C 标准,与其他 W3C 标准 CSS DOM 等能协同工作 – SVG-MDN
1. SVG 简介及坐标系统
1. 世界、视野、视窗
- 世界:SVG 中世界是无穷大的,通过 svg 标签下的代码定义其内容
- 视野:视野是观察世界的一个矩形区域,通过 svg 标签的 viewBox 属性配置 “x y width height”
- 视窗:浏览器开辟出来用来渲染 SVG 内容的区域,通过 svg 标签的 width height 属性控制大小
- 通常情况下 视野和视窗的大小是一致的,如果不一致,可以通过 svg 标签的 preserveAspectRatio 来控制视野到视窗的渲染规则(位置关系,包含关系)
2. 图形分组
<g>
用于创建分组- 分组上设置的属性子元素可以继承
- 可以用 transform 属性定义坐标变换
<g>
分组可以嵌套使用
3. 坐标系统
- svg 中使用笛卡尔直角坐标系
- 定义了一个原点与两个互相垂直的数轴
- 定义角度的方向是顺时针的
svg 中有四个坐标系
- 用户坐标系:世界的坐标
- 自身坐标系:每个图形元素或分组独立的与生俱来的,默认与前驱坐标系重叠,可以通过设置 transform 属性改变自身坐标系相对于前驱坐标系的位置;设置 x y 属性不会改变自身坐标系位置,但是会改变元素本身相对于自身坐标系的位置
- 前驱坐标系:父容器的坐标系
- 参考坐标系
4. 坐标变换
SVG 中 坐标变换是对一个坐标系到另一个坐标系的变换的描述
通过 transform 属性可以定义前驱坐标系到自身坐标系的线性变换,tranform 可以设置多个变换,先设置的先变换,后设置基于前一个变换后的自身坐标系再变换
2. 基本图形
1. 矩形 <rect>
属性
- x 左顶点坐标x
- y 左顶点坐标y
- width 矩形宽
- height 矩形高
- rx 圆角横向半径
- ry 圆角纵向半径
2. 圆形 <circle>
属性
- cx 圆心 x 坐标
- cy 圆心 y 坐标
- r 圆半径
2. 椭圆 <ellipse>
属性
- cx 圆心 x 坐标
- cy 圆心 y 坐标
- rx 椭圆横向半径
- ry 椭圆纵向半径
3. 直线 <line>
属性
- x1 起点 x 坐标
- y1 起点 y 坐标
- x2 终点 x 坐标
- y2 终点 y 坐标
4. 折线 <polyline>
属性
- points 包含所有点 x/y 坐标的字符串,例: points=”x1 y1 x2 y2 x3 y3 x4 y4”
5. 多边形 <polygon>
属性
- points 包含所有点 x/y 坐标的字符串,例: points=”x1 y1 x2 y2 x3 y3 x4 y4”
与折线区别,最后的点会与起始点自动连起来
3. 图形的基本属性
- fill 填充颜色
- stroke 描边颜色
- stroke-width 描边宽度
- transform 变形
4. 基本图形操作API
- 创建图形
document.createElementNS(ns, tagName)
tagName 就是 svg 标签 - 添加图形
element.appendChild(childElement)
- 设置获取属性
element.setAttribute(name, value)
element.getAttribute(name)
5. 颜色、渐变、画刷
- SVG 支持 RGB 和 HSL 两种颜色表示方式
- HSL 三个值分别表示 颜色[0,359]、饱和度[0,100]、亮度[0,100]
线性渐变
线性渐变使用
<linearGradient id="xx">
标签来定义,使用时,可以作为图形的填充属性值<rect fill="url(#xx)">
指向渐变的id线性渐变的属性
- x1 起点 x
- y1 起点 y
- x2 终点 x
- y2 终点 y
- gradientUnits 用于定义 图形的 x/y 坐标的的坐标值是如何计算的,默认值 objectBoundingBox 表示以自身坐标系为基准的[0,1] 的值,可以设置为 userSpaceOnUse 表示以世界坐标系为基准的像素值
线性渐变每个重要点的位置颜色通过
<stop offset="0.2" stop-color="#123456" />
定义在<linearGradient>
标签下
径向渐变
使用
<radialGradient id="yy">
定义,同样可以作为<rect fill="url(#xx)">
的填充值使用属性
- cx 显示圆的圆心 x
- cy 显示圆的圆心 y
- r 显示圆的半径
- fx 显示圆的中心 x
- fy 显示圆的中心 y
渐变的每个重要点的位置颜色通过
<stop offset="0.2" stop-color="#123456" />
定义在<radialGradient>
标签下
笔刷(重复填充)
使用
<pattern id="zzz">
标签定义 同样通过<rect fill="url(#zzz)">
作为填充使用,多个 pattern 会在 rect 中规则排列属性
1. patternUnits 切换当前填充物的 x y width height 的计算方式为以被填充图形为基准的 比例(objectBoundingBox); 或者按照当前用户坐标系的像素值(userSpaceOnUse)
2. patternContentUnits 与patternUnits 类似,但是定义的是 pattern 内部的他图形的 计算方式是 相对于 被填充图形的(objectBoundingBox)还是像素值(userSpaceOnUse)
6. 路径 Path
可以用来绘制各种图形,重点属性 ‘d’ 用来描述路由的命令,特定字母表示命令参数紧接着命令没有空格,多个参数用’,’或者空格隔开
命令的基本规律
- 命令区分大小写,大写命令表示坐标参数为绝对位置,小写则表示为相对位置
- 最后的参数表示要到达的位置
- 上一个命令结束的位置就是下一个命令开始的位置
- 命令可以重复参数表示重复执行同一个命令
1. 移动和直线
- M x,y 移动画笔到某个点
- L x,y 绘制直线到指定点
- H x 绘制水平直线到 x 位置
- V y 绘制竖直直线到 y 位置
- m\l\h\v 使用相对位置绘制
2. 弧线
弧线使用 A|a 命令,可以看做是画一个椭圆,然后从椭圆中截取一部分线段 参数如下
- rx 所在椭圆的 x 半轴长
- ry 所在椭圆的 y 半轴长
- xr 所在椭圆的长轴旋转角度
- laf 0|1 两个值表示是否选择截取的较长的弧线
- sf 0|1 两个值表示是否选择顺时针的弧线
- x 弧线的终点 x
- y 弧线的终点 y
3. 贝塞尔曲线
贝塞尔曲线需要定义三种点 起始点 结束点 控制点
二次贝塞尔曲线 通过 Q 命令表示,参数如下
- x1 控制点 x
- y1 控制点 y
- x 结束点 x
- y 结束点 y
三次贝塞尔曲线 通过 C 命令表示,参数如下
- x1 控制点1的 x
- y1 控制点1的 y
- x2 控制点2的 x
- y2 控制点2的 y
- x 结束点 x
- y 结束点 y
光滑的三次贝塞尔曲线 用 S 命令表示,参数与 C 命令比较少了 x1 与 y1 也就是少了控制点一,因为 S 命令默认会将命令前的贝赛尔曲线最后一个控制点的镜像作为控制点一,如果前一个命令是直线命令或者是二次贝塞尔曲线则命令会降级为二次贝塞尔曲线,这样就能绘制出平滑的曲线了
光滑的二次贝塞尔曲线 用 T 命令表示,与 S 一样绘制平滑曲线只不过是二次的
7. SVG 文本 <text>
<text>
标签定义文本,文本会按照基线对其,不同字体基线不一致,有如下属性定义文本位置
- x, y 定义文本基线的位置
- dx, dy 值可以是多个数值用空格隔开,表示每一个字符距离上一个字符的间距,属性值会向下传递,也就是说下一个的计算是按照上一个的位置来的
- style 设置样式
<tspan>
标签,用于在<text>
标签下,给文本分组设置,可以设置 dx dy ,设置后会覆盖父级的<text>
标签的设置文本居中显示
- 水平居中 text-anchor: middle; // start|end 以设置的 x 值为基准
- 垂直居中 dominant-baseline: central; // text-before-edge text-after-edge 根据文字盒子的上下边设置
8. 路径文本 <textPath>
<textPath>
作为<text>
标签的子元素,可以通过 xlink:href=”#path” 来指定定义好的路径,这样<textPath>
标签下的文字就可以按照路径显示了,超出路径的部分会被裁掉,文字的方向是路径切线的垂直法线, 可以在<textPath>
下定义<tspan>
标签用来文字分组属性
- startOffset 文字渲染在 路径的位置 单位是 百分比,配合父元素
<text>
设置 text-anchor: middle; 可以实现视觉上在路径上文字居中 - dx 在路径上距离上一个文本的距离
- dy 定义文字在自身法线上的位移,后续的文本也会继承这个位移
9. 超链接 <a>
超链接可以加到任意图形上,有以下属性
- xlink:href 指定链接地址
- xlink:title 指定链接提示
- target 指定打开目标
10. 图形引用 <use>
大批量使用同一个图形时,可以用
<use>
标签代替 图形标签的使用,浏览器会对其进行性能优化,图形通过 xlink:href=”#ss” 引用
<svg xmlns="http://www.w3.org/2000/svg" class="svg">
<defs>
<polygon id="star" points="0 -10 2 -2 10 0 2 2 0 10 -2 2 -10 0 -2 -2" fill="white"></polygon>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="#001122"></rect>
<g>
<use xlink:href="#star" opacity="0.5"></use>
<use xlink:href="#star" opacity="0.2"></use>
<use xlink:href="#star" opacity="0.3"></use>
</g>
</svg>
11. 裁切 <clipPath>
可以给
<clipPath>
添加图形子标签,这个裁剪出的新图形就是在这个子标签图形范围内的,使用时通过给图形设置 clip-path=”url(#xxx)” 来定义用哪个裁切
<svg xmlns="http://www.w3.org/2000/svg" class="svg">
<defs>
<radialGradient id="tower-bg" cx="0.5" cy="0.5" r="0.5">
<stop offset="0" stop-color="rgba(255, 255, 255, 1)"></stop>
<stop offset="1" stop-color="rgba(255, 255, 255, 0)"></stop>
</radialGradient>
<clipPath id="tower-clip">
<polygon points="-300 -40 10 0 -300 40" fill="rgba(255, 0, 0, 0.5)">
</polygon>
</clipPath>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="#001122"></rect>
<g id="tower" transform="translate(400 240)">
<polygon points="0 60 10 0 20 60" opacity="0.9" fill="white"></polygon>
<ellipse cx="10" cy="0" rx="300" ry="100" fill="url(#tower-bg)" clip-path="url(#tower-clip)"></ellipse>
</g>
</svg>
12. 蒙层 <mask>
可以在
<mask>
标签下定义图形 设置 fill 为 white 的部分表示可见 设置为 black 的部分表示不可见,也可以给图形设置为渐变的fill
<svg xmlns="http://www.w3.org/2000/svg" class="svg">
<defs>
<mask id="moon-mask">
<circle cx="100" cy="100" r="50" fill="white"></circle>
<circle cx="120" cy="80" r="50" fill="black"></circle>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="#001122"></rect>
<g id="moon">
<circle cx="100" cy="100" r="50" fill="yellow" mask="url(#moon-mask)"></circle>
</g>
</svg>
13. SVG 动画
动画原理:值关于时间的函数
定位动画的目标,可以通过 xlink:href=”url(#xxx)” 元素id 定位目标,或者将
<animate>
动画标签写在目标图形内部动画标签
<animate>
一般动画<animateTransform>
transform 动画<animateMotion>
轨迹运动
动画标签属性
- attributeType: XML | CSS
- attributeName: 需要变化的属性
- from\to: 变化的范围
- dur: 10s 变化的时间
- repeatCount: 循环次数
- path: 动画路径(animateMotion 可用)也可以使用
<mpath>
标签定义路径 - rotate: auto 指定路径动画图形自动旋转,使动画平滑
<svg xmlns="http://www.w3.org/2000/svg" class="svg">
<g>
<animateMotion xlink:href="#rect" path="M100 100, c100 100 20 67 200 100, s-200 100 200 160" dur="5s" rotate="auto">
</animateMotion>
<rect id="rect" fill="yellow" x="0" y="0" width="50" height="50"></rect>
<path d="M100 100, c100 100 20 67 200 100, s-200 100 200 160" stroke="red" fill="none"></path>
</g>
</svg>
14. 脚本动画
重点是通过
window.requestAnimationFrame(update)
来实现在每一帧,对图形进行属性更新, 这个方法告诉浏览器你希望执行动画并请求浏览器在下一次重绘之前调用指定函数update来更新动画, 通常在 update 函数中也需要再次调用这个方法
<div id="canvas">
<svg xmlns="http://www.w3.org/2000/svg" class="svg">
<g>
<path stroke="red" fill="none" d="M1 1, H500, M1 1,V500"></path>
<rect id="rect" fill="blue" x="0" y="0" width="10" height="10" opacity="0.8"></rect>
</g>
</svg>
</div>
<script>
window.onload = function () {
const NS = 'http://www.w3.org/2000/svg';
const a = 0.6;
let x = 0;
let y = 0;
function update() {
const rect = document.querySelector('#rect');
x += 0.2;
y = y + a * x;
rect.setAttribute('x', x);
rect.setAttribute('y', y);
// console.log(x, y)
window.requestAnimationFrame(update)
}
update();
window.requestAnimationFrame(update)
}
</script>