首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >天幕:六边形特效

天幕:六边形特效

作者头像
Jimmy_is_jimmy
发布2023-03-09 19:58:59
发布2023-03-09 19:58:59
8710
举报
文章被收录于专栏:call_me_Rcall_me_R

如题,我们将主要用 Canvas 来实现一个六边形布满天空的效果,如下:

前言

最近《三体》比较火热,本效果的创作灵感来自其片尾:

智子 2 号二维已经成功展开,将对人类进行实时监控

功能有:

  1. 绘制六边形
  2. 六边形效果
  3. 鼠标交互效果

其中,第 1 点是重点,详细讲解。

绘制六边形

绘制六边形,思路如下:

1. 找到六边形的点

我们使用到三角形的知识点 - 正弦(sine)sin(θ), 余弦(cosine)con(θ) 求距离。

应用到六边形上,我们以六边形的中心为圆心画圆,就可以很直观得观察到,如下:

假设我们设置圆心坐标为 (0, 0),圆的半径为 r,那么我们将得到右下角的点坐标为 (cos(360 / 6 / 2 deg) * r, sin(360 / 6 / 2 deg) * r)。同理,我们可以得到其他 5 个点的的坐标。相关代码如下:

代码语言:javascript
复制
/*
* x, y 为原点坐标
* r 为圆的半径
*/
function locate(x, y, r) {
  // locate hexagon point
  for(let i = 0; i < 6; i += 1) {
    particlePosition.push({
      x: x + Math.cos(Math.PI / 6 * (1 + 2 * i))*r,
      y: y + Math.sin(Math.PI / 6 * (1 + 2 * i))*r
    })
  }
}

2. 将点连线

我们定位到六边形的点之后,遍历这些点,将两点距离大于等于 r - 1 且小于等于 r + 1 的点连接起来。

代码语言:javascript
复制
for(let i = 0; i < particlePosition.length; i += 1) {
  for(let j = 0; j < particlePosition.length; j += 1) {
    let dx = particlePosition[i].x - particlePosition[j].x;
    let dy = particlePosition[i].y - particlePosition[j].y;
    let distance = Math.sqrt(dx * dx + dy * dy);
    
    if(distance >= (radius - 1) &amp;&amp; distance <= (radius + 1)) {
      // connect points
    }
  }
}

这里的判断规则为 distance >= (radius - 1) &amp;&amp; distance <= (radius + 1),读者可以自行更改。笔者这里以 1 为偏偏移值,是因为计算出来的两点距离不绝对等于 radius 值。

连线之后,效果如下图:

六边形效果

细心的读者,看到片头的 GIF 图就会发现六边形上的线条效果和六边形图片效果。

线条效果

这里使用的是 canvas 的线性渐变函数 createLinearGradient 来实现:

代码语言:javascript
复制
let randomArr = [Math.random(), Math.random(), Math.random()];
randomArr.sort(function(a,b){
  return a-b;
});
gradient = context.createLinearGradient(particlePosition[i].x, particlePosition[i].y, particlePosition[j].x, particlePosition[j].y);
gradient.addColorStop(randomArr[0], 'red');
gradient.addColorStop(randomArr[1], 'fuchsia');
gradient.addColorStop(randomArr[2], 'purple');
context.strokeStyle = gradient;

context.lineWidth = 1;
context.beginPath();
context.moveTo(particlePosition[i].x, particlePosition[i].y);
context.lineTo(particlePosition[j].x, particlePosition[j].y);
context.stroke();

六边形图片效果

六边形图片效果,本来是用 canvasclip 这个 api 去实现的,但是发现在本案例实现起来,翻车了,页面卡死了,故选择了操作 img 节点结合 css 来实现:

代码语言:javascript
复制
<img id="img" src="" alt="img"/>
代码语言:javascript
复制
#img{
  position: absolute;
  top: -100%;
  left: -100%;
  display: block;
  width: 100px;
  height: 100px;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  -webkit-clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  z-index: 99;
}
代码语言:javascript
复制
let imgDom = document.getElementById('img');
imgDom.style.width = Math.cos(Math.PI / 6) * radius * 2 + 'px';
imgDom.style.height = radius * 2 +'px';

鼠标交互

实现的鼠标交互的效果是:当鼠标移动时候,计算鼠标位置和圆心位置距离最近的点进行定位并绘制当前的六边形

代码语言:javascript
复制
// position image
imgDom.src = currentTarget.img.src;
imgDom.style.left = currentTarget.x - Math.cos(Math.PI / 6) * radius + "px";
imgDom.style.top = currentTarget.y - radius + "px";
// draw hexagon
context.beginPath();
for(let i = 0; i < 6; i += 1) {
  if(i === 0) {
    context.moveTo(currentTarget.x + Math.cos(Math.PI / 6)*radius, currentTarget.y + Math.sin(Math.PI / 6)*radius);
  } else {
    context.lineTo(currentTarget.x + Math.cos(Math.PI / 6 * (1 + 2 * i))*radius, currentTarget.y + Math.sin(Math.PI / 6 * (1 + 2 * i))*radius);
  }
}
context.closePath();

后文

当然,我们还考虑了六边形图片随机播放圆心的计算等问题。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 绘制六边形
  • 六边形效果
  • 鼠标交互
  • 后文
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档