canvas绘制经典星空连线效果
来自:https://segmentfault.com/a/1190000009675230

下面开始coding:
先写个canvas标签
<canvas height="620" width="1360" id="canvas"></canvas>
加上一些默认的样式:
*{
    margin:0;
    padding:0;
}
body{
    overflow: hidden;
}
这里的overflow:hidden是为了防止出现滚动条
下面开始写JS:
首先我们要得到那个 canvas 并得到绘制上下文:
var canvasEl = document.getElementById('canvas');
var ctx = canvasEl.getContext('2d');
var mousePos = [0, 0];
紧接着我们声明两个变量,分别用于存储“星星”和边:
var nodes = [];
var edges = [];
然后我们定义一些其他的变量:
var easingFactor = 5.0;  //缓动因子
var backgroundColor = '#000'; //背景颜色
var nodeColor = '#fff'; //点颜色
var edgeColor = '#fff'; //边颜色
var pageWidth = window.innerWidth, //窗口宽度
    pageHeight = window.innerHeight; //窗口高度
设置画布的大小铺满整个屏幕:
window.onresize = function () {
    canvasEl.width = pageWidth;
    canvasEl.height = pageHeight;
    if (nodes.length == 0) {
        constructNodes();
    }
    render();
};
window.onresize(); 
准备工作完成,我们要开始构建点了:
function constructNodes() {
    for (var i = 0; i < 100; i++) {
        var node = {
            drivenByMouse: i == 0,
            x: Math.random() * canvasEl.width,
            y: Math.random() * canvasEl.height,
            vx: Math.random() * 1 - 0.5,
            vy: Math.random() * 1 - 0.5,
            radius: Math.random() > 0.9 ? 3 + Math.random() * 3 : 1 + Math.random() * 3
        };
        nodes.push(node);
    }
    nodes.forEach(function (e) {
        nodes.forEach(function (e2) {
            if (e == e2) {
                return;
            }
            var edge = {
                from: e,
                to: e2
            }
            addEdge(edge);
        });
    });
}
先创建100个点,每个点设置6个属性,drivenByMouse属性只有第一个点为true,其他的点为false,第一个点作为鼠标跟随点,不显示出来,可以与其他点连线。x,y作为点的初始位置,取得是画布内的随机点,vx,vy表示点的初始速度,范围为-0.5到0.5之间的随机数,radius表示点的半径,大部分的点为小的,少数的点为大的。
点都构建完毕了,就要构建点与点之间的连线了,我们用到双重遍历,把两个点捆绑成一组,放到 edges 数组中。注意这里我用了另外一个函数来完成这件事,而没有直接用 edges.push() ,为什么?
假设我们之前连接了 A、B两点,也就是外侧循环是A,内侧循环是B,那么在下一次循环中,外侧为B,内侧为A,是不是也会创建一条边呢?而实际上,这两个边除了方向不一样以外是完全一样的,这完全没有必要而且占用资源。因此我们在 addEdge 函数中进行一个判断:
function addEdge(edge) {
    var ignore = false;
    edges.forEach(function (e) {
        if (e.from == edge.from & e.to == edge.to) {
            ignore = true;
        }
        if (e.to == edge.from & e.from == edge.to) {
            ignore = true;
        }
    });
    if (!ignore) {
        edges.push(edge);
    }
}
至此,我们的准备工作就完毕了,下面我们要让点动起来:
function step() {
    nodes.forEach(function (e) {
        if (e.drivenByMouse) {
            return;
        }
        e.x += e.vx;
        e.y += e.vy;
        function clamp(min, max, value) {
            if (value > max) {
                return max;
            } else if (value < min) {
                return min;
            } else {
                return value;
            }
        }
        if (e.x <= 0 || e.x >= canvasEl.width) {
            e.vx *= -1;
            e.x = clamp(0, canvasEl.width, e.x)
        }
        if (e.y <= 0 || e.y >= canvasEl.height) {
            e.vy *= -1;
            e.y = clamp(0, canvasEl.height, e.y)
        }
    });
    adjustNodeDrivenByMouse();
    render();
    window.requestAnimationFrame(step);
}
function adjustNodeDrivenByMouse() {
    nodes[0].x += (mousePos[0] - nodes[0].x) / easingFactor;
    nodes[0].y += (mousePos[1] - nodes[0].y) / easingFactor;
}
这段代码就是遍历粒子,并且更新其状态。根据一个简单的物理公式 s = s + v,每次执行都会 更新到点的下一步的状态。adjustNodeDrivenByMouse函将第一个点作为鼠标的跟随点,easingFactor为缓动因子可以让点的运动比鼠标运动的稍慢一点。
然后我们要让整个粒子系统连续地运转起来就需要一个timer了,但是十分不提倡大家使用 setInterval,而是尽可能使用 requestAnimationFrame,它能保证你的帧率锁定在当前浏览器的频率下,一般为60HZ。
剩下的就是绘制了
function render() {
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);
    edges.forEach(function (e) {
        var l = lengthOfEdge(e);
        var threshold = canvasEl.width / 8;
        if (l > threshold) {
            return;
        }
        ctx.strokeStyle = edgeColor;
        ctx.lineWidth = (1.0 - l / threshold) * 2.5;
        ctx.globalAlpha = 1.0 - l / threshold;
        ctx.beginPath();
        ctx.moveTo(e.from.x, e.from.y);
        ctx.lineTo(e.to.x, e.to.y);
        ctx.stroke();
    });
    ctx.globalAlpha = 1.0;
    nodes.forEach(function (e) {
        if (e.drivenByMouse) {
            return;
        }
        ctx.fillStyle = nodeColor;
        ctx.beginPath();
        ctx.arc(e.x, e.y, e.radius, 0, 2 * Math.PI);
        ctx.fill();
    });
}
function lengthOfEdge(edge) {
    return Math.sqrt(Math.pow((edge.from.x - edge.to.x), 2) + Math.pow((edge.from.y - edge.to.y), 2));
}
绘制的时候我们要判断线的长度如果大于某一个值,则不绘制该线了,如果在范围之内粗细,与颜色的透明度都与线的长度相关,点除了第一个鼠标跟随点,其他的画入即可。
最后加入鼠标移动事件,启动定时器:
window.onmousemove = function (e) {
    mousePos[0] = e.clientX;
    mousePos[1] = e.clientY;
}
window.requestAnimationFrame(step);
大功告成!!
canvas绘制经典星空连线效果的更多相关文章
- canvas绘制经典折线图(一)
		
