前言:本来懒得写这个博客,实在深感无聊,没啥事情做,出篇博客让大家看看。文章会尽可能简短。

简单效果

掉帧属录屏效果,尚未测试过性能,因为这个可以看自己调节。以下为一条贝塞尔曲线分了180段的效果描述。
颜色属于瞎编乱造,只为示例,不为效果负责。

来自新增2022/7/26

发了个包,应该说如果你想学习实现的思路,尽管往下看,如果你想要使用这个效果
ol-dynamic-curves地址
直接根据npm的内容使用即可。
里面装了一些比较常见的可能会处理到的API,新增删除,等等,以及配置的选项。
如果你想学习代码实现的核心内容
b站视频

准备步骤

1、先生成起点与终点的表示点。这个很重要,原因在于:openlayers 会智能的检测图层中的数据源(source)是否有需要更新的features,如果你没有设置features,或者不在视图内,是不会触发渲染、因此,也就不会触发我们需要的prerender事件。
2、监听图层的prerender 事件,顾名思义,prerender 意味着这是 openlayers 暴露出来的一个图层的渲染事件,prerender意味着在渲染前执行的一个函数,他会传入一个renderEvent对象。
3、获取到renderEvent之后,我们可以通过此API,获取一个有关于openlayers底层对当前图层绘制的canvas内容,里头主要封装了两个操作:绘制geometry, 设置样式

核心实现

贝塞尔曲线的实现

上图中可以看出,线是动态画出来的,其实是由一个个密集的线表示而绘制出来的一条曲线。为此,我们分为三点一个个去说。
1、动态增加的线
需要一个数组去记录当前应该渲染的线的集合。以及一个用于表示上一个结束点坐标的位置 去 在下一个阶段 作为 开始位置。

let lineCoords = []
let startPos = ...
layer.on('prerender',function(evt){let endPos = []lineCoords.push([startPos,endPos])let geometry = new MultiLineString(lineCoords);
})

2、曲线的绘制

本例使用二阶贝塞尔曲线绘制。不懂请去别处找资料。

主要在于控制点的寻找。以及如何获取当前贝塞尔曲线上的点坐标。
getCurrentEnd 函数 里面的计算 取自 百度百科上的贝塞尔曲线 二次方公式

function ConstantMultiVector2(c, pos) {return [c * pos[0], c * pos[1]];}function vector2Add(a, b) {return [a[0] + b[0], a[1] + b[1]];}/*** a = > [ lng,lat]* b = > [ lng,lat]* n => ratio* 二维向量线性插值*/function linerInperpote(a, b, n) {let curA = ConstantMultiVector2(1.0 - n, a);let curB = ConstantMultiVector2(n, b);return vector2Add(curA, curB);}//  获取 当前贝塞尔曲线上的 点坐标function getCurrenetEnd(originPos1, center, originPos2, times) {let curTimes = times / 180;let a = ConstantMultiVector2(Math.pow(1.0 - curTimes, 2), originPos1);let b = ConstantMultiVector2(2 * curTimes * (1 - curTimes), center);let c = ConstantMultiVector2(Math.pow(curTimes, 2), originPos2);return vector2Add(vector2Add(a, b), c);}

以下表示控制点为: 开始点 与结束点 的中点 的经纬度位置 ,经度减10作为控制点。

let controlPos = vector2Add(linerInperpote(originPos1, originPos2, 0.5),[-10.0, 0])

3、段数的处理(时间的增加)
涉及到动画必然跟时间会有联系。这里我们选择以线段的处理到达终点作为一个周期。
将整个线位置的输入过程分为180段。并非180段最好,这个看个人的设置。理论上来说段数越高,表现越明显,当然,运动会越慢,所以合适就好。

