线条样式
前言
"唰","唰","唰",还记得学习素描的时候,我们会用不同的笔来绘制各种各样的线条,有粗有细,有虚有实,有深有浅,有平行有相交。这一节我们将拿起画笔,就单纯的画画线,打打底子。那么在 Canvas 中,是通过什么属性或者方法来定义线条的样式呢?
为了本文讲解,本文中所有的 Canvas 上下文用 ctx 对象来表示
lineWidth 属性
js
ctx.lineWidth = 整数;
Linewidth 属性取值为整数,默认为1,单位 px。
这个属性前面的文章也用过,没有做任何的讲解,但是我想大家应该看了就知道这就是线宽的设置,通过文本意义应该一目了然。
js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 10;
ctx.strokeStyle = "hotpink";
ctx.moveTo(20, 30);
ctx.lineTo(180, 30);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 5;
ctx.moveTo(20, 70);
ctx.lineTo(180, 70);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(20, 120);
ctx.lineTo(180, 120);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

我们结合图和代码来看一下,第二条线和第三条线为啥线宽是一样的 5px 呢?那是因为在每一次绘制的时候,Canvas 会检测整个代码定义的样式,如果在新的路径中没有定义样式,则会延用原来定义样式。
我们再来看一下这种情况:
js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 10;
ctx.strokeStyle = "hotpink";
ctx.moveTo(20, 30);
ctx.lineTo(180, 30);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 5;
ctx.moveTo(20, 70);
ctx.lineTo(180, 70);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 15;
ctx.moveTo(20, 120);
ctx.lineTo(180, 120);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 10;
ctx.strokeStyle = "hotpink";
ctx.moveTo(20, 30);
ctx.lineTo(180, 30);
ctx.stroke();
ctx.lineWidth = 5;
ctx.moveTo(20, 70);
ctx.lineTo(180, 70);
ctx.stroke();
ctx.lineWidth = 15;
ctx.moveTo(20, 120);
ctx.lineTo(180, 120);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

当我们删掉 ctx.beginPath(); 时,三条线变成了一样的线宽,怎么回事呢?那是因为在同一个路径中,Canvas 会使用最后一次定义的样式,也就是说想使用不同的样式时,需要以开始一条新路(ctx.beginPath)径来重新定义线条的样式属性。
lineCap 属性
lineCap 属性用于显示线条开始处与结尾处线帽样式;
js
ctx.lineCap = "Butt";
// 取值为:
// "Butt": 默认值,无线帽
// "Round": 圆形线帽
// "Square": 方形线帽
对应的属性值大小写都可以,我们来用实际对比代码来看看这几个属性值有啥区别:
js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 1;
ctx.strokeStyle = "#cccccc";
ctx.moveTo(10, 0);
ctx.lineTo(10, 150);
ctx.moveTo(180, 0);
ctx.lineTo(180, 150);
ctx.stroke();
ctx.beginPath();
ctx.lineWidth = 20;
ctx.lineCap = "butt";
ctx.strokeStyle = "hotpink";
ctx.moveTo(10, 30);
ctx.lineTo(180, 30);
ctx.stroke();
ctx.beginPath();
ctx.lineCap = "round";
ctx.moveTo(10, 70);
ctx.lineTo(180, 70);
ctx.stroke();
ctx.beginPath();
ctx.lineCap = "square";
ctx.moveTo(10, 120);
ctx.lineTo(180, 120);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

我们可以从图片与代码中看到,"round" 和 "square" 的长度比 "butt" 更长,要长多少?从代码中我们能看到,长一个 lineWidth 的长度,两头平分,总结一下:有线帽的比没线帽的长一个线宽的长度。
lineJoin 属性
在 Canvas 中,lineJoin 属性定义两个线条交接处的样式。
js
ctx.lineJoin = "miter";
// 取值为:
// "miter": 默认值,尖角
// "round": 圆角
// "bevel": 斜角
我们具体来看看 lineJoin 属性分别有什么区别:
js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 20;
ctx.strokeStyle = "hotpink";
ctx.beginPath();
ctx.lineJoin = "miter";
ctx.moveTo(10, 20);
ctx.lineTo(180, 20);
ctx.lineTo(180, 40);
ctx.stroke();
ctx.beginPath();
ctx.lineJoin = "round";
ctx.moveTo(10, 70);
ctx.lineTo(180, 70);
ctx.lineTo(180, 100);
ctx.stroke();
ctx.beginPath();
ctx.lineJoin = "bevel";
ctx.moveTo(10, 120);
ctx.lineTo(180, 120);
ctx.lineTo(180, 140);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

从上图及代码中我们可以总结出三者的区别:
- mitter: 线条直接延伸交于点,为默认值;
- round:交接处是一个圆角,圆角所在圆直径等于线宽;
- bevel:交接处是一个斜角,斜角所在正方形的对角线长等于线宽。
setLineDash() 方法
在 Canvas 中,用setLineDash() 方法来设置线条虚实样式。
js
ctx.setLineDash(array);
// array 是一个数组组合
我们来看看具体怎么使用:
js
<template>
<canvas ref="cnv" width="200" height="150" style="border: 1px dashed gray"></canvas>
</template>
<script setup>
import {ref, onMounted} from "vue";
const cnv = ref();
const drawLine = () => {
const ctx = cnv.value.getContext('2d');
ctx.lineWidth = 2;
ctx.strokeStyle = "hotpink";
ctx.beginPath();
ctx.moveTo(10, 20);
ctx.lineTo(180, 20);
ctx.setLineDash([2, 2]);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 60);
ctx.lineTo(180, 60);
ctx.setLineDash([5, 5]);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 80);
ctx.lineTo(180, 80);
ctx.setLineDash([10, 5]);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(10, 120);
ctx.lineTo(180, 120);
ctx.setLineDash([20, 5, 5, 5]);
ctx.stroke();
}
onMounted(() => {
drawLine();
});
</script>

从上面的图我们可以总结出参数的规则:
- setLineDash() 方法接受一个整数数组;
- 数组索引偶数为实线长度,奇数为虚线间隔距离;
- 如果设置虚线绘制,即使使用 beginPath()方法也不能重置绘制的线为实线,需手动设置参数为[] 或 [num, 0]。
总结
本节内容很简单,主要就是针对线条的样式的设定,样式的设定给单调的线条有增多各种展示与应用的可能性。