迁徙线,动态轨迹线

 使用canvas结合贝塞尔曲线实现,效果如下

<template><div class="box"><div class="mapBox"><div class="map"><img src="/img/dataCockpit/map.png" alt="" /><div class="dot"><div class="title">地图</div><img src="/img/dataCockpit/dotIcon.png" alt="" /></div></div><!-- 线 --><canvas id="canvas" class="canvas"></canvas><!-- 点 --><canvas id="canvasPoint" class="canvas"></canvas><!-- 动态效果 --><canvas id="canvasMove" class="canvas"></canvas><img class="airport" id="airportIcon" src="" alt="" /></div></div>
</template><script>
export default {name: "homePage",data() {return {canvas: null,canvasPoint: null,canvasMove: null,center: {}, // 迁徙线起点位置directionArr: [], // 迁徙线终点位置endKeep: [], // 保存一下各个迁徙线起点end: [], // 运动中的各迁徙线时间p时所在位置p: 0, // 时间记录,每到1时变为0step: 0.005, // 时间每次递增量animationSpeed: 0.03, // 点动画效果圆圈每次增加量dotNumber: 25, // 动画迁徙线 动态的线的部分由多少个点组成rate: 1.053, // 1.033 贝塞尔曲线计算时用到的参数requestAnimationFrameName: "",compareData: [// 用于临时计算各终点位置的参数// { x: 0.65, y: 0.89 },// { x: 0.094, y: 0.76 },// { x: 0.95, y: 0.28 },// { x: 0.19, y: 0.19 },// { x: 0.49, y: 0.08 },],radius: 1, // 航路点半径radiusRing: 1,radiusRingMin: 1,radiusRingMax: 25, // 最大设为25时,涟漪消失的不会很突兀dotColor: "243,254,193",ringColor: "rgba(236,210,32,0.5)",plane: null,};},mounted() {this.plane = document.getElementById("airportIcon");this.init();},methods: {init() {// 获取需要画布达到的宽高数据const mapBox = document.getElementsByClassName("mapBox")[0];const width = mapBox.offsetWidth;const height = mapBox.offsetHeight;// 拿到三个画布,给定宽高const canvas = document.getElementById("canvas");const canvasPoint = document.getElementById("canvasPoint");const canvasMove = document.getElementById("canvasMove");canvas.width = width;canvas.height = height;canvasPoint.width = width;canvasPoint.height = height;canvasMove.width = width;canvasMove.height = height;this.canvas = canvas.getContext("2d");this.canvasPoint = canvasPoint.getContext("2d");this.canvasMove = canvasMove.getContext("2d");// 找到所有迁徙线起点,this.center = {x: Math.ceil(width * 0.66),y: Math.ceil(height * 0.74),};// 迁徙线终点this.directionArr = [{// 双湖县x: Math.ceil(width * 0.32),y: Math.ceil(height * 0.46),},{// 尼玛县x: Math.ceil(width * 0.14),y: Math.ceil(height * 0.66),},{// 申扎县x: Math.ceil(width * 0.36),y: Math.ceil(height * 0.76),},{// 班戈县x: Math.ceil(width * 0.5),y: Math.ceil(height * 0.76),},{// 安多县x: Math.ceil(width * 0.56),y: Math.ceil(height * 0.56),},{// 聂荣县x: Math.ceil(width * 0.74),y: Math.ceil(height * 0.6),},{// 巴青县县x: Math.ceil(width * 0.84),y: Math.ceil(height * 0.64),},{// 索县x: Math.ceil(width * 0.93),y: Math.ceil(height * 0.72),},{// 比如县x: Math.ceil(width * 0.8),y: Math.ceil(height * 0.72),},{// 嘉黎县x: Math.ceil(width * 0.73),y: Math.ceil(height * 0.82),},];// 各线终点 以下仅为参考,具体以项目要求为准for (let i = 0; i <= 10; i++) {// this.directionArr[i] = {//   x: Math.ceil(width * this.compareData[i].x),//   y: Math.ceil(height * this.compareData[i].y),// };this.endKeep[i] = {x: this.center.x,y: this.center.y,};}this.end = JSON.parse(JSON.stringify(this.endKeep));// 画线开始this.drawAllLine();},drawAllLine() {// 根据每个点分别画线this.directionArr.forEach((item) => {this.drawLine(item);});this.drawMove();},drawLine({ x, y }) {this.canvas.beginPath();this.canvas.moveTo(this.center.x, this.center.y); // 起始点(x,y)// 计算贝塞尔曲线控制点位置const coord = this.calcCp([x, y], [this.center.x, this.center.y]);this.canvas.quadraticCurveTo(coord.x, coord.y, x, y); //创建二次贝塞尔曲线// 线宽1this.canvas.lineWidth = 2.5;// 线颜色this.canvas.strokeStyle = "#5cb85c";this.canvas.stroke();this.canvas.closePath();},drawPoint(x1, y1) {// 最里圈小圆this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 1)`;this.canvasPoint.beginPath();this.canvasPoint.arc(x1, y1, this.radius, 0, 2 * Math.PI);this.canvasPoint.closePath();this.canvasPoint.fill();// 外层小圆this.canvasPoint.fillStyle = `rgba(${this.dotColor}, 0.3)`;this.canvasPoint.beginPath();this.canvasPoint.arc(x1, y1, this.accAdd(this.radius, 3), 0, 2 * Math.PI);this.canvasPoint.closePath();this.canvasPoint.fill();// 以下为涟漪部分if (this.radiusRing >= this.radiusRingMax) {this.radiusRing = this.radiusRingMin;}this.canvasPoint.fillStyle = this.ringColor;this.canvasPoint.beginPath();this.canvasPoint.arc(x1, y1, this.radiusRing, 0, 2 * Math.PI);this.canvasPoint.closePath();this.canvasPoint.fill();// this.radiusRing += 0.03;this.radiusRing += this.animationSpeed;this.ringColor =this.ringColor.split(",").slice(0, 3).join(",") +"," +(0.5 - (this.radiusRing - this.radiusRingMin) * 0.02) +")";},drawMivie(index) {// 获取当前时间p时贝塞尔曲线的x, y点const coord = this.calcCp([this.directionArr[index].x, this.directionArr[index].y],[this.center.x, this.center.y]);const x = this.calcRightNow(this.p,this.center.x,coord.x,this.directionArr[index].x);const y = this.calcRightNow(this.p,this.center.y,coord.y,this.directionArr[index].y);this.canvasMove.beginPath();this.canvasMove.moveTo(this.end[index].x, this.end[index].y);this.canvasMove.lineTo(x, y);const gnt1 = this.canvasMove.createLinearGradient(this.end[index].x,this.end[index].y,x,y);gnt1.addColorStop(0, "#fff");gnt1.addColorStop(1, "#ECD220");this.canvasMove.strokeStyle = gnt1;this.canvasMove.lineWidth = 1;this.canvasMove.stroke();// this.canvasMove.closePath();for (var i = 0; i < this.dotNumber; i++) {let _t =this.p - this.step * i * 2 >= 0? this.p - this.step * i * 2: 1 + (this.p - this.step * i * 2);const coord1 = this.calcCp([this.directionArr[index].x, this.directionArr[index].y],[this.center.x, this.center.y]);const x1 = this.calcRightNow(_t,this.center.x,coord1.x,this.directionArr[index].x);const y1 = this.calcRightNow(_t,this.center.y,coord1.y,this.directionArr[index].y);this.canvasMove.fillStyle ="rgba(" + this.dotColor + "," + (1 - (1 / this.dotNumber) * i) + ")";this.canvasMove.beginPath();this.canvasMove.arc(x1, y1, 1, 0, 2 * Math.PI);this.canvasMove.fill();this.canvasMove.closePath();}// 加个小飞机图标飞起来const xx = this.calcRightNow(this.p + this.step * 3,this.center.x,coord.x,this.directionArr[index].x);const yy = this.calcRightNow(this.p + this.step * 2,this.center.y,coord.y,this.directionArr[index].y);const img = this.createIcon(xx, yy, index);this.canvasMove.drawImage(img, xx - 8, yy - 8);this.end[index].x = x;this.end[index].y = y;},// 获取当前时间p时贝塞尔曲线的x, y点, 此方法不区分x ycalcRightNow(p, start, controlPoint, end) {return (Math.pow(1 - p, 2) * start +2 * p * (1 - p) * controlPoint +Math.pow(p, 2) * end);},getAngle(x, y) {var radian = Math.atan(y / x); // 弧度var angle = Math.floor(180 / (Math.PI / radian)); // 弧度转角度if (x < 0) {// x小于0的时候加上180°,即实际角度angle = angle + 180;}return angle;},createIcon(x, y, index) {const deg = this.getAngle(x - this.end[index].x, y - this.end[index].y);const c = document.createElement("canvas");c.width = 16;c.height = 16;const cCtx = c.getContext("2d");cCtx.translate(8, 8);if (y < this.end[index].y &&((Math.abs(deg) > 80 && Math.abs(deg) < 91) || (deg > 240 && deg < 270))) {cCtx.drawImage(this.plane, -8, -8);} else if (x >= this.end[index].x && y < this.end[index].y) {cCtx.rotate(((-deg + 20) * Math.PI) / 180);cCtx.drawImage(this.plane, -8, -8);cCtx.rotate(((deg - 20) * Math.PI) / 180);} else if (x < this.end[index].x && y < this.end[index].y) {cCtx.rotate(((-deg + 160) * Math.PI) / 180);cCtx.drawImage(this.plane, -8, -8);cCtx.rotate(((deg - 160) * Math.PI) / 180);} else if (x < this.end[index].x && y >= this.end[index].y) {cCtx.rotate(((-deg + 45) * Math.PI) / 180);cCtx.drawImage(this.plane, -8, -8);cCtx.rotate(((deg - 45) * Math.PI) / 180);} else {cCtx.rotate(((225 - deg) * Math.PI) / 180);cCtx.drawImage(this.plane, -8, -8);cCtx.rotate(((deg - 225) * Math.PI) / 180);}return c;},drawMove() {cancelAnimationFrame(this.requestAnimationFrameName);// 动态线的画布this.canvasMove.clearRect(0, 0, 10000, 10000);if (this.p >= 1) {this.p = this.step;this.end = JSON.parse(JSON.stringify(this.endKeep));}// 点的画布this.canvasPoint.clearRect(0, 0, 10000, 10000);this.drawPoint(this.center.x, this.center.y);this.directionArr.forEach((item, index) => {this.drawMivie(index);this.drawPoint(item.x, item.y);});this.p = this.accAdd(this.p, this.step);this.requestAnimationFrameName = requestAnimationFrame(this.drawMove);},/** num: 要被转换的数字* exnum: 当前中心坐标 不一定是x还是y*/calcCp(start, end) {let middleX = 0;let middleY = 0;if (start[0] > end[0] && start[1] > end[1]) {middleX = ((start[0] + end[0]) / 2) * this.rate;middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);}if (start[0] > end[0] && start[1] < end[1]) {middleX = ((start[0] + end[0]) / 2) * this.rate;middleY = ((start[1] + end[1]) / 2) * this.rate;}if (start[0] < end[0] && start[1] > end[1]) {middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);middleY = ((start[1] + end[1]) / 2) * (2 - this.rate);}if (start[0] < end[0] && start[1] < end[1]) {middleX = ((start[0] + end[0]) / 2) * (2 - this.rate);middleY = ((start[1] + end[1]) / 2) * this.rate;}return {x: middleX,y: middleY,};},accAdd(arg1, arg2) {let r1, r2, m;try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}m = Math.pow(10, Math.max(r1, r2));return (arg1 * m + arg2 * m) / m;},},
};
</script><style lang="scss" scoped>
.box {// background-color: #333;// height: 100vh;
}
.mapBox {// margin: 100px;position: relative;display: flex;align-items: center;justify-content: center;.map {width: 892px;height: 656px;position: relative;img {width: 100%;height: 100%;}.dot {width: 200px;display: flex;flex-direction: column;align-items: center;position: absolute;z-index: 99;top: 399px;left: 489px;animation: jump 4s infinite ease-in;.title {width: 180px;line-height: 38px;background: rgba(0, 4, 7, 0.79);border-radius: 19px;color: #ffffff;font-size: 18px;text-align: center;}img {width: 56px;}@keyframes jump {0% {top: 396px;transform: translateY(0);}50% {transform: translateY(-20%);}100% {top: 396px;transform: translateY(0);}}}}.canvas {position: absolute;top: 0;left: 0;}.airport {width: 16px;height: 16px;z-index: -1;position: absolute;}
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/885721.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLI LABS | Less-39 GET-Stacked Query Injection-Intiger Based

关注这个靶场的其它相关笔记&#xff1a;SQLI LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;过关流程 输入下面的链接进入靶场&#xff08;如果你的地址和我不一样&#xff0c;按照你本地的环境来&#xff09;&#xff1a; http://localhost/sqli-labs/Less-39/ 本关是堆…

NVM 介绍及使用指南

在日常的开发工作中&#xff0c;我们往往会遇到需要在同一台机器上同时管理多个版本的 Node.js 的情况。为了解决这个问题&#xff0c;我一个同事推荐了NVM&#xff08;Node Version Manager&#xff09;。NVM 是一个用于管理 Node.js 版本的工具&#xff0c;可以方便地在不同的…

web——[SUCTF 2019]EasySQL1——堆叠注入

这个题主要是讲述了堆叠注入的用法&#xff0c;来复现一下 什么是堆叠注入 堆叠注入&#xff1a;将多条SQL语句放在一起&#xff0c;并用分号;隔开。 1.查看数据库的名称 查看数据库名称 1;show databases; 发现有名称为ctftraining的数据库 2.对表进行查询 1;show tabl…

【ARM】MDK-烧录配置文件无权限访问

【更多软件使用问题请点击亿道电子官方网站】 1、 问题场景 客户代码编译正常、调试出现报错<Error: Flash Download failed - "Cortex-M4"> 仿真器识别正常&#xff0c;keil-Debug内显示相关信息、设备启动正常。 记录排查步骤&#xff0c;找到配置文件位…

深度解析 ICP 备案、公安备案、等保备案编号与统一社会信用代码

1. 前言 在当今数字化时代&#xff0c;各类网站和系统如雨后春笋般涌现&#xff0c;为了确保网络安全、合法运营以及用户信息保护&#xff0c;不同类型的备案工作应运而生。其中&#xff0c;ICP 备案、公安备案和等保备案尤为重要&#xff0c;它们各自对应的备案编号不仅是一串…

11个简单易用的电商购物车设计案例

文章目录 前言正文1.扁平化设计购物车2.无表格布局购物车3.美食购物车4.响应式购物车5.jQuery购物车6.动态价格更新购物车7.标签式滑动购物车8.动态商店与购物车一体化设计9.简约清爽的购物车设计10.基于Vue.js的购物车11.域名购物车 总结 前言 现在的电子商务网站&#xff0c…

turtlesim修改窗口大小;添加自己的小乌龟;

目前手边有humble版本ROS。以此为教程。其他版本以此类推 github中搜索ros&#xff0c;然后选择ros官网&#xff08;九点方阵那个图标&#xff09;。然后 在branch中&#xff0c;选择humble&#xff0c;然后复制链接。 git clone https://github.com/ros/ros_tutorials.git -…

电阻按材料分类、不同的电阻

TOC 按电阻材料工艺进行分类 3.1.线绕电阻&#xff1a; 使用高电阻率的康铜、锰铜或镍铬合金丝缠绕在陶瓷骨架&#xff08;一般采用陶瓷、塑料、涂覆绝缘层的金属骨架&#xff09;上制作而成的。玻璃釉绕线电阻表面被覆一层玻璃釉&#xff1b;涂漆线绕电阻表面被覆一层保护有…

spark的学习-06

SparkSQL读写数据的方式 1&#xff09;输入Source 方式一&#xff1a;给定读取数据源的类型和地址 spark.read.format("json").load(path) spark.read.format("csv").load(path) spark.read.format("parquet").load(path) 方式二&#xff1a…

react-markdown内容宽度溢出和换行不生效问题

情景复现&#xff1a; 解决办法&#xff0c;添加样式进行限制 /* index.css */ .markdown-container {word-break: break-word; /* 强制长单词断行 */white-space: pre-wrap; /* 保留空白符序列&#xff0c;但是正常地进行换行 */overflow-wrap: break-word; /* 在长单词或…

java双向链表解析实现双向链表的创建含代码

双向链表 一.双向链表二.创建MyListCode类实现双向链表创建一.AddFirst创建&#xff08;头插法&#xff09;二.AddLast创建&#xff08;尾叉法&#xff09;三.size四.remove(指定任意节点的首位删除)五.removeAll(包含任意属性值的所有删除)六.AddIndex(给任意位置添加一个节点…

VMWare虚拟机NAT模式下与外部主机(非宿主机)通信

VMWare虚拟机NAT模式下与外部主机(非宿主机)通信 1. VMWare虚拟机网络 VMWare的三种网络工作模式&#xff1a; Bridged&#xff1a;桥接模式NAT&#xff1a;网络地址转换模式Host-Only &#xff1a;仅主机模式 VMWare 网络连接配置界面如下&#xff1a; 在本次测试环境中&a…

Mac保护电池健康,延长电池使用寿命的好方法

使用Mac的过程中&#xff0c;如何延长电池的使用寿命是大家非常关心的问题&#xff0c;而养成一个良好的充电习惯能够有效的延长电池的使用寿命 避免过度充电和过度放电能够有效的保护电池&#xff0c;因此长时间的充电与长时间放点都不可取&#xff0c;但是在日常的使用过程中…

显示器接口种类 | 附图片

显示器接口类型主要包括VGA、DVI、HDMI、DP和USB Type-C等。 VGA、DVI、HDMI、DP和USB Type-C 1. 观察 VGA接口:15针 DP接口&#xff1a;在DP接口旁&#xff0c;都有一个“D”型的标志。 电脑主机&#xff1a;DP(D) 显示器&#xff1a;VGA(15针) Ref https://cloud.tenc…

qt QUndoCommand 与 QUndoStack详解

1、概述 QUndoCommand 和 QUndoStack 是 Qt 框架中用于实现撤销/重做&#xff08;undo/redo&#xff09;功能的两个核心类。QUndoCommand 是表示单个可撤销操作的基类&#xff0c;而 QUndoStack 则负责管理这些命令的堆栈&#xff0c;提供撤销和重做操作的接口。 QUndoCommand…

双指针(二)双指针到底是怎么个事

一.有效的三角形个数 有效的三角形个数 class Solution {public int triangleNumber(int[] nums) {Arrays.sort(nums);int i0,end nums.length-1;int count 0;for( i end;i>2;i--){int left 0;int right i-1;while(left<right){if(nums[left]nums[right]>nums…

springboot的增删改查商城小实践(b to c)

首先准备一张表&#xff0c;根据业务去设计表 订单编号是参与业务的&#xff0c;他那订单编号里面是有特殊意义的&#xff0c;比如说像什么一些年月日什么的&#xff0c;一些用户的ID都在那编号里面呢&#xff1f;不能拿这种东西当主件啊 根据数据量去决定数据类型 价格需要注意…

动态规划-背包问题——416.分割等和子集

1.题目解析 题目来源 416.分割等和子集——力扣 测试用例 2.算法原理 1.状态表示 这里背包问题基本上和母题的思路大相径庭&#xff0c;母题请见 [模板]01.背包 &#xff0c;这里的状态表示与装满背包的情况类似&#xff0c;第二个下标就是当选择的物品体积直接等于j时是否可…

开源 - Ideal库 -获取特殊时间扩展方法(四)

书接上回&#xff0c;我们继续来分享一些关于特殊时间获取的常用扩展方法。 01、获取当前日期所在月的第一个指定星期几 该方法和前面介绍的获取当前日期所在周的第一天&#xff08;周一&#xff09;核心思想是一样的&#xff0c;只是把求周一改成求周几而已&#xff0c;当然其…

移位寄存器设计—FDRE、SRL16E及原语约束

信号处理中&#xff0c;实现数据对齐时&#xff0c;常常对单bit或多bit信号进行打拍操作&#xff0c;这个可以通过移位寄存器实现&#xff0c;SLICEM中的SRL即为移位寄存器。 这里主要记录下不同写法的效果。 1 //同步复位2 module static_multi_bit_sreg_poor #(3 parame…