最终效果图如下: 实现步骤如下:注-引用了jQuery HTML代码 <!doctype html> <html lang="en"> <head&g ...
 - canvas实现粒子星空连线
		
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title>离 ...
 - Canvas学习:封装Canvas绘制基本图形API
		
Canvas学习:封装Canvas绘制基本图形API Canvas Canvas学习 从前面的文章中我们了解到,通过Canvas中的CanvasRenderingContext2D对象中的属性和方 ...
 - 使用 HTML5 Canvas 绘制出惊艳的水滴效果
		
HTML5 在不久前正式成为推荐标准,标志着全新的 Web 时代已经来临.在众多 HTML5 特性中,Canvas 元素用于在网页上绘制图形,该元素标签强大之处在于可以直接在 HTML 上进行图形操作 ...
 - Android自定义控件 -Canvas绘制折线图(实现动态报表效果)
		
有时候我们在项目中会遇到使用折线图等图形,Android的开源项目中为我们提供了很多插件,但是很多时候我们需要根据具体项目自定义这些图表,这一篇文章我们一起来看看如何在Android中使用Canvas ...
 - canvas绘制线和矩形
		
###canvas绘制矩形 HTML中的元素canvas只支持一种原生的图形绘制:矩形.所有其他的图形的绘制都至少需要生成一条路径 1.绘制矩形 canvas提供了三种方法绘制矩形: ----> ...
 - canvas绘制图像轮廓效果
		
在2d图形可视化开发中,经常要绘制对象的选中效果. 一般来说,表达对象选中可以使用边框,轮廓或者发光的效果. 发光的效果,可以使用canvas的阴影功能,比较容易实现,此处不在赘述. 绘制边框 绘制 ...
 - HTML5学习总结——canvas绘制象棋(canvas绘图)
		
一.HTML5学习总结——canvas绘制象棋 1.第一次:canvas绘制象棋(笨方法)示例代码: <!DOCTYPE html> <html> <head> & ...
 - 使用canvas绘制时钟
		
使用canvas绘制时钟 什么使canvas呢?HTML5 <canvas> 元素用于图形的绘制,通过脚本 (通常是JavaScript)来完成.<canvas> 标签只是图 ...
 
随机推荐
- .Net Core配置与自动更新
			
.Net Core 将之前Web.Config中的配置迁移到了appsettings.json文件中,并使用ConfigurationBuilder来读取这个配置文件.并可设置在配置文件变化以后,自动 ...
 - BZOJ4710 分特产
			
题目链接:戳我 容斥题. 设\(f[i]\)表示至多有i个人能够分到(也就是至少n-i个人分不到)的方案数 \(f[i]=\prod_{j=1}^mC_{a[j]+i-1}^i-1\) a[j]表示的 ...
 - C++中cin输入问题
			
场景:cin输入一个整数,但是在console输入了其他如字符,字符串.当判断输入错误再重新输入时[ 如while()内重复判断知道输入格式正确 ],可能进入死循环. 解释:console输入时所按的 ...
 - Mac 上安装python3
			
1.安装包管理器 去包管理器官网按照提示安装包管理器 Homebrew 2.安装python3 安装完Homebrew ,输入指令安装python3 brew install python3 3.安装 ...
 - “全栈2019”Java第四十二章:静态代码块与初始化顺序
			
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
 - linux命令提示符[root@localhost ~]#详解
			
[root@localhost ~]# 1. @之前代表当前登录用户 在Linux中管理员用户是root,还有一些普通用户: 在此例中,root代表当前登录用户 2. @之后代表当前计算机主机 ...
 - Kid的某些跳刺套路
			
需要按二段方向键的跳跃: 中途松开方向键的跳跃: 中途按下方向键的跳跃: 意想不到的小跳(如果上方有墙,小跳比大跳磕头跳的更远)(kid站的是最后的位置): 意想不到的小跳*2: 意想不到的小跳*3( ...
 - NOI2014起床困难综合症
			
Description 21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳.作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争.通过研究相关文献,他找 ...
 - Ionic——下一代 APP 开发框架
			
http://www.tuicool.com/articles/iY3ENvY 最近 Facebook React 团队释出了 React Native, 用来构建 Mobile Native 应用. ...
 - linux命令行添加图形化界面
			
安装一个图形化的包即可!! yum update grub2-common yum install fwupdate-efi yum groupinstall "GNOME Desktop& ...