利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果
利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果
前言
近日公司接到一个轨道系统的需求,需要将地铁线路及列车实时位置展示在大屏上。既然是大屏项目,那视觉效果当然是第一重点,咱们可以先来看看项目完成后的效果图。
可以看到中间线路里轨道的效果是非常炫酷的,那么本文的主要内容就是讲解如何在canvas上绘制出这种效果。
分析设计稿
先看看设计稿中的轨道效果
程序员解决问题时经常喜欢用到的方法是把一个大问题拆解为若干个小问题然后逐一处理,也就是分而治之,所以我在思考这个轨道效果的实现时,也是先考虑到将它拆解。
根据设计稿我们可以看到这个线路实际上是由 外层的空心线+发光效果+内层的斑马线+倒影 组成的,所以我们要做的就是如何处理这几个小问题。
实现效果
绘制空心线与发光效果
绘制空心线时我们需要利用到[CanvasRenderingContext2D.globalCompositeOperation](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation)这个属性,详细原理可以查看canvas 绘制双线技巧,本文不再做赘述。
了解实现原理之后动手就很容易了,简述思路就是:
通过ctx.globalCompositeOperation = "destination-out"绘制空心线,再利用canvas的阴影配置来模拟发光的效果。
直接上代码:
// 获取页面里的画布元素和其上下文对象
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// 由于ctx.globalCompositeOperation = "destination-out"会影响到画布上已有的图像
// 所以需要先创建一个离屏canvas,把空心线绘制到离屏canvas上,再将离屏canvas绘制到页面的画布中
var tempCanvas = document.createElement("canvas");
tempCanvas.width = 800;
tempCanvas.height = 800;
var tempCtx = tempCanvas.getContext("2d");
// 创建坐标点用来连线
var points = [createPoint(50, 50), createPoint(500, 50), createPoint(500, 500)];
// 配置参数
var options = {
color: "#03a4fe", // 轨道颜色
lineWidth: 26, // 总宽度
borderWidth: 8, // 边框宽度
shadowBlur: 20, // 阴影模糊半径
};
paint(ctx, points, options);
// 绘制
function paint(ctx, points, options) {
paintHollow(tempCtx, points, options);
// 将离屏canvas绘制到页面上
ctx.drawImage(tempCanvas, 0, 0);
}
/**
* 绘制空心线
* @param {*} ctx 画布上下文
* @param {*} points 坐标点的集合
* @param {*} options 配置
*/
function paintHollow(
ctx,
points,
{ color, lineWidth, borderWidth, shadowBlur }
) {
// 连线
paintLine(ctx, points);
// 添加配置参数
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.lineCap = "round";
ctx.lineJoin = "round";
// 利用阴影
ctx.shadowColor = color;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = shadowBlur;
ctx.stroke();
ctx.globalCompositeOperation = "destination-out";
ctx.lineWidth -= borderWidth;
ctx.strokeStyle = color;
ctx.stroke();
ctx.globalCompositeOperation = "source-over";
}
/**
* 根据点位绘制连线
* @param {*} ctx 画布上下文
* @param {Array} points 坐标点的集合
*/
function paintLine(ctx, points) {
var pointIndex = 0,
p0,
value,
pointCount = points.length;
p0 = points[0];
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
for (pointIndex = 1; pointIndex < pointCount; pointIndex++) {
value = points[pointIndex];
ctx.lineTo(value.x, value.y);
}
}
效果图
绘制倒影
可以看到设计稿里的倒影效果就是在轨道下方再次绘制了一条透明度较低的空心线,所以这里实现起来就比较简单了,稍微改造一下paintHollow方法就可以。
/**
* 绘制空心线
* @param {*} ctx 画布上下文
* @param {*} points 坐标点的集合
* @param {*} options 配置
* @param {*} isReflect 当前绘制的是否是倒影效果
*/
function paintHollow(
ctx,
points,
{ color, lineWidth, borderWidth, shadowBlur, reflectOffset },
isReflect = false
) {
if (!isReflect) {
// 绘制倒影的时候透明度降低
ctx.globalAlpha = 0.5;
// 通过自调绘制一个倒影效果出来
paintHollow(
ctx,
points.map(({ x, y }) => {
return { x, y: y + reflectOffset };
}),
{ color, lineWidth, borderWidth, shadowBlur: 0 },
true
);
ctx.globalAlpha = 1;
}
// 连线
paintLine(ctx, points);
// 添加配置参数
ctx.lineWidth = lineWidth;
ctx.strokeStyle = color;
ctx.lineCap = "round";
ctx.lineJoin = "round";
// 利用阴影
ctx.shadowColor = color;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = shadowBlur;
ctx.stroke();
ctx.globalCompositeOperation = "destination-out";
ctx.lineWidth -= borderWidth;
ctx.strokeStyle = color;
ctx.stroke();
ctx.globalCompositeOperation = "source-over";
}
效果图
绘制轨道中间的斑马线效果
中间的斑马线效果我们又可以再拆分为两个部分,先绘制一条底色的连线,然后再通过lineDash属性绘制一条虚线,就可以达到设计稿上的效果了。
/**
* 绘制轨道中间部分
* @param {*} ctx
* @param {*} points
* @param {*} param2
*/
function paintInner(
ctx,
points,
{ color, innerWidth, borderWidth, innerColor, shadowBlur }
) {
ctx.lineCap = "round";
ctx.lineJoin = "round";
paintLine(ctx, points);
ctx.lineWidth = innerWidth;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = shadowBlur;
ctx.strokeStyle = innerColor;
ctx.shadowColor = color;
// 先根据中间部分的颜色绘制一条线出来
ctx.stroke();
ctx.lineCap = "butt";
ctx.setLineDash([5, 15]);
ctx.lineDashOffset = 0;
const { r, g: green, b } = getRgba(color);
// 再根据轨道的主色调绘制一条透明度较低的虚线
ctx.strokeStyle = `rgba(${r},${green},${b},0.4)`;
ctx.stroke();
}
/**
* 获取一个颜色值的r,g,b,a
* @param {*} color
*/
function getRgba(color) {
if (!canvas1 || !ctx1) {
canvas1 = document.createElement("canvas");
canvas1.width = 1;
canvas1.height = 1;
ctx1 = canvas1.getContext("2d");
}
canvas1.width = 1;
ctx1.fillStyle = color;
ctx1.fillRect(0, 0, 1, 1);
const colorData = ctx1.getImageData(0, 0, 1, 1).data;
return {
r: colorData[0],
g: colorData[1],
b: colorData[2],
a: colorData[3],
};
}
效果图
至此我们就还原了设计稿上的轨道效果了!
结语
至此文章已经到达尾声,我们可以总结一下绘制这条轨道线路效果所用到的技术点
- CanvasRenderingContext2D.globalCompositeOperation
- CanvasRenderingContext2D.shadowBlur
- CanvasRenderingContext2D.setLineDash()
- 离屏canvas技巧
可以看到想要达到好的效果还是不容易的,需要我们灵活配合使用多种绘制技巧,希望这篇文章能对大家有所帮助!
如果对可视化感兴趣,可以和我交流,微信541002349. 另外关注公众号“ITMan彪叔” 可以及时收到更多有价值的文章。
利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果的更多相关文章
- 利用canvas和RGraph作图
利用canvas可以直接在页面中绘制各种复杂的图形,其中引用到一个Rgraph的插件. Rgraph插件使用非常方便,只需几步就可以完成一个折线图.饼图.柱状图,或是其中两者图形的结合! (1) 引用 ...
- canvas可视化效果之内阴影效果
canvas可视化效果之内阴影效果 楔子 在之前的一个轨道交通可视化项目中,运用到了很多绘制技巧. 可以参考 之前的一篇文章 <利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果> ...
- canvas 绘制双线技巧
楔子 最近一个项目,需要绘制双线的效果,双线效果表示的是轨道(类似铁轨之类的),如下图所示: 负责这块功能开发的小伙,姑且称之为L吧,最开始是通过数学计算的方式来实现这种双线,也就是在原来的路径的基础 ...
- 利用Canvas进行绘制XY坐标系
首先来一发图 绘制XY的坐标主要是利用Canvas setLeft和setBottom功能(Canvas内置坐标的功能) 1.首先WPF中的坐标系都是从左到右,从上到下的 即左上角位置(0,0)点,所 ...
- 小程序利用canvas 绘制图案 (生成海报, 生成有特色的头像)
小程序利用canvas 绘制图案 (生成海报, 生成有特色的头像) 微信小程序生成特色头像,海报等是比较常见的.下面我来介绍下实现该类小程序的过程. 首先选择前端来通过 canvas 绘制.这样比较节 ...
- 10分钟,利用canvas画一个小的loading界面
首先利用定义下canvas得样式 <canvas width="1024" height="720" id="canvas" styl ...
- Canvas绘画功能(待补充)
由于项目的前端需要用户手绘输入,所以我们利用Canvas控件做绘画面板,并且实现了许多功能,包括手绘笔画,清空画板,上传手绘图,下载手绘图,记录用户笔画,上传背景图.以后有时间都写到这篇博客中,今天晚 ...
- 利用canvas实现的中点Bresenham算法
Bresenham提出的直线生成算法的基本原理是,每次在最大位移方向上走一步,而另一个方向是走步还是不走步取决于误差项的判别,具体的实现过程大家可以去问度娘.我主要是利用canvas画布技术实现了这个 ...
- Canvas入门(1):绘制矩形、圆、直线、曲线等基本图形
来源:http://www.ido321.com/968.html 一.Canvas的基础知识 Canvas是HTML 5中新增的元素,专门用于绘制图形.canvas元素就相当于一块“画布”,一块无色 ...
随机推荐
- Java -- "final" 的理解
Java具有继承和多态的特性,这也造就了Java语言的面向对象的灵活性.但是,过于灵活就意味的有失控的可能性. 于是,产生了final 的概念 -- 为了数据的绝对安全,无法被后期修改,英文称之为 m ...
- Java学习之路(一)——JDK的下载与安装
(一).JDK是Java开发工具包 下载网址:https://www.oracle.com/java/technologies/javase-downloads.html (二).安装 下载JDK以后 ...
- delphi 设置默认控件得到焦点
如果同一窗体有多个按钮的话,追踪源码发现最后是taborder来的 如: 在空白窗体上拖入两个button (btn1,btn2) 如果在btn2设置default = True 运行后,默认焦点还是 ...
- 人体动作捕捉格式之BVH
BVH简介 BVH是BioVision公司推出的一种人体动作捕捉文件格式.这种文件以节点为核心元素,记录连续数帧内人体骨架的运动. BVH=? 研究一个东西的时候我比较喜欢先研究它的名字.BVH可以认 ...
- linux tcp Nagle算法,TCP_NODELAY和TCP_CORK 转载
转载自: http://www.cnhalo.net/2016/08/13/linux-tcp-nagle-cork/ http://abcdxyzk.github.io/blog/2018/07/0 ...
- 这些表情包你有吗?来 Battle 啊
"能用表情包解决的问题,绝不多说一个字." "当不知道回复什么的时候,甩过去一个表情包就好了." 放眼望去,谁的 QQ/微信 收藏中没有几页代表性的表情包,那真 ...
- 认识Redis集群——Redis Cluster
前言 Redis集群分三种模式:主从模式.sentinel模式.Redis Cluster.之前没有好好的全面理解Redis集群,特别是Redis Cluster,以为这就是redis集群的英文表达啊 ...
- Python基础数据类型与for循环
数据类型:int,bool,str,list, tuple元组,dict字典. 1.数字:12,3,4 在使用print打印数字时,在终端界面中无法判断出打印的是什么类型,当我们需要知道一个值是什么类 ...
- tcp黏包问题与udp为什么不黏包
1.先说下subprocess模块的用法,为了举个黏包的例子 # 通过一个例子 来认识网络编程中的一个重要的概念 # 所有的客户端执行server端下发的指令,执行完毕后,客户端将执行结果给返回给服务 ...
- 想换4K显示器了?那你搞懂啥是4K了吗?
前言 我们在科技资讯以及电脑显示器.数字电视等电子产品的宣传语中,经常能够看见4K的字样.最近,B站(哔哩哔哩)升级了HTML5播放器和视频云等相关服务,为广大用户提供了超高清(UHD: Ultra ...