图形开发学院|GraphAnyWhere
- 课程名称:图形系统开发实战课程:进阶篇(上)
- 课程章节:“图形样式”
- 原文地址:https://www.graphanywhere.com/graph/advanced/2-8.html
第八章 图形样式
1. 填充和描边
\quad
在图形系统实战—基础篇中,我们已经学习了绘制几何图形时,可以通过渲染上下文对象的strokeStyle
和fillStyle
两个属性指定几何图形边框效果和填充效果,而且这两个属性均可设置颜色、渐变、图案三种渲染效果。本文将进一步讲解这三种渲染效果的更多技术。
1.1 颜色
\quad
在图形开发中,色彩的运用是非常重要的,它能直接影响到图形的美观度和用户体验。 WEB网页 和 Canvas图形均采用 HTML颜色
作为颜色应用的基础。
\quad
HTML颜色
由红色、绿色、蓝色混合而成,其颜色值由一个十六进制符号来定义,这个符号由红色、绿色和蓝色的值组成(RGB)。各颜色的最小值是0(十六进制:#00)。最大值是255(十六进制:#FF)。三种颜色 红,绿,蓝的组合从0到255,一共可表达1600万种不同颜色(256 x 256 x 256)。
\quad
除了颜色值之外,HTML颜色
还可通过 透明度 描述元素的可见程度,可以用来实现层次感、透视效果等。具体来说透明度是通过RGBA颜色模式中的 Alpha
值来控制的。
\quad 在使用 HTML 颜色时可采用HEX、RGB、RGBA、HSL和HSLA等几种格式。
十六进制格式 (Hex格式)
十六进制颜色格式为:#RRGGBB
- 十六进制颜色值必须带有前导“#”字符。
- 其中RR(红色)、GG(绿色)和 BB(蓝色)是介于 00 和 FF 之间的十六进制整数,用于指定颜色的强度。例如,#FF0000显示为红色,因为红色被设置为其最高值(FF),而其他两个(绿色和蓝色)被设置为00。#00FF00显示为绿色,因为绿色被设置为其最高值(FF),而其他两个(红色和蓝色)被设置为00。
- 要显示黑色,所有颜色参数设置为00,如下所示:#000000。
- 要显示白色,所有颜色参数设置为FF,如下所示:#FFFFFF。
透明度
\quad 在十六进制表示法中,颜色的透明度由两位十六进制数表示,取值范围为00到FF。其中,00表示完全透明,FF表示完全不透明。例如,#FF0000表示红色不透明,#FF000080表示红色半透明。
\quad 可通过正则表达式判断是否为一个有效的十六进制颜色值,其代码如下:
function isValidateHex(color) {
if (color.startsWith('#')) {
// 首字符为#,颜色值必须为3/4/6/8个字符
let pattern = /^#?([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{4}|[0-9a-f]{3})$/i;
return pattern.test(color);
}
}
RGB格式
RGB颜色格式为:rgb(red, green, blue)
- 各个参数(红色、绿色和蓝色)定义的颜色强度值在0到255之间。例如rgb(255,0,0)显示为红色,因为红色被设置为其最高值(255),而其他两个(绿色和蓝色)被设置为0。rgb(0,255,0)显示为绿色,因为绿色被设置为其最高值(255),而其他两个(红色和蓝色)被设置为0。
- 若要显示黑色,所有颜色参数设置为0,如下所示:rgb(0,0,0)。
- 若要显示白色,所有颜色参数设置为255,如下所示:rgb(255,255,255)。
透明度
\quad 对应于RGB颜色格式,包含透明度的格式为:rgba(red, green, blue, alpha)
\quad rgba颜色值是具有Alpha通道的rgb颜色值的扩展,alpha通道指定颜色的不透明度。alpha 值为从0到1的十进制数,其中0表示完全透明,1表示完全不透明。例如,rgba(255, 0, 0, 1)表示红色不透明,rgba(255, 0, 0, 0.5)表示红色半透明。
\quad 可通过正则表达式判断是否为一个有效的RGB颜色值,其代码如下:
function isValidateRGB(color) {
if (color.toLowerCase().startsWith('rgb')) {
let pattern = /^rgba?\(\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*,\s*(\d{1,3}(?:\.\d+)?\%?)\s*(?:\s*,\s*((?:\d*\.?\d+)?)\s*)?\)$/i;
return pattern.test(color);
}
}
内置颜色名称
\quad
HTML支持140个颜色名,这其中包括了HTML4就开始支持的16个颜色名。注意这些关键字虽然不区分大小写,但此处为了可读性以混合的大小写列出。下表列出这140个颜色:
HSL(色相、饱和度、亮度)
HSL代表hue(色调)、saturation(饱和度)和lightness(亮度)。
- 色相指图像的相对明暗程度,也称之为色调,是色轮上从0到360的度数。0(或360)为红色,120为绿色,240为蓝色
- 饱和度可以描述为色彩的鲜艳程度。它是一个从0%到100%的百分比值。
- 100%是全彩,没有灰色阴影。
- 50%是50%的灰色,但你仍然可以看到颜色。
- 0%为完全灰色;你再也看不到颜色了。
- 亮度指色彩的明暗差异,可以描述为你想给颜色多少光,其中0%表示没有光(暗),50%表示50%光(既不暗也不亮),100%表示全光。
其格式为:hsl(hue, saturation, lightness),如下所示:
{"color":"hsl(0, 100%, 50%)"}
{"color":"hsl(120, 100%, 50%)"}
{"color":"hsl(240, 100%, 50%)"}
注意:Edge、Chrome、Firefox、Safari、Opera 10+和IE9+支持HSL颜色值。
透明度
\quad HSLA颜色值是HSL颜色值的扩展,具有Alpha通道,用于指定颜色的不透明度。alpha参数是介于0.0(完全透明)和1.0(完全不透明)之间的数字:
\quad 其格式为:hsla(hue, saturation, lightness, alpha),如下所示:
{"color":"hsla(0, 100%, 50%, 0.2)"}
{"color":"hsla(0, 100%, 50%, 0.4)"}
{"color":"hsla(0, 100%, 50%, 0.6)"}
{"color":"hsla(0, 100%, 50%, 0.8)"}
以下代码可判断是否为一个有效的颜色值:
function isValidateHSL(color) {
if (color.toLowerCase().startsWith('hsl')) {
let pattern = /^hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)$/i;
return pattern.test(color);
}
}
ColorUtil工具类
\quad
anyGraph
提供了一个通用的Color工具类,提供了将上述各种颜色格式转换为Color
类型格式的功能,也提供了将 Color
类型格式转换为上述各种颜色格式的功能,还提供了生成随机色,生成色带等功能,其方面名称如下表所示:
名称 | 说明 |
---|---|
toRgb() | 转换为rgb()字符串颜色值 |
toHex() | 转换为16进制字符串颜色值 |
toHSL() | 转换为HSL颜色对象 |
toHSV() | 转换为HSV颜色对象 |
toHSB() | 转换为HSB颜色对象 |
static fromHex(hex) | 将16进制颜色字符串 转换为Color对象 |
static fromString(str) | 将字符串颜色转换为Color对象 |
static fromHSL (hslColor) | 将HSL的字符串颜色转换为Color对象 |
static isValidColor(color) | 判断一个颜色值是否合法 |
static random() | 生成随机色 |
static band(color, count) | 生成色带 |
\quad
下面这些代码展示了 Color
类中的类型转换功能:
console.info(Color.fromString("#FFF").toString());
// return: rgb(255,255,255)
console.info(Color.fromString("#FFFFFFDF").toString());
// return: rgba(255,255,255,0.87)
let red = Color.fromString("red")
console.info(red.toHSL());
// return: {H: 0, S: 1, L: 1}
console.info(red.toHSB());
// return: {h: 0, s: 1, b: 1}
console.info(red.toHSV());
// return: {h: 0, s: 1, v: 255}
console.info(red.toHex());
// return: #ff0000
console.info(red.toRgb());
// return: rgb(255,0,0)
\quad
Color
类中包含了一个静态方法 band(color, count)
,该方法可生成指定颜色的色带值。下图为生成的 deeppink
色带。
1.2 渐变效果
\quad
在 图形系统开发实战课程-基础篇 第五章 渲染效果 中我们详细讲解了 Canvas
所提供渐变效果功能。渐变风格包括 “线性渐变”和“径向渐变”两种不同的风格, 这里我们简单回顾一下。
线性渐变
\quad 线性渐变是一种颜色过渡方式,它以一条直线(水平或垂直)为轴线,从起点到终点颜色进行顺序渐变。渲染上下文对象提供了建立线性渐变的方法,其定义如下:
CanvasGradient ctx.createLinearGradient(x0, y0, x1, y1);
参数 | 说明 |
---|---|
x0 | 起点的 x 轴坐标 |
y0 | 起点的 y 轴坐标 |
x1 | 终点的 x 轴坐标 |
y1 | 终点的 y 轴坐标 |
\quad
该方法的返回值是一个CanvasGradient对象,将该对象直接赋值给 ctx.fillStyle
属性 或 ctx.strokeStyle
属性,即可在图形中实现渐变效果。该对象还包含了一个方法,用于添加一个由偏移值和颜色值指定的 断点 到渐变对象中,API如下:
void gradient.addColorStop(offset, color);
参数 | 说明 |
---|---|
offset | 偏移位置,0到1之间的值 |
color | 颜色值 |
径向渐变
\quad 径向渐变是指从起点到终点颜色从内到外进行圆形渐变(从中间向外拉)。渲染上下文对象提供了建立径向渐变的方法,其定义如下:
CanvasGradient ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
参数 | 说明 |
---|---|
x0 | 开始圆形的 x 轴坐标 |
y0 | 开始圆形的 y 轴坐标 |
r0 | 开始圆形的半径 |
x1 | 结束圆形的 x 轴坐标 |
y1 | 结束圆形的 y 轴坐标 |
r1 | 结束圆形的半径 |
\quad
该方法的返回值是一个CanvasGradient对象,该对象包含了方法addColorStop()
与线性渐变的该方法完全一样。
anyGraph 中的实现
\quad 从上面这两类渐变的API中可以看出,创建渐变对象时需指定渐变位置。这对于静态图形而言没什么问题,而对于具体图形交互(平移和缩放) 功能的动态图形而言,位置信息是会随着图形的平移和缩放而发生改变的,因此我们不能在创建图形对象的时候就创建渐变对象,而应该是在渲染的时候创建渐变对象,且在图形对象平移或缩放的过程中,同步计算渐变对象的位置信息。
\quad
为此 anyGraph
设计了渐变对象类 Gradient
。我们通过一个具体样例来说明 Gradient
的设计,运行效果图如下:
\quad 这是一个展示矩形线性渐变的示例,
let colorSet = ["red", "orange", "gold", "green", "black", "blue"];
let width = 180;
let height = 120;
for (let x = 40; x < 800; x += width + 20) {
for (let y = 40; y < 400; y += height + 20) {
let color = colorSet[MathUtil.getRandomNum(0, colorSet.length - 1)];
let colorBand = Color.band(color, 10);
// 渐变对象
let gradient = new Gradient({
"type": "linear",
"coords": { "x1": x, "y1": y, "x2": x + width, "y2": y },
"colorStops": [{
"offset": "0.1",
"color": colorBand[2]
}, {
"offset": "0.95",
"color": colorBand[6]
}]
});
layer.getSource().add(new Rect({
x, y, width, height, "style": { "fillColor": gradient, "fillStyle": 1, "color": "none" }
}));
}
}
\quad
通过代码不难发现,在创建 Rect
矩形对象之前创建了 Gradient
渐变对象,该对象的坐标与矩形对象一致,通过type
属性指定为线性渐变,通过 colorStops
属性为渐变对象设定了 断点 。在创建 Rect
矩形对象时,通过该对象的style
属性的fillColor
属性将该渐变对象赋值到了矩形对象中。
\quad
这样就完成了为矩形对象指定渐变效果,在渲染 Rect
对象时,将会在执行 toPixel()
方法时同步为 Gradient
渐变对象计算像素坐标,在执行 draw()
方法时通过渲染上下文对象的createLinearGradient()
或 createRadialGradient()
API 实现渐变效果。
\quad 渐变效果在图形系统中有着广泛的应用。
- 色彩过渡:实现色彩之间的平滑过渡,使图像或画面的色彩更加自然和协调;
- 质感模拟:模拟出各种自然或非自然的质感,为设计作品增加更多的层次和细节;
- 视觉引导:引导用户的视觉焦点将用户的注意力引向特定的区域或元素等方面;
- 动态效果:渐变效果也可以用于实现动态的视觉效果。例如,在制作动画或动态背景时,可以使用渐变效果来创造出更加自然和流畅的运动轨迹。
\quad
下面这张图是使用 anyGraph
装载两个SVG文件,并使用渐变效果渲染图形,实现模拟真实世界的光照效果。
1.3 填充图案/纹理
\quad
渲染上下文对象提供了创建图案/纹理对象的方法createPattern()
,其定义如下:
CanvasPattern ctx.createPattern(image, repetition);
参数 | 说明 |
---|---|
image | 填充图案源 |
repetition | 重复方式 |
- 填充图案源可以是位图、也可以是的画布对象
- 重复方式包括:
- “repeat” :水平和垂直方向均重复
- “repeat-x” :仅水平方向重复
- “repeat-y” :仅垂直方向重复
- “no-repeat” :不重复
默认值是:repeat
\quad
绘制几何图形时,通过渲染上下文对象的strokeStyle
和fillStyle
两个属性指定几何图形边框效果和填充效果,而且这两个属性除了可以 使用颜色 和 渐变对象 外,还可以指定为 图案/纹理对象。
anyGraph 中的实现
\quad
和渐变的实现方式类似,anyGraph
提供了Pattern
类,可直接将该对象赋值给图形对象 style
属性的 fillColor
属性或 color
属性。
\quad 下面这个示例,使用图案/纹理对象填充了文字,其运行效果和源代码分别如下:
// graph对象
let graph = new Graph({
"target": "graphWrapper"
});
// 新建绘图图层
let layer = graph.addLayer();
let image = new ImageObject("./images/bg_06.jpg", function(){
let pattern = new Pattern({ "type": "image", "image": image });
layer.getSource().add(new Text({
"text": "图形开发",
"x": graph.getSize().width / 2,
"y": graph.getSize().height / 2,
"style": { "lineWidth": 4, "fillStyle": 0, "fillColor": pattern, "fontSize": 200, "fontName": "黑体", "textAlign": "center", "textBaseline": "middle" }
}));
});
2. 透明度
\quad Canvas 渲染上下文对象提供了通过颜色值设置图形对象的透明度和为画布指定透明度两种方式设置颜色的透明度。
2.1 通过颜色值设置透明度
\quad
在本章的1.1颜色部分介绍了 HTML颜色
,常见的设置颜色的方式有:
- 十六进制格式
- RGB格式
- 内置颜色名称
- HSL格式
\quad 除了内置颜色名称不能指定透明度外,其他三种方式均可以指定透明度。下面这个示例将绘制一个透明度为60%的红色矩形。
// 开始绘制路径
ctx.beginPath();
ctx.rect(50, 40, 200, 100);
ctx.fillStyle = "rgba(255,0,0,0.6)"; // Alpha:0.6
ctx.fill();
2.2 通过 globalAlpha 设置透明度
\quad
Canvas渲染上下文对象提供了 globalAlpha
属性, 该属性为在 canvas 绘图时设置图形和图像透明度的属性。
\quad
下面这个示例在fill()
之前设置了 ctx.globalAlpha = 0.6;
,其绘制的结果与上面那个示例运行的结果完全一样。
// 开始绘制路径
ctx.save();
ctx.beginPath();
ctx.rect(50, 40, 200, 100);
ctx.fillStyle = "rgb(255,0,0)";
ctx.globalAlpha = 0.6; // Alpha:0.6
ctx.fill();
ctx.restore();
\quad
globalAlpha
属性同样也是可以通过 ctx.save()和ctx.restore()
保存与恢复的。关于状态保存方面的内容请参见:图形系统开发实战-基础篇:第六章:画布操作
\quad
globalAlpha
数值的范围从 0.0(完全透明)到 1.0(完全不透明)。该属性不仅仅可以指定绘制图形的透明度,也可以指定绘制图像的透明度。下图是设置了 ctx.globalAlpha = 0.4
的运行效果。
2.3 anyGraph中的实现
\quad
anyGraph
中除了可以支持通过图形对象样式指定颜色透明度和globalAlpha
透明度之外,还可以设置图层的透明度。
颜色透明度
\quad 采用颜色透明度绘制图像比较简单,仅需在指定图形对象颜色的时候赋予颜色透明值即可。
globalAlpha 透明度
\quad
globalAlpha
透明度也是通过对象的style
属性中指定的,其属性名为 opacity
。
// 绘制图像
let image = new Image({
"x": 50,
"y": 50,
"width": 500,
"height": 450,
"style": { "opacity": 0.4 },
"src": "./images/ma.png"
});
layer.getSource().add(image);
图层透明度
\quad 本课程 第二章:图形管理类 中我们讲述过的图形渲染思路是:各个图层分别对应独立的Canvas画布,在画布中渲染各自图层中的数据,最后按图层顺序将各图层合并为完整的图形。
\quad
在这个思路的之上,图层透明度的实现就很简单了。在将各图层合并为完整的图形的时候,采用的是 ctx.drawImage()
方式,按照刚刚讲述的 globalAlpha
用法, 在执行 ctx.drawImage()
设置 globalAlpha
透明值,即可实现图层的透明控制。
\quad
Layer
图层类提供了 setOpacity(opacity)
和 getOpacity()
两个方法可设置和获取图层透明度,opacity
的取值范围仍旧是 0 ~ 1。下面这个示例演示图层透明度的功能。运行效果如下:
源代码如下:
<script type="module">
import { Graph, VectorSource, Layer, SvgFormat, debug } from "../../../src/index.js";
let layer1 = new Layer({
"name": "篮球",
"source": new VectorSource({
"dataType": "xml",
"fileUrl": "./images/basketball1.svg",
"format": new SvgFormat()
})
});
let layer2 = new Layer({
"name": "足球",
"source": new VectorSource({
"dataType": "xml",
"fileUrl": "./images/football2.svg",
"format": new SvgFormat()
})
});
// graph对象
let graph = new Graph({
"target": "graphWrapper",
"layers": [layer1, layer2],
});
// 图层1透明度控制
let slideBar1 = document.getElementById("slideBar1");
slideBar1.addEventListener("change", function (e) {
layer1.setOpacity(this.value / 100);
graph.render();
})
// 图层2透明度控制
let slideBar2 = document.getElementById("slideBar2");
slideBar2.addEventListener("change", function (e) {
layer2.setOpacity(this.value / 100);
graph.render();
})
</script>
3. 阴影
Canvas 渲染上下文对象提供了以下几个和阴影效果有关的属性。
属性 | 说明 |
---|---|
shadowBlur | 描述模糊效果程度的,float 类型的值。 |
shadowColor | 可以转换成 CSS 值的DOMString 字符串。 |
shadowOffsetX | 阴影水平偏移距离的 float 类型的值。默认值是 0。 |
shadowOffsetY | 阴影垂直偏移距离的 float 类型的值。默认值是 0。 |
\quad 关于这部分内容的讲解请参见:图形系统开发实战-基础篇:第五章:渲染效果
3.1 anyGraph中的实现
\quad
图形对象 Geometry
类的 Sytle
对象中包含了与Canvas 渲染上下文对象阴影属性对应的属性,如下表所示:
参数 | 说明 |
---|---|
shadowBlur | 模糊效果程度 |
shadowColor | 模糊颜色 |
shadowOffsetX | 阴影水平偏移距离 |
shadowOffsetY | 阴影垂直偏移距离 |
下面这个示例演示为图形对象设置阴影效果。运行效果如下:
源代码如下:
let colorSet = ["red", "orange", "gold", "green", "black", "blue"];
let radius = 60;
for (let x = 100; x < 800; x += radius * 2.5) {
for (let y = 100; y < 500; y += radius * 2.5) {
let color = colorSet[MathUtil.getRandomNum(0, colorSet.length - 1)];
let colorBand = Color.band(color, 10);
// 渐变对象
let gradient = new Gradient({
"type": "radial",
"coords": { "x1": x + radius / 2, "y1": y + radius / 2, "r1": 0, "x2": x, "y2": y, "r2": radius },
"colorStops": [{
"offset": "0.1",
"color": colorBand[2],
}, {
"offset": "0.95",
"color": colorBand[7]
}]
});
// 圆
layer.getSource().add(new Circle({
x, y, radius,
"style": {
"fillColor": gradient,
"fillStyle": 1,
"color": "none",
"shadowOffsetX": MathUtil.getRandomNum(3, 8),
"shadowOffsetY": MathUtil.getRandomNum(2, 5),
"shadowBlur": 30,
"shadowColor": colorBand[8]
}
}));
}
}
\quad “图形系统实战开发-进阶篇 第八章 图形样式” 的内容讲解到这里就结束了,如果觉得对你有帮助有收获,可以关注我们的官方账号,持续关注更多精彩内容。
相关资料
▶ 系列教程及代码资料:https://GraphAnyWhere.com
▶ 图形系统开发实战课程:进阶篇(上)——前言
▶ 图形系统开发实战课程:进阶篇(上)——1.基础知识
▶ 图形系统开发实战课程:进阶篇(上)——2.图形管理类(Graph)
▶ 图形系统开发实战课程:进阶篇(上)——3.图层类(Layer)
▶ 图形系统开发实战课程:进阶篇(上)——4.图形基本形状
▶ 图形系统开发实战课程:进阶篇(上)——5.图形交互操作:平移和缩放
▶ 图形系统开发实战课程:进阶篇(上)——6.图形交互操作:拾取
▶ 图形系统开发实战课程:进阶篇(上)——7.图形交互操作: 视点控制与动画
作者信息
作者 : 图形开发学院
CSDN: https://blog.csdn.net/2301_81340430?type=blog
官网:https://graphanywhere.com