let times = 0;
layer.on("prerender", (evt) => {if (times % 180 === 0) {times = 0;lineCoords = [];lastEndPos = startPos;}times++;layer.changed()
}

在结束一个周期时,初始化相关的变量。
调用layer.changed() 重复执行这个函数,即告诉openlayers框架:当窗口中存在该图层的features时,始终更新此图层。
4、渲染

layer.on("prerender", (evt) => {let geometry = new MultiLineString(multiCoords);let ctx = getVectorContext(evt);ctx.setStyle(new Style({stroke: new Stroke({// 模板字符串color: 'red',lineCap: "butt",width: 3,}),}));
}
ctx.drawGeometry(geometry);

样式处理

渐变色处理

理论上来说,你可以操作每一条线的颜色,但通常我们不会这么做,因为太损耗性能了。(理由跟canvas的底层设计有关,有兴趣可以去搜索下。总之fillStyle strokeStyle的设置耗时可能比绘制还长)

而可以看到上图,实际上就是个渐变色的应用。只不过是比较不常见的一个圆形渐变。不使用大家更常见的linear-gradient渐变 也就是线性渐变的原因如下图。

从表现形式上来说,我更喜欢一小段呈现出更加多变的颜色。而且只要颜色设置的较为相近,应该说线条的颜色还是会挺好看的。
圆形渐变许多人了解较少。这里特地说明一下。

主要分为开始圆跟结束圆的渐变色叠加,也就是说,我们大可以设置两个同样的色板,对开始圆的坐标进行偏移达到一种绚丽的效果。但更普遍的,我们一般只用一个圆就够了。
在使用之前,我们还需要计算当前两个线之间,开始点与结束点的距离以让整个圆在开始点的坐标将颜色扩散出去。同时将开始点与结束点都迁移到开始点的屏幕像素位置。

     // 通过getPixelFromCoordinate 获取当前位置对应的屏幕像素位置let getPixelFromCoordinate = this.map.getPixelFromCoordinate.bind(this.map);let startGrdPixelPos = getPixelFromCoordinate(pos1);let endGrdPixelPos = getPixelFromCoordinate(pos2);let xdiff = endGrdPixelPos[0] - startGrdPixelPos[0] let ydiff = endGrdPixelPos[1] - startGrdPixelPos[1]let radius = Math.pow(Math.pow(xdiff,2) + Math.pow(ydiff,2),0.5);var grd = ctx.context_.createRadialGradient(startGrdPixelPos[0],startGrdPixelPos[1],0,startGrdPixelPos[0],startGrdPixelPos[1],radius);grd.addColorStop(0, "yellow");grd.addColorStop(0.2, "red");grd.addColorStop(0.4, "pink");grd.addColorStop(0.6, "green");grd.addColorStop(0.8, "orange");grd.addColorStop(1, "blue");ctx.setStyle(new Style({stroke: new Stroke({// 模板字符串color: grd,lineCap: "butt",width: 3,}),}));ctx.drawGeometry...

箭头处理

本实例中箭头主要是通过添加Icon 的方式 对图片进行旋转达到的。所以说,对比使用逻辑去计算的箭头应该说方便许多。但是有一点在这里需要注意: 不要使用src 去 为Icon 添加图片。此处也困扰了我很久,后面我基本上确定这就是一个BUG。使用src属性在prerender函数这里调用setStyle你是创建不了图片的。至于是为什么,这里就不再赘述了。
因此,我们使用图片对象去做处理。

let arrowImage = new Image();
// 再说一次: 在vue 里面, 静态文件资源放于public目录下
// 意味着此时的请求路径,如果你的端口是8080,从本质上来说等于: http://localhost:8080/image/arrow1.png
// 你的目录结构应为 public/image/arrow1.png
// 再问我就自杀
arrowImage.src = "image/arrow1.png";
let arrowFlag = false;
arrowImage.onload = function () {arrowFlag = true;
};layer.on("prerender", (evt) => {let arrowGeometry = new Point(endPos);const dx = endPos[0] - lastEndPos[0];const dy = endPos[1] - lastEndPos[1];const rotation = Math.atan2(dy, dx);if (arrowFlag) {ctx.setImageStyle(new Icon({img: arrowImage,rotateWithView: true,rotation: -rotation,imgSize: [16, 16],}));}ctx.drawGeometry(arrowGeometry);
}

绘制多条动态曲线线段

经过上面的核心实现后,我们离应用还差一点。首先在多个不同的开始点与结束点坐标之中。我们如何计算一个大致的控制点 以及 如何描述一个曲线线段的运动。

仔细观察上述所需的属性。 我们将他 放置在一个类里统一管理。命名为source 表示 这个动态曲线的 数据源(有点像openlayers的设计,图层=》数据源=》?)。 但由于我们的实现是完全基于geometry变化进行处理的, 为了描述这个geometry的变化,我们往推进去的这一个feature 对他的属性挂载上这个数据源。

// 一个用于描述 flyLine 属性 的数据源
export default class FlyLineSource {constructor(options) {/*** 开始点位置经纬度表示*/this.startPos = options.startPos/*** 结束点位置经纬度表示*/this.endPos = options.endPos/*** 线段中点位置*/this.centerPos = this.linearInterpolation(this.startPos, this.endPos, 0.5)/*** 控制点位置*/this.controlPos = this.getControlPoint(this.startPos,this.endPos,this.centerPos,1.0)/*** 保存上一个结束点位置 初始则为 开始点*/this.lastEndPos = this.startPos/*** 计数器 用于分段*/this.times = 0;/*** 曲线 数组*/this.lineCoords = []/*** 箭头相关*/this.arrowImage = new Image()this.arrowImage.src = 'arrow.png'this.arrowLoad = falsethis.arrowImage.onload = () => {this.arrowLoad = true}}getControlPoint(startPos,endPos,centerPos,ratio){let xDiff = endPos[0] - startPos[0]let addX = startPos[0] + xDifflet addY = startPos[1]let controlX,controlYcontrolX = addXcontrolY = addYlet controlPos = [controlX , controlY]// this real control pos  return this.linearInterpolation(centerPos,controlPos, ratio)}getRenderState() {return {times: this.times,lineCoords: this.lineCoords,startPos: this.startPos,centerPos: this.centerPos,endPos: this.endPos,lastEndPos: this.lastEndPos,arrowImage: this.arrowImage,arrowLoad: this.arrowLoad,controlPos: this.controlPos}}setRenderState(options){for(let i in options){this[i] = options[i]            }}// 线性插值 函数 ... 此处的计算 只处理  二维  带x ,y 的 向量linearInterpolation(startPos, endPos, t) {let a = this.constantMultiVector2(1 - t, startPos)let b = this.constantMultiVector2(t, endPos)return this.vector2Add(a, b)}// 常数乘以二维向量数组 的函数constantMultiVector2(constant, vector2) {return [constant * vector2[0], constant * vector2[1]];}vector2Add(a, b) {return [a[0] + b[0], a[1] + b[1]]}}

在外部定义 生成一个特别的点 feature

 // 根据 pointPositions 生成 point FeaturesgeneratePointsFeatures() {for (let i = 0, ii = this.pointPositions.length; i < ii; i++) {let registeFeature = new Feature({geometry: new Point(fromLonLat(this.pointPositions[i][0])),flyLineSource: new FlyLineSource({startPos: this.pointPositions[i][0],endPos: this.pointPositions[i][1]})})this.pointsFeatures.push(registeFeature, new Feature({ geometry: new Point(fromLonLat(this.pointPositions[i][1])) }))}}

openlayers 绘制动态迁徙线、曲线相关推荐

  1. Path绘制动态的贝塞尔曲线、PathMeasure来绘制path动画

    上一篇的波浪曲线是左右重复平移,这次是每一帧绘制一条线,组成上下浮动的曲线,下面是效果图 public WaveView(Context context, @Nullable AttributeSet ...

  2. python画k线_python画k线,python绘制动态k线及均线

    内容导航: Q1:k线图上怎么画线 一般你看K线软件都有画图功能,以同花顺为例,在上方菜单栏找到画图,点击,会在菜单栏下方一行或者屏幕最右边一列出现相应的画图工具. Q2:求教使用python绘制K线 ...

  3. python绘制动态k线及均线,python : pyecharts 1.1.0 画K线图

    pip install pyecharts ; 会安装 pyecharts-1.1.0 画K线图 kline1.py # coding: utf-8 import os, sys from pyech ...

  4. python绘制动态k线及均线_Python绘制股票移动均线的实例

    1. 前沿 移动均线是股票最进本的指标,本文采用numpy.convolve计算股票的移动均线 2. numpy.convolve numpy.convolve(a, v, mode='full') ...

  5. MFC中绘制动态曲线

    在工控监测领域,经常需要动态绘制曲线,观察曲线的变化趋势,绘制波形图,绘制频谱等.在前面4讲中介绍了MFC经常用的TeeChart控件和Hight-Speed Chart Ctrl,这两个都是MFC绘 ...

  6. Qt学习:QtCharts绘制动态曲线,实时更新数据与坐标轴

    前言:很久之前写了这篇文章后一直未进行修改更新,最近重新整理了代码和扩展了使用方式.代码路径见github: https://github.com/LYH-ux/Qt_Projects.git ,下文 ...

  7. qlineseries绘制动态曲线_Qt绘制动态曲线

    首先*.pro文件中加一句 QT += charts 然后 mainwindow.cpp文件如下: #include "mainwindow.h" #include "u ...

  8. python画曲线-python绘制动态曲线教程

    从txt种获取数据 并且通过动态曲线显示 import numpy as np import matplotlib.pyplot as plt import matplotlib.animation ...

  9. [转载] MFC绘制动态曲线,用双缓冲绘图技术防闪烁

    转载的原文地址 先上效果图 随着时间的推移,曲线向右平移,同时X轴的时间坐标跟着更新. 一.如何绘制动态曲线 所谓动画,都是一帧一帧的图像连续呈现在用户面前形成的.所以如果你掌握了如何绘制静态曲线,那 ...

最新文章

  1. jq 解决 动画 淡入淡出,隐藏显示,多次操作BUG
  2. 在BAdI definition PRODUCT_R3_ADAPTER的implementation里获得download type
  3. 大数据告诉你,电商都把假货发给谁?
  4. JavaScript中Object.keys、Object.getOwnPropertyNames区别
  5. 钢铁侠头盔制作图纸下载_如何在10分钟内制作头盔图
  6. 金蝶记账王登录显示连接金蝶云服务器异常,金蝶KIS记账王系统初始化常见问题...
  7. atitit.MIZIAN 陕北方言 特有词汇 大词典 attilax 整理 a--g v1 q31.xlsx
  8. ztree 标准得json数据格式_Django+zTree构建组织架构树
  9. JSON: jasckson 字段 过滤
  10. vue v-for循环的用法
  11. (转载)C++:STL标准入门汇总
  12. 在java开发中关于class.getResourceAsStream(String name)与 class.getClassLoader().getResourceAsStream(String
  13. 包括8个html的网页设计作品,8个超棒的HTML5网站设计欣赏
  14. [OpenAirInterface实战-13] :OAI 基站配置文件详解
  15. vscode折叠/展开所有区域代码快捷键 注释快捷键
  16. IE浏览器提示代理服务器没有响应,但是360浏览器可以用
  17. python小游戏编程arcade----坦克动画图片合成
  18. 凸优化第一【凸集与凸优化简介】
  19. MFC-CListCtrl重绘,添加按钮到单元格
  20. epub与txt的区别是什么?有什么好用的epub阅读器

热门文章

  1. 我的博客园博客开通咯(qyl)
  2. oracle 倒库详细步骤,超详细的倒车入库步骤
  3. iOS渐变视图动画库、腰杆、音频水滴水波手势、多种对话框、四级展开效果等源码
  4. 现代企业架构师的5大特征及六种方法
  5. 计算机网盘变成红色是怎么回事,电脑小知识:硬盘变红了会带来哪些危害?
  6. uni-app生成分享图片( 使用 Painter 生成分享海报)
  7. Springboot毕业设计毕设作品,黑白图片和上色处理系统 开题报告
  8. 特斯拉自动驾驶功能更新:将上线红绿灯识别自动停车
  9. ApacheCon Asia 2022 启动,7 场阿里云大数据 +AI 议题分享等你围观
  10. Allan方差与随机误差辨识