关于canvas:利用canvas阴影功能与双线技巧绘制轨道交通大屏项目效果

1次阅读

共计 4314 个字符,预计需要花费 11 分钟才能阅读完成。

利用 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],
  };
}

效果图

至此咱们就还原了设计稿上的轨道成果了!

结语

至此文章曾经达到序幕,咱们能够总结一下绘制这条轨道线路成果所用到的技术点

  1. CanvasRenderingContext2D.globalCompositeOperation
  2. CanvasRenderingContext2D.shadowBlur
  3. CanvasRenderingContext2D.setLineDash()
  4. 离屏 canvas 技巧

能够看到想要达到好的成果还是不容易的,须要咱们灵便配合应用多种绘制技巧,心愿这篇文章能对大家有所帮忙!

如果对可视化感兴趣,能够和我交换,微信 541002349. 另外关注公众号“ITMan 彪叔”能够及时收到更多有价值的文章。

正文完
 0