前言分析设计稿实现效果绘制空心线与发光效果绘制倒影绘制轨道中间的斑马线效果结语
近日公司接到一个轨道系统的需求,需要将地铁线路及列车实时位置展示在大屏上。既然是大屏项目,那视觉效果当然是第一重点,咱们可以先来看看项目完成后的效果图。
先看看设计稿中的轨道效果
绘制空心线时我们需要利用到[CanvasRenderingContext2D.globalCompositeOperation](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation)这个属性,详细原理可以查看canvas 绘制双线技巧,本文不再做赘述。
了解实现原理之后动手就很容易了,简述思路就是:
通过ctx.globalCompositeOperation = "destination-out"绘制空心线,再利用canvas的阴影配置来模拟发光的效果。
直接上代码:
1// 获取页面里的画布元素和其上下文对象
2var canvas = document.getElementById("canvas");
3var ctx = canvas.getContext("2d");
4// 由于ctx.globalCompositeOperation = "destination-out"会影响到画布上已有的图像
5// 所以需要先创建一个离屏canvas,把空心线绘制到离屏canvas上,再将离屏canvas绘制到页面的画布中
6var tempCanvas = document.createElement("canvas");
7tempCanvas.width = 800;
8tempCanvas.height = 800;
9var tempCtx = tempCanvas.getContext("2d");
10// 创建坐标点用来连线
11var points = [createPoint(50, 50), createPoint(500, 50), createPoint(500, 500)];
12// 配置参数
13var options = {
14 color: "#03a4fe", // 轨道颜色
15 lineWidth: 26, // 总宽度
16 borderWidth: 8, // 边框宽度
17 shadowBlur: 20, // 阴影模糊半径
18};
19paint(ctx, points, options);
20// 绘制
21function paint(ctx, points, options) {
22 paintHollow(tempCtx, points, options);
23 // 将离屏canvas绘制到页面上
24 ctx.drawImage(tempCanvas, 0, 0);
25}
26/**
27 * 绘制空心线
28 * @param {*} ctx 画布上下文
29 * @param {*} points 坐标点的集合
30 * @param {*} options 配置
31 */
32function paintHollow(
33 ctx,
34 points,
35 { color, lineWidth, borderWidth, shadowBlur }
36) {
37 // 连线
38 paintLine(ctx, points);
39 // 添加配置参数
40 ctx.lineWidth = lineWidth;
41 ctx.strokeStyle = color;
42 ctx.lineCap = "round";
43 ctx.lineJoin = "round";
44 // 利用阴影
45 ctx.shadowColor = color;
46 ctx.shadowOffsetX = 0;
47 ctx.shadowOffsetY = 0;
48 ctx.shadowBlur = shadowBlur;
49 ctx.stroke();
50 ctx.globalCompositeOperation = "destination-out";
51 ctx.lineWidth -= borderWidth;
52 ctx.strokeStyle = color;
53 ctx.stroke();
54 ctx.globalCompositeOperation = "source-over";
55}
56/**
57 * 根据点位绘制连线
58 * @param {*} ctx 画布上下文
59 * @param {Array} points 坐标点的集合
60 */
61function paintLine(ctx, points) {
62 var pointIndex = 0,
63 p0,
64 value,
65 pointCount = points.length;
66 p0 = points[0];
67 ctx.beginPath();
68 ctx.moveTo(p0.x, p0.y);
69 for (pointIndex = 1; pointIndex < pointCount; pointIndex++) {
70 value = points[pointIndex];
71 ctx.lineTo(value.x, value.y);
72 }
73}
效果图
可以看到设计稿里的倒影效果就是在轨道下方再次绘制了一条透明度较低的空心线,所以这里实现起来就比较简单了,稍微改造一下paintHollow方法就可以。
1/**
2 * 绘制空心线
3 * @param {*} ctx 画布上下文
4 * @param {*} points 坐标点的集合
5 * @param {*} options 配置
6 * @param {*} isReflect 当前绘制的是否是倒影效果
7 */
8function paintHollow(
9 ctx,
10 points,
11 { color, lineWidth, borderWidth, shadowBlur, reflectOffset },
12 isReflect = false
13) {
14 if (!isReflect) {
15 // 绘制倒影的时候透明度降低
16 ctx.globalAlpha = 0.5;
17 // 通过自调绘制一个倒影效果出来
18 paintHollow(
19 ctx,
20 points.map(({ x, y }) => {
21 return { x, y: y + reflectOffset };
22 }),
23 { color, lineWidth, borderWidth, shadowBlur: 0 },
24 true
25 );
26 ctx.globalAlpha = 1;
27 }
28 // 连线
29 paintLine(ctx, points);
30 // 添加配置参数
31 ctx.lineWidth = lineWidth;
32 ctx.strokeStyle = color;
33 ctx.lineCap = "round";
34 ctx.lineJoin = "round";
35 // 利用阴影
36 ctx.shadowColor = color;
37 ctx.shadowOffsetX = 0;
38 ctx.shadowOffsetY = 0;
39 ctx.shadowBlur = shadowBlur;
40 ctx.stroke();
41 ctx.globalCompositeOperation = "destination-out";
42 ctx.lineWidth -= borderWidth;
43 ctx.strokeStyle = color;
44 ctx.stroke();
45 ctx.globalCompositeOperation = "source-over";
46}
效果图
中间的斑马线效果我们又可以再拆分为两个部分,先绘制一条底色的连线,然后再通过lineDash属性绘制一条虚线,就可以达到设计稿上的效果了。
1/**
2 * 绘制轨道中间部分
3 * @param {*} ctx
4 * @param {*} points
5 * @param {*} param2
6 */
7function paintInner(
8 ctx,
9 points,
10 { color, innerWidth, borderWidth, innerColor, shadowBlur }
11) {
12 ctx.lineCap = "round";
13 ctx.lineJoin = "round";
14 paintLine(ctx, points);
15 ctx.lineWidth = innerWidth;
16 ctx.shadowOffsetX = 0;
17 ctx.shadowOffsetY = 0;
18 ctx.shadowBlur = shadowBlur;
19 ctx.strokeStyle = innerColor;
20 ctx.shadowColor = color;
21 // 先根据中间部分的颜色绘制一条线出来
22 ctx.stroke();
23 ctx.lineCap = "butt";
24 ctx.setLineDash([5, 15]);
25 ctx.lineDashOffset = 0;
26 const { r, g: green, b } = getRgba(color);
27 // 再根据轨道的主色调绘制一条透明度较低的虚线
28 ctx.strokeStyle = `rgba(${r},${green},${b},0.4)`;
29 ctx.stroke();
30}
31/**
32 * 获取一个颜色值的r,g,b,a
33 * @param {*} color
34 */
35function getRgba(color) {
36 if (!canvas1 || !ctx1) {
37 canvas1 = document.createElement("canvas");
38 canvas1.width = 1;
39 canvas1.height = 1;
40 ctx1 = canvas1.getContext("2d");
41 }
42 canvas1.width = 1;
43 ctx1.fillStyle = color;
44 ctx1.fillRect(0, 0, 1, 1);
45 const colorData = ctx1.getImageData(0, 0, 1, 1).data;
46 return {
47 r: colorData[0],
48 g: colorData[1],
49 b: colorData[2],
50 a: colorData[3],
51 };
52}
效果图
至此文章已经到达尾声,我们可以总结一下绘制这条轨道线路效果所用到的技术点
CanvasRenderingContext2D.globalCompositeOperation
CanvasRenderingContext2D.shadowBlur
CanvasRenderingContext2D.setLineDash()
离屏canvas技巧
可以看到想要达到好的效果还是不容易的,需要我们灵活配合使用多种绘制技巧,希望这篇文章能对大家有所帮助!
如果对可视化感兴趣,可以和我交流,微信541002349. 另外关注公众号“ITMan彪叔” 可以及时收到更多有价值的文章。